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