node-opcua-server 2.51.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 (100) hide show
  1. package/.mocharc.yml +10 -0
  2. package/LICENSE +20 -0
  3. package/dist/base_server.d.ts +110 -0
  4. package/dist/base_server.js +476 -0
  5. package/dist/base_server.js.map +1 -0
  6. package/dist/factory.d.ts +10 -0
  7. package/dist/factory.js +24 -0
  8. package/dist/factory.js.map +1 -0
  9. package/dist/history_server_capabilities.d.ts +35 -0
  10. package/dist/history_server_capabilities.js +44 -0
  11. package/dist/history_server_capabilities.js.map +1 -0
  12. package/dist/i_channel_data.d.ts +13 -0
  13. package/dist/i_channel_data.js +3 -0
  14. package/dist/i_channel_data.js.map +1 -0
  15. package/dist/i_register_server_manager.d.ts +16 -0
  16. package/dist/i_register_server_manager.js +3 -0
  17. package/dist/i_register_server_manager.js.map +1 -0
  18. package/dist/i_server_side_publish_engine.d.ts +36 -0
  19. package/dist/i_server_side_publish_engine.js +50 -0
  20. package/dist/i_server_side_publish_engine.js.map +1 -0
  21. package/dist/i_socket_data.d.ts +11 -0
  22. package/dist/i_socket_data.js +3 -0
  23. package/dist/i_socket_data.js.map +1 -0
  24. package/dist/index.d.ts +14 -0
  25. package/dist/index.js +27 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/monitored_item.d.ts +173 -0
  28. package/dist/monitored_item.js +1006 -0
  29. package/dist/monitored_item.js.map +1 -0
  30. package/dist/node_sampler.d.ts +3 -0
  31. package/dist/node_sampler.js +76 -0
  32. package/dist/node_sampler.js.map +1 -0
  33. package/dist/opcua_server.d.ts +668 -0
  34. package/dist/opcua_server.js +2407 -0
  35. package/dist/opcua_server.js.map +1 -0
  36. package/dist/queue.d.ts +11 -0
  37. package/dist/queue.js +71 -0
  38. package/dist/queue.js.map +1 -0
  39. package/dist/register_server_manager.d.ts +92 -0
  40. package/dist/register_server_manager.js +574 -0
  41. package/dist/register_server_manager.js.map +1 -0
  42. package/dist/register_server_manager_hidden.d.ts +17 -0
  43. package/dist/register_server_manager_hidden.js +28 -0
  44. package/dist/register_server_manager_hidden.js.map +1 -0
  45. package/dist/register_server_manager_mdns_only.d.ts +19 -0
  46. package/dist/register_server_manager_mdns_only.js +58 -0
  47. package/dist/register_server_manager_mdns_only.js.map +1 -0
  48. package/dist/server_capabilities.d.ts +61 -0
  49. package/dist/server_capabilities.js +109 -0
  50. package/dist/server_capabilities.js.map +1 -0
  51. package/dist/server_end_point.d.ts +180 -0
  52. package/dist/server_end_point.js +825 -0
  53. package/dist/server_end_point.js.map +1 -0
  54. package/dist/server_engine.d.ts +311 -0
  55. package/dist/server_engine.js +1659 -0
  56. package/dist/server_engine.js.map +1 -0
  57. package/dist/server_publish_engine.d.ts +109 -0
  58. package/dist/server_publish_engine.js +531 -0
  59. package/dist/server_publish_engine.js.map +1 -0
  60. package/dist/server_publish_engine_for_orphan_subscriptions.d.ts +16 -0
  61. package/dist/server_publish_engine_for_orphan_subscriptions.js +50 -0
  62. package/dist/server_publish_engine_for_orphan_subscriptions.js.map +1 -0
  63. package/dist/server_session.d.ts +176 -0
  64. package/dist/server_session.js +734 -0
  65. package/dist/server_session.js.map +1 -0
  66. package/dist/server_subscription.d.ts +393 -0
  67. package/dist/server_subscription.js +1313 -0
  68. package/dist/server_subscription.js.map +1 -0
  69. package/dist/sessions_compatible_for_transfer.d.ts +2 -0
  70. package/dist/sessions_compatible_for_transfer.js +36 -0
  71. package/dist/sessions_compatible_for_transfer.js.map +1 -0
  72. package/dist/validate_filter.d.ts +5 -0
  73. package/dist/validate_filter.js +64 -0
  74. package/dist/validate_filter.js.map +1 -0
  75. package/package.json +88 -0
  76. package/source/base_server.ts +617 -0
  77. package/source/factory.ts +25 -0
  78. package/source/history_server_capabilities.ts +75 -0
  79. package/source/i_channel_data.ts +17 -0
  80. package/source/i_register_server_manager.ts +24 -0
  81. package/source/i_server_side_publish_engine.ts +77 -0
  82. package/source/i_socket_data.ts +11 -0
  83. package/source/index.ts +14 -0
  84. package/source/monitored_item.ts +1303 -0
  85. package/source/node_sampler.ts +82 -0
  86. package/source/opcua_server.ts +3742 -0
  87. package/source/queue.ts +73 -0
  88. package/source/register_server_manager.ts +744 -0
  89. package/source/register_server_manager_hidden.ts +33 -0
  90. package/source/register_server_manager_mdns_only.ts +69 -0
  91. package/source/server_capabilities.ts +177 -0
  92. package/source/server_end_point.ts +1182 -0
  93. package/source/server_engine.ts +2167 -0
  94. package/source/server_publish_engine.ts +657 -0
  95. package/source/server_publish_engine_for_orphan_subscriptions.ts +52 -0
  96. package/source/server_session.ts +931 -0
  97. package/source/server_subscription.ts +1792 -0
  98. package/source/sessions_compatible_for_transfer.ts +33 -0
  99. package/source/validate_filter.ts +86 -0
  100. package/test_helpers/create_certificates.js +1 -0
