module import * as crypto from "crypto" ; import { EventEmitter } from "events" ; import { addElement , AddressSpace , ContinuationPointManager , createExtObjArrayNode , ensureObjectIsSecure , ISessionBase , removeElement , UADynamicVariableArray , UAObject , UASessionDiagnosticsVariable , UASessionSecurityDiagnostics , DTSessionDiagnostics , DTSessionSecurityDiagnostics } from "node-opcua-address-space" ; import { assert } from "node-opcua-assert" ; import { minOPCUADate , randomGuid } from "node-opcua-basic-types" ; import { SessionDiagnosticsDataType , SessionSecurityDiagnosticsDataType , SubscriptionDiagnosticsDataType } from "node-opcua-common" ; import { QualifiedName , NodeClass } from "node-opcua-data-model" ; import { checkDebugFlag , make_debugLog } from "node-opcua-debug" ; import { makeNodeId , NodeId , NodeIdType , sameNodeId } from "node-opcua-nodeid" ; import { ObjectRegistry } from "node-opcua-object-registry" ; import { StatusCode , StatusCodes } from "node-opcua-status-code" ; import { WatchDog } from "node-opcua-utils" ; import { lowerFirstLetter } from "node-opcua-utils" ; import { ISubscriber , IWatchdogData2 } from "node-opcua-utils" ; import { IServerSession , IServerSessionBase , ServerSecureChannelLayer } from "node-opcua-secure-channel" ; import { ApplicationDescription , UserIdentityToken , CreateSubscriptionRequestOptions , EndpointDescription } from "node-opcua-types" ; import { ServerSidePublishEngine } from "./server_publish_engine" ; import { Subscription } from "./server_subscription" ; import { SubscriptionState } from "./server_subscription" ; import { ServerEngine } from "./server_engine" ; const debugLog = make_debugLog ( __filename ) ; const doDebug = checkDebugFlag ( __filename ) ; const theWatchDog = new WatchDog ( ) ; const registeredNodeNameSpace = 9999 ; function compareSessionId ( sessionDiagnostics1 , sessionDiagnostics2 ) { return sessionDiagnostics1 . sessionId . toString ( ) === sessionDiagnostics2 . sessionId . toString ( ) ; } function on_channel_abort ( this ) { debugLog ( "ON CHANNEL ABORT ON  SESSION!!!" ) ; event this . emit ( "channel_aborted" ) ; } interface SessionDiagnosticsDataTypeEx extends SessionDiagnosticsDataType { $session : any ; } interface SessionSecurityDiagnosticsDataTypeEx extends SessionSecurityDiagnosticsDataType { $session : any ; } /**
 *
 * 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.
 *
 */ export class ServerSession extends EventEmitter implements ISubscriber , ISessionBase , IServerSession , IServerSessionBase { public static registry = new ObjectRegistry ( ) ; public static maxPublishRequestInQueue = 100 ; public __status = "" ; public parent : ServerEngine ; public authenticationToken : NodeId ; public nodeId : NodeId ; public sessionName = "" ; public publishEngine : ServerSidePublishEngine ; public sessionObject : any ; public readonly creationDate : Date ; public sessionTimeout : number ; public sessionDiagnostics ? : UASessionDiagnosticsVariable < DTSessionDiagnostics > ; public sessionSecurityDiagnostics ? : UASessionSecurityDiagnostics < DTSessionSecurityDiagnostics > ; public subscriptionDiagnosticsArray ? : UADynamicVariableArray < SubscriptionDiagnosticsDataType > ; public channel ? : ServerSecureChannelLayer ; public nonce ? : Buffer ; public userIdentityToken ? : UserIdentityToken ; public clientDescription ? : ApplicationDescription ; public channelId ? : number | null ; public continuationPointManager : ContinuationPointManager ; public _watchDog ? : WatchDog ; public _watchDogData ? : IWatchdogData2 ; keepAlive : ( ) => void = WatchDog . emptyKeepAlive ; private _registeredNodesCounter : number ; private _registeredNodes : any ; private _registeredNodesInv : any ; private _cumulatedSubscriptionCount : number ; private _sessionDiagnostics ? : SessionDiagnosticsDataTypeEx ; private _sessionSecurityDiagnostics ? : SessionSecurityDiagnosticsDataTypeEx ; private channel_abort_event_handler : any ; constructor ( parent , sessionTimeout ) { super ( ) ; this . parent = parent ; ServerSession . registry . register ( this ) ; assert ( isFinite ( sessionTimeout ) ) ; assert ( sessionTimeout >= 0 , " sessionTimeout" ) ; this . sessionTimeout = sessionTimeout ; const authenticationTokenBuf = crypto . randomBytes ( 16 ) ; this . authenticationToken = new NodeId ( NodeIdType . BYTESTRING , authenticationTokenBuf ) ; const ownNamespaceIndex = 1 ; this . nodeId = new NodeId ( NodeIdType . GUID , randomGuid ( ) , ownNamespaceIndex ) ; assert ( this . authenticationToken instanceof NodeId ) ; assert ( this . nodeId instanceof NodeId ) ; this . _cumulatedSubscriptionCount = 0 ; this . publishEngine = new ServerSidePublishEngine ( { maxPublishRequestInQueue : ServerSession . maxPublishRequestInQueue } ) ; this . publishEngine . setMaxListeners ( 100 ) ; theWatchDog . addSubscriber ( this , this . sessionTimeout ) ; this . __status = "new" ; property type { ContinuationPointManager } this . continuationPointManager = new ContinuationPointManager ( ) ; property type { Date } this . creationDate = new Date ( ) ; this . _registeredNodesCounter = 0 ; this . _registeredNodes = { } ; this . _registeredNodesInv = { } ; } public getSessionId ( ) { return this . nodeId ; } public endpoint ? : EndpointDescription ; public getEndpointDescription ( ) { return this . endpoint ! ; } public dispose ( ) { debugLog ( "ServerSession#dispose()" ) ; assert ( ! this . sessionObject , " sessionObject has not been cleared !" ) ; this . parent = null as any as ServerEngine ; this . authenticationToken = new NodeId ( ) ; if ( this . publishEngine ) { this . publishEngine . dispose ( ) ; ( this as any ) . publishEngine = null ; } this . _sessionDiagnostics = undefined ; this . _registeredNodesCounter = 0 ; this . _registeredNodes = null ; this . _registeredNodesInv = null ; ( this as any ) . continuationPointManager = null ; this . removeAllListeners ( ) ; this . __status = "disposed" ; ServerSession . registry . unregister ( this ) ; } public get clientConnectionTime ( ) : Date { return this . creationDate ; } /**
     * return the number of milisecond since last session transaction occurs from client
     * the first transaction is the creation of the session
     */ public get clientLastContactTime ( ) : number { const lastSeen = this . _watchDogData ? this . _watchDogData . lastSeen : minOPCUADate . getTime ( ) ; return WatchDog . lastSeenToDuration ( lastSeen ) ; } public get status ( ) : string { return this . __status ; } public set status ( value ) { if ( value === "active" ) { this . _createSessionObjectInAddressSpace ( ) ; } this . __status = value ; } get addressSpace ( ) : AddressSpace | null { if ( this . parent && this . parent . addressSpace ) { return this . parent . addressSpace ! ; } return null ; } get currentPublishRequestInQueue ( ) : number { return this . publishEngine ? this . publishEngine . pendingPublishRequestCount : 0 ; } public updateClientLastContactTime ( ) { if ( this . _sessionDiagnostics && this . _sessionDiagnostics . clientLastContactTime ) { const currentTime = new Date ( ) ; if ( currentTime . getTime ( ) - 250 >= this . _sessionDiagnostics . clientLastContactTime . getTime ( ) ) { this . _sessionDiagnostics . clientLastContactTime = currentTime ; } } } method param currentTime { DateTime } private public onClientSeen ( ) { this . updateClientLastContactTime ( ) ; if ( this . _sessionDiagnostics ) { assert ( Object . prototype . hasOwnProperty . call ( this . _sessionDiagnostics , "currentMonitoredItemsCount" ) ) ; assert ( Object . prototype . hasOwnProperty . call ( this . _sessionDiagnostics , "currentSubscriptionsCount" ) ) ; assert ( Object . prototype . hasOwnProperty . call ( this . _sessionDiagnostics , "currentPublishRequestsInQueue" ) ) ; 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 ; } } } public incrementTotalRequestCount ( ) { if ( this . _sessionDiagnostics && this . _sessionDiagnostics . totalRequestCount ) { this . _sessionDiagnostics . totalRequestCount . totalCount += 1 ; } } public incrementRequestTotalCounter ( counterName ) { if ( this . _sessionDiagnostics ) { const propName = lowerFirstLetter ( counterName + "Count" ) ; if ( ! Object . prototype . hasOwnProperty . call ( this . _sessionDiagnostics , propName ) ) { console . log ( " cannot find" , propName ) ; } else { ( this . _sessionDiagnostics as any ) [ propName ] . totalCount += 1 ; } } } public incrementRequestErrorCounter ( counterName ) { this . parent ?. incrementRejectedRequestsCount ( ) ; if ( this . _sessionDiagnostics ) { const propName = lowerFirstLetter ( counterName + "Count" ) ; if ( ! Object . prototype . hasOwnProperty . call ( this . _sessionDiagnostics , propName ) ) { console . log ( " cannot find" , propName ) ; } else { ( this . _sessionDiagnostics as any ) [ propName ] . errorCount += 1 ; } } } /**
     * returns rootFolder.objects.server.serverDiagnostics.sessionsDiagnosticsSummary.sessionDiagnosticsArray
     */ public getSessionDiagnosticsArray ( ) { const server = this . addressSpace ! . rootFolder . objects . server ; return server . serverDiagnostics . sessionsDiagnosticsSummary . sessionDiagnosticsArray as any ; } /**
     * returns rootFolder.objects.server.serverDiagnostics.sessionsDiagnosticsSummary.sessionSecurityDiagnosticsArray
     */ public getSessionSecurityDiagnosticsArray ( ) { const server = this . addressSpace ! . rootFolder . objects . server ; return server . serverDiagnostics . sessionsDiagnosticsSummary . sessionSecurityDiagnosticsArray as any ; } /**
     * number of active subscriptions
     */ public get currentSubscriptionCount ( ) : number { return this . publishEngine ? this . publishEngine . subscriptionCount : 0 ; } /**
     * number of subscriptions ever created since this object is live
     */ public get cumulatedSubscriptionCount ( ) : number { return this . _cumulatedSubscriptionCount ; } /**
     * number of monitored items
     */ public get currentMonitoredItemCount ( ) : number { return this . publishEngine ? this . publishEngine . currentMonitoredItemCount : 0 ; } method param subscriptionId { Number } return { Subscription } public getSubscription ( subscriptionId ) { const subscription = this . publishEngine . getSubscriptionById ( subscriptionId ) ; if ( subscription && subscription . state === SubscriptionState . CLOSED ) { return null ; } assert ( ! subscription || subscription . state !== SubscriptionState . CLOSED , "CLOSED subscription shall not be managed by publish engine anymore" ) ; return subscription ; } method param subscriptionId { Number } return { StatusCode } public deleteSubscription ( subscriptionId ) { const subscription = this . getSubscription ( subscriptionId ) ; if ( ! subscription ) { return StatusCodes . BadSubscriptionIdInvalid ; } subscription . terminate ( ) ; if ( this . currentSubscriptionCount === 0 ) { const local_publishEngine = this . publishEngine ; local_publishEngine . cancelPendingPublishRequest ( ) ; } return StatusCodes . Good ; } method param deleteSubscriptions param reason public close ( deleteSubscriptions , reason ) { debugLog ( " closing session deleteSubscriptions = " , deleteSubscriptions ) ; if ( this . publishEngine ) { this . publishEngine . onSessionClose ( ) ; } theWatchDog . removeSubscriber ( this ) ; if ( ! deleteSubscriptions && this . currentSubscriptionCount !== 0 ) { console . log ( "TO DO : Closing session without deleting subscription not yet implemented" ) ; } this . _deleteSubscriptions ( ) ; assert ( this . currentSubscriptionCount === 0 ) ; assert ( this . currentSubscriptionCount === 0 ) ; this . status = "closed" ; this . _detach_channel ( ) ; event param deleteSubscriptions { Boolean } param reason { String } this . emit ( "session_closed" , this , deleteSubscriptions , reason ) ; if ( this . publishEngine ) { this . publishEngine . shutdown ( ) ; assert ( this . publishEngine . subscriptionCount === 0 ) ; this . publishEngine . dispose ( ) ; this . publishEngine = null as any as ServerSidePublishEngine ; } this . _removeSessionObjectFromAddressSpace ( ) ; assert ( ! this . sessionDiagnostics , "ServerSession#_removeSessionObjectFromAddressSpace must be called" ) ; assert ( ! this . sessionObject , "ServerSession#_removeSessionObjectFromAddressSpace must be called" ) ; } public registerNode ( nodeId ) { assert ( nodeId instanceof NodeId ) ; if ( nodeId . namespace === 0 && nodeId . identifierType === NodeIdType . NUMERIC ) { return nodeId ; } const key = nodeId . toString ( ) ; const registeredNode = this . _registeredNodes [ key ] ; if ( registeredNode ) { return registeredNode ; } const node = this . addressSpace ! . findNode ( nodeId ) ; if ( ! node ) { return nodeId ; } this . _registeredNodesCounter += 1 ; const aliasNodeId = makeNodeId ( this . _registeredNodesCounter , registeredNodeNameSpace ) ; this . _registeredNodes [ key ] = aliasNodeId ; this . _registeredNodesInv [ aliasNodeId . toString ( ) ] = node ; return aliasNodeId ; } public unRegisterNode ( aliasNodeId ) { assert ( aliasNodeId instanceof NodeId ) ; if ( aliasNodeId . namespace !== registeredNodeNameSpace ) { return ; } const node = this . _registeredNodesInv [ aliasNodeId . toString ( ) ] ; if ( ! node ) { return ; } this . _registeredNodesInv [ aliasNodeId . toString ( ) ] = null ; this . _registeredNodes [ node . nodeId . toString ( ) ] = null ; } public resolveRegisteredNode ( aliasNodeId ) { if ( aliasNodeId . namespace !== registeredNodeNameSpace ) { return aliasNodeId ; } const node = this . _registeredNodesInv [ aliasNodeId . toString ( ) ] ; if ( ! node ) { return aliasNodeId ; } return node . nodeId ; } /**
     * true if the underlying channel has been closed or aborted...
     */ public get aborted ( ) : boolean { if ( ! this . channel ) { return true ; } return this . channel . aborted ; } public createSubscription ( parameters ) { const subscription = this . parent . _createSubscriptionOnSession ( this , parameters ) ; assert ( ! Object . prototype . hasOwnProperty . call ( parameters , "id" ) ) ; this . assignSubscription ( subscription ) ; assert ( subscription . $session === this ) ; assert ( subscription . sessionId instanceof NodeId ) ; assert ( sameNodeId ( subscription . sessionId , this . nodeId ) ) ; return subscription ; } public _attach_channel ( channel ) { assert ( this . nonce && this . nonce instanceof Buffer ) ; this . channel = channel ; this . channelId = channel . channelId ; const key = this . authenticationToken . toString ( ) ; assert ( ! Object . prototype . hasOwnProperty . call ( channel . sessionTokens , key ) , "channel has already a session" ) ; channel . sessionTokens [ key ] = this ; this . channel_abort_event_handler = on_channel_abort . bind ( this ) ; channel . on ( "abort" , this . channel_abort_event_handler ) ; } public _detach_channel ( ) { const channel = this . channel ; if ( ! channel ) { return ; } assert ( this . nonce && this . nonce instanceof Buffer ) ; assert ( this . authenticationToken ) ; const key = this . authenticationToken . toString ( ) ; assert ( Object . prototype . hasOwnProperty . call ( channel . sessionTokens , key ) ) ; assert ( this . channel ) ; 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 ; } public _exposeSubscriptionDiagnostics ( subscription ) { debugLog ( "ServerSession#_exposeSubscriptionDiagnostics" ) ; assert ( subscription . $session === this ) ; const subscriptionDiagnosticsArray = this . _getSubscriptionDiagnosticsArray ( ) ; const subscriptionDiagnostics = subscription . subscriptionDiagnostics ; assert ( subscriptionDiagnostics . $subscription === subscription ) ; if ( subscriptionDiagnostics && subscriptionDiagnosticsArray ) { addElement ( subscriptionDiagnostics , subscriptionDiagnosticsArray ) ; } } public _unexposeSubscriptionDiagnostics ( subscription ) { const subscriptionDiagnosticsArray = this . _getSubscriptionDiagnosticsArray ( ) ; const subscriptionDiagnostics = subscription . subscriptionDiagnostics ; assert ( subscriptionDiagnostics instanceof SubscriptionDiagnosticsDataType ) ; if ( subscriptionDiagnostics && subscriptionDiagnosticsArray ) { removeElement ( subscriptionDiagnosticsArray , subscriptionDiagnostics ) ; } debugLog ( "ServerSession#_unexposeSubscriptionDiagnostics" ) ; } method private public watchdogReset ( ) { debugLog ( "Session#watchdogReset: the server session has expired and must be removed from the server" ) ; this . emit ( "timeout" ) ; } private _createSessionObjectInAddressSpace ( ) { if ( this . sessionObject ) { return ; } assert ( ! this . sessionObject , "ServerSession#_createSessionObjectInAddressSpace already called ?" ) ; this . sessionObject = null ; if ( ! this . addressSpace ) { debugLog ( "ServerSession#_createSessionObjectInAddressSpace : no addressSpace" ) ; return ; } const root = this . addressSpace . rootFolder ; 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 ; } 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 ( this ) { if ( sessionDiagnosticsDataType && sessionDiagnosticsVariableType ) { this . _sessionDiagnostics = this . addressSpace ! . constructExtensionObject ( sessionDiagnosticsDataType , { } ) ! as SessionDiagnosticsDataTypeEx ; this . _sessionDiagnostics . $session = this ; if ( this . parent . clientDescription ) { this . _sessionDiagnostics . clientDescription = this . parent . clientDescription ; } Object . defineProperty ( this . _sessionDiagnostics , "clientConnectionTime" , { get ( this ) { return this . $session . clientConnectionTime ; } } ) ; Object . defineProperty ( this . _sessionDiagnostics , "actualSessionTimeout" , { get ( this ) { return this . $session ?. sessionTimeout ; } } ) ; Object . defineProperty ( this . _sessionDiagnostics , "sessionId" , { get ( this ) { return this . $session ?. nodeId ; } } ) ; Object . defineProperty ( this . _sessionDiagnostics , "sessionName" , { get ( this ) { return this . $session ?. sessionName . toString ( ) ; } } ) ; this . sessionDiagnostics = sessionDiagnosticsVariableType . instantiate ( { browseName : new QualifiedName ( { name : "SessionDiagnostics" , namespaceIndex : 0 } ) , componentOf : this . sessionObject , extensionObject : this . _sessionDiagnostics , minimumSamplingInterval : 2000 } ) as UASessionDiagnosticsVariable < DTSessionDiagnostics > ; this . _sessionDiagnostics = this . sessionDiagnostics . $extensionObject as SessionDiagnosticsDataTypeEx ; assert ( this . _sessionDiagnostics . $session === this ) ; const sessionDiagnosticsArray = this . getSessionDiagnosticsArray ( ) ; addElement < SessionDiagnosticsDataType > ( this . _sessionDiagnostics , sessionDiagnosticsArray ) ; } } function createSessionSecurityDiagnosticsStuff ( this ) { if ( sessionSecurityDiagnosticsDataType && sessionSecurityDiagnosticsType ) { this . _sessionSecurityDiagnostics = this . addressSpace ! . constructExtensionObject ( sessionSecurityDiagnosticsDataType , { } ) ! as SessionSecurityDiagnosticsDataTypeEx ; this . _sessionSecurityDiagnostics . $session = this ; Object . defineProperty ( this . _sessionSecurityDiagnostics , "sessionId" , { get ( this ) { return this . $session ?. nodeId ; } } ) ; Object . defineProperty ( this . _sessionSecurityDiagnostics , "clientUserIdOfSession" , { get ( this ) { return "" ; } } ) ; Object . defineProperty ( this . _sessionSecurityDiagnostics , "clientUserIdHistory" , { get ( this ) { return [ ] ; } } ) ; Object . defineProperty ( this . _sessionSecurityDiagnostics , "authenticationMechanism" , { get ( this ) { return "" ; } } ) ; Object . defineProperty ( this . _sessionSecurityDiagnostics , "encoding" , { get ( this ) { return "" ; } } ) ; Object . defineProperty ( this . _sessionSecurityDiagnostics , "transportProtocol" , { get ( this ) { return "opc.tcp" ; } } ) ; Object . defineProperty ( this . _sessionSecurityDiagnostics , "securityMode" , { get ( this ) { const session = this . $session ; return session ?. channel ?. securityMode ; } } ) ; Object . defineProperty ( this . _sessionSecurityDiagnostics , "securityPolicyUri" , { get ( this ) { const session = this . $session ; return session ?. channel ?. securityPolicy ; } } ) ; Object . defineProperty ( this . _sessionSecurityDiagnostics , "clientCertificate" , { get ( this ) { const session = this . $session ; return session ?. channel ! . clientCertificate ; } } ) ; this . sessionSecurityDiagnostics = sessionSecurityDiagnosticsType . instantiate ( { browseName : new QualifiedName ( { name : "SessionSecurityDiagnostics" , namespaceIndex : 0 } ) , componentOf : this . sessionObject , extensionObject : this . _sessionSecurityDiagnostics , minimumSamplingInterval : 2000 } ) as UASessionSecurityDiagnostics < DTSessionSecurityDiagnostics > ; ensureObjectIsSecure ( this . sessionSecurityDiagnostics ) ; this . _sessionSecurityDiagnostics = this . sessionSecurityDiagnostics . $extensionObject as SessionSecurityDiagnosticsDataTypeEx ; assert ( this . _sessionSecurityDiagnostics . $session === this ) ; const sessionSecurityDiagnosticsArray = this . getSessionSecurityDiagnosticsArray ( ) ; const node = addElement < SessionSecurityDiagnosticsDataType > ( this . _sessionSecurityDiagnostics , sessionSecurityDiagnosticsArray ) ; ensureObjectIsSecure ( node ) ; } } function createSessionDiagnosticSummaryUAObject ( this ) { 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 : NodeClass . Object , nodeId : this . nodeId , references , typeDefinition : sessionDiagnosticsObjectType } ) as UAObject ; createSessionDiagnosticsStuff . call ( this ) ; createSessionSecurityDiagnosticsStuff . call ( this ) ; } function createSubscriptionDiagnosticsArray ( this ) { const subscriptionDiagnosticsArrayType = this . addressSpace ! . findVariableType ( "SubscriptionDiagnosticsArrayType" ) ! ; assert ( subscriptionDiagnosticsArrayType . nodeId . toString ( ) === "ns=0;i=2171" ) ; this . subscriptionDiagnosticsArray = createExtObjArrayNode < SubscriptionDiagnosticsDataType > ( this . sessionObject , { browseName : { namespaceIndex : 0 , name : "SubscriptionDiagnosticsArray" } , complexVariableType : "SubscriptionDiagnosticsArrayType" , indexPropertyName : "subscriptionId" , minimumSamplingInterval : 2000 , variableType : "SubscriptionDiagnosticsType" } ) ; } createSessionDiagnosticSummaryUAObject . call ( this ) ; createSubscriptionDiagnosticsArray . call ( this ) ; return this . sessionObject ; } private private _removeSessionObjectFromAddressSpace ( ) { if ( ! this . addressSpace ) { return ; } if ( this . sessionDiagnostics ) { const sessionDiagnosticsArray = this . getSessionDiagnosticsArray ( ) ! ; removeElement ( sessionDiagnosticsArray , this . sessionDiagnostics . $extensionObject ) ; this . addressSpace . deleteNode ( this . sessionDiagnostics ) ; assert ( this . _sessionDiagnostics ! . $session === this ) ; this . _sessionDiagnostics ! . $session = null ; this . _sessionDiagnostics = undefined ; this . sessionDiagnostics = undefined ; } if ( this . sessionSecurityDiagnostics ) { const sessionSecurityDiagnosticsArray = this . getSessionSecurityDiagnosticsArray ( ) ! ; removeElement ( sessionSecurityDiagnosticsArray , this . sessionSecurityDiagnostics . $extensionObject ) ; this . addressSpace . deleteNode ( this . sessionSecurityDiagnostics ) ; 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 private _getSubscriptionDiagnosticsArray ( ) { if ( ! this . addressSpace ) { if ( doDebug ) { console . warn ( "ServerSession#_getSubscriptionDiagnosticsArray : no addressSpace" ) ; } return null ; } const subscriptionDiagnosticsArray = this . subscriptionDiagnosticsArray ; if ( ! subscriptionDiagnosticsArray ) { return null ; } assert ( subscriptionDiagnosticsArray . browseName . toString ( ) === "SubscriptionDiagnosticsArray" ) ; return subscriptionDiagnosticsArray ; } private assignSubscription ( subscription ) { assert ( ! subscription . $session ) ; assert ( this . nodeId instanceof NodeId ) ; subscription . $session = this ; subscription . sessionId = this . nodeId ; this . _cumulatedSubscriptionCount += 1 ; this . emit ( "new_subscription" , subscription ) ; this . _exposeSubscriptionDiagnostics ( subscription ) ; subscription . once ( "terminated" , ( ) => { this . emit ( "subscription_terminated" , subscription ) ; } ) ; } private _deleteSubscriptions ( ) { assert ( this . publishEngine ) ; const subscriptions = this . publishEngine . subscriptions ; for ( const subscription of subscriptions ) { this . deleteSubscription ( subscription . id ) ; } } }