module import { EventEmitter } from "events" ; import * as async from "async" ; import * as chalk from "chalk" ; import { assert } from "node-opcua-assert" ; import { addElement , AddressSpace , BaseNode , bindExtObjArrayNode , ensureDatatypeExtractedWithCallback , ensureObjectIsSecure , MethodFunctor , removeElement , SessionContext , UADataType , UADynamicVariableArray , UAMethod , UAObject , UAServerDiagnosticsSummary , UAServerStatus , UAVariable , UAServerDiagnostics , BindVariableOptions , ISessionContext , DTServerStatus , resolveOpaqueOnAddressSpace , ContinuationData } from "node-opcua-address-space" ; import { generateAddressSpace } from "node-opcua-address-space/nodeJS" ; import { DataValue , coerceTimestampsToReturn , apply_timestamps_no_copy } from "node-opcua-data-value" ; import { ServerDiagnosticsSummaryDataType , ServerState , ServerStatusDataType , SubscriptionDiagnosticsDataType } from "node-opcua-common" ; import { AttributeIds , BrowseDirection , coerceLocalizedText , LocalizedTextLike , makeAccessLevelFlag , NodeClass , QualifiedName } from "node-opcua-data-model" ; import { coerceNodeId , makeNodeId , NodeId , NodeIdLike , NodeIdType , resolveNodeId } from "node-opcua-nodeid" ; import { BrowseResult } from "node-opcua-service-browse" ; import { ReadRequest , TimestampsToReturn } from "node-opcua-service-read" ; import { UInt32 } from "node-opcua-basic-types" ; import { CreateSubscriptionRequestLike } from "node-opcua-client" ; import { ExtraDataTypeManager } from "node-opcua-client-dynamic-extension-object" ; import { DataTypeIds , MethodIds , ObjectIds , VariableIds } from "node-opcua-constants" ; import { getCurrentClock , minOPCUADate } from "node-opcua-date-time" ; import { checkDebugFlag , make_debugLog , make_errorLog , make_warningLog , traceFromThisProjectOnly } from "node-opcua-debug" ; import { nodesets } from "node-opcua-nodesets" ; import { NumericRange } from "node-opcua-numeric-range" ; import { ObjectRegistry } from "node-opcua-object-registry" ; import { CallMethodResult } from "node-opcua-service-call" ; import { TransferResult } from "node-opcua-service-subscription" ; import { ApplicationDescription } from "node-opcua-service-endpoints" ; import { HistoryReadDetails , HistoryReadRequest , HistoryReadResult , HistoryReadValueId } from "node-opcua-service-history" ; import { StatusCode , StatusCodes , CallbackT , StatusCodeCallback } from "node-opcua-status-code" ; import { BrowseDescription , BrowsePath , BrowsePathResult , BuildInfo , BuildInfoOptions , ReadAtTimeDetails , ReadEventDetails , ReadProcessedDetails , ReadRawModifiedDetails , SessionDiagnosticsDataType , SessionSecurityDiagnosticsDataType , WriteValue , ReadValueId , TimeZoneDataType , ProgramDiagnosticDataType , CallMethodResultOptions } from "node-opcua-types" ; import { DataType , isValidVariant , Variant , VariantArrayType } from "node-opcua-variant" ; import { HistoryServerCapabilities , HistoryServerCapabilitiesOptions } from "./history_server_capabilities" ; import { MonitoredItem } from "./monitored_item" ; import { OperationLimits , ServerCapabilities , ServerCapabilitiesOptions } from "./server_capabilities" ; import { ServerSidePublishEngine } from "./server_publish_engine" ; import { ServerSidePublishEngineForOrphanSubscription } from "./server_publish_engine_for_orphan_subscriptions" ; import { ServerSession } from "./server_session" ; import { Subscription } from "./server_subscription" ; import { sessionsCompatibleForTransfer } from "./sessions_compatible_for_transfer" ; import { OPCUAServerOptions } from "./opcua_server" ; const debugLog = make_debugLog ( __filename ) ; const errorLog = make_errorLog ( __filename ) ; const warningLog = make_warningLog ( __filename ) ; const doDebug = checkDebugFlag ( __filename ) ; function upperCaseFirst ( str ) { return str . slice ( 0 , 1 ) . toUpperCase ( ) + str . slice ( 1 ) ; } function shutdownAndDisposeAddressSpace ( this ) { if ( this . addressSpace ) { this . addressSpace . shutdown ( ) ; this . addressSpace . dispose ( ) ; delete ( this as any ) . addressSpace ; } } function setSubscriptionDurable ( this , inputArguments , context , callback ) { assert ( Array . isArray ( inputArguments ) ) ; assert ( typeof callback === "function" ) ; assert ( Object . prototype . hasOwnProperty . call ( context , "session" ) , " expecting a session id in the context object" ) ; const session = context . session as ServerSession ; if ( ! session ) { return callback ( null , { statusCode : StatusCodes . BadInternalError } ) ; } const subscriptionId = inputArguments [ 0 ] . value as UInt32 ; const lifetimeInHours = inputArguments [ 1 ] . value as UInt32 ; const subscription = session . getSubscription ( subscriptionId ) ; if ( ! subscription ) { if ( this . findSubscription ( subscriptionId ) ) { return callback ( null , { statusCode : StatusCodes . BadUserAccessDenied } ) ; } return callback ( null , { statusCode : StatusCodes . BadSubscriptionIdInvalid } ) ; } if ( subscription . monitoredItemCount > 0 ) { return callback ( null , { statusCode : StatusCodes . BadInvalidState } ) ; } /**
     * MonitoredItems are used to monitor Variable Values for data changes and event notifier
     * Objects for new Events. Subscriptions are used to combine data changes and events of
     * the assigned MonitoredItems to an optimized stream of network messages. A reliable
     * delivery is ensured as long as the lifetime of the Subscription and the queues in the
     * MonitoredItems are long enough for a network interruption between OPC UA Client and
     * Server. All queues that ensure reliable delivery are normally kept in memory and a
     * Server restart would delete them.
     * There are use cases where OPC UA Clients have no permanent network connection to the
     * OPC UA Server or where reliable delivery of data changes and events is necessary
     * even if the OPC UA Server is restarted or the network connection is interrupted
     * for a longer time.
     * To ensure this reliable delivery, the OPC UA Server must store collected data and
     * events in non-volatile memory until the OPC UA Client has confirmed reception.
     * It is possible that there will be data lost if the Server is not shut down gracefully
     * or in case of power failure. But the OPC UA Server should store the queues frequently
     * even if the Server is not shut down.
     * The Method SetSubscriptionDurable defined in OPC 10000-5 is used to set a Subscription
     * into this durable mode and to allow much longer lifetimes and queue sizes than for normal
     * Subscriptions. The Method shall be called before the MonitoredItems are created in the
     * durable Subscription. The Server shall verify that the Method is called within the
     * Session context of the Session that owns the Subscription.
     *
     * A value of 0 for the parameter lifetimeInHours requests the highest lifetime supported by the Server.
     */ const highestLifetimeInHours = 24 * 100 ; const revisedLifetimeInHours = lifetimeInHours === 0 ? highestLifetimeInHours : Math . max ( 1 , Math . min ( lifetimeInHours , highestLifetimeInHours ) ) ; const currentLifeTimeInHours = ( subscription . lifeTimeCount * subscription . publishingInterval ) / ( 1000 * 60 * 60 ) ; if ( currentLifeTimeInHours < revisedLifetimeInHours ) { const requestedLifetimeCount = Math . ceil ( ( revisedLifetimeInHours * ( 1000 * 60 * 60 ) ) / subscription . publishingInterval ) ; subscription . modify ( { requestedMaxKeepAliveCount : subscription . maxKeepAliveCount , requestedPublishingInterval : subscription . publishingInterval , maxNotificationsPerPublish : subscription . maxNotificationsPerPublish , priority : subscription . priority , requestedLifetimeCount } ) ; } const callMethodResult = new CallMethodResult ( { statusCode : StatusCodes . Good , outputArguments : [ { dataType : DataType . UInt32 , arrayType : VariantArrayType . Scalar , value : revisedLifetimeInHours } ] } ) ; callback ( null , callMethodResult ) ; } function getMonitoredItemsId ( this , inputArguments , context , callback ) { assert ( Array . isArray ( inputArguments ) ) ; assert ( typeof callback === "function" ) ; assert ( Object . prototype . hasOwnProperty . call ( context , "session" ) , " expecting a session id in the context object" ) ; const session = context . session as ServerSession ; if ( ! session ) { return callback ( null , { statusCode : StatusCodes . BadInternalError } ) ; } const subscriptionId = inputArguments [ 0 ] . value ; const subscription = session . getSubscription ( subscriptionId ) ; if ( ! subscription ) { if ( this . findSubscription ( subscriptionId ) ) { return callback ( null , { statusCode : StatusCodes . BadUserAccessDenied } ) ; } return callback ( null , { statusCode : StatusCodes . BadSubscriptionIdInvalid } ) ; } const result = subscription . getMonitoredItems ( ) ; assert ( result . statusCode ) ; assert ( result . serverHandles . length === result . clientHandles . length ) ; const callMethodResult = new CallMethodResult ( { statusCode : result . statusCode , outputArguments : [ { dataType : DataType . UInt32 , arrayType : VariantArrayType . Array , value : result . serverHandles } , { dataType : DataType . UInt32 , arrayType : VariantArrayType . Array , value : result . clientHandles } ] } ) ; callback ( null , callMethodResult ) ; } function __bindVariable ( self , nodeId , options ? ) { options = options || { } ; const variable = self . addressSpace ! . findNode ( nodeId ) as UAVariable ; if ( variable && variable . bindVariable ) { variable . bindVariable ( options , true ) ; assert ( typeof variable . asyncRefresh === "function" ) ; assert ( typeof ( variable as any ) . refreshFunc === "function" ) ; } else { warningLog ( "Warning: cannot bind object with id " , nodeId . toString ( ) , " please check your nodeset.xml file or add this node programmatically" ) ; } } let next_subscriptionId = Math . ceil ( Math . random ( ) * 1000000 ) ; function _get_next_subscriptionId ( ) { debugLog ( " next_subscriptionId = " , next_subscriptionId ) ; return next_subscriptionId ++ ; } export type StringGetter = ( ) => string ; export interface ServerEngineOptions { applicationUri : string | StringGetter ; buildInfo ? : BuildInfoOptions ; isAuditing ? : boolean ; /**
     * set to true to enable serverDiagnostics
     */ serverDiagnosticsEnabled ? : boolean ; serverCapabilities ? : ServerCapabilitiesOptions ; historyServerCapabilities ? : HistoryServerCapabilitiesOptions ; } export interface CreateSessionOption { clientDescription ? : ApplicationDescription ; sessionTimeout ? : number ; } export type ClosingReason = "Timeout" | "Terminated" | "CloseSession" | "Forcing" ; /**
 *
 */ export class ServerEngine extends EventEmitter { public static readonly registry = new ObjectRegistry ( ) ; public isAuditing : boolean ; public serverDiagnosticsSummary : ServerDiagnosticsSummaryDataType ; public serverDiagnosticsEnabled : boolean ; public serverCapabilities : ServerCapabilities ; public historyServerCapabilities : HistoryServerCapabilities ; public clientDescription ? : ApplicationDescription ; public addressSpace : AddressSpace | null ; public _internalState : "creating" | "initializing" | "initialized" | "shutdown" | "disposed" ; private _sessions : { [ key ] : ServerSession ; } ; private _closedSessions : { [ key ] : ServerSession ; } ; private _orphanPublishEngine ? : ServerSidePublishEngineForOrphanSubscription ; private _shutdownTasks : ( ( this : ServerEngine ) => void ) [ ] ; private _applicationUri : string ; private _expectedShutdownTime ! : Date ; private _serverStatus : ServerStatusDataType ; constructor ( options ) { super ( ) ; options = options || ( { applicationUri : "" } as ServerEngineOptions ) ; options . buildInfo = options . buildInfo || { } ; ServerEngine . registry . register ( this ) ; this . _sessions = { } ; this . _closedSessions = { } ; this . _orphanPublishEngine = undefined ; this . isAuditing = typeof options . isAuditing === "boolean" ? options . isAuditing : false ; options . buildInfo . buildDate = options . buildInfo . buildDate || new Date ( ) ; this . _serverStatus = new ServerStatusDataType ( { buildInfo : options . buildInfo , currentTime : new Date ( ) , secondsTillShutdown : 0 , shutdownReason : { text : "" } , startTime : new Date ( ) , state : ServerState . NoConfiguration } ) ; options . serverCapabilities = options . serverCapabilities || { } ; options . serverCapabilities . serverProfileArray = options . serverCapabilities . serverProfileArray || [ "http://opcfoundation.org/UA-Profile/Server/Standard" , "http://opcfoundation.org/UA-Profile/Server/DataAccess" , "http://opcfoundation.org/UA-Profile/Server/Events" , "http://opcfoundation.org/UA-Profile/Client/HistoricalAccess" , "http://opcfoundation.org/UA-Profile/Server/Methods" , "http://opcfoundation.org/UA-Profile/Server/StandardEventSubscription" , "http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary" , "http://opcfoundation.org/UA-Profile/Server/FileAccess" , "http://opcfoundation.org/UA-Profile/Server/StateMachine" ] ; options . serverCapabilities . localeIdArray = options . serverCapabilities . localeIdArray || [ "en-EN" , "fr-FR" ] ; this . serverCapabilities = new ServerCapabilities ( options . serverCapabilities ) ; this . serverCapabilities . softwareCertificates = [ ] ; ( this . serverCapabilities as any ) . __defineGetter__ ( "minSupportedSampleRate" , ( ) => { return MonitoredItem . minimumSamplingInterval ; } ) ; this . historyServerCapabilities = new HistoryServerCapabilities ( options . historyServerCapabilities ) ; this . serverDiagnosticsSummary = new ServerDiagnosticsSummaryDataType ( ) ; assert ( Object . prototype . hasOwnProperty . call ( this . serverDiagnosticsSummary , "currentSessionCount" ) ) ; assert ( Object . prototype . hasOwnProperty . call ( this . serverDiagnosticsSummary , "currentSubscriptionCount" ) ) ; ( this . serverDiagnosticsSummary as any ) . __defineGetter__ ( "currentSubscriptionCount" , ( ) => { let counter = 0 ; Object . values ( this . _sessions ) . forEach ( ( session ) => { counter += session . currentSubscriptionCount ; } ) ; return counter ; } ) ; this . _internalState = "creating" ; this . setServerState ( ServerState . NoConfiguration ) ; this . addressSpace = null ; this . _shutdownTasks = [ ] ; this . _applicationUri = "" ; if ( typeof options . applicationUri === "function" ) { ( this as any ) . __defineGetter__ ( "_applicationUri" , options . applicationUri ) ; } else { this . _applicationUri = options . applicationUri || "<unset _applicationUri>" ; } options . serverDiagnosticsEnabled = Object . prototype . hasOwnProperty . call ( options , "serverDiagnosticsEnable" ) ? options . serverDiagnosticsEnabled : true ; this . serverDiagnosticsEnabled = options . serverDiagnosticsEnabled ! ; } public isStarted ( ) { return ! ! this . _serverStatus ! ; } public dispose ( ) { this . addressSpace = null ; assert ( Object . keys ( this . _sessions ) . length === 0 , "ServerEngine#_sessions not empty" ) ; this . _sessions = { } ; this . _closedSessions = { } ; assert ( Object . keys ( this . _closedSessions ) . length === 0 , "ServerEngine#_closedSessions not empty" ) ; this . _closedSessions = { } ; if ( this . _orphanPublishEngine ) { this . _orphanPublishEngine . dispose ( ) ; this . _orphanPublishEngine = undefined ; } this . _shutdownTasks = [ ] ; this . _serverStatus = null as any as ServerStatusDataType ; this . _internalState = "disposed" ; this . removeAllListeners ( ) ; ServerEngine . registry . unregister ( this ) ; } public get startTime ( ) : Date { return this . _serverStatus . startTime ! ; } public get currentTime ( ) : Date { return this . _serverStatus . currentTime ! ; } public get buildInfo ( ) : BuildInfo { return this . _serverStatus . buildInfo ; } method public registerShutdownTask ( task ) { assert ( typeof task === "function" ) ; this . _shutdownTasks . push ( task ) ; } method public shutdown ( ) { debugLog ( "ServerEngine#shutdown" ) ; this . _internalState = "shutdown" ; this . setServerState ( ServerState . Shutdown ) ; const tokens = Object . keys ( this . _sessions ) . map ( ( key ) => { const session = this . _sessions [ key ] ; return session . authenticationToken ; } ) ; if ( this . _orphanPublishEngine ) { this . _orphanPublishEngine . shutdown ( ) ; } for ( const token of tokens ) { this . closeSession ( token , true , "Terminated" ) ; } assert ( this . currentSessionCount === 0 ) ; assert ( this . currentSubscriptionCount === 0 , "all subscriptions must have been terminated" ) ; this . _shutdownTasks . push ( shutdownAndDisposeAddressSpace ) ; for ( const task of this . _shutdownTasks ) { task . call ( this ) ; } this . dispose ( ) ; } /**
     * the number of active sessions
     */ public get currentSessionCount ( ) : number { return this . serverDiagnosticsSummary . currentSessionCount ; } /**
     * the cumulated number of sessions that have been opened since this object exists
     */ public get cumulatedSessionCount ( ) : number { return this . serverDiagnosticsSummary . cumulatedSessionCount ; } /**
     * the number of active subscriptions.
     */ public get currentSubscriptionCount ( ) : number { return this . serverDiagnosticsSummary . currentSubscriptionCount ; } /**
     * the cumulated number of subscriptions that have been created since this object exists
     */ public get cumulatedSubscriptionCount ( ) : number { return this . serverDiagnosticsSummary . cumulatedSubscriptionCount ; } public get rejectedSessionCount ( ) : number { return this . serverDiagnosticsSummary . rejectedSessionCount ; } public get rejectedRequestsCount ( ) : number { return this . serverDiagnosticsSummary . rejectedRequestsCount ; } public get sessionAbortCount ( ) : number { return this . serverDiagnosticsSummary . sessionAbortCount ; } public get sessionTimeoutCount ( ) : number { return this . serverDiagnosticsSummary . sessionTimeoutCount ; } public get publishingIntervalCount ( ) : number { return this . serverDiagnosticsSummary . publishingIntervalCount ; } public incrementSessionTimeoutCount ( ) { if ( this . serverDiagnosticsSummary && this . serverDiagnosticsEnabled ) { this . serverDiagnosticsSummary . sessionTimeoutCount += 1 ; } } public incrementSessionAbortCount ( ) { if ( this . serverDiagnosticsSummary && this . serverDiagnosticsEnabled ) { this . serverDiagnosticsSummary . sessionAbortCount += 1 ; } } public incrementRejectedRequestsCount ( ) { if ( this . serverDiagnosticsSummary && this . serverDiagnosticsEnabled ) { this . serverDiagnosticsSummary . rejectedRequestsCount += 1 ; } } /**
     * increment rejected session count (also increment rejected requests count)
     */ public incrementRejectedSessionCount ( ) { if ( this . serverDiagnosticsSummary && this . serverDiagnosticsEnabled ) { this . serverDiagnosticsSummary . rejectedSessionCount += 1 ; } this . incrementRejectedRequestsCount ( ) ; } public incrementSecurityRejectedRequestsCount ( ) { if ( this . serverDiagnosticsSummary && this . serverDiagnosticsEnabled ) { this . serverDiagnosticsSummary . securityRejectedRequestsCount += 1 ; } this . incrementRejectedRequestsCount ( ) ; } /**
     * increment rejected session count (also increment rejected requests count)
     */ public incrementSecurityRejectedSessionCount ( ) { if ( this . serverDiagnosticsSummary && this . serverDiagnosticsEnabled ) { this . serverDiagnosticsSummary . securityRejectedSessionCount += 1 ; } this . incrementSecurityRejectedRequestsCount ( ) ; } public setShutdownTime ( date ) { this . _expectedShutdownTime = date ; } public setShutdownReason ( reason ) { this . addressSpace ?. rootFolder . objects . server . serverStatus . shutdownReason . setValueFromSource ( { dataType : DataType . LocalizedText , value : coerceLocalizedText ( reason ) ! } ) ; } method return public secondsTillShutdown ( ) { if ( ! this . _expectedShutdownTime ) { return 0 ; } const now = Date . now ( ) ; return Math . max ( 0 , Math . ceil ( ( this . _expectedShutdownTime . getTime ( ) - now ) / 1000 ) ) ; } /**
     * the name of the server
     */ public get serverName ( ) : string { return this . _serverStatus . buildInfo ! . productName ! ; } /**
     * the server urn
     */ public get serverNameUrn ( ) : string { return this . _applicationUri ; } /**
     * the urn of the server namespace
     */ public get serverNamespaceUrn ( ) : string { return this . _applicationUri ; } public get serverStatus ( ) : ServerStatusDataType { return this . _serverStatus ; } public setServerState ( serverState ) { assert ( serverState !== null && serverState !== undefined ) ; this . addressSpace ?. rootFolder ?. objects ?. server ?. serverStatus ?. state ?. setValueFromSource ( { dataType : DataType . Int32 , value : serverState } ) ; } public getServerDiagnosticsEnabledFlag ( ) { const server = this . addressSpace ! . rootFolder . objects . server ; const serverDiagnostics = server . getComponentByName ( "ServerDiagnostics" ) as UAVariable ; if ( ! serverDiagnostics ) { return false ; } return serverDiagnostics . readValue ( ) . value . value ; } method async param options param options . nodeset_filename { String } param callback public initialize ( options , callback ) { assert ( ! this . addressSpace ) ; this . _internalState = "initializing" ; options = options || { } ; assert ( typeof callback === "function" ) ; options . nodeset_filename = options . nodeset_filename || nodesets . standard ; const startTime = new Date ( ) ; debugLog ( "Loading " , options . nodeset_filename , "..." ) ; this . addressSpace = AddressSpace . create ( ) ; const serverNamespace = this . addressSpace . registerNamespace ( this . serverNamespaceUrn ) ; assert ( serverNamespace . index === 1 ) ; generateAddressSpace ( this . addressSpace , options . nodeset_filename , ( ) => { if ( ! this . addressSpace ) { throw new Error ( "Internal error" ) ; } const addressSpace = this . addressSpace ; const endTime = new Date ( ) ; debugLog ( "Loading " , options . nodeset_filename , " done : " , endTime . getTime ( ) - startTime . getTime ( ) , " ms" ) ; const bindVariableIfPresent = ( nodeId , opts ) => { assert ( nodeId instanceof NodeId ) ; assert ( ! nodeId . isEmpty ( ) ) ; const obj = addressSpace . findNode ( nodeId ) ; if ( obj ) { __bindVariable ( this , nodeId , opts ) ; } return obj ; } ; const server_NamespaceArray_Id = makeNodeId ( VariableIds . Server_NamespaceArray ) ; bindVariableIfPresent ( server_NamespaceArray_Id , { get ( ) { return new Variant ( { arrayType : VariantArrayType . Array , dataType : DataType . String , value : addressSpace . getNamespaceArray ( ) . map ( ( x ) => x . namespaceUri ) } ) ; } , set : null } ) ; const server_NameUrn_var = new Variant ( { arrayType : VariantArrayType . Array , dataType : DataType . String , value : [ this . serverNameUrn ] } ) ; const server_ServerArray_Id = makeNodeId ( VariableIds . Server_ServerArray ) ; bindVariableIfPresent ( server_ServerArray_Id , { get ( ) { return server_NameUrn_var ; } , set : null } ) ; const namespaces = makeNodeId ( ObjectIds . Server_Namespaces ) ; const namespacesNode = addressSpace . findNode ( namespaces ) as UAObject ; if ( namespacesNode ) { for ( const ns of namespacesNode . getComponents ( ) ) { const defaultUserRolePermissions = ns . getChildByName ( "DefaultUserRolePermissions" ) as UAVariable | null ; if ( defaultUserRolePermissions ) { defaultUserRolePermissions . setValueFromSource ( { dataType : DataType . Null } ) ; } const defaultRolePermissions = ns . getChildByName ( "DefaultRolePermissions" ) as UAVariable | null ; if ( defaultRolePermissions ) { defaultRolePermissions . setValueFromSource ( { dataType : DataType . Null } ) ; } } } const bindStandardScalar = ( id , dataType , func , setter_func ? ) => { assert ( typeof id === "number" , "expecting id to be a number" ) ; assert ( typeof func === "function" ) ; assert ( typeof setter_func === "function" || ! setter_func ) ; assert ( dataType !== null ) ; let setter_func2 = null ; if ( setter_func ) { setter_func2 = ( variant ) => { const variable2 = ! ! variant . value ; setter_func ( variable2 ) ; return StatusCodes . Good ; } ; } const nodeId = makeNodeId ( id ) ; if ( ! isValidVariant ( VariantArrayType . Scalar , dataType , func ( ) ) ) { errorLog ( "func" , func ( ) ) ; throw new Error ( "bindStandardScalar : func doesn't provide an value of type " + DataType [ dataType ] ) ; } return bindVariableIfPresent ( nodeId , { get ( ) { return new Variant ( { arrayType : VariantArrayType . Scalar , dataType , value : func ( ) } ) ; } , set : setter_func2 } ) ; } ; const bindStandardArray = ( id , variantDataType , dataType , func ) => { assert ( typeof func === "function" ) ; assert ( variantDataType !== null ) ; const nodeId = makeNodeId ( id ) ; assert ( isValidVariant ( VariantArrayType . Array , variantDataType , func ( ) ) ) ; bindVariableIfPresent ( nodeId , { get ( ) { const value = func ( ) ; assert ( Array . isArray ( value ) ) ; return new Variant ( { arrayType : VariantArrayType . Array , dataType : variantDataType , value } ) ; } , set : null } ) ; } ; bindStandardScalar ( VariableIds . Server_EstimatedReturnTime , DataType . DateTime , ( ) => minOPCUADate ) ; const timeZoneDataType = addressSpace . findDataType ( resolveNodeId ( DataTypeIds . TimeZoneDataType ) ) ! ; const timeZone = new TimeZoneDataType ( { daylightSavingInOffset : false , offset : 0 } ) ; bindStandardScalar ( VariableIds . Server_LocalTime , DataType . ExtensionObject , ( ) => { return timeZone ; } ) ; bindStandardScalar ( VariableIds . Server_ServiceLevel , DataType . Byte , ( ) => { return 255 ; } ) ; bindStandardScalar ( VariableIds . Server_Auditing , DataType . Boolean , ( ) => { return this . isAuditing ; } ) ; const engine = this ; const makeNotReadableIfEnabledFlagIsFalse = ( variable ) => { const originalIsReadable = variable . isReadable ; variable . isUserReadable = checkReadableFlag ; function checkReadableFlag ( this , context ) { const isEnabled = engine . serverDiagnosticsEnabled ; return originalIsReadable . call ( this , context ) && isEnabled ; } for ( const c of variable . getAggregates ( ) ) { if ( c . nodeClass === NodeClass . Variable ) { makeNotReadableIfEnabledFlagIsFalse ( c as UAVariable ) ; } } } ; const bindServerDiagnostics = ( ) => { bindStandardScalar ( VariableIds . Server_ServerDiagnostics_EnabledFlag , DataType . Boolean , ( ) => { return this . serverDiagnosticsEnabled ; } , ( newFlag ) => { this . serverDiagnosticsEnabled = newFlag ; } ) ; const nodeId = makeNodeId ( VariableIds . Server_ServerDiagnostics_ServerDiagnosticsSummary ) ; const serverDiagnosticsSummaryNode = addressSpace . findNode ( nodeId ) as UAServerDiagnosticsSummary < ServerDiagnosticsSummaryDataType > ; if ( serverDiagnosticsSummaryNode ) { serverDiagnosticsSummaryNode . bindExtensionObject ( this . serverDiagnosticsSummary ) ; this . serverDiagnosticsSummary = serverDiagnosticsSummaryNode . $extensionObject ; makeNotReadableIfEnabledFlagIsFalse ( serverDiagnosticsSummaryNode ) ; } } ; const bindServerStatus = ( ) => { const serverStatusNode = addressSpace . findNode ( makeNodeId ( VariableIds . Server_ServerStatus ) ) as UAServerStatus < DTServerStatus > ; if ( ! serverStatusNode ) { return ; } if ( serverStatusNode ) { serverStatusNode . bindExtensionObject ( this . _serverStatus ) ; serverStatusNode . minimumSamplingInterval = 1000 ; } const currentTimeNode = addressSpace . findNode ( makeNodeId ( VariableIds . Server_ServerStatus_CurrentTime ) ) as UAVariable ; if ( currentTimeNode ) { currentTimeNode . minimumSamplingInterval = 1000 ; } const secondsTillShutdown = addressSpace . findNode ( makeNodeId ( VariableIds . Server_ServerStatus_SecondsTillShutdown ) ) as UAVariable ; if ( secondsTillShutdown ) { secondsTillShutdown . minimumSamplingInterval = 1000 ; } assert ( serverStatusNode . $extensionObject ) ; serverStatusNode . $extensionObject = new Proxy ( serverStatusNode . $extensionObject , { get ( target , prop ) { if ( prop === "currentTime" ) { serverStatusNode . currentTime . touchValue ( ) ; return new Date ( ) ; } else if ( prop === "secondsTillShutdown" ) { serverStatusNode . secondsTillShutdown . touchValue ( ) ; return engine . secondsTillShutdown ( ) ; } return ( target as any ) [ prop ] ; } } ) ; this . _serverStatus = serverStatusNode . $extensionObject ; } ; const bindServerCapabilities = ( ) => { bindStandardArray ( VariableIds . Server_ServerCapabilities_ServerProfileArray , DataType . String , DataType . String , ( ) => { return this . serverCapabilities . serverProfileArray ; } ) ; bindStandardArray ( VariableIds . Server_ServerCapabilities_LocaleIdArray , DataType . String , "LocaleId" , ( ) => { return this . serverCapabilities . localeIdArray ; } ) ; bindStandardScalar ( VariableIds . Server_ServerCapabilities_MinSupportedSampleRate , DataType . Double , ( ) => { return this . serverCapabilities . minSupportedSampleRate ; } ) ; bindStandardScalar ( VariableIds . Server_ServerCapabilities_MaxBrowseContinuationPoints , DataType . UInt16 , ( ) => { return this . serverCapabilities . maxBrowseContinuationPoints ; } ) ; bindStandardScalar ( VariableIds . Server_ServerCapabilities_MaxQueryContinuationPoints , DataType . UInt16 , ( ) => { return this . serverCapabilities . maxQueryContinuationPoints ; } ) ; bindStandardScalar ( VariableIds . Server_ServerCapabilities_MaxHistoryContinuationPoints , DataType . UInt16 , ( ) => { return this . serverCapabilities . maxHistoryContinuationPoints ; } ) ; bindStandardArray ( VariableIds . Server_ServerCapabilities_SoftwareCertificates , DataType . ExtensionObject , "SoftwareCertificates" , ( ) => { return this . serverCapabilities . softwareCertificates ; } ) ; bindStandardScalar ( VariableIds . Server_ServerCapabilities_MaxArrayLength , DataType . UInt32 , ( ) => { return this . serverCapabilities . maxArrayLength ; } ) ; bindStandardScalar ( VariableIds . Server_ServerCapabilities_MaxStringLength , DataType . UInt32 , ( ) => { return this . serverCapabilities . maxStringLength ; } ) ; bindStandardScalar ( VariableIds . Server_ServerCapabilities_MaxByteStringLength , DataType . UInt32 , ( ) => { return this . serverCapabilities . maxByteStringLength ; } ) ; const bindOperationLimits = ( operationLimits ) => { assert ( operationLimits !== null && typeof operationLimits === "object" ) ; const keys = Object . keys ( operationLimits ) ; keys . forEach ( ( key ) => { const uid = "Server_ServerCapabilities_OperationLimits_" + upperCaseFirst ( key ) ; const nodeId = makeNodeId ( ( VariableIds as any ) [ uid ] ) ; assert ( ! nodeId . isEmpty ( ) ) ; bindStandardScalar ( ( VariableIds as any ) [ uid ] , DataType . UInt32 , ( ) => { return ( operationLimits as any ) [ key ] ; } ) ; } ) ; } ; bindOperationLimits ( this . serverCapabilities . operationLimits ) ; function fix_ProgramStateMachineType_ProgramDiagnostics ( ) { const nodeId = coerceNodeId ( "i=2399" ) ; const variable = addressSpace . findNode ( nodeId ) as UAVariable ; if ( variable ) { ( variable as any ) . $extensionObject = new ProgramDiagnosticDataType ( { } ) ; } } fix_ProgramStateMachineType_ProgramDiagnostics ( ) ; } ; const bindHistoryServerCapabilities = ( ) => { bindStandardScalar ( VariableIds . HistoryServerCapabilities_MaxReturnDataValues , DataType . UInt32 , ( ) => { return this . historyServerCapabilities . maxReturnDataValues ; } ) ; bindStandardScalar ( VariableIds . HistoryServerCapabilities_MaxReturnEventValues , DataType . UInt32 , ( ) => { return this . historyServerCapabilities . maxReturnEventValues ; } ) ; bindStandardScalar ( VariableIds . HistoryServerCapabilities_AccessHistoryDataCapability , DataType . Boolean , ( ) => { return this . historyServerCapabilities . accessHistoryDataCapability ; } ) ; bindStandardScalar ( VariableIds . HistoryServerCapabilities_AccessHistoryEventsCapability , DataType . Boolean , ( ) => { return this . historyServerCapabilities . accessHistoryEventsCapability ; } ) ; bindStandardScalar ( VariableIds . HistoryServerCapabilities_InsertDataCapability , DataType . Boolean , ( ) => { return this . historyServerCapabilities . insertDataCapability ; } ) ; bindStandardScalar ( VariableIds . HistoryServerCapabilities_ReplaceDataCapability , DataType . Boolean , ( ) => { return this . historyServerCapabilities . replaceDataCapability ; } ) ; bindStandardScalar ( VariableIds . HistoryServerCapabilities_UpdateDataCapability , DataType . Boolean , ( ) => { return this . historyServerCapabilities . updateDataCapability ; } ) ; bindStandardScalar ( VariableIds . HistoryServerCapabilities_InsertEventCapability , DataType . Boolean , ( ) => { return this . historyServerCapabilities . insertEventCapability ; } ) ; bindStandardScalar ( VariableIds . HistoryServerCapabilities_ReplaceEventCapability , DataType . Boolean , ( ) => { return this . historyServerCapabilities . replaceEventCapability ; } ) ; bindStandardScalar ( VariableIds . HistoryServerCapabilities_UpdateEventCapability , DataType . Boolean , ( ) => { return this . historyServerCapabilities . updateEventCapability ; } ) ; bindStandardScalar ( VariableIds . HistoryServerCapabilities_DeleteEventCapability , DataType . Boolean , ( ) => { return this . historyServerCapabilities . deleteEventCapability ; } ) ; bindStandardScalar ( VariableIds . HistoryServerCapabilities_DeleteRawCapability , DataType . Boolean , ( ) => { return this . historyServerCapabilities . deleteRawCapability ; } ) ; bindStandardScalar ( VariableIds . HistoryServerCapabilities_DeleteAtTimeCapability , DataType . Boolean , ( ) => { return this . historyServerCapabilities . deleteAtTimeCapability ; } ) ; bindStandardScalar ( VariableIds . HistoryServerCapabilities_InsertAnnotationCapability , DataType . Boolean , ( ) => { return this . historyServerCapabilities . insertAnnotationCapability ; } ) ; } ; bindServerDiagnostics ( ) ; bindServerStatus ( ) ; bindServerCapabilities ( ) ; bindHistoryServerCapabilities ( ) ; const bindExtraStuff = ( ) => { bindStandardScalar ( VariableIds . DataTypeDescriptionType_DataTypeVersion , DataType . String , ( ) => { return "0" ; } ) ; const namingRuleDataTypeNode = addressSpace . findDataType ( resolveNodeId ( DataTypeIds . NamingRuleType ) ) ! as UADataType ; if ( namingRuleDataTypeNode ) { const namingRuleType = ( namingRuleDataTypeNode as any ) . _getEnumerationInfo ( ) . nameIndex ; if ( ! namingRuleType ) { throw new Error ( "Cannot find Enumeration definition for NamingRuleType" ) ; } bindStandardScalar ( VariableIds . ModellingRuleType_NamingRule , DataType . Int32 , ( ) => { return 0 ; } ) ; bindStandardScalar ( VariableIds . ModellingRule_Mandatory_NamingRule , DataType . Int32 , ( ) => { return namingRuleType . Mandatory ? namingRuleType . Mandatory . value : 0 ; } ) ; bindStandardScalar ( VariableIds . ModellingRule_Optional_NamingRule , DataType . Int32 , ( ) => { return namingRuleType . Optional ? namingRuleType . Optional . value : 0 ; } ) ; bindStandardScalar ( VariableIds . ModellingRule_ExposesItsArray_NamingRule , DataType . Int32 , ( ) => { return namingRuleType . ExposesItsArray ? namingRuleType . ExposesItsArray . value : 0 ; } ) ; bindStandardScalar ( VariableIds . ModellingRule_MandatoryPlaceholder_NamingRule , DataType . Int32 , ( ) => { return namingRuleType . MandatoryPlaceholder ? namingRuleType . MandatoryPlaceholder . value : 0 ; } ) ; } } ; bindExtraStuff ( ) ; this . __internal_bindMethod ( makeNodeId ( MethodIds . Server_GetMonitoredItems ) , getMonitoredItemsId . bind ( this ) ) ; this . __internal_bindMethod ( makeNodeId ( MethodIds . Server_SetSubscriptionDurable ) , setSubscriptionDurable . bind ( this ) ) ; const fixGetMonitoredItemArgs = ( ) => { const objects = this . addressSpace ! . rootFolder ?. objects ; if ( ! objects || ! objects . server || ! objects . server . getMonitoredItems ) { return ; } const outputArguments = objects . server . getMonitoredItems . outputArguments ! ; const dataValue = outputArguments . readValue ( ) ; assert ( dataValue . value . value [ 0 ] . arrayDimensions . length === 1 && dataValue . value . value [ 0 ] . arrayDimensions [ 0 ] === 0 ) ; assert ( dataValue . value . value [ 1 ] . arrayDimensions . length === 1 && dataValue . value . value [ 1 ] . arrayDimensions [ 0 ] === 0 ) ; } ; fixGetMonitoredItemArgs ( ) ; const prepareServerDiagnostics = ( ) => { const addressSpace1 = this . addressSpace ! ; if ( ! addressSpace1 . rootFolder . objects ) { return ; } const server = addressSpace1 . rootFolder . objects . server ; if ( ! server ) { return ; } const serverDiagnosticsNode = server . getComponentByName ( "ServerDiagnostics" ) as UAServerDiagnostics ; if ( ! serverDiagnosticsNode ) { return ; } if ( true ) { serverDiagnosticsNode . enabledFlag . userAccessLevel = makeAccessLevelFlag ( "CurrentRead" ) ; serverDiagnosticsNode . enabledFlag . accessLevel = makeAccessLevelFlag ( "CurrentRead" ) ; } const samplingIntervalDiagnosticsArray = serverDiagnosticsNode . getComponentByName ( "SamplingIntervalDiagnosticsArray" ) ; if ( samplingIntervalDiagnosticsArray ) { addressSpace . deleteNode ( samplingIntervalDiagnosticsArray ) ; const s = serverDiagnosticsNode . getComponents ( ) ; } const subscriptionDiagnosticsArrayNode = serverDiagnosticsNode . getComponentByName ( "SubscriptionDiagnosticsArray" ) ! as UADynamicVariableArray < SessionDiagnosticsDataType > ; assert ( subscriptionDiagnosticsArrayNode . nodeClass === NodeClass . Variable ) ; bindExtObjArrayNode ( subscriptionDiagnosticsArrayNode , "SubscriptionDiagnosticsType" , "subscriptionId" ) ; makeNotReadableIfEnabledFlagIsFalse ( subscriptionDiagnosticsArrayNode ) ; const sessionsDiagnosticsSummary = serverDiagnosticsNode . getComponentByName ( "SessionsDiagnosticsSummary" ) ! ; const sessionDiagnosticsArray = sessionsDiagnosticsSummary . getComponentByName ( "SessionDiagnosticsArray" ) ! as UADynamicVariableArray < SessionDiagnosticsDataType > ; assert ( sessionDiagnosticsArray . nodeClass === NodeClass . Variable ) ; bindExtObjArrayNode ( sessionDiagnosticsArray , "SessionDiagnosticsVariableType" , "sessionId" ) ; const varType = addressSpace . findVariableType ( "SessionSecurityDiagnosticsType" ) ; if ( ! varType ) { debugLog ( "Warning cannot find SessionSecurityDiagnosticsType variable Type" ) ; } else { const sessionSecurityDiagnosticsArray = sessionsDiagnosticsSummary . getComponentByName ( "SessionSecurityDiagnosticsArray" ) ! as UADynamicVariableArray < SessionSecurityDiagnosticsDataType > ; assert ( sessionSecurityDiagnosticsArray . nodeClass === NodeClass . Variable ) ; bindExtObjArrayNode ( sessionSecurityDiagnosticsArray , "SessionSecurityDiagnosticsType" , "sessionId" ) ; ensureObjectIsSecure ( sessionSecurityDiagnosticsArray ) ; } } ; prepareServerDiagnostics ( ) ; this . _internalState = "initialized" ; this . setServerState ( ServerState . Running ) ; setImmediate ( ( ) => callback ( ) ) ; } ) ; } method param nodeId { NodeId | String } param browseDescription param browseDescription . browseDirection { BrowseDirection } param browseDescription . referenceTypeId { String | NodeId } param context return public browseSingleNode ( nodeId , browseDescription , context ? ) { const addressSpace = this . addressSpace ! ; return addressSpace . browseSingleNode ( nodeId , browseDescription , context ) ; } public async browseWithAutomaticExpansion ( nodesToBrowse , context ? ) { for ( const browseDescription of nodesToBrowse ) { const nodeId = resolveNodeId ( browseDescription . nodeId ) ; const node = this . addressSpace ! . findNode ( nodeId ) ; if ( node ) { if ( node . onFirstBrowseAction ) { try { await node . onFirstBrowseAction ( ) ; node . onFirstBrowseAction = undefined ; } catch ( err ) { if ( err instanceof Error ) { errorLog ( "onFirstBrowseAction method has failed" , err . message ) ; } errorLog ( err ) ; } assert ( node . onFirstBrowseAction === undefined , "expansion can only be made once" ) ; } } } return this . browse ( nodesToBrowse , context ) ; } /**
     *
     */ public browse ( nodesToBrowse , context ? ) { const results = [ ] ; for ( const browseDescription of nodesToBrowse ) { const nodeId = resolveNodeId ( browseDescription . nodeId ) ; const r = this . browseSingleNode ( nodeId , browseDescription , context ) ; results . push ( r ) ; } return results ; } method param context param nodeId param attributeId param timestampsToReturn return public readSingleNode ( context , nodeId , attributeId , timestampsToReturn ? ) { context . currentTime = getCurrentClock ( ) ; return this . _readSingleNode ( context , new ReadValueId ( { attributeId , nodeId : resolveNodeId ( nodeId ) } ) , timestampsToReturn ) ; } return public read ( context , readRequest ) { assert ( context instanceof SessionContext ) ; assert ( readRequest instanceof ReadRequest ) ; assert ( readRequest . maxAge >= 0 ) ; const timestampsToReturn = readRequest . timestampsToReturn ; const nodesToRead = readRequest . nodesToRead || [ ] ; assert ( Array . isArray ( nodesToRead ) ) ; context . currentTime = getCurrentClock ( ) ; const dataValues = [ ] ; for ( const readValueId of nodesToRead ) { const dataValue = this . _readSingleNode ( context , readValueId , timestampsToReturn ) ; if ( timestampsToReturn === TimestampsToReturn . Server ) { dataValue . sourceTimestamp = null ; dataValue . sourcePicoseconds = 0 ; } if ( ( timestampsToReturn === TimestampsToReturn . Both || timestampsToReturn === TimestampsToReturn . Server ) && ( ! dataValue . serverTimestamp || dataValue . serverTimestamp . getTime ( ) === minOPCUADate . getTime ( ) ) ) { dataValue . serverTimestamp = context . currentTime . timestamp ; dataValue . sourcePicoseconds = 0 ; } dataValues . push ( dataValue ) ; } return dataValues ; } method param context param writeValue param callback param callback . err param callback . statusCode async public writeSingleNode ( context , writeValue , callback ) { assert ( context instanceof SessionContext ) ; assert ( typeof callback === "function" ) ; assert ( writeValue . schema . name === "WriteValue" ) ; assert ( writeValue . value instanceof DataValue ) ; if ( writeValue . value . value === null ) { return callback ( null , StatusCodes . BadTypeMismatch ) ; } assert ( writeValue . value . value instanceof Variant ) ; const nodeId = writeValue . nodeId ; const obj = this . __findNode ( nodeId ) as UAVariable ; if ( ! obj ) { return callback ( null , StatusCodes . BadNodeIdUnknown ) ; } else { obj . writeAttribute ( context , writeValue , callback ) ; } } method param context param nodesToWrite param callback param callback . err param callback . results async public write ( context , nodesToWrite , callback ) { assert ( context instanceof SessionContext ) ; assert ( typeof callback === "function" ) ; context . currentTime = getCurrentClock ( ) ; ensureDatatypeExtractedWithCallback ( this . addressSpace ! , ( err2 , extraDataTypeManager ? ) => { if ( err2 ) { return callback ( err2 ) ; } const performWrite = ( writeValue , inner_callback ) => { resolveOpaqueOnAddressSpace ( this . addressSpace ! , writeValue . value . value ! ) . then ( ( ) => { this . writeSingleNode ( context , writeValue , inner_callback ) ; } ) . catch ( inner_callback ) ; } ; async . map ( nodesToWrite , performWrite , ( err ? , statusCodes ? ) => { assert ( Array . isArray ( statusCodes ) ) ; callback ( err ! , statusCodes as StatusCode [ ] ) ; } ) ; } ) ; } /**
     *
     */ public historyReadSingleNode ( context , nodeId , attributeId , historyReadDetails , timestampsToReturn , continuationData , callback ) { if ( timestampsToReturn === TimestampsToReturn . Invalid ) { callback ( null , new HistoryReadResult ( { statusCode : StatusCodes . BadTimestampsToReturnInvalid } ) ) ; return ; } assert ( context instanceof SessionContext ) ; this . _historyReadSingleNode ( context , new HistoryReadValueId ( { nodeId } ) , historyReadDetails , timestampsToReturn , continuationData , callback ) ; } method param context { SessionContext } param historyReadRequest { HistoryReadRequest } param historyReadRequest . requestHeader { RequestHeader } param historyReadRequest . historyReadDetails { HistoryReadDetails } param historyReadRequest . timestampsToReturn { TimestampsToReturn } param historyReadRequest . releaseContinuationPoints { Boolean } param historyReadRequest . nodesToRead { HistoryReadValueId [ ] } param callback param callback . err param callback . results { HistoryReadResult [ ] } public historyRead ( context , historyReadRequest , callback ) { assert ( context instanceof SessionContext ) ; assert ( historyReadRequest instanceof HistoryReadRequest ) ; assert ( typeof callback === "function" ) ; const timestampsToReturn = historyReadRequest . timestampsToReturn ; const historyReadDetails = historyReadRequest . historyReadDetails ! as HistoryReadDetails ; const releaseContinuationPoints = historyReadRequest . releaseContinuationPoints ; assert ( historyReadDetails instanceof HistoryReadDetails ) ; const nodesToRead = historyReadRequest . nodesToRead || ( [ ] as HistoryReadValueId [ ] ) ; assert ( Array . isArray ( nodesToRead ) ) ; interface M { nodeToRead : HistoryReadValueId ; processDetail : ReadProcessedDetails ; index : number ; } const _q = async ( m ) : Promise < HistoryReadResult > => { return new Promise ( ( resolve ) => { const continuationPoint = m . nodeToRead . continuationPoint ; this . _historyReadSingleNode ( context , m . nodeToRead , m . processDetail , timestampsToReturn , { continuationPoint , releaseContinuationPoints } , ( err , result ? ) => { if ( err && ! result ) { errorLog ( "Internal error" , err . message ) ; result = new HistoryReadResult ( { statusCode : StatusCodes . BadInternalError } ) ; } resolve ( result ) ; } ) ; } ) ; } ; if ( historyReadDetails instanceof ReadProcessedDetails ) { if ( ! historyReadDetails . aggregateType || historyReadDetails . aggregateType . length !== nodesToRead . length ) { return callback ( null , [ new HistoryReadResult ( { statusCode : StatusCodes . BadInvalidArgument } ) ] ) ; } const promises = [ ] ; let index = 0 ; for ( const nodeToRead of nodesToRead ) { const aggregateType = historyReadDetails . aggregateType [ index ] ; const processDetail = new ReadProcessedDetails ( { ... historyReadDetails , aggregateType : [ aggregateType ] } ) ; promises . push ( _q ( { nodeToRead , processDetail , index } ) ) ; index ++ ; } Promise . all ( promises ) . then ( ( results ) => { callback ( null , results ) ; } ) ; return ; } const _r = async ( nodeToRead , index ) => { const continuationPoint = nodeToRead . continuationPoint ; return new Promise < HistoryReadResult > ( ( resolve , reject ) => { this . _historyReadSingleNode ( context , nodeToRead , historyReadDetails , timestampsToReturn , { continuationPoint , releaseContinuationPoints , index } , ( err , result ? ) => { if ( err && ! result ) { result = new HistoryReadResult ( { statusCode : StatusCodes . BadInternalError } ) ; } resolve ( result ) ; } ) ; } ) ; } ; const promises = [ ] ; let index = 0 ; for ( const nodeToRead of nodesToRead ) { promises . push ( _r ( nodeToRead , index ) ) ; index ++ ; } Promise . all ( promises ) . then ( ( results ) => { callback ( null , results ) ; } ) ; } public getOldestUnactivatedSession ( ) { const tmp = Object . values ( this . _sessions ) . filter ( ( session1 ) => { return session1 . status === "new" ; } ) ; if ( tmp . length === 0 ) { return null ; } let session = tmp [ 0 ] ; for ( let i = 1 ; i < tmp . length ; i ++ ) { const c = tmp [ i ] ; if ( session . creationDate . getTime ( ) < c . creationDate . getTime ( ) ) { session = c ; } } return session ; } class method param options param options . sessionTimeout { Number } param options . clientDescription { ApplicationDescription } return { ServerSession } public createSession ( options ) { options = options || { } ; debugLog ( "createSession : increasing serverDiagnosticsSummary cumulatedSessionCount/currentSessionCount " ) ; this . serverDiagnosticsSummary . cumulatedSessionCount += 1 ; this . serverDiagnosticsSummary . currentSessionCount += 1 ; this . clientDescription = options . clientDescription || new ApplicationDescription ( { } ) ; const sessionTimeout = options . sessionTimeout || 1000 ; assert ( typeof sessionTimeout === "number" ) ; const session = new ServerSession ( this , sessionTimeout ) ; debugLog ( "createSession :sessionTimeout = " , session . sessionTimeout ) ; const key = session . authenticationToken . toString ( ) ; this . _sessions [ key ] = session ; session . on ( "new_subscription" , ( subscription ) => { this . serverDiagnosticsSummary . cumulatedSubscriptionCount += 1 ; } ) ; session . on ( "subscription_terminated" , ( subscription ) => { } ) ; session . on ( "timeout" , ( ) => { this . serverDiagnosticsSummary . sessionTimeoutCount += 1 ; session . sessionName = session . sessionName || "" ; const channel = session . channel ; errorLog ( chalk . cyan ( "Server: closing SESSION " ) , session . status , chalk . yellow ( session . sessionName ) , chalk . yellow ( session . nodeId . toString ( ) ) , chalk . cyan ( " because of timeout = " ) , session . sessionTimeout , chalk . cyan ( " has expired without a keep alive" ) , chalk . bgCyan ( "channel = " ) , channel ?. remoteAddress , " port = " , channel ?. remotePort ) ; this . closeSession ( session . authenticationToken , false , "Timeout" ) ; this . incrementSessionTimeoutCount ( ) ; } ) ; return session ; } method param authenticationToken param deleteSubscriptions { Boolean } param { String } reason public closeSession ( authenticationToken , deleteSubscriptions , reason ) { reason = reason || "CloseSession" ; assert ( typeof reason === "string" ) ; assert ( reason === "Timeout" || reason === "Terminated" || reason === "CloseSession" || reason === "Forcing" ) ; debugLog ( "ServerEngine.closeSession " , authenticationToken . toString ( ) , deleteSubscriptions ) ; const session = this . getSession ( authenticationToken ) ; if ( ! session ) { throw new Error ( "cannot find session with this authenticationToken " + authenticationToken . toString ( ) ) ; } if ( ! deleteSubscriptions ) { if ( ! this . _orphanPublishEngine ) { this . _orphanPublishEngine = new ServerSidePublishEngineForOrphanSubscription ( { maxPublishRequestInQueue : 0 } ) ; } debugLog ( "transferring remaining live subscription to orphanPublishEngine !" ) ; ServerSidePublishEngine . transferSubscriptionsToOrphan ( session . publishEngine , this . _orphanPublishEngine ) ; } session . close ( deleteSubscriptions , reason ) ; assert ( session . status === "closed" ) ; debugLog ( " engine.serverDiagnosticsSummary.currentSessionCount -= 1;" ) ; this . serverDiagnosticsSummary . currentSessionCount -= 1 ; delete this . _sessions [ authenticationToken . toString ( ) ] ; session . dispose ( ) ; } public findSubscription ( subscriptionId ) { const subscriptions = [ ] ; Object . values ( this . _sessions ) . map ( ( session ) => { if ( subscriptions . length ) { return ; } const subscription = session . publishEngine . getSubscriptionById ( subscriptionId ) ; if ( subscription ) { subscriptions . push ( subscription ) ; } } ) ; if ( subscriptions . length ) { assert ( subscriptions . length === 1 ) ; return subscriptions [ 0 ] ; } return this . findOrphanSubscription ( subscriptionId ) ; } public findOrphanSubscription ( subscriptionId ) { if ( ! this . _orphanPublishEngine ) { return null ; } return this . _orphanPublishEngine . getSubscriptionById ( subscriptionId ) ; } public deleteOrphanSubscription ( subscription ) { if ( ! this . _orphanPublishEngine ) { return StatusCodes . BadInternalError ; } assert ( this . findSubscription ( subscription . id ) ) ; const c = this . _orphanPublishEngine . subscriptionCount ; subscription . terminate ( ) ; subscription . dispose ( ) ; assert ( this . _orphanPublishEngine . subscriptionCount === c - 1 ) ; return StatusCodes . Good ; } method param session { ServerSession } param subscriptionId { IntegerId } param sendInitialValues { Boolean } return { TransferResult } public async transferSubscription ( session , subscriptionId , sendInitialValues ) { if ( subscriptionId <= 0 ) { return new TransferResult ( { statusCode : StatusCodes . BadSubscriptionIdInvalid } ) ; } const subscription = this . findSubscription ( subscriptionId ) ; if ( ! subscription ) { return new TransferResult ( { statusCode : StatusCodes . BadSubscriptionIdInvalid } ) ; } if ( ! subscription . $session ) { return new TransferResult ( { statusCode : StatusCodes . BadInternalError } ) ; } if ( ! sessionsCompatibleForTransfer ( subscription . $session , session ) ) { return new TransferResult ( { statusCode : StatusCodes . BadUserAccessDenied } ) ; } subscription . subscriptionDiagnostics . transferRequestCount ++ ; if ( ( session . publishEngine as any ) === subscription . publishEngine ) { return new TransferResult ( { statusCode : StatusCodes . BadNothingToDo } ) ; } if ( session === subscription . $session ) { return new TransferResult ( { statusCode : StatusCodes . BadNothingToDo } ) ; } subscription . subscriptionDiagnostics . transferredToAltClientCount ++ ; subscription . subscriptionDiagnostics . transferredToSameClientCount ++ ; const nbSubscriptionBefore = session . publishEngine . subscriptionCount ; subscription . $session . _unexposeSubscriptionDiagnostics ( subscription ) ; await ServerSidePublishEngine . transferSubscription ( subscription , session . publishEngine , sendInitialValues ) ; subscription . $session = session ; session . _exposeSubscriptionDiagnostics ( subscription ) ; assert ( ( subscription . publishEngine as any ) === session . publishEngine ) ; const result = new TransferResult ( { availableSequenceNumbers : subscription . getAvailableSequenceNumbers ( ) , statusCode : StatusCodes . Good } ) ; if ( doDebug ) { debugLog ( "TransferResult" , result . toString ( ) ) ; } return result ; } method param authenticationToken param activeOnly return { ServerSession } public getSession ( authenticationToken , activeOnly ? ) { if ( ! authenticationToken || ( authenticationToken . identifierType && authenticationToken . identifierType !== NodeIdType . BYTESTRING ) ) { return null ; } const key = authenticationToken . toString ( ) ; let session = this . _sessions [ key ] ; if ( ! activeOnly && ! session ) { session = this . _closedSessions [ key ] ; } return session ; } /**
     */ public browsePath ( browsePath ) { return this . addressSpace ! . browsePath ( browsePath ) ; } method param nodesToRefresh { Array < Object > } param callback param callback . err param callback . data async public refreshValues ( nodesToRefresh , maxAge , callback ) { const referenceTime = new Date ( Date . now ( ) - maxAge ) ; assert ( callback instanceof Function ) ; const objectMap = { } ; for ( const nodeToRefresh of nodesToRefresh ) { if ( nodeToRefresh instanceof ReadValueId && nodeToRefresh . attributeId !== AttributeIds . Value ) { continue ; } const obj = this . addressSpace ! . findNode ( nodeToRefresh . nodeId ) ; if ( ! obj || ! ( obj . nodeClass === NodeClass . Variable ) ) { continue ; } if ( typeof ( obj as any ) . refreshFunc !== "function" ) { continue ; } const key = obj . nodeId . toString ( ) ; if ( objectMap [ key ] ) { continue ; } objectMap [ key ] = obj ; } const objectArray = Object . values ( objectMap ) ; if ( objectArray . length === 0 ) { return callback ( null , [ ] ) ; } async . map ( objectArray , ( obj , inner_callback ) => { if ( obj . nodeClass !== NodeClass . Variable ) { inner_callback ( null , new DataValue ( { statusCode : StatusCodes . BadNodeClassInvalid } ) ) ; return ; } try { ( obj as UAVariable ) . asyncRefresh ( referenceTime , ( err , dataValue ) => { inner_callback ( err , dataValue ) ; } ) ; } catch ( err ) { if ( ! ( err instanceof Error ) ) { throw new Error ( "internal error" ) ; } errorLog ( "asyncRefresh internal error" , err ) ; inner_callback ( err ) ; } } , ( err ? , arrResult ? ) => { callback ( err || null , arrResult as DataValue [ ] ) ; } ) ; } private _exposeSubscriptionDiagnostics ( subscription ) { debugLog ( "ServerEngine#_exposeSubscriptionDiagnostics" ) ; const subscriptionDiagnosticsArray = this . _getServerSubscriptionDiagnosticsArrayNode ( ) ; const subscriptionDiagnostics = subscription . subscriptionDiagnostics ; assert ( ( subscriptionDiagnostics as any ) . $subscription === subscription ) ; assert ( subscriptionDiagnostics instanceof SubscriptionDiagnosticsDataType ) ; if ( subscriptionDiagnostics && subscriptionDiagnosticsArray ) { addElement ( subscriptionDiagnostics , subscriptionDiagnosticsArray ) ; } } protected _unexposeSubscriptionDiagnostics ( subscription ) { const subscriptionDiagnosticsArray = this . _getServerSubscriptionDiagnosticsArrayNode ( ) ; const subscriptionDiagnostics = subscription . subscriptionDiagnostics ; assert ( subscriptionDiagnostics instanceof SubscriptionDiagnosticsDataType ) ; if ( subscriptionDiagnostics && subscriptionDiagnosticsArray ) { const node = ( subscriptionDiagnosticsArray as any ) [ subscription . id ] ; removeElement ( subscriptionDiagnosticsArray , subscriptionDiagnostics ) ; } debugLog ( "ServerEngine#_unexposeSubscriptionDiagnostics" ) ; } return { Subscription } public _createSubscriptionOnSession ( session , request ) { assert ( Object . prototype . hasOwnProperty . call ( request , "requestedPublishingInterval" ) ) ; assert ( Object . prototype . hasOwnProperty . call ( request , "requestedLifetimeCount" ) ) ; assert ( Object . prototype . hasOwnProperty . call ( request , "requestedMaxKeepAliveCount" ) ) ; assert ( Object . prototype . hasOwnProperty . call ( request , "maxNotificationsPerPublish" ) ) ; assert ( Object . prototype . hasOwnProperty . call ( request , "publishingEnabled" ) ) ; assert ( Object . prototype . hasOwnProperty . call ( request , "priority" ) ) ; const publishingInterval = request . requestedPublishingInterval || 0 ; const maxKeepAliveCount = request . requestedMaxKeepAliveCount || 0 ; const lifeTimeCount = request . requestedLifetimeCount || 0 ; const subscription = new Subscription ( { id : _get_next_subscriptionId ( ) , lifeTimeCount , maxKeepAliveCount , maxNotificationsPerPublish : request . maxNotificationsPerPublish , priority : request . priority || 0 , publishEngine : session . publishEngine as any , publishingEnabled : request . publishingEnabled , publishingInterval , sessionId : NodeId . nullNodeId } ) ; this . _exposeSubscriptionDiagnostics ( subscription ) ; assert ( ( subscription . publishEngine as any ) === session . publishEngine ) ; session . publishEngine . add_subscription ( subscription ) ; const engine = this ; subscription . once ( "terminated" , function ( this ) { engine . _unexposeSubscriptionDiagnostics ( this ) ; } ) ; return subscription ; } private __findNode ( nodeId ) { if ( nodeId . namespace >= ( this . addressSpace ?. getNamespaceArray ( ) . length || 0 ) ) { return null ; } const namespace = this . addressSpace ! . getNamespace ( nodeId . namespace ) ! ; return namespace . findNode2 ( nodeId ) ! ; } private _readSingleNode ( context , nodeToRead , timestampsToReturn ? ) { assert ( context instanceof SessionContext ) ; const nodeId = nodeToRead . nodeId ! ; const attributeId = nodeToRead . attributeId ! ; const indexRange = nodeToRead . indexRange ! ; const dataEncoding = nodeToRead . dataEncoding ; if ( timestampsToReturn === TimestampsToReturn . Invalid ) { return new DataValue ( { statusCode : StatusCodes . BadTimestampsToReturnInvalid } ) ; } timestampsToReturn = coerceTimestampsToReturn ( timestampsToReturn ) ; const obj = this . __findNode ( nodeId ! ) ; let dataValue ; if ( ! obj ) { return new DataValue ( { statusCode : StatusCodes . BadNodeIdUnknown } ) ; } else { dataValue = obj . readAttribute ( context , attributeId , indexRange , dataEncoding ) ; dataValue = apply_timestamps_no_copy ( dataValue , timestampsToReturn , attributeId ) ; return dataValue ; } } private _historyReadSingleNode ( context , nodeToRead , historyReadDetails , timestampsToReturn , continuationData , callback ) { assert ( context instanceof SessionContext ) ; assert ( callback instanceof Function ) ; const nodeId = nodeToRead . nodeId ; const indexRange = nodeToRead . indexRange ; const dataEncoding = nodeToRead . dataEncoding ; const continuationPoint = nodeToRead . continuationPoint ; timestampsToReturn = coerceTimestampsToReturn ( timestampsToReturn ) ; if ( timestampsToReturn === TimestampsToReturn . Invalid ) { return callback ( null , new HistoryReadResult ( { statusCode : StatusCodes . BadTimestampsToReturnInvalid } ) ) ; } const obj = this . __findNode ( nodeId ) as UAVariable ; if ( ! obj ) { callback ( null , new HistoryReadResult ( { statusCode : StatusCodes . BadNodeIdUnknown } ) ) ; return ; } else { if ( ! obj . historyRead ) { const msg = " this node doesn't provide historyRead! probably not a UAVariable\n " + obj . nodeId . toString ( ) + " " + obj . browseName . toString ( ) + "\n" + "with " + nodeToRead . toString ( ) + "\n" + "HistoryReadDetails " + historyReadDetails . toString ( ) ; if ( doDebug ) { debugLog ( chalk . cyan ( "ServerEngine#_historyReadSingleNode " ) , chalk . white . bold ( msg ) ) ; } const err = new Error ( msg ) ; setImmediate ( callback . bind ( null , err ) ) ; return ; } obj . historyRead ( context , historyReadDetails , indexRange , dataEncoding , continuationData , ( err , result ? ) => { if ( err || ! result ) { return callback ( err ) ; } assert ( result ! . statusCode instanceof StatusCode ) ; assert ( result ! . isValid ( ) ) ; callback ( err , result ) ; } ) ; } } /**
     */ private __internal_bindMethod ( nodeId , func ) { assert ( typeof func === "function" ) ; assert ( nodeId instanceof NodeId ) ; const methodNode = this . addressSpace ! . findNode ( nodeId ) ! as UAMethod ; if ( ! methodNode ) { return ; } if ( methodNode && methodNode . bindMethod ) { methodNode . bindMethod ( func ) ; } else { warningLog ( chalk . yellow ( "WARNING:  cannot bind a method with id " ) + chalk . cyan ( nodeId . toString ( ) ) + chalk . yellow ( ". please check your nodeset.xml file or add this node programmatically" ) ) ; warningLog ( traceFromThisProjectOnly ( ) ) ; } } private _getServerSubscriptionDiagnosticsArrayNode ( ) { if ( ! this . addressSpace ) { if ( doDebug ) { console . warn ( "ServerEngine#_getServerSubscriptionDiagnosticsArray : no addressSpace" ) ; } return null ; } const subscriptionDiagnosticsType = this . addressSpace . findVariableType ( "SubscriptionDiagnosticsType" ) ; if ( ! subscriptionDiagnosticsType ) { if ( doDebug ) { console . warn ( "ServerEngine#_getServerSubscriptionDiagnosticsArray " + ": cannot find SubscriptionDiagnosticsType" ) ; } } const subscriptionDiagnosticsArrayNode = this . addressSpace . findNode ( makeNodeId ( VariableIds . Server_ServerDiagnostics_SubscriptionDiagnosticsArray ) ) ! ; return subscriptionDiagnosticsArrayNode as UADynamicVariableArray < SubscriptionDiagnosticsDataType > ; } }