@@ -0,0 +1,1006 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.MonitoredItem = void 0;
13
+ /**
14
+ * @module node-opcua-server
15
+ */
16
+ // tslint:disable:no-console
17
+ const chalk = require("chalk");
18
+ const events_1 = require("events");
19
+ const node_opcua_assert_1 = require("node-opcua-assert");
20
+ const node_opcua_address_space_1 = require("node-opcua-address-space");
21
+ const node_opcua_data_model_1 = require("node-opcua-data-model");
22
+ const node_opcua_data_model_2 = require("node-opcua-data-model");
23
+ const node_opcua_data_value_1 = require("node-opcua-data-value");
24
+ const node_opcua_debug_1 = require("node-opcua-debug");
25
+ const node_opcua_numeric_range_1 = require("node-opcua-numeric-range");
26
+ const node_opcua_object_registry_1 = require("node-opcua-object-registry");
27
+ const node_opcua_service_filter_1 = require("node-opcua-service-filter");
28
+ const node_opcua_service_read_1 = require("node-opcua-service-read");
29
+ const node_opcua_service_subscription_1 = require("node-opcua-service-subscription");
30
+ const node_opcua_service_subscription_2 = require("node-opcua-service-subscription");
31
+ const node_opcua_status_code_1 = require("node-opcua-status-code");
32
+ const node_opcua_types_1 = require("node-opcua-types");
33
+ const node_opcua_variant_1 = require("node-opcua-variant");
34
+ const node_sampler_1 = require("./node_sampler");
35
+ const validate_filter_1 = require("./validate_filter");
36
+ const defaultItemToMonitor = new node_opcua_service_read_1.ReadValueId({
37
+ attributeId: node_opcua_data_model_2.AttributeIds.Value,
38
+ indexRange: undefined
39
+ });
40
+ const debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename);
41
+ const doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename);
42
+ function _adjust_sampling_interval(samplingInterval, node_minimumSamplingInterval) {
43
+ (0, node_opcua_assert_1.assert)(typeof node_minimumSamplingInterval === "number", "expecting a number");
44
+ if (samplingInterval === 0) {
45
+ return node_minimumSamplingInterval === 0
46
+ ? samplingInterval
47
+ : Math.max(MonitoredItem.minimumSamplingInterval, node_minimumSamplingInterval);
48
+ }
49
+ (0, node_opcua_assert_1.assert)(samplingInterval >= 0, " this case should have been prevented outside");
50
+ samplingInterval = samplingInterval || MonitoredItem.defaultSamplingInterval;
51
+ samplingInterval = Math.max(samplingInterval, MonitoredItem.minimumSamplingInterval);
52
+ samplingInterval = Math.min(samplingInterval, MonitoredItem.maximumSamplingInterval);
53
+ samplingInterval =
54
+ node_minimumSamplingInterval === 0 ? samplingInterval : Math.max(samplingInterval, node_minimumSamplingInterval);
55
+ return samplingInterval;
56
+ }
57
+ const maxQueueSize = 5000;
58
+ function _adjust_queue_size(queueSize) {
59
+ queueSize = Math.min(queueSize, maxQueueSize);
60
+ queueSize = Math.max(1, queueSize);
61
+ return queueSize;
62
+ }
63
+ function _validate_parameters(monitoringParameters) {
64
+ // xx assert(options instanceof MonitoringParameters);
65
+ (0, node_opcua_assert_1.assert)(monitoringParameters.hasOwnProperty("clientHandle"));
66
+ (0, node_opcua_assert_1.assert)(monitoringParameters.hasOwnProperty("samplingInterval"));
67
+ (0, node_opcua_assert_1.assert)(isFinite(monitoringParameters.clientHandle));
68
+ (0, node_opcua_assert_1.assert)(isFinite(monitoringParameters.samplingInterval));
69
+ (0, node_opcua_assert_1.assert)(typeof monitoringParameters.discardOldest === "boolean");
70
+ (0, node_opcua_assert_1.assert)(isFinite(monitoringParameters.queueSize));
71
+ (0, node_opcua_assert_1.assert)(monitoringParameters.queueSize >= 0);
72
+ }
73
+ function statusCodeHasChanged(newDataValue, oldDataValue) {
74
+ (0, node_opcua_assert_1.assert)(newDataValue instanceof node_opcua_data_value_1.DataValue);
75
+ (0, node_opcua_assert_1.assert)(oldDataValue instanceof node_opcua_data_value_1.DataValue);
76
+ return newDataValue.statusCode !== oldDataValue.statusCode;
77
+ }
78
+ function valueHasChanged(newDataValue, oldDataValue, deadbandType, deadbandValue) {
79
+ (0, node_opcua_assert_1.assert)(newDataValue instanceof node_opcua_data_value_1.DataValue);
80
+ (0, node_opcua_assert_1.assert)(oldDataValue instanceof node_opcua_data_value_1.DataValue);
81
+ switch (deadbandType) {
82
+ case node_opcua_service_subscription_2.DeadbandType.None:
83
+ (0, node_opcua_assert_1.assert)(newDataValue.value instanceof node_opcua_variant_1.Variant);
84
+ (0, node_opcua_assert_1.assert)(newDataValue.value instanceof node_opcua_variant_1.Variant);
85
+ // No Deadband calculation should be applied.
86
+ return (0, node_opcua_service_subscription_2.isOutsideDeadbandNone)(oldDataValue.value, newDataValue.value);
87
+ case node_opcua_service_subscription_2.DeadbandType.Absolute:
88
+ // AbsoluteDeadband
89
+ return (0, node_opcua_service_subscription_2.isOutsideDeadbandAbsolute)(oldDataValue.value, newDataValue.value, deadbandValue);
90
+ default:
91
+ // Percent_2 PercentDeadband (This type is specified in Part 8).
92
+ (0, node_opcua_assert_1.assert)(deadbandType === node_opcua_service_subscription_2.DeadbandType.Percent);
93
+ // The range of the deadbandValue is from 0.0 to 100.0 Percent.
94
+ (0, node_opcua_assert_1.assert)(deadbandValue >= 0 && deadbandValue <= 100);
95
+ // DeadbandType = PercentDeadband
96
+ // For this type of deadband the deadbandValue is defined as the percentage of the EURange. That is,
97
+ // it applies only to AnalogItems with an EURange Property that defines the typical value range for the
98
+ // item. This range shall be multiplied with the deadbandValue and then compared to the actual value change
99
+ // to determine the need for a data change notification. The following pseudo code shows how the deadband
100
+ // is calculated:
101
+ // DataChange if (absolute value of (last cached value - current value) >
102
+ // (deadbandValue/100.0) * ((high-low) of EURange)))
103
+ //
104
+ // Specifying a deadbandValue outside of this range will be rejected and reported with the
105
+ // StatusCode BadDeadbandFilterInvalid (see Table 27).
106
+ // If the Value of the MonitoredItem is an array, then the deadband calculation logic shall be applied to
107
+ // each element of the array. If an element that requires a DataChange is found, then no further
108
+ // deadband checking is necessary and the entire array shall be returned.
109
+ (0, node_opcua_assert_1.assert)(this.node !== null, "expecting a valid address_space object here to get access the the EURange");
110
+ const euRangeNode = this.node.getChildByName("EURange");
111
+ if (euRangeNode && euRangeNode.nodeClass === node_opcua_data_model_1.NodeClass.Variable) {
112
+ // double,double
113
+ const rangeVariant = euRangeNode.readValue().value;
114
+ return (0, node_opcua_service_subscription_2.isOutsideDeadbandPercent)(oldDataValue.value, newDataValue.value, deadbandValue, rangeVariant.value);
115
+ }
116
+ else {
117
+ console.log("EURange is not of type Variable");
118
+ }
119
+ return true;
120
+ }
121
+ }
122
+ function timestampHasChanged(t1, t2) {
123
+ if (t1 || !t2 || t2 || !t1) {
124
+ return true;
125
+ }
126
+ if (!t1 || !t2) {
127
+ return false;
128
+ }
129
+ return t1.getTime() !== t2.getTime();
130
+ }
131
+ function isGoodish(statusCode) {
132
+ return statusCode.value < 0x10000000;
133
+ }
134
+ function apply_dataChange_filter(newDataValue, oldDataValue) {
135
+ var _a;
136
+ /* istanbul ignore next */
137
+ if (!this.filter || !(this.filter instanceof node_opcua_service_subscription_2.DataChangeFilter)) {
138
+ throw new Error("Internal Error");
139
+ }
140
+ const trigger = this.filter.trigger;
141
+ // istanbul ignore next
142
+ if (doDebug) {
143
+ try {
144
+ debugLog("filter pass ?", node_opcua_service_subscription_2.DataChangeTrigger[trigger], (_a = this.oldDataValue) === null || _a === void 0 ? void 0 : _a.toString(), newDataValue.toString());
145
+ if (trigger === node_opcua_service_subscription_2.DataChangeTrigger.Status ||
146
+ trigger === node_opcua_service_subscription_2.DataChangeTrigger.StatusValue ||
147
+ trigger === node_opcua_service_subscription_2.DataChangeTrigger.StatusValueTimestamp) {
148
+ debugLog("statusCodeHasChanged ", statusCodeHasChanged(newDataValue, oldDataValue));
149
+ }
150
+ if (trigger === node_opcua_service_subscription_2.DataChangeTrigger.StatusValue || trigger === node_opcua_service_subscription_2.DataChangeTrigger.StatusValueTimestamp) {
151
+ debugLog("valueHasChanged ", valueHasChanged.call(this, newDataValue, oldDataValue, this.filter.deadbandType, this.filter.deadbandValue));
152
+ }
153
+ if (trigger === node_opcua_service_subscription_2.DataChangeTrigger.StatusValueTimestamp) {
154
+ debugLog("timestampHasChanged ", timestampHasChanged(newDataValue.sourceTimestamp, oldDataValue.sourceTimestamp));
155
+ }
156
+ }
157
+ catch (err) {
158
+ console.log(err);
159
+ }
160
+ }
161
+ switch (trigger) {
162
+ case node_opcua_service_subscription_2.DataChangeTrigger.Status: {
163
+ //
164
+ // Status
165
+ // Report a notification ONLY if the StatusCode associated with
166
+ // the value changes. See Table 166 for StatusCodes defined in
167
+ // this standard. Part 8 specifies additional StatusCodes that are
168
+ // valid in particular for device data.
169
+ return statusCodeHasChanged(newDataValue, oldDataValue);
170
+ }
171
+ case node_opcua_service_subscription_2.DataChangeTrigger.StatusValue: {
172
+ // filtering value changes.
173
+ // change. The Deadband filter can be used in addition for
174
+ // Report a notification if either the StatusCode or the value
175
+ // StatusValue
176
+ // This is the default setting if no filter is set.
177
+ return (statusCodeHasChanged(newDataValue, oldDataValue) ||
178
+ valueHasChanged.call(this, newDataValue, oldDataValue, this.filter.deadbandType, this.filter.deadbandValue));
179
+ }
180
+ default: {
181
+ // StatusValueTimestamp
182
+ // Report a notification if either StatusCode, value or the
183
+ // SourceTimestamp change.
184
+ //
185
+ // If a Deadband filter is specified,this trigger has the same behavior as STATUS_VALUE_1.
186
+ //
187
+ // If the DataChangeFilter is not applied to the monitored item, STATUS_VALUE_1
188
+ // is the default reporting behavior
189
+ (0, node_opcua_assert_1.assert)(trigger === node_opcua_service_subscription_2.DataChangeTrigger.StatusValueTimestamp);
190
+ return (timestampHasChanged(newDataValue.sourceTimestamp, oldDataValue.sourceTimestamp) ||
191
+ statusCodeHasChanged(newDataValue, oldDataValue) ||
192
+ valueHasChanged.call(this, newDataValue, oldDataValue, this.filter.deadbandType, this.filter.deadbandValue));
193
+ }
194
+ }
195
+ return false;
196
+ }
197
+ function apply_filter(newDataValue) {
198
+ if (!this.oldDataValue) {
199
+ return true; // keep
200
+ }
201
+ if (this.filter instanceof node_opcua_service_subscription_2.DataChangeFilter) {
202
+ return apply_dataChange_filter.call(this, newDataValue, this.oldDataValue);
203
+ }
204
+ else {
205
+ // if filter not set, by default report changes to Status or Value only
206
+ if (newDataValue.statusCode.value !== this.oldDataValue.statusCode.value) {
207
+ return true; // Keep because statusCode has changed ...
208
+ }
209
+ return !(0, node_opcua_variant_1.sameVariant)(newDataValue.value, this.oldDataValue.value);
210
+ }
211
+ return true; // keep
212
+ // else {
213
+ // return !sameDataValue(newDataValue, this.oldDataValue, TimestampsToReturn.Neither);
214
+ // }
215
+ // return true; // keep
216
+ }
217
+ function setSemanticChangeBit(notification) {
218
+ if (notification instanceof node_opcua_service_subscription_1.MonitoredItemNotification) {
219
+ notification.value.statusCode = node_opcua_status_code_1.StatusCode.makeStatusCode(notification.value.statusCode || node_opcua_status_code_1.StatusCodes.Good, "SemanticChanged");
220
+ }
221
+ else if (notification instanceof node_opcua_data_value_1.DataValue) {
222
+ notification.statusCode = node_opcua_status_code_1.StatusCode.makeStatusCode(notification.statusCode || node_opcua_status_code_1.StatusCodes.Good, "SemanticChanged");
223
+ }
224
+ }
225
+ const useCommonTimer = true;
226
+ function isSourceNewerThan(a, b) {
227
+ var _a, _b;
228
+ if (!b) {
229
+ return true;
230
+ }
231
+ const at = ((_a = a.sourceTimestamp) === null || _a === void 0 ? void 0 : _a.getTime()) || 0;
232
+ const bt = ((_b = b.sourceTimestamp) === null || _b === void 0 ? void 0 : _b.getTime()) || 0;
233
+ if (at === bt) {
234
+ return a.sourcePicoseconds > b.sourcePicoseconds;
235
+ }
236
+ return at > bt;
237
+ }
238
+ /**
239
+ * a server side monitored item
240
+ *
241
+ * - Once created, the MonitoredItem will raised an "samplingEvent" event every "samplingInterval" millisecond
242
+ * until {{#crossLink "MonitoredItem/terminate:method"}}{{/crossLink}} is called.
243
+ *
244
+ * - It is up to the event receiver to call {{#crossLink "MonitoredItem/recordValue:method"}}{{/crossLink}}.
245
+ *
246
+ */
247
+ class MonitoredItem extends events_1.EventEmitter {
248
+ constructor(options) {
249
+ super();
250
+ this.samplingInterval = -1;
251
+ this.discardOldest = true;
252
+ this.queueSize = 0;
253
+ this.samplingFunc = null;
254
+ this._is_sampling = false;
255
+ (0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(options, "monitoredItemId"));
256
+ (0, node_opcua_assert_1.assert)(!options.monitoringMode, "use setMonitoring mode explicitly to activate the monitored item");
257
+ options.itemToMonitor = options.itemToMonitor || defaultItemToMonitor;
258
+ this._samplingId = undefined;
259
+ this.clientHandle = 0; // invalid
260
+ this.filter = null;
261
+ this._set_parameters(options);
262
+ this.monitoredItemId = options.monitoredItemId; // ( known as serverHandle)
263
+ this.queue = [];
264
+ this.overflow = false;
265
+ this.oldDataValue = new node_opcua_data_value_1.DataValue({ statusCode: node_opcua_status_code_1.StatusCodes.BadDataUnavailable }); // unset initially
266
+ // user has to call setMonitoringMode
267
+ this.monitoringMode = node_opcua_service_subscription_1.MonitoringMode.Invalid;
268
+ this.timestampsToReturn = (0, node_opcua_data_value_1.coerceTimestampsToReturn)(options.timestampsToReturn);
269
+ this.itemToMonitor = options.itemToMonitor;
270
+ this._node = null;
271
+ this._semantic_version = 0;
272
+ if (doDebug) {
273
+ debugLog("Monitoring ", options.itemToMonitor.toString());
274
+ }
275
+ this._on_node_disposed_listener = null;
276
+ MonitoredItem.registry.register(this);
277
+ }
278
+ get node() {
279
+ return this._node;
280
+ }
281
+ set node(someNode) {
282
+ throw new Error("Unexpected way to set node");
283
+ }
284
+ setNode(node) {
285
+ (0, node_opcua_assert_1.assert)(!this.node || this.node === node, "node already set");
286
+ this._node = node;
287
+ this._semantic_version = node.semantic_version;
288
+ this._on_node_disposed_listener = () => this._on_node_disposed(this._node);
289
+ this._node.on("dispose", this._on_node_disposed_listener);
290
+ }
291
+ setMonitoringMode(monitoringMode) {
292
+ (0, node_opcua_assert_1.assert)(monitoringMode !== node_opcua_service_subscription_1.MonitoringMode.Invalid);
293
+ if (monitoringMode === this.monitoringMode) {
294
+ // nothing to do
295
+ return;
296
+ }
297
+ const old_monitoringMode = this.monitoringMode;
298
+ this.monitoringMode = monitoringMode;
299
+ if (this.monitoringMode === node_opcua_service_subscription_1.MonitoringMode.Disabled) {
300
+ this._stop_sampling();
301
+ // OPCUA 1.03 part 4 : $5.12.4
302
+ // setting the mode to DISABLED causes all queued Notifications to be deleted
303
+ this._empty_queue();
304
+ }
305
+ else {
306
+ (0, node_opcua_assert_1.assert)(this.monitoringMode === node_opcua_service_subscription_1.MonitoringMode.Sampling || this.monitoringMode === node_opcua_service_subscription_1.MonitoringMode.Reporting);
307
+ // OPCUA 1.03 part 4 : $5.12.1.3
308
+ // When a MonitoredItem is enabled (i.e. when the MonitoringMode is changed from DISABLED to
309
+ // SAMPLING or REPORTING) or it is created in the enabled state, the Server shall report the first
310
+ // sample as soon as possible and the time of this sample becomes the starting point for the next
311
+ // sampling interval.
312
+ const recordInitialValue = old_monitoringMode === node_opcua_service_subscription_1.MonitoringMode.Invalid || old_monitoringMode === node_opcua_service_subscription_1.MonitoringMode.Disabled;
313
+ this._start_sampling(recordInitialValue);
314
+ }
315
+ }
316
+ /**
317
+ * Terminate the MonitoredItem.
318
+ * @method terminate
319
+ *
320
+ * This will stop the internal sampling timer.
321
+ */
322
+ terminate() {
323
+ this._stop_sampling();
324
+ }
325
+ dispose() {
326
+ if (doDebug) {
327
+ debugLog("DISPOSING MONITORED ITEM", this._node.nodeId.toString());
328
+ }
329
+ this._stop_sampling();
330
+ MonitoredItem.registry.unregister(this);
331
+ if (this._on_node_disposed_listener) {
332
+ this._node.removeListener("dispose", this._on_node_disposed_listener);
333
+ this._on_node_disposed_listener = null;
334
+ }
335
+ // x assert(this._samplingId === null,"Sampling Id must be null");
336
+ this.oldDataValue = undefined;
337
+ this.queue = [];
338
+ this.itemToMonitor = null;
339
+ this.filter = null;
340
+ this.monitoredItemId = 0;
341
+ this._node = null;
342
+ this._semantic_version = 0;
343
+ this.$subscription = undefined;
344
+ this.removeAllListeners();
345
+ (0, node_opcua_assert_1.assert)(!this._samplingId);
346
+ (0, node_opcua_assert_1.assert)(!this._value_changed_callback);
347
+ (0, node_opcua_assert_1.assert)(!this._semantic_changed_callback);
348
+ (0, node_opcua_assert_1.assert)(!this._attribute_changed_callback);
349
+ (0, node_opcua_assert_1.assert)(!this._on_opcua_event_received_callback);
350
+ this._on_opcua_event_received_callback = null;
351
+ this._attribute_changed_callback = null;
352
+ this._semantic_changed_callback = null;
353
+ this._on_opcua_event_received_callback = null;
354
+ }
355
+ get isSampling() {
356
+ return (!!this._samplingId ||
357
+ typeof this._value_changed_callback === "function" ||
358
+ typeof this._attribute_changed_callback === "function");
359
+ }
360
+ toString() {
361
+ var _a;
362
+ let str = "";
363
+ str += `monitored item nodeId : ${(_a = this.node) === null || _a === void 0 ? void 0 : _a.nodeId.toString()} \n`;
364
+ str += ` sampling interval : ${this.samplingInterval} \n`;
365
+ str += ` monitoredItemId : ${this.monitoredItemId} \n`;
366
+ return str;
367
+ }
368
+ /**
369
+ * @param dataValue the whole dataValue
370
+ * @param skipChangeTest indicates whether recordValue should not check that dataValue is really
371
+ * different from previous one, ( by checking timestamps but also variant value)
372
+ * @private
373
+ *
374
+ * Notes:
375
+ * - recordValue can only be called within timer event
376
+ * - for performance reason, dataValue may be a shared value with the underlying node,
377
+ * therefore recordValue must clone the dataValue to make sure it retains a snapshot
378
+ * of the contain at the time recordValue was called.
379
+ *
380
+ */
381
+ recordValue(dataValue, skipChangeTest, indexRange) {
382
+ var _a;
383
+ (0, node_opcua_assert_1.assert)(dataValue instanceof node_opcua_data_value_1.DataValue);
384
+ (0, node_opcua_assert_1.assert)(dataValue !== this.oldDataValue, "recordValue expects different dataValue to be provided");
385
+ (0, node_opcua_assert_1.assert)(!dataValue.value || dataValue.value !== this.oldDataValue.value, "recordValue expects different dataValue.value to be provided");
386
+ (0, node_opcua_assert_1.assert)(!dataValue.value || dataValue.value.isValid(), "expecting a valid variant value");
387
+ const hasSemanticChanged = this.node && this.node.semantic_version !== this._semantic_version;
388
+ // xx console.log("`\n----------------------------",skipChangeTest,this.clientHandle,
389
+ // this.node.listenerCount("value_changed"),this.node.nodeId.toString());
390
+ // xx console.log("events ---- ",this.node.eventNames().join("-"));
391
+ // xx console.log("indexRange = ",indexRange ? indexRange.toString() :"");
392
+ // xx console.log("this.itemToMonitor.indexRange = ",this.itemToMonitor.indexRange.toString());
393
+ if (!hasSemanticChanged && indexRange && this.itemToMonitor.indexRange) {
394
+ // we just ignore changes that do not fall within our range
395
+ // ( unless semantic bit has changed )
396
+ if (!node_opcua_numeric_range_1.NumericRange.overlap(indexRange, this.itemToMonitor.indexRange)) {
397
+ return; // no overlap !
398
+ }
399
+ }
400
+ (0, node_opcua_assert_1.assert)(this.itemToMonitor, "must have a valid itemToMonitor(have this monitoredItem been disposed already ?");
401
+ // extract the range that we are interested with
402
+ dataValue = (0, node_opcua_data_value_1.extractRange)(dataValue, this.itemToMonitor.indexRange);
403
+ // istanbul ignore next
404
+ if (doDebug) {
405
+ debugLog("MonitoredItem#recordValue", this.node.nodeId.toString(), this.node.browseName.toString(), " has Changed = ", !(0, node_opcua_data_value_1.sameDataValue)(dataValue, this.oldDataValue), "skipChangeTest = ", skipChangeTest, "hasSemanticChanged = ", hasSemanticChanged);
406
+ }
407
+ // if semantic has changed, value need to be enqueued regardless of other assumptions
408
+ if (hasSemanticChanged) {
409
+ debugLog("_enqueue_value => because hasSemanticChanged");
410
+ setSemanticChangeBit(dataValue);
411
+ this._semantic_version = this.node.semantic_version;
412
+ return this._enqueue_value(dataValue);
413
+ debugLog("_enqueue_value => because hasSemanticChanged 2");
414
+ }
415
+ const useIndexRange = this.itemToMonitor.indexRange && !this.itemToMonitor.indexRange.isEmpty();
416
+ if (!skipChangeTest) {
417
+ const hasChanged = !(0, node_opcua_data_value_1.sameDataValue)(dataValue, this.oldDataValue);
418
+ if (!hasChanged) {
419
+ return;
420
+ }
421
+ }
422
+ if (!apply_filter.call(this, dataValue)) {
423
+ return;
424
+ }
425
+ if (useIndexRange) {
426
+ // when an indexRange is provided , make sure that no record happens unless
427
+ // extracted variant in the selected range has really changed.
428
+ // istanbul ignore next
429
+ if (doDebug) {
430
+ debugLog("Current : ", this.oldDataValue.toString());
431
+ debugLog("New : ", dataValue.toString());
432
+ debugLog("indexRange=", indexRange);
433
+ }
434
+ if ((0, node_opcua_variant_1.sameVariant)(dataValue.value, this.oldDataValue.value)) {
435
+ return;
436
+ }
437
+ }
438
+ // processTriggerItems
439
+ this.triggerLinkedItems();
440
+ if (doDebug) {
441
+ debugLog("RECORD VALUE ", (_a = this.node) === null || _a === void 0 ? void 0 : _a.nodeId.toString());
442
+ }
443
+ // store last value
444
+ this._enqueue_value(dataValue);
445
+ }
446
+ hasLinkItem(linkedMonitoredItemId) {
447
+ if (!this._linkedItems) {
448
+ return false;
449
+ }
450
+ return this._linkedItems.findIndex((x) => x === linkedMonitoredItemId) > 0;
451
+ }
452
+ addLinkItem(linkedMonitoredItemId) {
453
+ if (linkedMonitoredItemId === this.monitoredItemId) {
454
+ return node_opcua_status_code_1.StatusCodes.BadMonitoredItemIdInvalid;
455
+ }
456
+ this._linkedItems = this._linkedItems || [];
457
+ if (this.hasLinkItem(linkedMonitoredItemId)) {
458
+ return node_opcua_status_code_1.StatusCodes.BadMonitoredItemIdInvalid; // nothing to do
459
+ }
460
+ this._linkedItems.push(linkedMonitoredItemId);
461
+ return node_opcua_status_code_1.StatusCodes.Good;
462
+ }
463
+ removeLinkItem(linkedMonitoredItemId) {
464
+ if (!this._linkedItems || linkedMonitoredItemId === this.monitoredItemId) {
465
+ return node_opcua_status_code_1.StatusCodes.BadMonitoredItemIdInvalid;
466
+ }
467
+ const index = this._linkedItems.findIndex((x) => x === linkedMonitoredItemId);
468
+ if (index === -1) {
469
+ return node_opcua_status_code_1.StatusCodes.BadMonitoredItemIdInvalid;
470
+ }
471
+ this._linkedItems.splice(index, 1);
472
+ return node_opcua_status_code_1.StatusCodes.Good;
473
+ }
474
+ /**
475
+ * @internals
476
+ */
477
+ triggerLinkedItems() {
478
+ var _a, _b;
479
+ if (!this.$subscription || !this._linkedItems) {
480
+ return;
481
+ }
482
+ // see https://reference.opcfoundation.org/v104/Core/docs/Part4/5.12.1/#5.12.1.6
483
+ for (const linkItem of this._linkedItems) {
484
+ const linkedMonitoredItem = this.$subscription.getMonitoredItem(linkItem);
485
+ if (!linkedMonitoredItem) {
486
+ // monitoredItem may have been deleted
487
+ continue;
488
+ }
489
+ if (linkedMonitoredItem.monitoringMode === node_opcua_service_subscription_1.MonitoringMode.Disabled) {
490
+ continue;
491
+ }
492
+ if (linkedMonitoredItem.monitoringMode === node_opcua_service_subscription_1.MonitoringMode.Reporting) {
493
+ continue;
494
+ }
495
+ (0, node_opcua_assert_1.assert)(linkedMonitoredItem.monitoringMode === node_opcua_service_subscription_1.MonitoringMode.Sampling);
496
+ // istanbul ignore next
497
+ if (doDebug) {
498
+ debugLog("triggerLinkedItems => ", (_a = this.node) === null || _a === void 0 ? void 0 : _a.nodeId.toString(), (_b = linkedMonitoredItem.node) === null || _b === void 0 ? void 0 : _b.nodeId.toString());
499
+ }
500
+ linkedMonitoredItem.trigger();
501
+ }
502
+ }
503
+ get hasMonitoredItemNotifications() {
504
+ return this.queue.length > 0 || (this._triggeredNotifications !== undefined && this._triggeredNotifications.length > 0);
505
+ }
506
+ /**
507
+ * @internals
508
+ */
509
+ trigger() {
510
+ setImmediate(() => {
511
+ this._triggeredNotifications = this._triggeredNotifications || [];
512
+ const notifications = this.extractMonitoredItemNotifications(true);
513
+ this._triggeredNotifications = [].concat(this._triggeredNotifications, notifications);
514
+ });
515
+ }
516
+ extractMonitoredItemNotifications(bForce = false) {
517
+ if (!bForce && this.monitoringMode === node_opcua_service_subscription_1.MonitoringMode.Sampling && this._triggeredNotifications) {
518
+ const notifications1 = this._triggeredNotifications;
519
+ this._triggeredNotifications = undefined;
520
+ return notifications1;
521
+ }
522
+ if (!bForce && this.monitoringMode !== node_opcua_service_subscription_1.MonitoringMode.Reporting) {
523
+ return [];
524
+ }
525
+ const notifications = this.queue;
526
+ this._empty_queue();
527
+ // apply semantic changed bit if necessary
528
+ if (notifications.length > 0 && this.node && this._semantic_version < this.node.semantic_version) {
529
+ const dataValue = notifications[notifications.length - 1];
530
+ setSemanticChangeBit(dataValue);
531
+ (0, node_opcua_assert_1.assert)(this.node.nodeClass === node_opcua_data_model_1.NodeClass.Variable);
532
+ this._semantic_version = this.node.semantic_version;
533
+ }
534
+ return notifications;
535
+ }
536
+ modify(timestampsToReturn, monitoringParameters) {
537
+ (0, node_opcua_assert_1.assert)(monitoringParameters instanceof node_opcua_service_subscription_1.MonitoringParameters);
538
+ const old_samplingInterval = this.samplingInterval;
539
+ this.timestampsToReturn = timestampsToReturn || this.timestampsToReturn;
540
+ if (old_samplingInterval !== 0 && monitoringParameters.samplingInterval === 0) {
541
+ monitoringParameters.samplingInterval = MonitoredItem.minimumSamplingInterval; // fastest possible
542
+ }
543
+ // spec says: Illegal request values for parameters that can be revised do not generate errors. Instead the
544
+ // server will choose default values and indicate them in the corresponding revised parameter
545
+ this._set_parameters(monitoringParameters);
546
+ this._adjust_queue_to_match_new_queue_size();
547
+ this._adjust_sampling(old_samplingInterval);
548
+ if (monitoringParameters.filter) {
549
+ const statusCodeFilter = (0, validate_filter_1.validateFilter)(monitoringParameters.filter, this.itemToMonitor, this.node);
550
+ if (statusCodeFilter.isNot(node_opcua_status_code_1.StatusCodes.Good)) {
551
+ return new node_opcua_service_subscription_1.MonitoredItemModifyResult({
552
+ statusCode: statusCodeFilter
553
+ });
554
+ }
555
+ }
556
+ // validate filter
557
+ // note : The DataChangeFilter does not have an associated result structure.
558
+ const filterResult = null; // new subscription_service.DataChangeFilter
559
+ return new node_opcua_service_subscription_1.MonitoredItemModifyResult({
560
+ filterResult,
561
+ revisedQueueSize: this.queueSize,
562
+ revisedSamplingInterval: this.samplingInterval,
563
+ statusCode: node_opcua_status_code_1.StatusCodes.Good
564
+ });
565
+ }
566
+ resendInitialValues() {
567
+ return __awaiter(this, void 0, void 0, function* () {
568
+ // tte first Publish response(s) after the TransferSubscriptions call shall contain the current values of all
569
+ // Monitored Items in the Subscription where the Monitoring Mode is set to Reporting.
570
+ // the first Publish response after the TransferSubscriptions call shall contain only the value changes since
571
+ // the last Publish response was sent.
572
+ // This parameter only applies to MonitoredItems used for monitoring Attribute changes.
573
+ this._stop_sampling();
574
+ return this._start_sampling(true);
575
+ });
576
+ }
577
+ /**
578
+ * @method _on_sampling_timer
579
+ * @private
580
+ * request
581
+ *
582
+ */
583
+ _on_sampling_timer() {
584
+ // istanbul ignore next
585
+ if (doDebug) {
586
+ debugLog("MonitoredItem#_on_sampling_timer", this.node ? this.node.nodeId.toString() : "null", " isSampling?=", this._is_sampling);
587
+ }
588
+ if (this._samplingId) {
589
+ (0, node_opcua_assert_1.assert)(this.monitoringMode === node_opcua_service_subscription_1.MonitoringMode.Sampling || this.monitoringMode === node_opcua_service_subscription_1.MonitoringMode.Reporting);
590
+ if (this._is_sampling) {
591
+ // previous sampling call is not yet completed..
592
+ // there is nothing we can do about it except waiting until next tick.
593
+ // note : see also issue #156 on github
594
+ return;
595
+ }
596
+ // xx console.log("xxxx ON SAMPLING");
597
+ (0, node_opcua_assert_1.assert)(!this._is_sampling, "sampling func shall not be re-entrant !! fix it");
598
+ if (!this.samplingFunc) {
599
+ throw new Error("internal error : missing samplingFunc");
600
+ }
601
+ this._is_sampling = true;
602
+ this.samplingFunc.call(this, this.oldDataValue, (err, newDataValue) => {
603
+ if (!this._samplingId) {
604
+ // item has been disposed. The monitored item has been disposed while the async sampling func
605
+ // was taking place ... just ignore this
606
+ return;
607
+ }
608
+ if (err) {
609
+ console.log(" SAMPLING ERROR =>", err);
610
+ }
611
+ else {
612
+ // only record value if source timestamp is newer
613
+ // xx if (newDataValue && isSourceNewerThan(newDataValue, this.oldDataValue)) {
614
+ this._on_value_changed(newDataValue);
615
+ // xx }
616
+ }
617
+ this._is_sampling = false;
618
+ });
619
+ }
620
+ else {
621
+ /* istanbul ignore next */
622
+ debugLog("_on_sampling_timer call but MonitoredItem has been terminated !!! ");
623
+ }
624
+ }
625
+ _stop_sampling() {
626
+ // debugLog("MonitoredItem#_stop_sampling");
627
+ /* istanbul ignore next */
628
+ if (!this.node) {
629
+ throw new Error("Internal Error");
630
+ }
631
+ if (this._on_opcua_event_received_callback) {
632
+ (0, node_opcua_assert_1.assert)(typeof this._on_opcua_event_received_callback === "function");
633
+ this.node.removeListener("event", this._on_opcua_event_received_callback);
634
+ this._on_opcua_event_received_callback = null;
635
+ }
636
+ if (this._attribute_changed_callback) {
637
+ (0, node_opcua_assert_1.assert)(typeof this._attribute_changed_callback === "function");
638
+ const event_name = (0, node_opcua_address_space_1.makeAttributeEventName)(this.itemToMonitor.attributeId);
639
+ this.node.removeListener(event_name, this._attribute_changed_callback);
640
+ this._attribute_changed_callback = null;
641
+ }
642
+ if (this._value_changed_callback) {
643
+ // samplingInterval was 0 for a exception-based data Item
644
+ // we setup a event listener that we need to unwind here
645
+ (0, node_opcua_assert_1.assert)(typeof this._value_changed_callback === "function");
646
+ (0, node_opcua_assert_1.assert)(!this._samplingId);
647
+ this.node.removeListener("value_changed", this._value_changed_callback);
648
+ this._value_changed_callback = null;
649
+ }
650
+ if (this._semantic_changed_callback) {
651
+ (0, node_opcua_assert_1.assert)(typeof this._semantic_changed_callback === "function");
652
+ (0, node_opcua_assert_1.assert)(!this._samplingId);
653
+ this.node.removeListener("semantic_changed", this._semantic_changed_callback);
654
+ this._semantic_changed_callback = null;
655
+ }
656
+ if (this._samplingId) {
657
+ this._clear_timer();
658
+ }
659
+ (0, node_opcua_assert_1.assert)(!this._samplingId);
660
+ (0, node_opcua_assert_1.assert)(!this._value_changed_callback);
661
+ (0, node_opcua_assert_1.assert)(!this._semantic_changed_callback);
662
+ (0, node_opcua_assert_1.assert)(!this._attribute_changed_callback);
663
+ (0, node_opcua_assert_1.assert)(!this._on_opcua_event_received_callback);
664
+ }
665
+ _on_value_changed(dataValue, indexRange) {
666
+ (0, node_opcua_assert_1.assert)(dataValue instanceof node_opcua_data_value_1.DataValue);
667
+ this.recordValue(dataValue, false, indexRange);
668
+ }
669
+ _on_semantic_changed() {
670
+ const dataValue = this.node.readValue();
671
+ this._on_value_changed(dataValue);
672
+ }
673
+ _on_opcua_event(eventData) {
674
+ // TO DO : => Improve Filtering, bearing in mind that ....
675
+ // Release 1.04 8 OPC Unified Architecture, Part 9
676
+ // 4.5 Condition state synchronization
677
+ // To ensure a Client is always informed, the three special EventTypes
678
+ // (RefreshEndEventType, RefreshStartEventType and RefreshRequiredEventType)
679
+ // ignore the Event content filtering associated with a Subscription and will always be
680
+ // delivered to the Client.
681
+ var _a;
682
+ // istanbul ignore next
683
+ if (!this.filter || !(this.filter instanceof node_opcua_service_filter_1.EventFilter)) {
684
+ throw new Error("Internal Error : a EventFilter is requested");
685
+ }
686
+ const addressSpace = (_a = eventData.$eventDataSource) === null || _a === void 0 ? void 0 : _a.addressSpace;
687
+ if (!(0, node_opcua_address_space_1.checkWhereClause)(addressSpace, node_opcua_address_space_1.SessionContext.defaultContext, this.filter.whereClause, eventData)) {
688
+ return;
689
+ }
690
+ const selectClauses = this.filter.selectClauses ? this.filter.selectClauses : [];
691
+ const eventFields = (0, node_opcua_address_space_1.extractEventFields)(node_opcua_address_space_1.SessionContext.defaultContext, selectClauses, eventData);
692
+ // istanbul ignore next
693
+ if (doDebug) {
694
+ console.log(" RECEIVED INTERNAL EVENT THAT WE ARE MONITORING");
695
+ console.log(this.filter ? this.filter.toString() : "no filter");
696
+ eventFields.forEach((e) => {
697
+ console.log(e.toString());
698
+ });
699
+ }
700
+ this._enqueue_event(eventFields);
701
+ }
702
+ _getSession() {
703
+ if (!this.$subscription) {
704
+ return null;
705
+ }
706
+ if (!this.$subscription.$session) {
707
+ return null;
708
+ }
709
+ return this.$subscription.$session;
710
+ }
711
+ _start_sampling(recordInitialValue) {
712
+ // istanbul ignore next
713
+ if (!this.node) {
714
+ throw new Error("Internal Error");
715
+ }
716
+ setImmediate(() => this.__start_sampling(recordInitialValue));
717
+ }
718
+ __start_sampling(recordInitialValue) {
719
+ // istanbul ignore next
720
+ if (!this.node) {
721
+ return; // we just want to ignore here ...
722
+ }
723
+ // make sure oldDataValue is scrapped so first data recording can happen
724
+ this.oldDataValue = new node_opcua_data_value_1.DataValue({ statusCode: node_opcua_status_code_1.StatusCodes.BadDataUnavailable }); // unset initially
725
+ this._stop_sampling();
726
+ const context = new node_opcua_address_space_1.SessionContext({
727
+ session: this._getSession()
728
+ });
729
+ if (this.itemToMonitor.attributeId === node_opcua_data_model_2.AttributeIds.EventNotifier) {
730
+ // istanbul ignore next
731
+ if (doDebug) {
732
+ debugLog("xxxxxx monitoring EventNotifier on", this.node.nodeId.toString(), this.node.browseName.toString());
733
+ }
734
+ // we are monitoring OPCUA Event
735
+ this._on_opcua_event_received_callback = this._on_opcua_event.bind(this);
736
+ this.node.on("event", this._on_opcua_event_received_callback);
737
+ return;
738
+ }
739
+ if (this.itemToMonitor.attributeId !== node_opcua_data_model_2.AttributeIds.Value) {
740
+ // sampling interval only applies to Value Attributes.
741
+ this.samplingInterval = 0; // turned to exception-based regardless of requested sampling interval
742
+ // non value attribute only react on value change
743
+ this._attribute_changed_callback = this._on_value_changed.bind(this);
744
+ const event_name = (0, node_opcua_address_space_1.makeAttributeEventName)(this.itemToMonitor.attributeId);
745
+ this.node.on(event_name, this._attribute_changed_callback);
746
+ if (recordInitialValue) {
747
+ // read initial value
748
+ const dataValue = this.node.readAttribute(context, this.itemToMonitor.attributeId);
749
+ this.recordValue(dataValue, true);
750
+ }
751
+ return;
752
+ }
753
+ if (this.samplingInterval === 0) {
754
+ // we have a exception-based dataItem : event based model, so we do not need a timer
755
+ // rather , we setup the "value_changed_event";
756
+ this._value_changed_callback = this._on_value_changed.bind(this);
757
+ this._semantic_changed_callback = this._on_semantic_changed.bind(this);
758
+ this.node.on("value_changed", this._value_changed_callback);
759
+ this.node.on("semantic_changed", this._semantic_changed_callback);
760
+ // initiate first read
761
+ if (recordInitialValue) {
762
+ /* await */ new Promise((resolve) => {
763
+ this.node.readValueAsync(context, (err, dataValue) => {
764
+ if (!err && dataValue) {
765
+ this.recordValue(dataValue, true);
766
+ }
767
+ resolve();
768
+ });
769
+ });
770
+ }
771
+ }
772
+ else {
773
+ this._set_timer();
774
+ if (recordInitialValue) {
775
+ setImmediate(() => {
776
+ this._on_sampling_timer();
777
+ });
778
+ }
779
+ }
780
+ }
781
+ _set_parameters(monitoredParameters) {
782
+ _validate_parameters(monitoredParameters);
783
+ // only change clientHandle if it is valid (0<X<MAX)
784
+ if (monitoredParameters.clientHandle !== 0 && monitoredParameters.clientHandle !== 4294967295) {
785
+ this.clientHandle = monitoredParameters.clientHandle;
786
+ }
787
+ // The Server may support data that is collected based on a sampling model or generated based on an
788
+ // exception-based model. The fastest supported sampling interval may be equal to 0, which indicates
789
+ // that the data item is exception-based rather than being sampled at some period. An exception-based
790
+ // model means that the underlying system does not require sampling and reports data changes.
791
+ if (this.node && this.node.nodeClass === node_opcua_data_model_1.NodeClass.Variable) {
792
+ this.samplingInterval = _adjust_sampling_interval(monitoredParameters.samplingInterval, this.node ? this.node.minimumSamplingInterval : 0);
793
+ }
794
+ else {
795
+ this.samplingInterval = _adjust_sampling_interval(monitoredParameters.samplingInterval, 0);
796
+ }
797
+ this.discardOldest = monitoredParameters.discardOldest;
798
+ this.queueSize = _adjust_queue_size(monitoredParameters.queueSize);
799
+ // change filter
800
+ this.filter = monitoredParameters.filter || null;
801
+ }
802
+ _setOverflowBit(notification) {
803
+ if (notification.hasOwnProperty("value")) {
804
+ (0, node_opcua_assert_1.assert)(notification.value.statusCode.equals(node_opcua_status_code_1.StatusCodes.Good));
805
+ notification.value.statusCode = node_opcua_status_code_1.StatusCode.makeStatusCode(notification.value.statusCode, "Overflow | InfoTypeDataValue");
806
+ (0, node_opcua_assert_1.assert)((0, node_opcua_data_value_1.sameStatusCode)(notification.value.statusCode, node_opcua_status_code_1.StatusCodes.GoodWithOverflowBit));
807
+ (0, node_opcua_assert_1.assert)(notification.value.statusCode.hasOverflowBit);
808
+ }
809
+ // console.log(chalk.cyan("Setting Over"), !!this.$subscription, !!this.$subscription!.subscriptionDiagnostics);
810
+ if (this.$subscription && this.$subscription.subscriptionDiagnostics) {
811
+ this.$subscription.subscriptionDiagnostics.monitoringQueueOverflowCount++;
812
+ }
813
+ // to do eventQueueOverFlowCount
814
+ }
815
+ _enqueue_notification(notification) {
816
+ if (this.queueSize === 1) {
817
+ // https://reference.opcfoundation.org/v104/Core/docs/Part4/5.12.1/#5.12.1.5
818
+ // If the queue size is one, the queue becomes a buffer that always contains the newest
819
+ // Notification. In this case, if the sampling interval of the MonitoredItem is faster
820
+ // than the publishing interval of the Subscription, the MonitoredItem will be over
821
+ // sampling and the Client will always receive the most up-to-date value.
822
+ // The discard policy is ignored if the queue size is one.
823
+ // ensure queue size
824
+ if (!this.queue || this.queue.length !== 1) {
825
+ this.queue = [];
826
+ }
827
+ this.queue[0] = notification;
828
+ (0, node_opcua_assert_1.assert)(this.queue.length === 1);
829
+ }
830
+ else {
831
+ if (this.discardOldest) {
832
+ // push new value to queue
833
+ this.queue.push(notification);
834
+ if (this.queue.length > this.queueSize) {
835
+ this.overflow = true;
836
+ this.queue.shift(); // remove front element
837
+ // set overflow bit
838
+ this._setOverflowBit(this.queue[0]);
839
+ }
840
+ }
841
+ else {
842
+ if (this.queue.length < this.queueSize) {
843
+ this.queue.push(notification);
844
+ }
845
+ else {
846
+ this.overflow = true;
847
+ this._setOverflowBit(notification);
848
+ this.queue[this.queue.length - 1] = notification;
849
+ }
850
+ }
851
+ }
852
+ (0, node_opcua_assert_1.assert)(this.queue.length >= 1);
853
+ }
854
+ _makeDataChangeNotification(dataValue) {
855
+ if (this.clientHandle === -1 || this.clientHandle === 4294967295) {
856
+ debugLog("Invalid client handle");
857
+ }
858
+ const attributeId = this.itemToMonitor.attributeId;
859
+ // if dataFilter is specified ....
860
+ if (this.filter && this.filter instanceof node_opcua_service_subscription_2.DataChangeFilter) {
861
+ if (this.filter.trigger === node_opcua_service_subscription_2.DataChangeTrigger.Status) {
862
+ /** */
863
+ }
864
+ }
865
+ dataValue = (0, node_opcua_data_value_1.apply_timestamps)(dataValue, this.timestampsToReturn, attributeId);
866
+ return new node_opcua_service_subscription_1.MonitoredItemNotification({
867
+ clientHandle: this.clientHandle,
868
+ value: dataValue
869
+ });
870
+ }
871
+ /**
872
+ * @method _enqueue_value
873
+ * @param dataValue {DataValue} the dataValue to enqueue
874
+ * @private
875
+ */
876
+ _enqueue_value(dataValue) {
877
+ // preconditions:
878
+ if (doDebug) {
879
+ debugLog("_enqueue_value = ", dataValue.toString());
880
+ }
881
+ (0, node_opcua_assert_1.assert)(dataValue instanceof node_opcua_data_value_1.DataValue);
882
+ // lets verify that, if status code is good then we have a valid Variant in the dataValue
883
+ (0, node_opcua_assert_1.assert)(!isGoodish(dataValue.statusCode) || dataValue.value instanceof node_opcua_variant_1.Variant);
884
+ // xx assert(isGoodish(dataValue.statusCode) || util.isNullOrUndefined(dataValue.value) );
885
+ // let's check that data Value is really a different object
886
+ // we may end up with corrupted queue if dataValue are recycled and stored as is in notifications
887
+ (0, node_opcua_assert_1.assert)(dataValue !== this.oldDataValue, "dataValue cannot be the same object twice!");
888
+ // Xx // todo ERN !!!! PLEASE CHECK this !!!
889
+ // Xx // let make a clone, so we have a snapshot
890
+ // Xx dataValue = dataValue.clone();
891
+ // let's check that data Value is really a different object
892
+ // we may end up with corrupted queue if dataValue are recycled and stored as is in notifications
893
+ (0, node_opcua_assert_1.assert)(!this.oldDataValue || !dataValue.value || dataValue.value !== this.oldDataValue.value, "dataValue cannot be the same object twice!");
894
+ if (!(!this.oldDataValue ||
895
+ !this.oldDataValue.value ||
896
+ !dataValue.value ||
897
+ !(dataValue.value.value instanceof Object) ||
898
+ dataValue.value.value !== this.oldDataValue.value.value) &&
899
+ !(dataValue.value.value instanceof Date)) {
900
+ throw new Error("dataValue.value.value cannot be the same object twice! " +
901
+ this.node.browseName.toString() +
902
+ " " +
903
+ dataValue.toString() +
904
+ " " +
905
+ chalk.cyan(this.oldDataValue.toString()));
906
+ }
907
+ // istanbul ignore next
908
+ if (doDebug) {
909
+ debugLog("MonitoredItem#_enqueue_value", this.node.nodeId.toString());
910
+ }
911
+ this.oldDataValue = dataValue;
912
+ const notification = this._makeDataChangeNotification(dataValue);
913
+ this._enqueue_notification(notification);
914
+ }
915
+ _makeEventFieldList(eventFields) {
916
+ (0, node_opcua_assert_1.assert)(Array.isArray(eventFields));
917
+ return new node_opcua_types_1.EventFieldList({
918
+ clientHandle: this.clientHandle,
919
+ eventFields
920
+ });
921
+ }
922
+ _enqueue_event(eventFields) {
923
+ if (doDebug) {
924
+ debugLog(" MonitoredItem#_enqueue_event");
925
+ }
926
+ const notification = this._makeEventFieldList(eventFields);
927
+ this._enqueue_notification(notification);
928
+ }
929
+ _empty_queue() {
930
+ // empty queue
931
+ this.queue = [];
932
+ this.overflow = false;
933
+ }
934
+ _clear_timer() {
935
+ if (this._samplingId) {
936
+ if (useCommonTimer) {
937
+ (0, node_sampler_1.removeFromTimer)(this);
938
+ }
939
+ else {
940
+ clearInterval(this._samplingId);
941
+ }
942
+ this._samplingId = undefined;
943
+ }
944
+ }
945
+ _set_timer() {
946
+ (0, node_opcua_assert_1.assert)(this.samplingInterval >= MonitoredItem.minimumSamplingInterval);
947
+ (0, node_opcua_assert_1.assert)(!this._samplingId);
948
+ if (useCommonTimer) {
949
+ this._samplingId = (0, node_sampler_1.appendToTimer)(this);
950
+ }
951
+ else {
952
+ // settle periodic sampling
953
+ this._samplingId = setInterval(() => {
954
+ this._on_sampling_timer();
955
+ }, this.samplingInterval);
956
+ }
957
+ // xx console.log("MonitoredItem#_set_timer",this._samplingId);
958
+ }
959
+ _adjust_queue_to_match_new_queue_size() {
960
+ // adjust queue size if necessary
961
+ if (this.queueSize < this.queue.length) {
962
+ if (this.discardOldest) {
963
+ this.queue.splice(0, this.queue.length - this.queueSize);
964
+ }
965
+ else {
966
+ const lastElement = this.queue[this.queue.length - 1];
967
+ // only keep queueSize first element, discard others
968
+ this.queue.splice(this.queueSize);
969
+ this.queue[this.queue.length - 1] = lastElement;
970
+ }
971
+ }
972
+ if (this.queueSize <= 1) {
973
+ this.overflow = false;
974
+ // unset OverFlowBit
975
+ if (this.queue.length === 1) {
976
+ if (this.queue[0] instanceof node_opcua_service_subscription_1.MonitoredItemNotification) {
977
+ const el = this.queue[0];
978
+ if (el.value.statusCode.hasOverflowBit) {
979
+ el.value.statusCode.unset("Overflow | InfoTypeDataValue");
980
+ }
981
+ }
982
+ }
983
+ }
984
+ (0, node_opcua_assert_1.assert)(this.queue.length <= this.queueSize);
985
+ }
986
+ _adjust_sampling(old_samplingInterval) {
987
+ if (old_samplingInterval !== this.samplingInterval) {
988
+ this._start_sampling(false);
989
+ }
990
+ }
991
+ _on_node_disposed(node) {
992
+ this._on_value_changed(new node_opcua_data_value_1.DataValue({
993
+ sourceTimestamp: new Date(),
994
+ statusCode: node_opcua_status_code_1.StatusCodes.BadNodeIdInvalid
995
+ }));
996
+ this._stop_sampling();
997
+ node.removeListener("dispose", this._on_node_disposed_listener);
998
+ this._on_node_disposed_listener = null;
999
+ }
1000
+ }
1001
+ exports.MonitoredItem = MonitoredItem;
1002
+ MonitoredItem.registry = new node_opcua_object_registry_1.ObjectRegistry();
1003
+ MonitoredItem.minimumSamplingInterval = 50; // 50 ms as a minimum sampling interval
1004
+ MonitoredItem.defaultSamplingInterval = 1500; // 1500 ms as a default sampling interval
1005
+ MonitoredItem.maximumSamplingInterval = 1000 * 60 * 60; // 1 hour !
1006
+ //# sourceMappingURL=monitored_item.js.map