UNPKG

node-opcua-client

Version:

pure nodejs OPCUA SDK - module client

163 lines 7.64 kB
"use strict"; /** * @module node-opcua-client */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ClientSessionKeepAliveManager = void 0; const chalk_1 = __importDefault(require("chalk")); const events_1 = require("events"); const node_opcua_assert_1 = require("node-opcua-assert"); const node_opcua_basic_types_1 = require("node-opcua-basic-types"); const node_opcua_common_1 = require("node-opcua-common"); const node_opcua_constants_1 = require("node-opcua-constants"); const node_opcua_debug_1 = require("node-opcua-debug"); const node_opcua_nodeid_1 = require("node-opcua-nodeid"); const node_opcua_secure_channel_1 = require("node-opcua-secure-channel"); const serverStatusStateNodeId = (0, node_opcua_nodeid_1.coerceNodeId)(node_opcua_constants_1.VariableIds.Server_ServerStatus_State); const debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename); const doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename); const warningLog = (0, node_opcua_debug_1.make_warningLog)(__filename); class ClientSessionKeepAliveManager extends events_1.EventEmitter { session; timerId; pingTimeout; lastKnownState; transactionInProgress = false; count = 0; checkInterval; constructor(session) { super(); this.session = session; this.timerId = undefined; this.pingTimeout = 0; this.checkInterval = 0; this.count = 0; } start(keepAliveInterval) { (0, node_opcua_assert_1.assert)(!this.timerId); /* c8 ignore next*/ if (this.session.timeout < 600) { warningLog(`[NODE-OPCUA-W13] ClientSessionKeepAliveManager detected that the session timeout (${this.session.timeout} ms) is really too small: please adjust it to a greater value ( at least 1000))`); } /* c8 ignore next*/ if (this.session.timeout < 100) { throw new Error(`ClientSessionKeepAliveManager detected that the session timeout (${this.session.timeout} ms) is really too small: please adjust it to a greater value ( at least 1000))`); } const selectedCheckInterval = keepAliveInterval || Math.min(Math.floor(Math.min((this.session.timeout * 2) / 3, 20000)), node_opcua_secure_channel_1.ClientSecureChannelLayer.defaultTransportTimeout); this.checkInterval = selectedCheckInterval; this.pingTimeout = Math.floor(Math.min(Math.max(50, selectedCheckInterval / 2), 20000)); // make sure first one is almost immediate this.timerId = setTimeout(() => this.ping_server(), this.pingTimeout); } stop() { if (this.timerId) { debugLog("ClientSessionKeepAliveManager#stop"); clearTimeout(this.timerId); this.timerId = undefined; } else { debugLog("warning ClientSessionKeepAliveManager#stop ignore (already stopped)"); } } ping_server() { this._ping_server().then((delta) => { if (!this.session || this.session.hasBeenClosed()) { return; // stop here } if (this.timerId) { const timeout = Math.max(1, this.checkInterval - delta); this.timerId = setTimeout(() => this.ping_server(), timeout); } }); } /** * @private * when a session is opened on a server, the client shall send request on a regular basis otherwise the server * session object might time out. * start_ping make sure that ping_server is called on a regular basis to prevent session to timeout. * */ async _ping_server() { const session = this.session; if (!session || session.isReconnecting) { debugLog("ClientSessionKeepAliveManager#ping_server => no session available"); return 0; } if (!this.timerId) { return 0; // keep-alive has been canceled .... } const now = Date.now(); const timeSinceLastServerContact = now - session.lastResponseReceivedTime.getTime(); if (timeSinceLastServerContact < this.pingTimeout) { debugLog("ClientSessionKeepAliveManager#ping_server skipped because last communication with server was not that long ago ping timeout=", Math.round(this.pingTimeout), "timeSinceLastServerContact = ", timeSinceLastServerContact); // no need to send a ping yet return timeSinceLastServerContact - this.pingTimeout; } if (session.isReconnecting) { debugLog("ClientSessionKeepAliveManager#ping_server skipped because client is reconnecting"); return 0; } if (session.hasBeenClosed()) { debugLog("ClientSessionKeepAliveManager#ping_server skipped because client is reconnecting"); return 0; } debugLog("ClientSessionKeepAliveManager#ping_server timeSinceLastServerContact=", timeSinceLastServerContact, "timeout", this.session.timeout); if (this.transactionInProgress) { // readVariable already taking place ! Ignore return 0; } this.transactionInProgress = true; // Server_ServerStatus_State return new Promise((resolve) => { session.read({ nodeId: serverStatusStateNodeId, attributeId: node_opcua_basic_types_1.AttributeIds.Value }, (err, dataValue) => { this.transactionInProgress = false; if (err || !dataValue || !dataValue.value) { if (err) { warningLog(chalk_1.default.cyan(" warning : ClientSessionKeepAliveManager#ping_server "), chalk_1.default.yellow(err.message)); } /** * @event failure * raised when the server is not responding or is responding with en error to * the keep alive read Variable value transaction */ this.emit("failure"); // also simulate a connection by closing the channel abruptly from our end ... warningLog("Keep alive has failed, considering a network outage is in place, forcing a reconnection"); terminateConnection(session._client); resolve(0); return; } if (dataValue.statusCode.isGood()) { const newState = dataValue.value.value; // c8 ignore next if (newState !== this.lastKnownState && this.lastKnownState) { warningLog("ClientSessionKeepAliveManager#Server state has changed = ", node_opcua_common_1.ServerState[newState], " was ", node_opcua_common_1.ServerState[this.lastKnownState]); } this.lastKnownState = newState; this.count++; // increase successful counter } debugLog("emit keepalive"); this.emit("keepalive", this.lastKnownState, this.count); resolve(0); }); }); } } exports.ClientSessionKeepAliveManager = ClientSessionKeepAliveManager; function terminateConnection(client) { if (!client) return; const channel = client._secureChannel; if (!channel) { return; } channel.forceConnectionBreak(); } //# sourceMappingURL=client_session_keepalive_manager.js.map