UNPKG

node-opcua-server

Version:

pure nodejs OPCUA SDK - module server

739 lines 36.1 kB
"use strict"; /** * @module node-opcua-server */ // tslint:disable:no-console Object.defineProperty(exports, "__esModule", { value: true }); exports.ServerSession = void 0; const crypto_1 = require("crypto"); const events_1 = require("events"); const node_opcua_assert_1 = require("node-opcua-assert"); const node_opcua_address_space_1 = require("node-opcua-address-space"); const node_opcua_basic_types_1 = require("node-opcua-basic-types"); const node_opcua_common_1 = require("node-opcua-common"); const node_opcua_data_model_1 = require("node-opcua-data-model"); const node_opcua_debug_1 = require("node-opcua-debug"); const node_opcua_nodeid_1 = require("node-opcua-nodeid"); const node_opcua_object_registry_1 = require("node-opcua-object-registry"); const node_opcua_status_code_1 = require("node-opcua-status-code"); const node_opcua_utils_1 = require("node-opcua-utils"); const node_opcua_utils_2 = require("node-opcua-utils"); const server_publish_engine_1 = require("./server_publish_engine"); const server_subscription_1 = require("./server_subscription"); const debugLog = (0, node_opcua_debug_1.make_debugLog)(__filename); const errorLog = (0, node_opcua_debug_1.make_errorLog)(__filename); const doDebug = (0, node_opcua_debug_1.checkDebugFlag)(__filename); const theWatchDog = new node_opcua_utils_1.WatchDog(); const registeredNodeNameSpace = 9999; function on_channel_abort() { debugLog("ON CHANNEL ABORT ON SESSION!!!"); /** * @event channel_aborted */ this.emit("channel_aborted"); } /** * * A Server session object. * * **from OPCUA Spec 1.02:** * * * Sessions are created to be independent of the underlying communications connection. Therefore, if a communication * connection fails, the Session is not immediately affected. The exact mechanism to recover from an underlying * communication connection error depends on the SecureChannel mapping as described in Part 6. * * * Sessions are terminated by the Server automatically if the Client fails to issue a Service request on the Session * within the timeout period negotiated by the Server in the CreateSession Service response. This protects the Server * against Client failures and against situations where a failed underlying connection cannot be re-established. * * * Clients shall be prepared to submit requests in a timely manner to prevent the Session from closing automatically. * * * Clients may explicitly terminate Sessions using the CloseSession Service. * * * When a Session is terminated, all outstanding requests on the Session are aborted and BadSessionClosed StatusCodes * are returned to the Client. In addition, the Server deletes the entry for the Client from its * SessionDiagnosticsArray Variable and notifies any other Clients who were subscribed to this entry. * */ class ServerSession extends events_1.EventEmitter { constructor(parent, userManager, sessionTimeout) { super(); this.__status = "new"; this.sessionName = ""; this.keepAlive = node_opcua_utils_1.WatchDog.emptyKeepAlive; this.parent = parent; // SessionEngine ServerSession.registry.register(this); this.sessionContext = new node_opcua_address_space_1.SessionContext({ session: this, server: { userManager } }); (0, node_opcua_assert_1.assert)(isFinite(sessionTimeout)); (0, node_opcua_assert_1.assert)(sessionTimeout >= 0, " sessionTimeout"); this.sessionTimeout = sessionTimeout; const authenticationTokenBuf = (0, crypto_1.randomBytes)(16); this.authenticationToken = new node_opcua_nodeid_1.NodeId(node_opcua_nodeid_1.NodeIdType.BYTESTRING, authenticationTokenBuf); // the sessionId const ownNamespaceIndex = 1; // addressSpace.getOwnNamespace().index; this.nodeId = new node_opcua_nodeid_1.NodeId(node_opcua_nodeid_1.NodeIdType.GUID, (0, node_opcua_basic_types_1.randomGuid)(), ownNamespaceIndex); (0, node_opcua_assert_1.assert)(this.authenticationToken instanceof node_opcua_nodeid_1.NodeId); (0, node_opcua_assert_1.assert)(this.nodeId instanceof node_opcua_nodeid_1.NodeId); this._cumulatedSubscriptionCount = 0; this.publishEngine = new server_publish_engine_1.ServerSidePublishEngine({ maxPublishRequestInQueue: ServerSession.maxPublishRequestInQueue }); this.publishEngine.setMaxListeners(100); theWatchDog.addSubscriber(this, this.sessionTimeout); this.__status = "new"; /** * the continuation point manager for this session * @property continuationPointManager * @type {ContinuationPointManager} */ this.continuationPointManager = new node_opcua_address_space_1.ContinuationPointManager(); /** * @property creationDate * @type {Date} */ this.creationDate = new Date(); this._registeredNodesCounter = 0; this._registeredNodes = {}; this._registeredNodesInv = {}; } getSessionId() { return this.nodeId; } getEndpointDescription() { return this.endpoint; } dispose() { debugLog("ServerSession#dispose()"); (0, node_opcua_assert_1.assert)(!this.sessionObject, " sessionObject has not been cleared !"); this.parent = null; this.authenticationToken = new node_opcua_nodeid_1.NodeId(); if (this.publishEngine) { this.publishEngine.dispose(); this.publishEngine = null; } this._sessionDiagnostics = undefined; this._registeredNodesCounter = 0; this._registeredNodes = null; this._registeredNodesInv = null; this.continuationPointManager = null; this.removeAllListeners(); this.__status = "disposed"; ServerSession.registry.unregister(this); } get clientConnectionTime() { return this.creationDate; } /** * return the number of milisecond since last session transaction occurs from client * the first transaction is the creation of the session */ get clientLastContactTime() { const lastSeen = this._watchDogData ? this._watchDogData.lastSeen : (0, node_opcua_basic_types_1.getMinOPCUADate)().getTime(); return node_opcua_utils_1.WatchDog.lastSeenToDuration(lastSeen); } get status() { return this.__status; } set status(value) { if (value === "active") { this._createSessionObjectInAddressSpace(); } if (this.__status !== value) { this.emit("statusChanged", value); } this.__status = value; } get addressSpace() { if (this.parent && this.parent.addressSpace) { return this.parent.addressSpace; } return null; } get currentPublishRequestInQueue() { return this.publishEngine ? this.publishEngine.pendingPublishRequestCount : 0; } updateClientLastContactTime() { if (this._sessionDiagnostics && this._sessionDiagnostics.clientLastContactTime) { const currentTime = new Date(); // do not record all ticks as this may be overwhelming, if (currentTime.getTime() - 250 >= this._sessionDiagnostics.clientLastContactTime.getTime()) { this._sessionDiagnostics.clientLastContactTime = currentTime; } } } /** * required for watch dog * @param currentTime {DateTime} * @private */ onClientSeen() { this.updateClientLastContactTime(); if (this._sessionDiagnostics) { // see https://opcfoundation-onlineapplications.org/mantis/view.php?id=4111 (0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(this._sessionDiagnostics, "currentMonitoredItemsCount")); (0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(this._sessionDiagnostics, "currentSubscriptionsCount")); (0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(this._sessionDiagnostics, "currentPublishRequestsInQueue")); // note : https://opcfoundation-onlineapplications.org/mantis/view.php?id=4111 // sessionDiagnostics extension object uses a different spelling // here with an S !!!! if (this._sessionDiagnostics.currentMonitoredItemsCount !== this.currentMonitoredItemCount) { this._sessionDiagnostics.currentMonitoredItemsCount = this.currentMonitoredItemCount; } if (this._sessionDiagnostics.currentSubscriptionsCount !== this.currentSubscriptionCount) { this._sessionDiagnostics.currentSubscriptionsCount = this.currentSubscriptionCount; } if (this._sessionDiagnostics.currentPublishRequestsInQueue !== this.currentPublishRequestInQueue) { this._sessionDiagnostics.currentPublishRequestsInQueue = this.currentPublishRequestInQueue; } } } incrementTotalRequestCount() { if (this._sessionDiagnostics && this._sessionDiagnostics.totalRequestCount) { this._sessionDiagnostics.totalRequestCount.totalCount += 1; } } incrementRequestTotalCounter(counterName) { if (this._sessionDiagnostics) { const propName = (0, node_opcua_utils_2.lowerFirstLetter)(counterName + "Count"); // istanbul ignore next if (!Object.prototype.hasOwnProperty.call(this._sessionDiagnostics, propName)) { errorLog("incrementRequestTotalCounter: cannot find", propName); // xx return; } else { this._sessionDiagnostics[propName].totalCount += 1; } } } incrementRequestErrorCounter(counterName) { this.parent?.incrementRejectedRequestsCount(); if (this._sessionDiagnostics) { const propName = (0, node_opcua_utils_2.lowerFirstLetter)(counterName + "Count"); // istanbul ignore next if (!Object.prototype.hasOwnProperty.call(this._sessionDiagnostics, propName)) { errorLog("incrementRequestErrorCounter: cannot find", propName); // xx return; } else { this._sessionDiagnostics[propName].errorCount += 1; } } } /** * returns rootFolder.objects.server.serverDiagnostics.sessionsDiagnosticsSummary.sessionDiagnosticsArray */ getSessionDiagnosticsArray() { const server = this.addressSpace.rootFolder.objects.server; return server.serverDiagnostics.sessionsDiagnosticsSummary.sessionDiagnosticsArray; } /** * returns rootFolder.objects.server.serverDiagnostics.sessionsDiagnosticsSummary.sessionSecurityDiagnosticsArray */ getSessionSecurityDiagnosticsArray() { const server = this.addressSpace.rootFolder.objects.server; return server.serverDiagnostics.sessionsDiagnosticsSummary.sessionSecurityDiagnosticsArray; } /** * number of active subscriptions */ get currentSubscriptionCount() { return this.publishEngine ? this.publishEngine.subscriptionCount : 0; } /** * number of subscriptions ever created since this object is live */ get cumulatedSubscriptionCount() { return this._cumulatedSubscriptionCount; } /** * number of monitored items */ get currentMonitoredItemCount() { return this.publishEngine ? this.publishEngine.currentMonitoredItemCount : 0; } /** * retrieve an existing subscription by subscriptionId * @param subscriptionId {Number} */ getSubscription(subscriptionId) { if (!this.publishEngine) return null; const subscription = this.publishEngine.getSubscriptionById(subscriptionId); if (subscription && subscription.state === server_subscription_1.SubscriptionState.CLOSED) { // subscription is CLOSED but has not been notified yet // it should be considered as excluded return null; } (0, node_opcua_assert_1.assert)(!subscription || subscription.state !== server_subscription_1.SubscriptionState.CLOSED, "CLOSED subscription shall not be managed by publish engine anymore"); return subscription; } /** * @param subscriptionId {Number} * @return {StatusCode} */ deleteSubscription(subscriptionId) { const subscription = this.getSubscription(subscriptionId); if (!subscription) { return node_opcua_status_code_1.StatusCodes.BadSubscriptionIdInvalid; } // xx this.publishEngine.remove_subscription(subscription); subscription.terminate(); if (this.currentSubscriptionCount === 0) { const local_publishEngine = this.publishEngine; local_publishEngine.cancelPendingPublishRequest(); } return node_opcua_status_code_1.StatusCodes.Good; } /** * close a ServerSession, this will also delete the subscriptions if the flag is set. * * Spec extract: * * If a Client invokes the CloseSession Service then all Subscriptions associated with the Session are also deleted * if the deleteSubscriptions flag is set to TRUE. If a Server terminates a Session for any other reason, * Subscriptions associated with the Session, are not deleted. Each Subscription has its own lifetime to protect * against data loss in the case of a Session termination. In these cases, the Subscription can be reassigned to * another Client before its lifetime expires. * * @param deleteSubscriptions : should we delete subscription ? * @param [reason = "CloseSession"] the reason for closing the session * (shall be "Timeout", "Terminated" or "CloseSession") * */ close(deleteSubscriptions, reason) { debugLog(" closing session deleteSubscriptions = ", deleteSubscriptions); if (this.publishEngine) { this.publishEngine.onSessionClose(); } theWatchDog.removeSubscriber(this); // --------------- delete associated subscriptions --------------------- if (!deleteSubscriptions && this.currentSubscriptionCount !== 0) { // I don't know what to do yet if deleteSubscriptions is false errorLog("TO DO : Closing session without deleting subscription not yet implemented"); // to do: Put subscriptions in safe place for future transfer if any } this._deleteSubscriptions(); (0, node_opcua_assert_1.assert)(this.currentSubscriptionCount === 0); // Post-Conditions (0, node_opcua_assert_1.assert)(this.currentSubscriptionCount === 0); this.status = "closed"; this._detach_channel(); /** * @event session_closed * @param deleteSubscriptions {Boolean} * @param reason {String} */ this.emit("session_closed", this, deleteSubscriptions, reason); // ---------------- shut down publish engine if (this.publishEngine) { // remove subscription this.publishEngine.shutdown(); (0, node_opcua_assert_1.assert)(this.publishEngine.subscriptionCount === 0); this.publishEngine.dispose(); this.publishEngine = null; } this._removeSessionObjectFromAddressSpace(); (0, node_opcua_assert_1.assert)(!this.sessionDiagnostics, "ServerSession#_removeSessionObjectFromAddressSpace must be called"); (0, node_opcua_assert_1.assert)(!this.sessionObject, "ServerSession#_removeSessionObjectFromAddressSpace must be called"); } registerNode(nodeId) { (0, node_opcua_assert_1.assert)(nodeId instanceof node_opcua_nodeid_1.NodeId); if (nodeId.namespace === 0 && nodeId.identifierType === node_opcua_nodeid_1.NodeIdType.NUMERIC) { return nodeId; } const key = nodeId.toString(); const registeredNode = this._registeredNodes[key]; if (registeredNode) { // already registered return registeredNode; } const node = this.addressSpace.findNode(nodeId); if (!node) { return nodeId; } this._registeredNodesCounter += 1; const aliasNodeId = (0, node_opcua_nodeid_1.makeNodeId)(this._registeredNodesCounter, registeredNodeNameSpace); this._registeredNodes[key] = aliasNodeId; this._registeredNodesInv[aliasNodeId.toString()] = node; return aliasNodeId; } unRegisterNode(aliasNodeId) { (0, node_opcua_assert_1.assert)(aliasNodeId instanceof node_opcua_nodeid_1.NodeId); if (aliasNodeId.namespace !== registeredNodeNameSpace) { return; // not a registered Node } const node = this._registeredNodesInv[aliasNodeId.toString()]; if (!node) { return; } this._registeredNodesInv[aliasNodeId.toString()] = null; this._registeredNodes[node.nodeId.toString()] = null; } resolveRegisteredNode(aliasNodeId) { if (aliasNodeId.namespace !== registeredNodeNameSpace) { return aliasNodeId; // not a registered Node } const node = this._registeredNodesInv[aliasNodeId.toString()]; if (!node) { return aliasNodeId; } return node.nodeId; } /** * true if the underlying channel has been closed or aborted... */ get aborted() { if (!this.channel) { return true; } return this.channel.aborted; } createSubscription(parameters) { const subscription = this.parent._createSubscriptionOnSession(this, parameters); (0, node_opcua_assert_1.assert)(!Object.prototype.hasOwnProperty.call(parameters, "id")); this.assignSubscription(subscription); (0, node_opcua_assert_1.assert)(subscription.$session === this); (0, node_opcua_assert_1.assert)(subscription.sessionId instanceof node_opcua_nodeid_1.NodeId); (0, node_opcua_assert_1.assert)((0, node_opcua_nodeid_1.sameNodeId)(subscription.sessionId, this.nodeId)); return subscription; } _attach_channel(channel) { (0, node_opcua_assert_1.assert)(this.nonce && this.nonce instanceof Buffer); this.channel = channel; this.channelId = channel.channelId; const key = this.authenticationToken.toString(); (0, node_opcua_assert_1.assert)(!Object.prototype.hasOwnProperty.call(channel.sessionTokens, key), "channel has already a session"); channel.sessionTokens[key] = this; // when channel is aborting this.channel_abort_event_handler = on_channel_abort.bind(this); channel.on("abort", this.channel_abort_event_handler); } _detach_channel() { const channel = this.channel; // istanbul ignore next if (!channel) { return; // already detached ! // throw new Error("expecting a valid channel"); } (0, node_opcua_assert_1.assert)(this.nonce && this.nonce instanceof Buffer); (0, node_opcua_assert_1.assert)(this.authenticationToken); const key = this.authenticationToken.toString(); (0, node_opcua_assert_1.assert)(Object.prototype.hasOwnProperty.call(channel.sessionTokens, key)); (0, node_opcua_assert_1.assert)(this.channel); (0, node_opcua_assert_1.assert)(typeof this.channel_abort_event_handler === "function"); channel.removeListener("abort", this.channel_abort_event_handler); delete channel.sessionTokens[key]; this.channel = undefined; this.channelId = undefined; } _exposeSubscriptionDiagnostics(subscription) { debugLog("ServerSession#_exposeSubscriptionDiagnostics"); (0, node_opcua_assert_1.assert)(subscription.$session === this); const subscriptionDiagnosticsArray = this._getSubscriptionDiagnosticsArray(); const subscriptionDiagnostics = subscription.subscriptionDiagnostics; (0, node_opcua_assert_1.assert)(subscriptionDiagnostics.$subscription === subscription); if (subscriptionDiagnostics && subscriptionDiagnosticsArray) { // subscription.id,"on session", session.nodeId.toString()); (0, node_opcua_address_space_1.addElement)(subscriptionDiagnostics, subscriptionDiagnosticsArray); } } _unexposeSubscriptionDiagnostics(subscription) { const subscriptionDiagnosticsArray = this._getSubscriptionDiagnosticsArray(); const subscriptionDiagnostics = subscription.subscriptionDiagnostics; (0, node_opcua_assert_1.assert)(subscriptionDiagnostics instanceof node_opcua_common_1.SubscriptionDiagnosticsDataType); if (subscriptionDiagnostics && subscriptionDiagnosticsArray) { // subscription.id,"on session", session.nodeId.toString()); (0, node_opcua_address_space_1.removeElement)(subscriptionDiagnosticsArray, (a) => a.subscriptionId === subscription.id); } debugLog("ServerSession#_unexposeSubscriptionDiagnostics"); } /** * used as a callback for the Watchdog * @private */ watchdogReset() { debugLog("Session#watchdogReset: the server session has expired and must be removed from the server"); // the server session has expired and must be removed from the server this.emit("timeout"); } _createSessionObjectInAddressSpace() { if (this.sessionObject) { return; } (0, node_opcua_assert_1.assert)(!this.sessionObject, "ServerSession#_createSessionObjectInAddressSpace already called ?"); this.sessionObject = null; if (!this.addressSpace) { debugLog("ServerSession#_createSessionObjectInAddressSpace : no addressSpace"); return; // no addressSpace } const root = this.addressSpace.rootFolder; (0, node_opcua_assert_1.assert)(root, "expecting a root object"); if (!root.objects) { debugLog("ServerSession#_createSessionObjectInAddressSpace : no object folder"); return false; } if (!root.objects.server) { debugLog("ServerSession#_createSessionObjectInAddressSpace : no server object"); return false; } // self.addressSpace.findNode(makeNodeId(ObjectIds.Server_ServerDiagnostics)); const serverDiagnosticsNode = root.objects.server.serverDiagnostics; if (!serverDiagnosticsNode || !serverDiagnosticsNode.sessionsDiagnosticsSummary) { debugLog("ServerSession#_createSessionObjectInAddressSpace :" + " no serverDiagnostics.sessionsDiagnosticsSummary"); return false; } const sessionDiagnosticsObjectType = this.addressSpace.findObjectType("SessionDiagnosticsObjectType"); const sessionDiagnosticsDataType = this.addressSpace.findDataType("SessionDiagnosticsDataType"); const sessionDiagnosticsVariableType = this.addressSpace.findVariableType("SessionDiagnosticsVariableType"); const sessionSecurityDiagnosticsDataType = this.addressSpace.findDataType("SessionSecurityDiagnosticsDataType"); const sessionSecurityDiagnosticsType = this.addressSpace.findVariableType("SessionSecurityDiagnosticsType"); const namespace = this.addressSpace.getOwnNamespace(); function createSessionDiagnosticsStuff() { if (sessionDiagnosticsDataType && sessionDiagnosticsVariableType) { // the extension object this._sessionDiagnostics = this.addressSpace.constructExtensionObject(sessionDiagnosticsDataType, {}); this._sessionDiagnostics.$session = this; // install property getter on property that are unlikely to change if (this.parent.clientDescription) { this._sessionDiagnostics.clientDescription = this.parent.clientDescription; } Object.defineProperty(this._sessionDiagnostics, "clientConnectionTime", { get() { return this.$session.clientConnectionTime; } }); Object.defineProperty(this._sessionDiagnostics, "actualSessionTimeout", { get() { return this.$session?.sessionTimeout; } }); Object.defineProperty(this._sessionDiagnostics, "sessionId", { get() { return this.$session ? this.$session.nodeId : node_opcua_nodeid_1.NodeId.nullNodeId; } }); Object.defineProperty(this._sessionDiagnostics, "sessionName", { get() { return this.$session ? this.$session.sessionName.toString() : ""; } }); this.sessionDiagnostics = sessionDiagnosticsVariableType.instantiate({ browseName: new node_opcua_data_model_1.QualifiedName({ name: "SessionDiagnostics", namespaceIndex: 0 }), componentOf: this.sessionObject, extensionObject: this._sessionDiagnostics, minimumSamplingInterval: 2000 // 2 seconds }); this._sessionDiagnostics = this.sessionDiagnostics.$extensionObject; (0, node_opcua_assert_1.assert)(this._sessionDiagnostics.$session === this); const sessionDiagnosticsArray = this.getSessionDiagnosticsArray(); // add sessionDiagnostics into sessionDiagnosticsArray (0, node_opcua_address_space_1.addElement)(this._sessionDiagnostics, sessionDiagnosticsArray); } } function createSessionSecurityDiagnosticsStuff() { if (sessionSecurityDiagnosticsDataType && sessionSecurityDiagnosticsType) { // the extension object this._sessionSecurityDiagnostics = this.addressSpace.constructExtensionObject(sessionSecurityDiagnosticsDataType, {}); this._sessionSecurityDiagnostics.$session = this; /* sessionId: NodeId; clientUserIdOfSession: UAString; clientUserIdHistory: UAString[] | null; authenticationMechanism: UAString; encoding: UAString; transportProtocol: UAString; securityMode: MessageSecurityMode; securityPolicyUri: UAString; clientCertificate: ByteString; */ Object.defineProperty(this._sessionSecurityDiagnostics, "sessionId", { get() { return this.$session?.nodeId; } }); Object.defineProperty(this._sessionSecurityDiagnostics, "clientUserIdOfSession", { get() { return ""; // UAString // TO DO : implement } }); Object.defineProperty(this._sessionSecurityDiagnostics, "clientUserIdHistory", { get() { return []; // UAString[] | null } }); Object.defineProperty(this._sessionSecurityDiagnostics, "authenticationMechanism", { get() { return ""; } }); Object.defineProperty(this._sessionSecurityDiagnostics, "encoding", { get() { return ""; } }); Object.defineProperty(this._sessionSecurityDiagnostics, "transportProtocol", { get() { return "opc.tcp"; } }); Object.defineProperty(this._sessionSecurityDiagnostics, "securityMode", { get() { const session = this.$session; return session?.channel?.securityMode; } }); Object.defineProperty(this._sessionSecurityDiagnostics, "securityPolicyUri", { get() { const session = this.$session; return session?.channel?.securityPolicy; } }); Object.defineProperty(this._sessionSecurityDiagnostics, "clientCertificate", { get() { const session = this.$session; return session?.channel.clientCertificate; } }); this.sessionSecurityDiagnostics = sessionSecurityDiagnosticsType.instantiate({ browseName: new node_opcua_data_model_1.QualifiedName({ name: "SessionSecurityDiagnostics", namespaceIndex: 0 }), componentOf: this.sessionObject, extensionObject: this._sessionSecurityDiagnostics, minimumSamplingInterval: 2000 // 2 seconds }); (0, node_opcua_address_space_1.ensureObjectIsSecure)(this.sessionSecurityDiagnostics); this._sessionSecurityDiagnostics = this.sessionSecurityDiagnostics .$extensionObject; (0, node_opcua_assert_1.assert)(this._sessionSecurityDiagnostics.$session === this); const sessionSecurityDiagnosticsArray = this.getSessionSecurityDiagnosticsArray(); // add sessionDiagnostics into sessionDiagnosticsArray const node = (0, node_opcua_address_space_1.addElement)(this._sessionSecurityDiagnostics, sessionSecurityDiagnosticsArray); (0, node_opcua_address_space_1.ensureObjectIsSecure)(node); } } function createSessionDiagnosticSummaryUAObject() { const references = []; if (sessionDiagnosticsObjectType) { references.push({ isForward: true, nodeId: sessionDiagnosticsObjectType, referenceType: "HasTypeDefinition" }); } this.sessionObject = namespace.createNode({ browseName: this.sessionName || "Session-" + this.nodeId.toString(), componentOf: serverDiagnosticsNode.sessionsDiagnosticsSummary, nodeClass: node_opcua_data_model_1.NodeClass.Object, nodeId: this.nodeId, references, typeDefinition: sessionDiagnosticsObjectType }); createSessionDiagnosticsStuff.call(this); createSessionSecurityDiagnosticsStuff.call(this); } function createSubscriptionDiagnosticsArray() { const subscriptionDiagnosticsArrayType = this.addressSpace.findVariableType("SubscriptionDiagnosticsArrayType"); (0, node_opcua_assert_1.assert)(subscriptionDiagnosticsArrayType.nodeId.toString() === "ns=0;i=2171"); this.subscriptionDiagnosticsArray = (0, node_opcua_address_space_1.createExtObjArrayNode)(this.sessionObject, { browseName: { namespaceIndex: 0, name: "SubscriptionDiagnosticsArray" }, complexVariableType: "SubscriptionDiagnosticsArrayType", indexPropertyName: "subscriptionId", minimumSamplingInterval: 2000, // 2 seconds variableType: "SubscriptionDiagnosticsType" }); } createSessionDiagnosticSummaryUAObject.call(this); createSubscriptionDiagnosticsArray.call(this); return this.sessionObject; } async resendMonitoredItemInitialValues() { for (const subscription of this.publishEngine.subscriptions) { await subscription.resendInitialValues(); } } /** * * @private */ _removeSessionObjectFromAddressSpace() { // todo : dump session statistics in a file or somewhere for deeper diagnostic analysis on closed session if (!this.addressSpace) { return; } if (this.sessionDiagnostics) { const sessionDiagnosticsArray = this.getSessionDiagnosticsArray(); (0, node_opcua_address_space_1.removeElement)(sessionDiagnosticsArray, (a) => (0, node_opcua_nodeid_1.sameNodeId)(a.sessionId, this.getSessionId())); this.addressSpace.deleteNode(this.sessionDiagnostics); (0, node_opcua_assert_1.assert)(this._sessionDiagnostics.$session === this); this._sessionDiagnostics.$session = null; this._sessionDiagnostics = undefined; this.sessionDiagnostics = undefined; } if (this.sessionSecurityDiagnostics) { const sessionSecurityDiagnosticsArray = this.getSessionSecurityDiagnosticsArray(); (0, node_opcua_address_space_1.removeElement)(sessionSecurityDiagnosticsArray, (a) => (0, node_opcua_nodeid_1.sameNodeId)(a.sessionId, this.getSessionId())); this.addressSpace.deleteNode(this.sessionSecurityDiagnostics); (0, node_opcua_assert_1.assert)(this._sessionSecurityDiagnostics.$session === this); this._sessionSecurityDiagnostics.$session = null; this._sessionSecurityDiagnostics = undefined; this.sessionSecurityDiagnostics = undefined; } if (this.sessionObject) { this.addressSpace.deleteNode(this.sessionObject); this.sessionObject = null; } } /** * * @private */ _getSubscriptionDiagnosticsArray() { if (!this.addressSpace) { // istanbul ignore next if (doDebug) { console.warn("ServerSession#_getSubscriptionDiagnosticsArray : no addressSpace"); } return null; // no addressSpace } const subscriptionDiagnosticsArray = this.subscriptionDiagnosticsArray; if (!subscriptionDiagnosticsArray) { return null; // no subscriptionDiagnosticsArray } (0, node_opcua_assert_1.assert)(subscriptionDiagnosticsArray.browseName.toString() === "SubscriptionDiagnosticsArray"); return subscriptionDiagnosticsArray; } assignSubscription(subscription) { (0, node_opcua_assert_1.assert)(!subscription.$session); (0, node_opcua_assert_1.assert)(this.nodeId instanceof node_opcua_nodeid_1.NodeId); subscription.$session = this; (0, node_opcua_assert_1.assert)(subscription.sessionId === this.nodeId); this._cumulatedSubscriptionCount += 1; // Notify the owner that a new subscription has been created // @event new_subscription // @param {Subscription} subscription this.emit("new_subscription", subscription); // add subscription diagnostics to SubscriptionDiagnosticsArray this._exposeSubscriptionDiagnostics(subscription); subscription.once("terminated", () => { // Notify the owner that a new subscription has been terminated // @event subscription_terminated // @param {Subscription} subscription this.emit("subscription_terminated", subscription); }); } _deleteSubscriptions() { if (!this.publishEngine) return; const subscriptions = this.publishEngine.subscriptions; for (const subscription of subscriptions) { this.deleteSubscription(subscription.id); } } } exports.ServerSession = ServerSession; ServerSession.registry = new node_opcua_object_registry_1.ObjectRegistry(); ServerSession.maxPublishRequestInQueue = 100; //# sourceMappingURL=server_session.js.map