node-opcua-address-space 2.181.1 → 2.182.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/helpers/ensure_secure_access.ts +53 -2
- package/api/index.ts +30 -1
- package/api/loader/generateAddressSpaceRaw.ts +44 -139
- package/api/loader/load_nodeset2.ts +5 -0
- package/api/loader/narrow_instance_datatypes.ts +264 -0
- package/api/loader/nodeset_builtin_formats.ts +143 -0
- package/api/loader/nodeset_format.ts +153 -0
- package/api/loader/nodeset_image.ts +37 -4
- package/api/loader/nodeset_image_codec.ts +153 -34
- package/api/loader/nodeset_record.ts +26 -2
- package/api/loader/nodeset_record_applier.ts +54 -3
- package/api/loader/nodeset_records_to_xml.ts +620 -0
- package/api/loader/nodeset_source.ts +185 -26
- package/api/loader/nodeset_to_image.ts +16 -5
- package/api/loader/nodeset_xml_header.ts +119 -0
- package/api/loader/nodeset_xml_primitives.ts +209 -0
- package/api/loader/nodeset_xml_producer.ts +61 -2
- package/api/loader/parsers/variant_parser.ts +9 -0
- package/bin/nodeset_equivalence.js +750 -0
- package/bin/nodeset_tool_common.js +72 -0
- package/bin/opcua-nodeset-image.js +93 -103
- package/bin/opcua-nodeset-live.js +139 -0
- package/dist/api/helpers/ensure_secure_access.d.ts +20 -0
- package/dist/api/helpers/ensure_secure_access.js +39 -0
- package/dist/api/helpers/ensure_secure_access.js.map +1 -1
- package/dist/api/index.d.ts +7 -2
- package/dist/api/index.js +21 -1
- package/dist/api/index.js.map +1 -1
- package/dist/api/loader/generateAddressSpaceRaw.d.ts +1 -7
- package/dist/api/loader/generateAddressSpaceRaw.js +32 -125
- package/dist/api/loader/generateAddressSpaceRaw.js.map +1 -1
- package/dist/api/loader/load_nodeset2.js +4 -0
- package/dist/api/loader/load_nodeset2.js.map +1 -1
- package/dist/api/loader/narrow_instance_datatypes.d.ts +30 -0
- package/dist/api/loader/narrow_instance_datatypes.js +217 -0
- package/dist/api/loader/narrow_instance_datatypes.js.map +1 -0
- package/dist/api/loader/nodeset_builtin_formats.d.ts +27 -0
- package/dist/api/loader/nodeset_builtin_formats.js +133 -0
- package/dist/api/loader/nodeset_builtin_formats.js.map +1 -0
- package/dist/api/loader/nodeset_format.d.ts +123 -0
- package/dist/api/loader/nodeset_format.js +63 -0
- package/dist/api/loader/nodeset_format.js.map +1 -0
- package/dist/api/loader/nodeset_image.d.ts +17 -1
- package/dist/api/loader/nodeset_image.js +32 -4
- package/dist/api/loader/nodeset_image.js.map +1 -1
- package/dist/api/loader/nodeset_image_codec.d.ts +4 -0
- package/dist/api/loader/nodeset_image_codec.js +142 -53
- package/dist/api/loader/nodeset_image_codec.js.map +1 -1
- package/dist/api/loader/nodeset_record.d.ts +26 -2
- package/dist/api/loader/nodeset_record.js +11 -2
- package/dist/api/loader/nodeset_record.js.map +1 -1
- package/dist/api/loader/nodeset_record_applier.js +52 -3
- package/dist/api/loader/nodeset_record_applier.js.map +1 -1
- package/dist/api/loader/nodeset_records_to_xml.d.ts +16 -0
- package/dist/api/loader/nodeset_records_to_xml.js +536 -0
- package/dist/api/loader/nodeset_records_to_xml.js.map +1 -0
- package/dist/api/loader/nodeset_source.d.ts +68 -7
- package/dist/api/loader/nodeset_source.js +157 -20
- package/dist/api/loader/nodeset_source.js.map +1 -1
- package/dist/api/loader/nodeset_to_image.d.ts +4 -4
- package/dist/api/loader/nodeset_to_image.js +14 -4
- package/dist/api/loader/nodeset_to_image.js.map +1 -1
- package/dist/api/loader/nodeset_xml_header.d.ts +22 -0
- package/dist/api/loader/nodeset_xml_header.js +113 -0
- package/dist/api/loader/nodeset_xml_header.js.map +1 -0
- package/dist/api/loader/nodeset_xml_primitives.d.ts +37 -0
- package/dist/api/loader/nodeset_xml_primitives.js +196 -0
- package/dist/api/loader/nodeset_xml_primitives.js.map +1 -0
- package/dist/api/loader/nodeset_xml_producer.js +56 -1
- package/dist/api/loader/nodeset_xml_producer.js.map +1 -1
- package/dist/api/loader/parsers/variant_parser.js +7 -0
- package/dist/api/loader/parsers/variant_parser.js.map +1 -1
- package/dist/impl/_instantiate_helpers.js +1 -0
- package/dist/impl/_instantiate_helpers.js.map +1 -1
- package/dist/impl/alarms_and_conditions/ua_alarm_condition_impl.js +4 -0
- package/dist/impl/alarms_and_conditions/ua_alarm_condition_impl.js.map +1 -1
- package/dist/impl/alarms_and_conditions/ua_certificate_expiration_alarm_impl.js +14 -0
- package/dist/impl/alarms_and_conditions/ua_certificate_expiration_alarm_impl.js.map +1 -1
- package/dist/impl/base_node_impl.d.ts +9 -0
- package/dist/impl/base_node_impl.js +11 -0
- package/dist/impl/base_node_impl.js.map +1 -1
- package/dist/impl/base_node_private.js +67 -97
- package/dist/impl/base_node_private.js.map +1 -1
- package/dist/impl/nodeset_tools/construct_namespace_dependency.js +5 -1
- package/dist/impl/nodeset_tools/construct_namespace_dependency.js.map +1 -1
- package/dist/impl/nodeset_tools/nodeset_to_records.js +61 -15
- package/dist/impl/nodeset_tools/nodeset_to_records.js.map +1 -1
- package/dist/impl/nodeset_tools/nodeset_to_xml.d.ts +1 -8
- package/dist/impl/nodeset_tools/nodeset_to_xml.js +174 -190
- package/dist/impl/nodeset_tools/nodeset_to_xml.js.map +1 -1
- package/dist/impl/ua_data_type_impl.d.ts +21 -0
- package/dist/impl/ua_data_type_impl.js +27 -2
- package/dist/impl/ua_data_type_impl.js.map +1 -1
- package/dist/impl/ua_object_type_impl.d.ts +9 -0
- package/dist/impl/ua_object_type_impl.js +12 -1
- package/dist/impl/ua_object_type_impl.js.map +1 -1
- package/dist/impl/ua_variable_impl.js +27 -10
- package/dist/impl/ua_variable_impl.js.map +1 -1
- package/dist/impl/ua_variable_impl_ext_obj.js +19 -2
- package/dist/impl/ua_variable_impl_ext_obj.js.map +1 -1
- package/dist/impl/ua_variable_type_impl.d.ts +8 -1
- package/dist/impl/ua_variable_type_impl.js +39 -2
- package/dist/impl/ua_variable_type_impl.js.map +1 -1
- package/dist/tsconfig_base.tsbuildinfo +1 -1
- package/distNodeJS/generate_address_space.js +4 -0
- package/distNodeJS/generate_address_space.js.map +1 -1
- package/impl/_instantiate_helpers.ts +3 -0
- package/impl/alarms_and_conditions/ua_alarm_condition_impl.ts +10 -0
- package/impl/alarms_and_conditions/ua_certificate_expiration_alarm_impl.ts +17 -0
- package/impl/base_node_impl.ts +15 -0
- package/impl/base_node_private.ts +97 -146
- package/impl/nodeset_tools/construct_namespace_dependency.ts +5 -1
- package/impl/nodeset_tools/nodeset_to_records.ts +66 -16
- package/impl/nodeset_tools/nodeset_to_xml.ts +127 -160
- package/impl/ua_data_type_impl.ts +40 -2
- package/impl/ua_object_type_impl.ts +16 -3
- package/impl/ua_variable_impl.ts +27 -10
- package/impl/ua_variable_impl_ext_obj.ts +20 -2
- package/impl/ua_variable_type_impl.ts +48 -2
- package/package.json +29 -27
- package/source_nodejs/generate_address_space.ts +7 -1
|
@@ -23,12 +23,29 @@ import { type LocalizedText, makeAccessLevelFlag, NodeClass, QualifiedName } fro
|
|
|
23
23
|
import { make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
|
|
24
24
|
import type { ExtensionObject } from "node-opcua-extension-object";
|
|
25
25
|
import { NodeId, NodeIdType } from "node-opcua-nodeid";
|
|
26
|
+
import { StatusCodes } from "node-opcua-status-code";
|
|
26
27
|
import { EnumDefinition, StructureDefinition, StructureType } from "node-opcua-types";
|
|
27
28
|
import { isNullOrUndefined, lowerFirstLetter } from "node-opcua-utils";
|
|
28
29
|
import { DataType, Variant, VariantArrayType } from "node-opcua-variant";
|
|
29
30
|
import XMLWriter from "xml-writer";
|
|
30
31
|
import { makeDefinitionMap } from "../../api/loader/decode_xml_extension_object.js";
|
|
31
32
|
import type { DefinitionMap2 } from "../../api/loader/make_xml_extension_object_parser.js";
|
|
33
|
+
import {
|
|
34
|
+
_dumpLocalizedText,
|
|
35
|
+
_dumpNodeId,
|
|
36
|
+
_dumpQualifiedName,
|
|
37
|
+
_dumpXmlElement,
|
|
38
|
+
b,
|
|
39
|
+
coerceInt64ToInt32,
|
|
40
|
+
findXsdNamespaceUri,
|
|
41
|
+
getPrefix,
|
|
42
|
+
initXmlWriterEx,
|
|
43
|
+
int64ToDecimalString,
|
|
44
|
+
makeTypeXsd,
|
|
45
|
+
n,
|
|
46
|
+
restoreDefaultNamespace,
|
|
47
|
+
startElementEx
|
|
48
|
+
} from "../../api/loader/nodeset_xml_primitives.js";
|
|
32
49
|
import { SessionContext } from "../../api/session_context.js";
|
|
33
50
|
import type { XmlWriter } from "../../api/xml_writer.js";
|
|
34
51
|
import { BaseNodeImpl, getReferenceType } from "../base_node_impl.js";
|
|
@@ -46,6 +63,8 @@ import {
|
|
|
46
63
|
} from "./construct_namespace_dependency.js";
|
|
47
64
|
import { type NodesetWalkEvent, namespaceToWalkEvents, nodeToWalkEvents, sortByNodeId } from "./nodeset_to_records.js";
|
|
48
65
|
|
|
66
|
+
export { coerceInt64ToInt32, initXmlWriterEx, makeTypeXsd } from "../../api/loader/nodeset_xml_primitives.js";
|
|
67
|
+
|
|
49
68
|
const debugLog = make_debugLog("nodeset_to_xml");
|
|
50
69
|
const warningLog = make_warningLog("nodeset_to_xml");
|
|
51
70
|
const errorLog = make_errorLog("nodeset_to_xml");
|
|
@@ -70,28 +89,6 @@ function _dumpDescription(xw: XmlWriter, node: { description?: LocalizedText }):
|
|
|
70
89
|
}
|
|
71
90
|
}
|
|
72
91
|
|
|
73
|
-
function translateNodeId(xw: XmlWriter, nodeId: NodeId): NodeId {
|
|
74
|
-
assert(nodeId instanceof NodeId);
|
|
75
|
-
const nn = xw.translationTable.get(nodeId.namespace);
|
|
76
|
-
const translatedNode = new NodeId(nodeId.identifierType, nodeId.value, nn);
|
|
77
|
-
return translatedNode;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function n(xw: XmlWriter, nodeId: NodeId): string {
|
|
81
|
-
return translateNodeId(xw, nodeId).toString().replace("ns=0;", "");
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function translateBrowseName(xw: XmlWriter, browseName: QualifiedName): QualifiedName {
|
|
85
|
-
assert(browseName instanceof QualifiedName);
|
|
86
|
-
const nn = xw.translationTable.get(browseName.namespaceIndex);
|
|
87
|
-
const translatedBrowseName = new QualifiedName({ namespaceIndex: nn, name: browseName.name });
|
|
88
|
-
return translatedBrowseName;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function b(xw: XmlWriter, browseName: QualifiedName): string {
|
|
92
|
-
return translateBrowseName(xw, browseName).toString().replace("ns=0;", "");
|
|
93
|
-
}
|
|
94
|
-
|
|
95
92
|
function hasHigherPriorityThan(namespaceIndex1: number, namespaceIndex2: number, priorityTable: number[]) {
|
|
96
93
|
const order1 = priorityTable[namespaceIndex1];
|
|
97
94
|
const order2 = priorityTable[namespaceIndex2];
|
|
@@ -129,6 +126,7 @@ function _dumpReferences(xw: XmlWriter, node: BaseNode) {
|
|
|
129
126
|
// below already tolerates it being absent.
|
|
130
127
|
const connectsToReferenceType = addressSpace.findReferenceType("ConnectsTo");
|
|
131
128
|
const hasEventSourceReferenceType = _mustFindReferenceType(addressSpace, "HasEventSource");
|
|
129
|
+
const hierarchicalReferencesType = _mustFindReferenceType(addressSpace, "HierarchicalReferences");
|
|
132
130
|
|
|
133
131
|
function referenceToKeep(reference: UAReference): boolean {
|
|
134
132
|
// resolved here: a reference nobody has looked at since the load carries only the NodeId of its type
|
|
@@ -165,10 +163,26 @@ function _dumpReferences(xw: XmlWriter, node: BaseNode) {
|
|
|
165
163
|
} else if (referenceType.isSubtypeOf(organizesReferencesType) && !reference.isForward) {
|
|
166
164
|
// Organizes inverse — the current node is organized by an external folder
|
|
167
165
|
return true;
|
|
166
|
+
} else if (referenceType.isSubtypeOf(organizesReferencesType) && reference.isForward) {
|
|
167
|
+
// Organizes forward into another namespace: the target lives in a document this export
|
|
168
|
+
// does not write, so nothing else will ever declare this reference and dropping it
|
|
169
|
+
// loses it outright. Within our own namespace the target declares the inverse and this
|
|
170
|
+
// end stays silent, exactly as the Aggregates branch above decides it
|
|
171
|
+
return targetedNamespaceIndex !== node.nodeId.namespace;
|
|
168
172
|
} else if (connectsToReferenceType && referenceType.isSubtypeOf(connectsToReferenceType) && reference.isForward) {
|
|
169
173
|
return true;
|
|
170
174
|
} else if (referenceType.isSubtypeOf(hasEventSourceReferenceType) && reference.isForward) {
|
|
171
175
|
return true;
|
|
176
|
+
} else if (referenceType.isSubtypeOf(hierarchicalReferencesType)) {
|
|
177
|
+
// a hierarchical reference type the branches above do not name: a companion
|
|
178
|
+
// specification's own, such as Robotics' Requires, Controls and HasSafetyStates. Those
|
|
179
|
+
// fell through to `return false` and were dropped in both directions, so a nodeset that
|
|
180
|
+
// defines its own hierarchical reference type lost every reference that used it.
|
|
181
|
+
// Decided the way Aggregates is decided just above: this end writes the reference
|
|
182
|
+
// unless the other end has the higher priority and will write it instead
|
|
183
|
+
return reference.isForward
|
|
184
|
+
? !_hasHigherPriorityThan(xw, targetedNamespaceIndex, node.nodeId.namespace)
|
|
185
|
+
: targetedNamespaceIndex !== node.nodeId.namespace;
|
|
172
186
|
}
|
|
173
187
|
return false;
|
|
174
188
|
}
|
|
@@ -192,36 +206,6 @@ function _dumpReferences(xw: XmlWriter, node: BaseNode) {
|
|
|
192
206
|
}
|
|
193
207
|
xw.endElement();
|
|
194
208
|
}
|
|
195
|
-
function _dumpLocalizedText(xw: XmlWriter, v: LocalizedText) {
|
|
196
|
-
const uax = getPrefix(xw, "http://opcfoundation.org/UA/2008/02/Types.xsd");
|
|
197
|
-
if (v.locale?.length) {
|
|
198
|
-
xw.startElement(`${uax}Locale`);
|
|
199
|
-
xw.text(v.locale);
|
|
200
|
-
xw.endElement();
|
|
201
|
-
}
|
|
202
|
-
xw.startElement(`${uax}Text`);
|
|
203
|
-
if (v.text) {
|
|
204
|
-
xw.text(v.text);
|
|
205
|
-
}
|
|
206
|
-
xw.endElement();
|
|
207
|
-
}
|
|
208
|
-
function _dumpQualifiedName(xw: XmlWriter, v: QualifiedName) {
|
|
209
|
-
const uax = getPrefix(xw, "http://opcfoundation.org/UA/2008/02/Types.xsd");
|
|
210
|
-
const t = translateBrowseName(xw, v);
|
|
211
|
-
if (t.name) {
|
|
212
|
-
xw.startElement(`${uax}Name`);
|
|
213
|
-
xw.text(t.name);
|
|
214
|
-
xw.endElement();
|
|
215
|
-
}
|
|
216
|
-
if (t.namespaceIndex) {
|
|
217
|
-
xw.startElement(`${uax}NamespaceIndex`);
|
|
218
|
-
xw.text(t.namespaceIndex.toString());
|
|
219
|
-
xw.endElement();
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
function _dumpXmlElement(xw: XmlWriter, v: string) {
|
|
223
|
-
xw.text(v);
|
|
224
|
-
}
|
|
225
209
|
/*
|
|
226
210
|
<uax:ExtensionObject>
|
|
227
211
|
<uax:TypeId>
|
|
@@ -239,78 +223,6 @@ function _dumpXmlElement(xw: XmlWriter, v: string) {
|
|
|
239
223
|
</uax:Body>
|
|
240
224
|
</uax:ExtensionObject>
|
|
241
225
|
*/
|
|
242
|
-
type XmlNamespaceUri = string;
|
|
243
|
-
type NamespaceUri = string;
|
|
244
|
-
type XmlNs = string;
|
|
245
|
-
interface XmlWriterEx extends XmlWriter {
|
|
246
|
-
map: Record<XmlNamespaceUri, XmlNs>;
|
|
247
|
-
stackMap: Record<XmlNamespaceUri, XmlNs>[];
|
|
248
|
-
namespaceArray: NamespaceUri[];
|
|
249
|
-
}
|
|
250
|
-
export function initXmlWriterEx(xw: XmlWriter, map: Record<XmlNamespaceUri, XmlNs>, namespaceArray: NamespaceUri[]): void {
|
|
251
|
-
const xwe = xw as XmlWriterEx;
|
|
252
|
-
xwe.map = map;
|
|
253
|
-
xwe.stackMap = [];
|
|
254
|
-
xwe.namespaceArray = namespaceArray;
|
|
255
|
-
}
|
|
256
|
-
function findXsdNamespaceUri(xw: XmlWriter, nodeId: NodeId): string {
|
|
257
|
-
const xwe = xw as XmlWriterEx;
|
|
258
|
-
if (!xwe.namespaceArray) {
|
|
259
|
-
return "";
|
|
260
|
-
}
|
|
261
|
-
const namespace = xwe.namespaceArray[nodeId.namespace];
|
|
262
|
-
if (namespace === "http://opcfoundation.org/UA/") {
|
|
263
|
-
return "http://opcfoundation.org/UA/2008/02/Types.xsd";
|
|
264
|
-
}
|
|
265
|
-
// c8 ignore next
|
|
266
|
-
if (!namespace) {
|
|
267
|
-
return "";
|
|
268
|
-
}
|
|
269
|
-
return `${namespace.replace(/\/$/, "")}/Types.xsd`;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
function getPrefix(xw: XmlWriter, namespace: XmlNamespaceUri): XmlNs {
|
|
273
|
-
const xwe = xw as XmlWriterEx;
|
|
274
|
-
if (!xwe.map) return "";
|
|
275
|
-
const p = xwe.map[namespace] || "";
|
|
276
|
-
return p ? `${p}:` : "";
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
function restoreDefaultNamespace(xw: XmlWriter) {
|
|
280
|
-
const xwe = xw as XmlWriterEx;
|
|
281
|
-
if (!xwe.map) return;
|
|
282
|
-
const previousMap = xwe.stackMap.pop();
|
|
283
|
-
if (previousMap) {
|
|
284
|
-
xwe.map = previousMap;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
function setDefaultNamespace(xw: XmlWriter, namespace: XmlNamespaceUri): void {
|
|
289
|
-
const xwe = xw as XmlWriterEx;
|
|
290
|
-
if (!xwe.map) return;
|
|
291
|
-
if (xwe.map[namespace] !== "") {
|
|
292
|
-
xw.writeAttribute("xmlns", namespace);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
xwe.stackMap.push({
|
|
296
|
-
...xwe.map
|
|
297
|
-
});
|
|
298
|
-
xwe.map[namespace] = "";
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
function startElementEx(xw: XmlWriter, _ns: XmlNs, name: string, defaultNamespace: XmlNamespaceUri) {
|
|
302
|
-
const _xwe = xw as XmlWriterEx;
|
|
303
|
-
xw.startElement(name);
|
|
304
|
-
setDefaultNamespace(xw, defaultNamespace);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
function _dumpNodeId(xw: XmlWriter, v: NodeId) {
|
|
308
|
-
const xmlns = getPrefix(xw, "http://opcfoundation.org/UA/2008/02/Types.xsd");
|
|
309
|
-
xw.startElement(`${xmlns}Identifier`);
|
|
310
|
-
xw.text(n(xw, v));
|
|
311
|
-
xw.endElement();
|
|
312
|
-
}
|
|
313
|
-
|
|
314
226
|
function _dumpVariantValue(xw: XmlWriter, dataTypeNodeId: NodeId, dataType: DataType, addressSpace: IAddressSpace, value: unknown) {
|
|
315
227
|
if (value === undefined || value === null) {
|
|
316
228
|
return;
|
|
@@ -449,7 +361,7 @@ function _dumpVariantInnerValue(
|
|
|
449
361
|
break;
|
|
450
362
|
case DataType.Int64:
|
|
451
363
|
case DataType.UInt64:
|
|
452
|
-
xw.text((value as
|
|
364
|
+
xw.text(int64ToDecimalString(value as Int64, dataType === DataType.Int64));
|
|
453
365
|
break;
|
|
454
366
|
case DataType.Boolean:
|
|
455
367
|
case DataType.SByte:
|
|
@@ -662,7 +574,6 @@ function _dumpValue(xw: XmlWriter, node: UAVariable | UAVariableType, variant: V
|
|
|
662
574
|
return;
|
|
663
575
|
}
|
|
664
576
|
|
|
665
|
-
const dataTypeName = dataTypeNode.browseName.name?.toString();
|
|
666
577
|
const baseDataTypeName = DataType[variant.dataType];
|
|
667
578
|
|
|
668
579
|
if (baseDataTypeName === "Null") {
|
|
@@ -673,9 +584,13 @@ function _dumpValue(xw: XmlWriter, node: UAVariable | UAVariableType, variant: V
|
|
|
673
584
|
// determine if dataTypeName is a ExtensionObject
|
|
674
585
|
const isExtensionObject = variant.dataType === DataType.ExtensionObject;
|
|
675
586
|
|
|
676
|
-
|
|
587
|
+
// nothing to serialize: a scalar with no value, or an array that is null rather than empty.
|
|
588
|
+
// This is the one thing _isDefaultValue used to catch here that still needs catching -- it is
|
|
589
|
+
// a test for the absence of a value, not for a value that happens to equal a type's default
|
|
590
|
+
if (variant.value === null || variant.value === undefined) {
|
|
677
591
|
return;
|
|
678
592
|
}
|
|
593
|
+
|
|
679
594
|
xw.startElement("Value");
|
|
680
595
|
|
|
681
596
|
const uax = getPrefix(xw, "http://opcfoundation.org/UA/2008/02/Types.xsd");
|
|
@@ -702,7 +617,11 @@ function _dumpValue(xw: XmlWriter, node: UAVariable | UAVariableType, variant: V
|
|
|
702
617
|
switch (variant.arrayType) {
|
|
703
618
|
case VariantArrayType.Matrix:
|
|
704
619
|
case VariantArrayType.Array:
|
|
705
|
-
|
|
620
|
+
// the built-in type, not the DataType's browse name: the uax schema defines
|
|
621
|
+
// ListOfInt32 and ListOfString, and nothing named after a nodeset's own DataType.
|
|
622
|
+
// A `ListOfIdType` is not an element any reader knows, so the reader that meets it
|
|
623
|
+
// walks straight past into the first child and takes the array for a scalar
|
|
624
|
+
startElementEx(xw, uax, `ListOf${baseDataTypeName}`, "http://opcfoundation.org/UA/2008/02/Types.xsd");
|
|
706
625
|
variant.value.forEach(encodeXml);
|
|
707
626
|
restoreDefaultNamespace(xw);
|
|
708
627
|
xw.endElement();
|
|
@@ -720,12 +639,15 @@ function _dumpValue(xw: XmlWriter, node: UAVariable | UAVariableType, variant: V
|
|
|
720
639
|
}
|
|
721
640
|
|
|
722
641
|
function _dumpArrayDimensionsAttribute(xw: XmlWriter, node: UAVariableType | UAVariable) {
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
642
|
+
// ArrayDimensions="0" is written only when the document declared it: node-opcua synthesizes [0]
|
|
643
|
+
// on the variables it generates, so writing it unconditionally would invent the attribute on
|
|
644
|
+
// every generated node. See arrayDimensionsWereDeclared in nodeset_record_applier
|
|
645
|
+
if (!node.arrayDimensions || node.valueRank <= 0) return;
|
|
646
|
+
// all-zero dimensions are what node-opcua synthesizes when nobody declared any, at whatever
|
|
647
|
+
// rank: [0] for a one-dimensional value, [0,0] for a matrix
|
|
648
|
+
const degenerate = node.arrayDimensions.every((d) => d === 0);
|
|
649
|
+
if (degenerate && !(node as unknown as { arrayDimensionsWereDeclared?: boolean }).arrayDimensionsWereDeclared) return;
|
|
650
|
+
xw.writeAttribute("ArrayDimensions", node.arrayDimensions.join(","));
|
|
729
651
|
}
|
|
730
652
|
|
|
731
653
|
function getParent(node: BaseNode): BaseNode | null {
|
|
@@ -749,6 +671,10 @@ function dumpCommonAttributes(xw: XmlWriter, node: BaseNode) {
|
|
|
749
671
|
if (Object.hasOwn(node, "symbolicName")) {
|
|
750
672
|
xw.writeAttribute("SymbolicName", (node as unknown as { symbolicName: string }).symbolicName);
|
|
751
673
|
}
|
|
674
|
+
// Released is the default and is left unwritten; the other two the document declared
|
|
675
|
+
if (node.releaseStatus) {
|
|
676
|
+
xw.writeAttribute("ReleaseStatus", node.releaseStatus);
|
|
677
|
+
}
|
|
752
678
|
if (Object.hasOwn(node, "isAbstract")) {
|
|
753
679
|
const isAbstract = (node as unknown as { isAbstract: boolean }).isAbstract;
|
|
754
680
|
if (isAbstract) {
|
|
@@ -773,8 +699,11 @@ function dumpCommonAttributes(xw: XmlWriter, node: BaseNode) {
|
|
|
773
699
|
}
|
|
774
700
|
// access policy: undefined means "inherit from the namespace", an empty rolePermissions array
|
|
775
701
|
// means "this node deliberately grants nothing".
|
|
776
|
-
|
|
777
|
-
|
|
702
|
+
// what the document declared, which survives whatever the loader was asked to enforce; the
|
|
703
|
+
// enforced flag is the fallback for a node built in code rather than loaded
|
|
704
|
+
const accessRestrictions = node.declaredAccessRestrictions ?? node.accessRestrictions?.toString();
|
|
705
|
+
if (accessRestrictions !== undefined) {
|
|
706
|
+
xw.writeAttribute("AccessRestrictions", accessRestrictions);
|
|
778
707
|
}
|
|
779
708
|
if (node.rolePermissions !== undefined && node.rolePermissions.length === 0) {
|
|
780
709
|
xw.writeAttribute("HasNoPermissions", "true");
|
|
@@ -785,6 +714,19 @@ function dumpCommonAttributes(xw: XmlWriter, node: BaseNode) {
|
|
|
785
714
|
xw.writeAttribute("MinimumSamplingInterval", minimumSamplingInterval);
|
|
786
715
|
}
|
|
787
716
|
}
|
|
717
|
+
// EventNotifier lives on UAObject, UAObjectType and UAView in the XSD. 0 is the default and is
|
|
718
|
+
// left unwritten; anything else the document declared. It was written nowhere at all until now,
|
|
719
|
+
// so every node that declared one lost it on the way out
|
|
720
|
+
if (node.nodeClass === NodeClass.Object || node.nodeClass === NodeClass.ObjectType || node.nodeClass === NodeClass.View) {
|
|
721
|
+
// typeof, not truthiness: a node with a child called "EventNotifier" resolves the name to
|
|
722
|
+
// that child through the shared child accessors, so `node.eventNotifier` is a BaseNode
|
|
723
|
+
// there and not the attribute. PublishedEventsType is one, and stringifying it wrote a
|
|
724
|
+
// whole node dump into the attribute
|
|
725
|
+
const eventNotifier = (node as unknown as { eventNotifier?: unknown }).eventNotifier;
|
|
726
|
+
if (typeof eventNotifier === "number" && eventNotifier !== 0) {
|
|
727
|
+
xw.writeAttribute("EventNotifier", eventNotifier.toString());
|
|
728
|
+
}
|
|
729
|
+
}
|
|
788
730
|
// Historizing exists in the XSD on UAVariable only: UAVariableType is restricted to
|
|
789
731
|
// DataType/ValueRank/ArrayDimensions. UAVariableTypeImpl nevertheless owns a `historizing`
|
|
790
732
|
// property, so the node class must be tested here rather than the presence of the property.
|
|
@@ -819,19 +761,6 @@ function dumpCommonElements(xw: XmlWriter, node: BaseNode) {
|
|
|
819
761
|
_dumpRolePermissions(xw, node);
|
|
820
762
|
}
|
|
821
763
|
|
|
822
|
-
export function coerceInt64ToInt32(int64: Int64): number {
|
|
823
|
-
if (typeof int64 === "number") {
|
|
824
|
-
return int64 as number;
|
|
825
|
-
}
|
|
826
|
-
if (int64[0] === 0xffffffff && int64[1] === 0xffffffff) {
|
|
827
|
-
return 0xffffffff;
|
|
828
|
-
}
|
|
829
|
-
if (int64[0] !== 0) {
|
|
830
|
-
warningLog("coerceInt64ToInt32 , loosing high word in conversion");
|
|
831
|
-
}
|
|
832
|
-
return int64[1];
|
|
833
|
-
}
|
|
834
|
-
|
|
835
764
|
function _dumpEnumDefinition(xw: XmlWriter, enumDefinition: EnumDefinition) {
|
|
836
765
|
enumDefinition.fields = enumDefinition.fields || [];
|
|
837
766
|
|
|
@@ -860,6 +789,12 @@ function _dumpStructureDefinition(
|
|
|
860
789
|
// do not repeat elements that are already defined in base structure in the xml ouput!
|
|
861
790
|
const fields = structureDefinition.fields || [];
|
|
862
791
|
const nbFieldsInBase: number = baseStructureDefinition ? baseStructureDefinition.fields?.length || 0 : 0;
|
|
792
|
+
// Part 3 8.51: in a *WithSubtypedValues structure, isOptional is where "subtyping allowed" is
|
|
793
|
+
// held, and the document spells that AllowSubTypes. Written as IsOptional it said the opposite
|
|
794
|
+
// of what it meant, and the AllowSubTypes the document had was simply gone
|
|
795
|
+
const subtypedValues =
|
|
796
|
+
structureDefinition.structureType === StructureType.StructureWithSubtypedValues ||
|
|
797
|
+
structureDefinition.structureType === StructureType.UnionWithSubtypedValues;
|
|
863
798
|
|
|
864
799
|
for (let index = nbFieldsInBase; index < fields.length; index++) {
|
|
865
800
|
const defItem = fields[index];
|
|
@@ -872,8 +807,8 @@ function _dumpStructureDefinition(
|
|
|
872
807
|
if (defItem.valueRank !== undefined && defItem.valueRank !== -1) {
|
|
873
808
|
xw.writeAttribute("ValueRank", defItem.valueRank);
|
|
874
809
|
}
|
|
875
|
-
if (defItem.isOptional
|
|
876
|
-
xw.writeAttribute("IsOptional",
|
|
810
|
+
if (defItem.isOptional) {
|
|
811
|
+
xw.writeAttribute(subtypedValues ? "AllowSubTypes" : "IsOptional", "true");
|
|
877
812
|
}
|
|
878
813
|
if (defItem.maxStringLength !== undefined && defItem.maxStringLength !== 0) {
|
|
879
814
|
xw.writeAttribute("MaxStringLength", defItem.maxStringLength);
|
|
@@ -892,9 +827,37 @@ function _dumpStructureDefinition(
|
|
|
892
827
|
function _dumpUADataTypeDefinition(xw: XmlWriter, uaDataType: UADataType) {
|
|
893
828
|
const uaDataTypeBase = uaDataType.subtypeOfObj;
|
|
894
829
|
|
|
830
|
+
// an OptionSet first: node-opcua builds neither an enumeration nor a structure for one, so
|
|
831
|
+
// neither branch below would fire and the whole <Definition> would be dropped. The fields are
|
|
832
|
+
// the ones the loader kept, see UADataTypeImpl.isOptionSetDataType
|
|
833
|
+
const asImpl = uaDataType as unknown as {
|
|
834
|
+
isOptionSetDataType?: boolean;
|
|
835
|
+
declaredDefinitionFields?: Array<{ name?: string | null; value?: number; description?: unknown }>;
|
|
836
|
+
declaredDefinitionName?: string;
|
|
837
|
+
};
|
|
838
|
+
// what the document called this definition, or -- for a DataType built here, which never had a
|
|
839
|
+
// declaration -- the browse name, which is the only name available
|
|
840
|
+
const definitionName = asImpl.declaredDefinitionName ?? b(xw, uaDataType.browseName);
|
|
841
|
+
if (asImpl.isOptionSetDataType && asImpl.declaredDefinitionFields) {
|
|
842
|
+
xw.startElement("Definition");
|
|
843
|
+
xw.writeAttribute("Name", definitionName);
|
|
844
|
+
xw.writeAttribute("IsOptionSet", "true");
|
|
845
|
+
for (const field of asImpl.declaredDefinitionFields) {
|
|
846
|
+
xw.startElement("Field");
|
|
847
|
+
xw.writeAttribute("Name", field.name || "");
|
|
848
|
+
if (field.value !== undefined && field.value !== null) {
|
|
849
|
+
xw.writeAttribute("Value", field.value);
|
|
850
|
+
}
|
|
851
|
+
_dumpDescription(xw, field as { description?: LocalizedText });
|
|
852
|
+
xw.endElement();
|
|
853
|
+
}
|
|
854
|
+
xw.endElement();
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
|
|
895
858
|
if (uaDataType.isEnumeration()) {
|
|
896
859
|
xw.startElement("Definition");
|
|
897
|
-
xw.writeAttribute("Name",
|
|
860
|
+
xw.writeAttribute("Name", definitionName);
|
|
898
861
|
_dumpEnumDefinition(xw, uaDataType.getEnumDefinition());
|
|
899
862
|
xw.endElement();
|
|
900
863
|
return;
|
|
@@ -909,9 +872,11 @@ function _dumpUADataTypeDefinition(xw: XmlWriter, uaDataType: UADataType) {
|
|
|
909
872
|
const t = true;
|
|
910
873
|
if (t || dataValue.statusCode.isGood()) {
|
|
911
874
|
const definition = uaDataType.getStructureDefinition();
|
|
912
|
-
|
|
875
|
+
// a structure's supertype need not be a structure: `Structure` itself subtypes BaseDataType,
|
|
876
|
+
// which has no definition at all. Asking it for one threw, and took the whole export with it
|
|
877
|
+
const baseDefinition = uaDataTypeBase?.isStructure() ? uaDataTypeBase.getStructureDefinition() : null;
|
|
913
878
|
xw.startElement("Definition");
|
|
914
|
-
xw.writeAttribute("Name",
|
|
879
|
+
xw.writeAttribute("Name", definitionName);
|
|
915
880
|
if (definition.structureType === StructureType.Union) {
|
|
916
881
|
xw.writeAttribute("IsUnion", "true");
|
|
917
882
|
}
|
|
@@ -993,8 +958,14 @@ function dumpUAVariable(xw: XmlWriter, node: UAVariable) {
|
|
|
993
958
|
// sub elements
|
|
994
959
|
dumpCommonElements(xw, node);
|
|
995
960
|
|
|
996
|
-
|
|
997
|
-
|
|
961
|
+
// a value the loader made up for a variable the document left unvalued is not written
|
|
962
|
+
// back out; see valueWasSynthesized in nodeset_record_applier. What used to stand here was
|
|
963
|
+
// _isDefaultValue, which dropped every zero and every empty string alike -- suppressing the
|
|
964
|
+
// made-up ones at the cost of the ones the document really did declare
|
|
965
|
+
const synthesized = (node as unknown as { valueWasSynthesized?: boolean }).valueWasSynthesized;
|
|
966
|
+
const dataValue = (node as UAVariableImpl).$dataValue;
|
|
967
|
+
const value = dataValue.value;
|
|
968
|
+
if (value && !synthesized && !dataValue.statusCode.equals(StatusCodes.BadWaitingForInitialData)) {
|
|
998
969
|
_dumpValue(xw, node, value);
|
|
999
970
|
}
|
|
1000
971
|
}
|
|
@@ -1236,10 +1207,6 @@ BaseNodeImpl.prototype.dumpXML = function (this: BaseNodeImpl, xw: XmlWriter) {
|
|
|
1236
1207
|
writeWalkEvents(xw, this.addressSpace, translationTable, events);
|
|
1237
1208
|
};
|
|
1238
1209
|
|
|
1239
|
-
export function makeTypeXsd(namespaceUri: string): string {
|
|
1240
|
-
return `${namespaceUri.replace(/\/$/, "")}/Type.xsd`;
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
1210
|
NamespaceImpl.prototype.toNodeset2XML = function (this: NamespaceImpl) {
|
|
1244
1211
|
const namespaceArrayNode = this.addressSpace.findNode(VariableIds.Server_NamespaceArray);
|
|
1245
1212
|
const namespaceArray: string[] = namespaceArrayNode
|
|
@@ -52,6 +52,10 @@ export interface EnumerationInfo {
|
|
|
52
52
|
export interface UADataTypeOptions extends InternalBaseNodeOptions {
|
|
53
53
|
partialDefinition: StructureFieldOptions[] | EnumFieldOptions[];
|
|
54
54
|
isUnion?: boolean;
|
|
55
|
+
/** the DataType is a bit mask, not an enumeration; see UADataTypeImpl.isOptionSet */
|
|
56
|
+
isOptionSet?: boolean;
|
|
57
|
+
/** the <Definition> Name as declared; see UADataTypeImpl.declaredDefinitionName */
|
|
58
|
+
definitionName?: string;
|
|
55
59
|
isAbstract?: boolean;
|
|
56
60
|
symbolicName?: string;
|
|
57
61
|
}
|
|
@@ -94,11 +98,37 @@ export class UADataTypeImpl extends BaseNodeImpl implements UADataType {
|
|
|
94
98
|
private $partialDefinition?: StructureFieldOptionsEx[] | EnumFieldOptions[];
|
|
95
99
|
private $fullDefinition?: StructureDefinition | EnumDefinition;
|
|
96
100
|
|
|
101
|
+
/**
|
|
102
|
+
* whether the nodeset declared this DataType with `IsOptionSet="true"`: a bit mask whose fields
|
|
103
|
+
* are bit positions rather than an enumeration whose fields are values. node-opcua builds no
|
|
104
|
+
* enumeration and no structure for one -- it is a plain UInt16 or UInt32 subtype -- so without
|
|
105
|
+
* this flag and the fields kept in $partialDefinition, an OptionSet that goes into the address
|
|
106
|
+
* space cannot come out again as the DataType the document declared.
|
|
107
|
+
*/
|
|
108
|
+
public isOptionSetDataType = false;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* the Name attribute of the <Definition> element as the nodeset spelled it. The published
|
|
112
|
+
* catalogue uses both spellings -- 467 bare names and 211 namespace-prefixed ones -- so there
|
|
113
|
+
* is no form to normalise to: recomputing it from the browse name rewrote every bare one.
|
|
114
|
+
* Undefined for a DataType this address space built itself, which has no declaration to keep.
|
|
115
|
+
*/
|
|
116
|
+
public declaredDefinitionName?: string;
|
|
117
|
+
|
|
118
|
+
/** the fields of the <Definition> as declared, for a DataType the address space models no further */
|
|
119
|
+
public get declaredDefinitionFields(): StructureFieldOptionsEx[] | EnumFieldOptions[] | undefined {
|
|
120
|
+
return this.$partialDefinition;
|
|
121
|
+
}
|
|
122
|
+
|
|
97
123
|
constructor(options: UADataTypeOptions) {
|
|
98
124
|
super(options);
|
|
99
125
|
if (options.partialDefinition) {
|
|
100
126
|
this.$partialDefinition = options.partialDefinition;
|
|
101
127
|
}
|
|
128
|
+
this.isOptionSetDataType = !!options.isOptionSet;
|
|
129
|
+
if (options.definitionName) {
|
|
130
|
+
this.declaredDefinitionName = options.definitionName;
|
|
131
|
+
}
|
|
102
132
|
this.isAbstract = options.isAbstract === undefined || options.isAbstract === null ? false : options.isAbstract;
|
|
103
133
|
this.symbolicName = options.symbolicName || this.browseName.name || "";
|
|
104
134
|
}
|
|
@@ -261,7 +291,12 @@ export class UADataTypeImpl extends BaseNodeImpl implements UADataType {
|
|
|
261
291
|
}
|
|
262
292
|
getStructureDefinition(): StructureDefinition {
|
|
263
293
|
const definition = this._getDefinition();
|
|
264
|
-
assert
|
|
294
|
+
// named: a bare assert here threw an Error with an empty message, which told a caller
|
|
295
|
+
// nothing at all about which DataType had refused and what it had instead
|
|
296
|
+
assert(
|
|
297
|
+
definition instanceof StructureDefinition,
|
|
298
|
+
`${this.browseName.toString()} (${this.nodeId.toString()}) has no StructureDefinition: ${definition ? definition.constructor.name : "none"}`
|
|
299
|
+
);
|
|
265
300
|
return definition as StructureDefinition;
|
|
266
301
|
}
|
|
267
302
|
|
|
@@ -271,7 +306,10 @@ export class UADataTypeImpl extends BaseNodeImpl implements UADataType {
|
|
|
271
306
|
}
|
|
272
307
|
getEnumDefinition(): EnumDefinition {
|
|
273
308
|
const definition = this._getDefinition();
|
|
274
|
-
assert(
|
|
309
|
+
assert(
|
|
310
|
+
definition instanceof EnumDefinition,
|
|
311
|
+
`${this.browseName.toString()} (${this.nodeId.toString()}) has no EnumDefinition: ${definition ? definition.constructor.name : "none"}`
|
|
312
|
+
);
|
|
275
313
|
return definition as EnumDefinition;
|
|
276
314
|
}
|
|
277
315
|
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
import type {
|
|
6
6
|
AddObjectOptions,
|
|
7
|
+
AddReferenceOpts,
|
|
7
8
|
BaseNodeEvents,
|
|
8
9
|
InstantiateObjectOptions,
|
|
9
10
|
ISessionContext,
|
|
10
11
|
UAObject,
|
|
11
|
-
UAObjectType
|
|
12
|
-
UAReference
|
|
12
|
+
UAObjectType
|
|
13
13
|
} from "node-opcua-address-space-base";
|
|
14
14
|
import { assert } from "node-opcua-assert";
|
|
15
15
|
import { AttributeIds, type LocalizedTextLike, NodeClass } from "node-opcua-data-model";
|
|
@@ -28,11 +28,20 @@ import { assertUnusedChildBrowseName, topMostParentIsObjectTypeOrVariableType }
|
|
|
28
28
|
|
|
29
29
|
export interface UAObjectTypeOptions extends InternalBaseNodeOptions {
|
|
30
30
|
isAbstract?: boolean | null;
|
|
31
|
+
/** the EventNotifier the nodeset declared; see UAObjectTypeImpl.eventNotifier */
|
|
32
|
+
eventNotifier?: number;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
export class UAObjectTypeImpl extends BaseNodeImpl<BaseNodeEvents> implements UAObjectType {
|
|
34
36
|
public readonly nodeClass = NodeClass.ObjectType;
|
|
35
37
|
public readonly isAbstract: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* the EventNotifier the nodeset declared. The XSD allows it on a UAObjectType and nodesets use
|
|
40
|
+
* it, but nothing here held it: the attribute was dropped at load, and -- because the name was
|
|
41
|
+
* then free -- a child called EventNotifier claimed `eventNotifier` through the shared child
|
|
42
|
+
* accessors, so reading it handed back a BaseNode. PublishedEventsType is such a type.
|
|
43
|
+
*/
|
|
44
|
+
public readonly eventNotifier: number;
|
|
36
45
|
/**
|
|
37
46
|
* returns true if the object has some opcua methods
|
|
38
47
|
*/
|
|
@@ -54,6 +63,7 @@ export class UAObjectTypeImpl extends BaseNodeImpl<BaseNodeEvents> implements UA
|
|
|
54
63
|
constructor(options: UAObjectTypeOptions) {
|
|
55
64
|
super(options);
|
|
56
65
|
this.isAbstract = options.isAbstract ?? false;
|
|
66
|
+
this.eventNotifier = options.eventNotifier ?? 0;
|
|
57
67
|
}
|
|
58
68
|
|
|
59
69
|
public readAttribute(context: ISessionContext, attributeId: AttributeIds): DataValue {
|
|
@@ -118,7 +128,10 @@ export class UAObjectTypeImpl extends BaseNodeImpl<BaseNodeEvents> implements UA
|
|
|
118
128
|
const baseObjectType = addressSpace.findObjectType("BaseObjectType") as UAObjectType;
|
|
119
129
|
assert(baseObjectType, "BaseObjectType must be defined in the address space");
|
|
120
130
|
|
|
121
|
-
|
|
131
|
+
// caller-supplied references (typically an inverse aggregate reference to a
|
|
132
|
+
// parent that has no dedicated option, e.g. HasOrderedComponent) are created
|
|
133
|
+
// with the node, so the NodeIdManager sees the parent when naming it.
|
|
134
|
+
const references: AddReferenceOpts[] = options.references ? [...options.references] : [];
|
|
122
135
|
|
|
123
136
|
const copyAlsoModellingRules = topMostParentIsObjectTypeOrVariableType(addressSpace, options);
|
|
124
137
|
|
package/impl/ua_variable_impl.ts
CHANGED
|
@@ -283,8 +283,19 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
283
283
|
|
|
284
284
|
this.historizing = !!options.historizing; // coerced to boolean"
|
|
285
285
|
|
|
286
|
-
// a Variant built from null takes the constructor's short path; the options form costs twice as much
|
|
287
|
-
|
|
286
|
+
// a Variant built from null takes the constructor's short path; the options form costs twice as much.
|
|
287
|
+
// Stamped with the clock of the construction: a variable nobody gives a value to (a nodeset
|
|
288
|
+
// Variable of an abstract data type has no default to make up) is read as it is, and the
|
|
289
|
+
// requested timestamps are expected on that Uncertain result too (FEAT-34)
|
|
290
|
+
const now = getCurrentClock();
|
|
291
|
+
this.$dataValue = new DataValue({
|
|
292
|
+
statusCode: StatusCodes.UncertainInitialValue,
|
|
293
|
+
value: new Variant(null),
|
|
294
|
+
sourceTimestamp: now.timestamp,
|
|
295
|
+
sourcePicoseconds: now.picoseconds,
|
|
296
|
+
serverTimestamp: now.timestamp,
|
|
297
|
+
serverPicoseconds: now.picoseconds
|
|
298
|
+
});
|
|
288
299
|
|
|
289
300
|
if (options.value) {
|
|
290
301
|
this.bindVariable(options.value);
|
|
@@ -405,23 +416,27 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
405
416
|
context = SessionContext.defaultContext;
|
|
406
417
|
}
|
|
407
418
|
|
|
419
|
+
// A Bad result the variable makes up here carries the requested timestamps: a
|
|
420
|
+
// DataValue may carry them whatever its status, and the CTT expects them on every
|
|
421
|
+
// result of a Read with TimestampsToReturn Source or Both (Attribute Read 037 for
|
|
422
|
+
// the encoding, Base Info Core Structure 2 001 for the arguments of a method the
|
|
423
|
+
// nodeset reserves to a role nobody holds, FEAT-34). The clock of the denial is
|
|
424
|
+
// the honest source timestamp: the value behind it stays undisclosed.
|
|
408
425
|
if (context.isAccessRestricted(this)) {
|
|
409
|
-
return
|
|
426
|
+
return makeNowDataValue({ statusCode: StatusCodes.BadSecurityModeInsufficient });
|
|
410
427
|
}
|
|
411
428
|
|
|
412
429
|
if (!this.isReadable(context)) {
|
|
413
|
-
return
|
|
430
|
+
return makeNowDataValue({ statusCode: StatusCodes.BadNotReadable });
|
|
414
431
|
}
|
|
415
432
|
if (!this.checkPermissionPrivate(context, PermissionType.Read)) {
|
|
416
|
-
return
|
|
433
|
+
return makeNowDataValue({ statusCode: StatusCodes.BadUserAccessDenied });
|
|
417
434
|
}
|
|
418
435
|
if (!this.isUserReadable(context)) {
|
|
419
|
-
return
|
|
436
|
+
return makeNowDataValue({ statusCode: StatusCodes.BadNotReadable });
|
|
420
437
|
}
|
|
421
438
|
if (!isValidDataEncoding(dataEncoding)) {
|
|
422
|
-
// Table 51: no encoding can be applied to a non-Structure value
|
|
423
|
-
// (Attribute Read 037) still expects the requested timestamps on this Bad
|
|
424
|
-
// result, and a DataValue may carry them whatever its status.
|
|
439
|
+
// Table 51: no encoding can be applied to a non-Structure value
|
|
425
440
|
return makeNowDataValue({ statusCode: StatusCodes.BadDataEncodingInvalid });
|
|
426
441
|
}
|
|
427
442
|
|
|
@@ -1307,8 +1322,10 @@ export class UAVariableImpl<T extends UAVariableEvents & ListenerSignature<T> =
|
|
|
1307
1322
|
|
|
1308
1323
|
let func: (innerCallback: (err: Error | null, dataValue: DataValue) => void) => void;
|
|
1309
1324
|
|
|
1325
|
+
// stamped like the same gates of readValue(): the requested timestamps are expected
|
|
1326
|
+
// on a Bad result too (FEAT-34)
|
|
1310
1327
|
const answerStatusOnly = (statusCode: StatusCode) => (innerCallback: (err: Error | null, dataValue: DataValue) => void) =>
|
|
1311
|
-
innerCallback(null,
|
|
1328
|
+
innerCallback(null, makeNowDataValue({ statusCode }));
|
|
1312
1329
|
|
|
1313
1330
|
if (context.isAccessRestricted(this)) {
|
|
1314
1331
|
// same gate as readValue(): a variable bound with a simple get()
|