UNPKG

node-opcua-client

Version:

pure nodejs OPCUA SDK - module client

313 lines 14.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ClientMonitoredItemImpl = void 0; /** * @module node-opcua-client-private */ // tslint:disable:unified-signatures // tslint:disable:no-empty const events_1 = require("events"); const node_opcua_assert_1 = require("node-opcua-assert"); const node_opcua_data_model_1 = require("node-opcua-data-model"); const node_opcua_data_value_1 = require("node-opcua-data-value"); const node_opcua_debug_1 = require("node-opcua-debug"); const node_opcua_service_filter_1 = require("node-opcua-service-filter"); const node_opcua_service_read_1 = require("node-opcua-service-read"); const node_opcua_service_subscription_1 = require("node-opcua-service-subscription"); const node_opcua_status_code_1 = require("node-opcua-status-code"); const client_monitored_item_1 = require("../client_monitored_item"); const client_monitored_item_toolbox_1 = require("../client_monitored_item_toolbox"); const client_subscription_impl_1 = require("./client_subscription_impl"); const debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename); const warningLog = (0, node_opcua_debug_1.make_warningLog)(__filename); const doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename); /** * ClientMonitoredItem * @class ClientMonitoredItem * @extends ClientMonitoredItemBase * * - event: * - "initialized" * - "err" * - "changed" * * note: this.monitoringMode = subscription_service.MonitoringMode.Reporting; */ class ClientMonitoredItemImpl extends events_1.EventEmitter { itemToMonitor; monitoringParameters; subscription; monitoringMode; statusCode; monitoredItemId; result; filterResult; timestampsToReturn; #pendingDataValue; #pendingEvents; internalSetMonitoringMode(monitoringMode) { this.monitoringMode = monitoringMode; } constructor(subscription, itemToMonitor, monitoringParameters, timestampsToReturn, monitoringMode = node_opcua_service_subscription_1.MonitoringMode.Reporting) { super(); this.statusCode = node_opcua_status_code_1.StatusCodes.BadDataUnavailable; this.subscription = subscription; this.itemToMonitor = new node_opcua_service_read_1.ReadValueId(itemToMonitor); this.monitoringParameters = new node_opcua_service_subscription_1.MonitoringParameters(monitoringParameters); this.monitoringMode = monitoringMode; (0, node_opcua_assert_1.assert)(this.monitoringParameters.clientHandle === 0xffffffff, "should not have a client handle yet"); (0, node_opcua_assert_1.assert)(subscription.session, "expecting session"); timestampsToReturn = (0, node_opcua_data_value_1.coerceTimestampsToReturn)(timestampsToReturn); this.timestampsToReturn = timestampsToReturn; } toString() { let ret = ""; ret += "itemToMonitor: " + this.itemToMonitor.toString() + "\n"; ret += "monitoringParameters: " + this.monitoringParameters.toString() + "\n"; ret += "timestampsToReturn: " + this.timestampsToReturn.toString() + "\n"; ret += "itemToMonitor " + this.itemToMonitor.nodeId + "\n"; ret += "statusCode " + this.statusCode?.toString() + "\n"; ret += "result =" + this.result?.toString() + "\n"; return ret; } terminate(...args) { const done = args[0]; (0, node_opcua_assert_1.assert)(typeof done === "function"); const subscription = this.subscription; subscription._delete_monitored_items([this], (err) => { if (done) { done(err); } }); } modify(...args) { if (args.length === 2) { return this.modify(args[0], null, args[1]); } const parameters = args[0]; const timestampsToReturn = args[1]; const callback = args[2]; this.timestampsToReturn = timestampsToReturn || this.timestampsToReturn; client_monitored_item_toolbox_1.ClientMonitoredItemToolbox._toolbox_modify(this.subscription, [this], parameters, this.timestampsToReturn, (err, results) => { if (err) { return callback(err); } if (!results) { return callback(new Error("internal error")); } (0, node_opcua_assert_1.assert)(results.length === 1); callback(null, results[0]); }); } setMonitoringMode(...args) { const monitoringMode = args[0]; const callback = args[1]; client_monitored_item_toolbox_1.ClientMonitoredItemToolbox._toolbox_setMonitoringMode(this.subscription, [this], monitoringMode, (err, statusCodes) => { callback(err ? err : null, statusCodes ? statusCodes[0] : undefined); }); } /** * @internal * @param value * @private */ _notify_value_change(value) { // it is possible that the first notification arrives before the CreateMonitoredItemsRequest is fully proceed // in this case we need to put the dataValue aside so we can send the notification changed after // the node-opcua client had time to fully install the on("changed") event handler if (this.statusCode?.value === node_opcua_status_code_1.StatusCodes.BadDataUnavailable.value) { this.#pendingDataValue = this.#pendingDataValue || []; this.#pendingDataValue.push(value); return; } /** * Notify the observers that the MonitoredItem value has changed on the server side. * @event changed * @param value */ try { this.emit("changed", value); } catch (err) { warningLog("[NODE-OPCUA-W28] Exception raised inside the event handler called by ClientMonitoredItem.on('change')", err); warningLog(" Please verify the application using this node-opcua client"); } } /** * @internal * @param eventFields * @private */ _notify_event(eventFields) { if (this.statusCode?.value === node_opcua_status_code_1.StatusCodes.BadDataUnavailable.value) { this.#pendingEvents = this.#pendingEvents || []; this.#pendingEvents.push(eventFields); return; } /** * Notify the observers that the MonitoredItem value has changed on the server side. * @event changed * @param value */ try { this.emit("changed", eventFields); } catch (err) { warningLog("[NODE-OPCUA-W29] Exception raised inside the event handler called by ClientMonitoredItem.on('change')", err); warningLog(" Please verify the application using this node-opcua client"); } } /** * @internal * @private */ _prepare_for_monitoring() { (0, node_opcua_assert_1.assert)(this.monitoringParameters.clientHandle === 4294967295, "should not have a client handle yet"); const subscription = this.subscription; this.monitoringParameters.clientHandle = subscription._nextClientHandle(); (0, node_opcua_assert_1.assert)(this.monitoringParameters.clientHandle > 0 && this.monitoringParameters.clientHandle !== 4294967295); // If attributeId is EventNotifier then monitoring parameters need a filter. // The filter must then either be DataChangeFilter, EventFilter or AggregateFilter. // todo can be done in another way? // todo implement AggregateFilter // todo support DataChangeFilter // todo support whereClause if (this.itemToMonitor.attributeId === node_opcua_data_model_1.AttributeIds.EventNotifier) { // // see OPCUA Spec 1.02 part 4 page 65 : 5.12.1.4 Filter // see part 4 page 130: 7.16.3 EventFilter // part 3 page 11 : 4.6 Event Model // To monitor for Events, the attributeId element of the ReadValueId structure is the // the id of the EventNotifierAttribute // OPC Unified Architecture 1.02, Part 4 5.12.1.2 Sampling interval page 64: // "A Client shall define a sampling interval of 0 if it subscribes for Events." // toDO // note : the EventFilter is used when monitoring Events. this.monitoringParameters.filter = this.monitoringParameters.filter || new node_opcua_service_filter_1.EventFilter({}); const filter = this.monitoringParameters.filter; // c8 ignore next if (!filter) { return { error: "Internal Error" }; } if (filter.schema.name !== "EventFilter") { return { error: "Mismatch between attributeId and filter in monitoring parameters : " + "Got a " + filter.schema.name + " but a EventFilter object is required " + "when itemToMonitor.attributeId== AttributeIds.EventNotifier" }; } } else if (this.itemToMonitor.attributeId === node_opcua_data_model_1.AttributeIds.Value) { // the DataChangeFilter and the AggregateFilter are used when monitoring Variable Values // The Value Attribute is used when monitoring Variables. Variable values are monitored for a change // in value or a change in their status. The filters defined in this standard (see 7.16.2) and in Part 8 are // used to determine if the value change is large enough to cause a Notification to be generated for the // to do : check 'DataChangeFilter' && 'AggregateFilter' } else { if (this.monitoringParameters.filter) { return { error: "Mismatch between attributeId and filter in monitoring parameters : " + "no filter expected when attributeId is not Value or EventNotifier" }; } } return { itemToMonitor: this.itemToMonitor, monitoringMode: this.monitoringMode, requestedParameters: this.monitoringParameters }; } /** * @internal * @param monitoredItemResult * @private */ _applyResult(monitoredItemResult) { this.statusCode = monitoredItemResult.statusCode; if (monitoredItemResult.statusCode.isGood()) { this.result = monitoredItemResult; this.monitoredItemId = monitoredItemResult.monitoredItemId; this.monitoringParameters.samplingInterval = monitoredItemResult.revisedSamplingInterval; this.monitoringParameters.queueSize = monitoredItemResult.revisedQueueSize; this.filterResult = monitoredItemResult.filterResult || undefined; } // some PublishRequest with DataNotificationChange might have been sent by the server, before the monitored // item has been fully initialized it is time to process now any pending notification that were put on hold. if (this.#pendingDataValue) { const dataValues = this.#pendingDataValue; this.#pendingDataValue = undefined; setImmediate(() => { dataValues.map((dataValue) => this._notify_value_change(dataValue)); }); } if (this.#pendingEvents) { const events = this.#pendingEvents; this.#pendingEvents = undefined; setImmediate(() => { events.map((event) => this._notify_event(event)); }); } } _before_create() { const subscription = this.subscription; subscription._add_monitored_item(this.monitoringParameters.clientHandle, this); } /** * @internal * @param monitoredItemResult * @private */ _after_create(monitoredItemResult) { this._applyResult(monitoredItemResult); if (this.statusCode.isGood()) { /** * Notify the observers that the monitored item is now fully initialized. * @event initialized */ this.emit("initialized"); } else { /** * Notify the observers that the monitored item has failed to initialize. * @event err * @param statusCode {StatusCode} */ const err = new Error(monitoredItemResult.statusCode.toString()); this._terminate_and_emit(err); } } _terminate_and_emit(err) { if (this._terminated) { return; // already terminated } if (err) { this.emit("err", err.message); } (0, node_opcua_assert_1.assert)(!this._terminated); this._terminated = true; /** * Notify the observer that this monitored item has been terminated. * @event terminated */ this.emit("terminated", err); this.removeAllListeners(); // also remove from subscription const clientHandle = this.monitoringParameters.clientHandle; this.subscription._removeMonitoredItem(clientHandle); } } exports.ClientMonitoredItemImpl = ClientMonitoredItemImpl; // tslint:disable:no-var-requires // tslint:disable:max-line-length const thenify_ex_1 = require("thenify-ex"); const opts = { multiArgs: false }; ClientMonitoredItemImpl.prototype.terminate = (0, thenify_ex_1.withCallback)(ClientMonitoredItemImpl.prototype.terminate); ClientMonitoredItemImpl.prototype.setMonitoringMode = (0, thenify_ex_1.withCallback)(ClientMonitoredItemImpl.prototype.setMonitoringMode); ClientMonitoredItemImpl.prototype.modify = (0, thenify_ex_1.withCallback)(ClientMonitoredItemImpl.prototype.modify); client_monitored_item_1.ClientMonitoredItem.create = (subscription, itemToMonitor, monitoringParameters, timestampsToReturn, monitoringMode = node_opcua_service_subscription_1.MonitoringMode.Reporting) => { return (0, client_subscription_impl_1.ClientMonitoredItem_create)(subscription, itemToMonitor, monitoringParameters, timestampsToReturn, monitoringMode); }; //# sourceMappingURL=client_monitored_item_impl.js.map