UNPKG

node-opcua-client

Version:

pure nodejs OPCUA SDK - module client

1,414 lines (1,263 loc) 86.8 kB
/** * @module node-opcua-client-private */ import chalk from "chalk"; import { EventEmitter } from "events"; import { assert } from "node-opcua-assert"; import type { DateTime } from "node-opcua-basic-types"; import { type ExtraDataTypeManager, extractDataValueToPromote, getExtensionObjectConstructor, getExtraDataTypeManager, type PseudoDataValue, promoteOpaqueStructure } from "node-opcua-client-dynamic-extension-object"; import type { AggregateFunction } from "node-opcua-constants"; import type { Certificate, Nonce } from "node-opcua-crypto/web"; import { BrowseDirection, type LocalizedTextLike } from "node-opcua-data-model"; import { DataValue } from "node-opcua-data-value"; import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug"; import type { ExtensionObject } from "node-opcua-extension-object"; import { coerceNodeId, NodeId, type NodeIdLike, resolveNodeId } from "node-opcua-nodeid"; import { type BrowseDescriptionLike, getArgumentDefinitionHelper, getBuiltInDataType, type IBasicSessionAsync2, type IBasicTransportSettings, type NodeAttributes, type ResponseCallback, readAllAttributes, readNamespaceArray } from "node-opcua-pseudo-session"; import type { AnyConstructorFunc } from "node-opcua-schemas"; import { ClientSecureChannelLayer, requestHandleNotSetValue, type SignatureData } from "node-opcua-secure-channel"; import { BrowseDescription, BrowseRequest, BrowseResponse, BrowseResult } from "node-opcua-service-browse"; import { CallMethodRequest, type CallMethodResult, CallRequest, CallResponse } from "node-opcua-service-call"; import type { EndpointDescription } from "node-opcua-service-endpoints"; import { HistoryData, HistoryReadRequest, HistoryReadResponse, type HistoryReadResult, ReadProcessedDetails, ReadRawModifiedDetails } from "node-opcua-service-history"; import { QueryFirstRequest, QueryFirstResponse } from "node-opcua-service-query"; import { AttributeIds, ReadRequest, ReadResponse, ReadValueId, type ReadValueIdOptions, TimestampsToReturn } from "node-opcua-service-read"; import { RegisterNodesRequest, RegisterNodesResponse, UnregisterNodesRequest, UnregisterNodesResponse } from "node-opcua-service-register-node"; import { CreateMonitoredItemsRequest, CreateMonitoredItemsResponse, CreateSubscriptionRequest, CreateSubscriptionResponse, DeleteMonitoredItemsRequest, DeleteMonitoredItemsResponse, DeleteSubscriptionsRequest, DeleteSubscriptionsResponse, ModifyMonitoredItemsRequest, ModifyMonitoredItemsResponse, ModifySubscriptionRequest, ModifySubscriptionResponse, PublishRequest, PublishResponse, RepublishRequest, RepublishResponse, SetMonitoringModeRequest, SetMonitoringModeResponse, SetPublishingModeRequest, SetPublishingModeResponse, SetTriggeringRequest, type SetTriggeringRequestOptions, SetTriggeringResponse, TransferSubscriptionsRequest, TransferSubscriptionsResponse } from "node-opcua-service-subscription"; import { type BrowsePath, type BrowsePathResult, TranslateBrowsePathsToNodeIdsRequest, TranslateBrowsePathsToNodeIdsResponse } from "node-opcua-service-translate-browse-path"; import { WriteRequest, WriteResponse, WriteValue } from "node-opcua-service-write"; import { type Callback, type CallbackT, type ErrorCallback, type StatusCode, StatusCodes } from "node-opcua-status-code"; import { ActivateSessionRequest, type AggregateConfigurationOptions, BrowseNextRequest, BrowseNextResponse, CloseSessionRequest, type HistoryReadValueIdOptions, UserTokenType, type WriteValueOptions } from "node-opcua-types"; import { buffer_ellipsis, getFunctionParameterNames, isNullOrUndefined } from "node-opcua-utils"; import { DataType, type Variant, type VariantLike } from "node-opcua-variant"; import type { ArgumentDefinition, CallMethodRequestLike, ClientSession, CreateMonitoredItemsRequestLike, CreateSubscriptionRequestLike, DeleteMonitoredItemsRequestLike, DeleteSubscriptionsRequestLike, ExtraReadHistoryValueParameters, HistoryReadValueIdOptions2, MethodId, ModifyMonitoredItemsRequestLike, ModifySubscriptionRequestLike, MonitoredItemData, QueryFirstRequestLike, SetMonitoringModeRequestLike, SubscriptionId, TransferSubscriptionsRequestLike } from "../client_session"; import { ClientSessionKeepAliveManager } from "../client_session_keepalive_manager"; import type { ClientSubscription } from "../client_subscription"; import type { Request, Response } from "../common"; import type { UserIdentityInfo } from "../user_identity_info"; import { ClientSidePublishEngine } from "./client_publish_engine"; import { ClientSubscriptionImpl } from "./client_subscription_impl"; import type { IClientBase } from "./i_private_client"; import { repair_client_session } from "./reconnection/reconnection"; const helpAPIChange = process.env.DEBUG && process.env.DEBUG.match(/API/); const debugLog = make_debugLog(__filename); const doDebug = checkDebugFlag(__filename); const warningLog = make_warningLog(__filename); const errorLog = make_errorLog(__filename); let pendingTransactionMessageDisplayed = false; function coerceBrowseDescription(data: any): BrowseDescription { if (typeof data === "string" || data instanceof NodeId) { return coerceBrowseDescription({ browseDirection: BrowseDirection.Forward, includeSubtypes: true, nodeClassMask: 0, nodeId: data, referenceTypeId: "HierarchicalReferences", resultMask: 63 }); } else { data.nodeId = resolveNodeId(data.nodeId); data.referenceTypeId = data.referenceTypeId ? resolveNodeId(data.referenceTypeId) : null; return new BrowseDescription(data); } } function coerceReadValueId(node: any): ReadValueId { if (typeof node === "string" || node instanceof NodeId) { return new ReadValueId({ attributeId: AttributeIds.Value, dataEncoding: undefined, // {namespaceIndex: 0, name: undefined} indexRange: undefined, nodeId: resolveNodeId(node) }); } else { assert(node instanceof Object); return new ReadValueId(node); } } const emptyUint32Array = new Uint32Array(0); type EmptyCallback = (err?: Error) => void; export interface Reconnectable { _reconnecting: { reconnecting: boolean; pendingCallbacks: EmptyCallback[]; pendingTransactions: { request: Request; callback: (err: Error | null, response?: Response) => void }[]; }; } /** * @class ClientSession */ export class ClientSessionImpl extends EventEmitter implements ClientSession, Reconnectable { static reconnectingElement: WeakMap<ClientSessionImpl, Reconnectable> = new WeakMap(); public timeout: number; public authenticationToken?: NodeId; public requestedMaxReferencesPerNode: number; public sessionId: NodeId; public lastRequestSentTime: Date; public lastResponseReceivedTime: Date; public serverCertificate: Certificate; public userIdentityInfo?: UserIdentityInfo; public name = ""; public serverNonce?: Nonce; public serverSignature?: SignatureData; // todo : remove ? public serverEndpoints: EndpointDescription[] = []; public _client: IClientBase | null; public _closed: boolean; public _reconnecting: { reconnecting: boolean; pendingCallbacks: EmptyCallback[]; pendingTransactions: any[]; }; /** * @internal */ public _closeEventHasBeenEmitted: boolean; private _publishEngine: ClientSidePublishEngine | null; private _keepAliveManager?: ClientSessionKeepAliveManager; private $$namespaceArray?: string[]; private recursive_repair_detector = 0; constructor(client: IClientBase) { super(); this.serverCertificate = Buffer.alloc(0); this.sessionId = new NodeId(); this._closeEventHasBeenEmitted = false; this._client = client; this._publishEngine = null; this._closed = false; this._reconnecting = { reconnecting: false, pendingCallbacks: [], pendingTransactions: [] }; this.requestedMaxReferencesPerNode = 10000; this.lastRequestSentTime = new Date(1, 1, 1970); this.lastResponseReceivedTime = new Date(1, 1, 1970); this.timeout = 0; } getTransportSettings(): IBasicTransportSettings { return this._client!.getTransportSettings(); } /** * the endpoint on which this session is operating * @property endpoint * @type {EndpointDescription} */ get endpoint(): EndpointDescription { return this._client!.endpoint!; } get subscriptionCount(): number { return this._publishEngine ? this._publishEngine.subscriptionCount : 0; } get isReconnecting(): boolean { return this._client ? this._client.isReconnecting || this._reconnecting?.reconnecting : false; } protected resolveNodeId(nodeId: NodeIdLike): NodeIdLike { return resolveNodeId(nodeId); } public getPublishEngine(): ClientSidePublishEngine { if (!this._publishEngine) { this._publishEngine = new ClientSidePublishEngine(this); } return this._publishEngine!; } public changeUser(userIdentityInfo: UserIdentityInfo): Promise<StatusCode>; public changeUser(userIdentityInfo: UserIdentityInfo, callback: CallbackT<StatusCode>): void; public changeUser(userIdentityInfo: UserIdentityInfo, callback?: CallbackT<StatusCode>): any { userIdentityInfo = userIdentityInfo || { type: UserTokenType.Anonymous }; if (!this._client || !this.userIdentityInfo) { warningLog("changeUser: invalid session"); return callback!(null, StatusCodes.BadInternalError); } const old_userIdentity: UserIdentityInfo = this.userIdentityInfo; this._client._activateSession(this, userIdentityInfo, (err1: Error | null, session2?: ClientSessionImpl) => { if (err1) { this.userIdentityInfo = old_userIdentity; warningLog("activate session error = ", err1.message); return callback!(null, StatusCodes.BadUserAccessDenied); } this.userIdentityInfo = userIdentityInfo; callback!(null, StatusCodes.Good); }); } /** * * @example * * ```javascript * session.browse("RootFolder",function(err,browseResult) { * if(err) return callback(err); * console.log(browseResult.toString()); * callback(); * } ); * ``` * * * @example * * ``` javascript * const browseDescription = { * nodeId: "ObjectsFolder", * referenceTypeId: "Organizes", * browseDirection: BrowseDirection.Inverse, * includeSubtypes: true, * nodeClassMask: 0, * resultMask: 63 * } * session.browse(browseDescription,function(err, browseResult) { * if(err) return callback(err); * console.log(browseResult.toString()); * callback(); * }); * ``` * @example * * ``` javascript * session.browse([ "RootFolder", "ObjectsFolder"],function(err, browseResults) { * assert(browseResults.length === 2); * }); * ``` * * @example * ``` javascript * const browseDescriptions = [ * { * nodeId: "ObjectsFolder", * referenceTypeId: "Organizes", * browseDirection: BrowseDirection.Inverse, * includeSubtypes: true, * nodeClassMask: 0, * resultMask: 63 * }, * // {...} * ] * session.browse(browseDescriptions,function(err, browseResults) { * * }); * ``` * * */ public browse(nodeToBrowse: BrowseDescriptionLike, callback: ResponseCallback<BrowseResult>): void; public browse(nodesToBrowse: BrowseDescriptionLike[], callback: ResponseCallback<BrowseResult[]>): void; public async browse(nodeToBrowse: BrowseDescriptionLike): Promise<BrowseResult>; public async browse(nodesToBrowse: BrowseDescriptionLike[]): Promise<BrowseResult[]>; /** * @internal * @param args */ public browse(...args: any[]): any { const arg0 = args[0]; const isArray = Array.isArray(arg0); const callback: ResponseCallback<BrowseResult[] | BrowseResult> = args[1]; assert(typeof callback === "function"); assert(isFinite(this.requestedMaxReferencesPerNode)); const nodesToBrowse: BrowseDescription[] = (isArray ? arg0 : [arg0 as BrowseDescription]).map(coerceBrowseDescription); const request = new BrowseRequest({ nodesToBrowse, requestedMaxReferencesPerNode: this.requestedMaxReferencesPerNode }); this.performMessageTransaction(request, (err: Error | null, response?: Response) => { if (err) { return callback(err); } /* c8 ignore next */ if (!response || !(response instanceof BrowseResponse)) { return callback(new Error("Internal Error")); } const results: BrowseResult[] = response.results ? response.results : []; if (this.requestedMaxReferencesPerNode > 0) { for (let i = 0; i < results.length; i++) { const r = results[i]; /* c8 ignore next */ if (r.references && r.references.length > this.requestedMaxReferencesPerNode) { warningLog( chalk.yellow("warning") + " BrowseResponse : the server didn't take into" + " account our requestedMaxReferencesPerNode " ); warningLog(" this.requestedMaxReferencesPerNode= " + this.requestedMaxReferencesPerNode); warningLog(" got " + r.references.length + "for " + nodesToBrowse[i].nodeId.toString()); warningLog(" continuationPoint ", r.continuationPoint); } } } for (const r of results) { r.references = r.references || /* c8 ignore next */ []; } assert(results[0] instanceof BrowseResult); return callback(null, isArray ? results : results[0]); }); } public browseNext( continuationPoint: Buffer, releaseContinuationPoints: boolean, callback: ResponseCallback<BrowseResult> ): void; public browseNext( continuationPoints: Buffer[], releaseContinuationPoints: boolean, callback: ResponseCallback<BrowseResult[]> ): void; public async browseNext(continuationPoint: Buffer, releaseContinuationPoints: boolean): Promise<BrowseResult>; public async browseNext(continuationPoints: Buffer[], releaseContinuationPoints: boolean): Promise<BrowseResult[]>; public browseNext(...args: any[]): any { const arg0 = args[0]; const isArray = Array.isArray(arg0); const releaseContinuationPoints = args[1] as boolean; const callback: any = args[2]; assert(typeof callback === "function", "expecting a callback function here"); const continuationPoints: Buffer[] = isArray ? arg0 : [arg0 as Buffer]; const request = new BrowseNextRequest({ continuationPoints, releaseContinuationPoints }); this.performMessageTransaction(request, (err: Error | null, response?: Response) => { /* c8 ignore next */ if (err) { return callback(err); } /* c8 ignore next */ if (!response || !(response instanceof BrowseNextResponse)) { return callback(new Error("Internal Error")); } const results: BrowseResult[] = response.results ? response.results : []; for (const r of results) { r.references = r.references || []; } assert(results[0] instanceof BrowseResult); return callback(null, isArray ? results : results[0]); }); } /** * * @example * * ```javascript * session.readVariableValue("ns=2;s=Furnace_1.Temperature",function(err,dataValue) { * if(err) { return callback(err); } * if (dataValue.isGood()) { * } * console.log(dataValue.toString()); * callback(); * }); * ``` * * @example * * ```javascript * session.readVariableValue(["ns=0;i=2257","ns=0;i=2258"],function(err,dataValues) { * if (!err) { * console.log(dataValues[0].toString()); * console.log(dataValues[1].toString()); * } * }); * ``` * * @example * ```javascript * const dataValues = await session.readVariableValue(["ns=1;s=Temperature","ns=1;s=Pressure"]); * ``` * * @deprecated */ public readVariableValue(nodeId: NodeIdLike, callback: ResponseCallback<DataValue>): void; public readVariableValue(nodeIds: NodeIdLike[], callback: ResponseCallback<DataValue[]>): void; public async readVariableValue(nodeId: NodeIdLike): Promise<DataValue>; public async readVariableValue(nodeIds: NodeIdLike[]): Promise<DataValue[]>; /** * @internal * @param args */ public readVariableValue(...args: any[]): any { const callback = args[1]; assert(typeof callback === "function"); const isArray = Array.isArray(args[0]); const nodes = isArray ? args[0] : [args[0]]; const nodesToRead = nodes.map(coerceReadValueId); const request = new ReadRequest({ nodesToRead, timestampsToReturn: TimestampsToReturn.Neither }); this.performMessageTransaction(request, (err: Error | null, response?: Response) => { /* c8 ignore next */ if (err) { return callback(err); } /* c8 ignore next */ if (!(response instanceof ReadResponse)) { return callback(new Error("Internal Error")); } /* c8 ignore next */ if (response.responseHeader.serviceResult.isNot(StatusCodes.Good)) { return callback(new Error(response.responseHeader.serviceResult.toString())); } /* c8 ignore next */ if (!response.results) { response.results = []; } assert(nodes.length === response.results.length); callback(null, isArray ? response.results : response.results[0]); }); } /** * * @example * * ```javascript * // es5 * session.readHistoryValue( * "ns=5;s=Simulation Examples.Functions.Sine1", * "2015-06-10T09:00:00.000Z", * "2015-06-10T09:01:00.000Z", function(err,dataValues) { * * }); * ``` * * ```javascript * // es6 * const dataValues = await session.readHistoryValue( * "ns=5;s=Simulation Examples.Functions.Sine1", * "2015-06-10T09:00:00.000Z", * "2015-06-10T09:01:00.000Z"); * ``` * @param nodeToRead the read value id * @param start the start time in UTC format * @param end the end time in UTC format * @param callback */ public readHistoryValue( nodesToRead: NodeIdLike[] | HistoryReadValueIdOptions2[], start: DateTime, end: DateTime, callback: (err: Error | null, results?: HistoryReadResult[]) => void ): void; public readHistoryValue( nodesToRead: NodeIdLike[] | HistoryReadValueIdOptions2[], start: DateTime, end: DateTime, options: ExtraReadHistoryValueParameters | undefined, callback: (err: Error | null, results?: HistoryReadResult[]) => void ): void; public async readHistoryValue( nodesToRead: NodeIdLike[] | HistoryReadValueIdOptions2[], start: DateTime, end: DateTime, options?: ExtraReadHistoryValueParameters ): Promise<HistoryReadResult[]>; public readHistoryValue( nodeToRead: NodeIdLike | HistoryReadValueIdOptions2, start: DateTime, end: DateTime, callback: (err: Error | null, results?: HistoryReadResult) => void ): void; public readHistoryValue( nodeToRead: NodeIdLike | HistoryReadValueIdOptions2, start: DateTime, end: DateTime, options: ExtraReadHistoryValueParameters | undefined, callback: (err: Error | null, results?: HistoryReadResult) => void ): void; public async readHistoryValue( nodeToRead: NodeIdLike | HistoryReadValueIdOptions2, start: DateTime, end: DateTime, parameters: ExtraReadHistoryValueParameters ): Promise<HistoryReadResult>; public readHistoryValue(...args: any[]): any { const startTime = args[1]; const endTime = args[2]; let options: ExtraReadHistoryValueParameters = {}; let callback = args[3]; if (typeof callback !== "function") { options = args[3]; callback = args[4]; } assert(typeof callback === "function"); // adjust parameters options.numValuesPerNode = options.numValuesPerNode || 0; options.returnBounds = options.returnBounds || options.returnBounds === undefined ? true : false; options.isReadModified = options.isReadModified || false; options.timestampsToReturn = options.timestampsToReturn ?? TimestampsToReturn.Both; const arg0 = args[0]; const isArray = Array.isArray(arg0); const nodes = isArray ? arg0 : [arg0]; const nodesToRead: HistoryReadValueIdOptions[] = []; for (const node of nodes) { if (!node.nodeId) { nodesToRead.push({ continuationPoint: undefined, dataEncoding: undefined, // {namespaceIndex: 0, name: undefined}, indexRange: undefined, nodeId: this.resolveNodeId(node as NodeIdLike) }); } else { nodesToRead.push(node as HistoryReadValueIdOptions); } } const readRawModifiedDetails = new ReadRawModifiedDetails({ endTime, isReadModified: false, numValuesPerNode: options.numValuesPerNode, returnBounds: options.returnBounds, startTime }); const request = new HistoryReadRequest({ historyReadDetails: readRawModifiedDetails, nodesToRead, releaseContinuationPoints: false, timestampsToReturn: options.timestampsToReturn }); request.nodesToRead = request.nodesToRead || []; assert(nodes.length === request.nodesToRead.length); this.historyRead(request, (err: Error | null, response?: HistoryReadResponse) => { /* c8 ignore next */ if (err) { return callback(err); } /* c8 ignore next */ if (!response || !(response instanceof HistoryReadResponse)) { return callback(new Error("Internal Error")); } response.results = response.results || []; assert(nodes.length === response.results.length); callback(null, isArray ? response.results : response.results[0]); }); } public historyRead(request: HistoryReadRequest, callback: Callback<HistoryReadResponse>): void; public historyRead(request: HistoryReadRequest): Promise<HistoryReadResponse>; public historyRead(request: HistoryReadRequest, callback?: CallbackT<HistoryReadResponse>): any { /* c8 ignore next */ if (!callback) { throw new Error("expecting a callback"); } this.performMessageTransaction(request, (err: Error | null, response) => { /* c8 ignore next */ if (err) { return callback(err); } /* c8 ignore next */ if (!response || !(response instanceof HistoryReadResponse)) { return callback(new Error("Internal Error")); } if (response.responseHeader.serviceResult.isNot(StatusCodes.Good)) { return callback(new Error(response.responseHeader.serviceResult.toString())); } response.results = response.results || /* c8 ignore next */ []; // perform ExtensionObject resolution const promises = response.results.map(async (result) => { if (result.historyData && result.historyData instanceof HistoryData) { if (result.historyData.dataValues) { await promoteOpaqueStructure(this, result.historyData.dataValues); } } }); Promise.all(promises) .then(() => { callback(null, response); }) .catch((err) => callback(err)); }); } public readAggregateValue( nodesToRead: HistoryReadValueIdOptions[], startTime: DateTime, endTime: DateTime, aggregateFn: AggregateFunction[], processingInterval: number, callback: Callback<HistoryReadResult[]> ): void; public async readAggregateValue( nodesToRead: HistoryReadValueIdOptions[], startTime: DateTime, endTime: DateTime, aggregateFn: AggregateFunction[], processingInterval: number ): Promise<HistoryReadResult[]>; public readAggregateValue( nodeToRead: HistoryReadValueIdOptions, startTime: DateTime, endTime: DateTime, aggregateFn: AggregateFunction, processingInterval: number, callback: Callback<HistoryReadResult> ): void; public async readAggregateValue( nodeToRead: HistoryReadValueIdOptions, startTime: DateTime, endTime: DateTime, aggregateFn: AggregateFunction, processingInterval: number ): Promise<HistoryReadResult>; public readAggregateValue( nodesToRead: HistoryReadValueIdOptions[], startTime: DateTime, endTime: DateTime, aggregateFn: AggregateFunction[], processingInterval: number, aggregateConfiguration: AggregateConfigurationOptions, callback: Callback<HistoryReadResult[]> ): void; public async readAggregateValue( nodesToRead: HistoryReadValueIdOptions[], startTime: DateTime, endTime: DateTime, aggregateFn: AggregateFunction[], processingInterval: number, aggregateConfiguration: AggregateConfigurationOptions ): Promise<HistoryReadResult[]>; public readAggregateValue( nodeToRead: HistoryReadValueIdOptions, startTime: DateTime, endTime: DateTime, aggregateFn: AggregateFunction, processingInterval: number, aggregateConfiguration: AggregateConfigurationOptions, callback: Callback<HistoryReadResult> ): void; public async readAggregateValue( nodeToRead: HistoryReadValueIdOptions, startTime: DateTime, endTime: DateTime, aggregateFn: AggregateFunction, processingInterval: number, aggregateConfiguration: AggregateConfigurationOptions ): Promise<HistoryReadResult>; public readAggregateValue( arg0: HistoryReadValueIdOptions[] | HistoryReadValueIdOptions, startTime: DateTime, endTime: DateTime, aggregateFn: AggregateFunction[] | AggregateFunction, processingInterval: number, ...args: any[] ): any { const callback = typeof args[0] === "function" ? args[0] : args[1]; assert(typeof callback === "function"); const defaultAggregateFunction = { percentDataBad: 100, percentDataGood: 100, treatUncertainAsBad: true, useServerCapabilitiesDefaults: true, useSlopedExtrapolation: false }; const aggregateConfiguration = typeof args[0] === "function" ? defaultAggregateFunction : args[0]; const isArray = Array.isArray(arg0); const nodesToRead: HistoryReadValueIdOptions[] = isArray ? (arg0 as HistoryReadValueIdOptions[]) : [arg0 as HistoryReadValueIdOptions]; const aggregateFns: AggregateFunction[] = Array.isArray(aggregateFn) ? (aggregateFn as AggregateFunction[]) : [aggregateFn as AggregateFunction]; assert(aggregateFns.length === nodesToRead.length); const readProcessedDetails = new ReadProcessedDetails({ aggregateType: aggregateFns, endTime, processingInterval, startTime, aggregateConfiguration }); const request = new HistoryReadRequest({ historyReadDetails: readProcessedDetails, nodesToRead, releaseContinuationPoints: false, timestampsToReturn: TimestampsToReturn.Both }); assert(nodesToRead.length === request.nodesToRead!.length); this.performMessageTransaction(request, (err: Error | null, response) => { /* c8 ignore next */ if (err) { return callback(err); } /* c8 ignore next */ if (!response || !(response instanceof HistoryReadResponse)) { return callback(new Error("Internal Error")); } if (response.responseHeader.serviceResult.isNot(StatusCodes.Good)) { return callback(new Error(response.responseHeader.serviceResult.toString())); } response.results = response.results || /* c8 ignore next */ []; assert(nodesToRead.length === response.results.length); callback(null, isArray ? response.results : response.results[0]); }); } /** * * @param nodesToWrite {WriteValue[]} - the array of value to write. One or more elements. * @param {Function} callback - the callback function * @param callback.err {object|null} the error if write has failed or null if OK * @param callback.statusCodes {StatusCode[]} - an array of status code of each write * * @example * * const nodesToWrite = [ * { * nodeId: "ns=1;s=SetPoint1", * attributeId: opcua.AttributeIds.Value, * value: { * statusCode: Good, * value: { * dataType: opcua.DataType.Double, * value: 100.0 * } * } * }, * { * nodeId: "ns=1;s=SetPoint2", * attributeIds opcua.AttributeIds.Value, * value: { * statusCode: Good, * value: { * dataType: opcua.DataType.Double, * value: 45.0 * } * } * } * ]; * session.write(nodesToWrite,function (err,statusCodes) { * if(err) { return callback(err);} * // * }); * * @param nodeToWrite {WriteValue} - the value to write * @param callback - the callback function * @param callback.err {object|null} the error if write has failed or null if OK * @param callback.statusCode {StatusCodes} - the status code of the write * * @example * * const nodeToWrite = { * nodeId: "ns=1;s=SetPoint", * attributeId: opcua.AttributeIds.Value, * value: { * statusCode: Good, * value: { * dataType: opcua.DataType.Double, * value: 100.0 * } * } * }; * session.write(nodeToWrite,function (err,statusCode) { * if(err) { return callback(err);} * // * }); * * * @param nodeToWrite {WriteValue} - the value to write * @return {Promise<StatusCode>} * * @example * * ```javascript * session.write(nodeToWrite).then(function(statusCode) { }); * ``` * * @example * * ```javascript * const statusCode = await session.write(nodeToWrite); * ``` * * @param nodesToWrite {Array<WriteValue>} - the value to write * @return {Promise<Array<StatusCode>>} * * @example * ```javascript * session.write(nodesToWrite).then(function(statusCodes) { }); * ``` * * @example * ```javascript * const statusCodes = await session.write(nodesToWrite); * ``` */ public write(nodeToWrite: WriteValueOptions, callback: ResponseCallback<StatusCode>): void; public write(nodesToWrite: WriteValueOptions[], callback: ResponseCallback<StatusCode[]>): void; public async write(nodesToWrite: WriteValueOptions[]): Promise<StatusCode[]>; public async write(nodeToWrite: WriteValueOptions): Promise<StatusCode>; /** * @internal * @param args */ public write(...args: any[]): any { const arg0 = args[0]; const isArray = Array.isArray(arg0); const nodesToWrite = isArray ? arg0 : [arg0]; const callback = args[1]; assert(typeof callback === "function"); const request = new WriteRequest({ nodesToWrite }); this.performMessageTransaction(request, (err: Error | null, response?: Response) => { /* c8 ignore next */ if (err) { return callback(err, response); } /* c8 ignore next */ if (!response || !(response instanceof WriteResponse)) { return callback(new Error("Internal Error")); } /* c8 ignore next */ if (response.responseHeader.serviceResult.isNot(StatusCodes.Good)) { return callback(new Error(response.responseHeader.serviceResult.toString())); } response.results = response.results || /* c8 ignore next */ []; assert(nodesToWrite.length === response.results.length); callback(null, isArray ? response.results : response.results[0]); }); } /** * @deprecated use session.write instead * * @param nodeId {NodeId} - the node id of the node to write * @param value {Variant} - the value to write * @param callback {Function} * @param callback.err {object|null} the error if write has failed or null if OK * @param callback.statusCode {StatusCode} - the status code of the write * * @param nodeId {NodeId} - the node id of the node to write * @param value {Variant} - the value to write * @return {Promise<StatusCode>} - the status code of the write * * * @example * // please use session.write instead of session.writeSingleNode * // as follow * const statusCode = await session.write({ * nodeId, * attributeId: AttributeIds.Value, * value: { * statusCode: Good, * sourceTimestamp: new Date(), // optional, some server may not accept this * value: { * dataType: opcua.DataType.Double, * value: 100.0 * } * } * }); * * */ public writeSingleNode(nodeId: NodeIdLike, value: VariantLike, callback: ResponseCallback<StatusCode>): void; public writeSingleNode(nodeId: NodeIdLike, value: VariantLike): Promise<StatusCode>; public writeSingleNode(...args: any[]): any { const nodeId = args[0] as NodeIdLike; const value = args[1] as VariantLike; const callback = args[2]; assert(typeof callback === "function"); const nodeToWrite = new WriteValue({ attributeId: AttributeIds.Value, indexRange: undefined, nodeId: this.resolveNodeId(nodeId), value: new DataValue({ value }) }); this.write(nodeToWrite, (err, statusCode) => { /* c8 ignore next */ if (err) { return callback(err); } assert(statusCode); callback(null, statusCode); }); } /** * * @example * * * ``` javascript * session.readAllAttributes("ns=2;s=Furnace_1.Temperature",function(err,data) { * if(data.statusCode.isGood()) { * console.log(" nodeId = ",data.nodeId.toString()); * console.log(" browseName = ",data.browseName.toString()); * console.log(" description = ",data.description.toString()); * console.log(" value = ",data.value.toString())); * } * }); * ``` * * @param nodes array of nodeId to read * @param node nodeId to read * @param callback */ public readAllAttributes(node: NodeIdLike, callback: (err: Error | null, data?: NodeAttributes) => void): void; public readAllAttributes(nodes: NodeIdLike[], callback: (err: Error | null, data?: NodeAttributes[]) => void): void; public readAllAttributes(...args: any[]): void { const nodes = args[0] as NodeIdLike[]; const callback = args[1] as (err: Error | null, data?: NodeAttributes[]) => void; readAllAttributes(this, nodes) .then((data: any) => callback(null, data)) .catch((err: Error) => callback(err)); } /** * * * @example * * ```javascript * ``` * * form1: reading a single node * * ``` javascript * const nodeToRead = { * nodeId: "ns=2;s=Furnace_1.Temperature", * attributeId: AttributeIds.BrowseName * }; * * session.read(nodeToRead,function(err,dataValue) { * if (!err) { * console.log(dataValue.toString()); * } * }); * ``` * * * @param nodesToRead {Array<ReadValueId>} - an array of nodeId to read or a ReadValueId * @param [maxAge] {Number} * @param callback {Function} - the callback function * @param callback.err {Error|null} - the error or null if the transaction was OK} * @param callback.dataValues {Array<DataValue>} * * @example * * ``` javascript * const nodesToRead = [ * { * nodeId: "ns=2;s=Furnace_1.Temperature", * attributeId: AttributeIds.BrowseName * } * ]; * session.read(nodesToRead,function(err,dataValues) { * if (!err) { * dataValues.forEach(dataValue=>console.log(dataValue.toString())); * } * }); * ``` * */ public read(nodeToRead: ReadValueIdOptions, maxAge: number, callback: ResponseCallback<DataValue>): void; public read(nodesToRead: ReadValueIdOptions[], maxAge: number, callback: ResponseCallback<DataValue[]>): void; public read(nodeToRead: ReadValueIdOptions, callback: ResponseCallback<DataValue>): void; public read(nodesToRead: ReadValueIdOptions[], callback: ResponseCallback<DataValue[]>): void; public read(nodeToRead: ReadValueIdOptions, maxAge?: number): Promise<DataValue>; public read(nodeToRead: ReadValueIdOptions[], maxAge?: number): Promise<DataValue[]>; /** * @internal * @param args */ public read(...args: any[]): any { if (args.length === 2) { return this.read(args[0], 0, args[1]); } assert(args.length === 3); const isArray = Array.isArray(args[0]); const nodesToRead = isArray ? args[0] : [args[0]]; assert(Array.isArray(nodesToRead)); const maxAge = args[1]; const callback = args[2]; assert(typeof callback === "function"); /* c8 ignore next */ if (helpAPIChange) { // the read method deprecation detection and warning if ( !(getFunctionParameterNames(callback)[1] === "dataValues" || getFunctionParameterNames(callback)[1] === "dataValue") ) { warningLog(chalk.red("[NODE-OPCUA-E04] the ClientSession#read API has changed !!, please fix the client code")); warningLog(chalk.red(" replace ..:")); warningLog(chalk.cyan(" session.read(nodesToRead,function(err,nodesToRead,results) {}")); warningLog(chalk.red(" with .... :")); warningLog(chalk.cyan(" session.read(nodesToRead,function(err,dataValues) {}")); warningLog(""); warningLog( chalk.yellow( "please make sure to refactor your code and check that " + "the second argument of your callback function is named" ), chalk.cyan("dataValue" + (isArray ? "s" : "")) ); warningLog(chalk.cyan("to make this exception disappear")); throw new Error("ERROR ClientSession#read API has changed !!, please fix the client code"); } } // coerce nodeIds for (const node of nodesToRead) { node.nodeId = this.resolveNodeId(node.nodeId); } const request = new ReadRequest({ maxAge, nodesToRead, timestampsToReturn: TimestampsToReturn.Both }); this.performMessageTransaction(request, (err: Error | null, response?: Response) => { /* c8 ignore next */ if (err) { return callback(err, response); } /* c8 ignore next */ if (!response || !(response instanceof ReadResponse)) { return callback(new Error("Internal Error")); } // perform ExtensionObject resolution promoteOpaqueStructure(this, response.results!) .then(() => { response.results = response.results || /* c8 ignore next */ []; callback(null, isArray ? response.results : response.results[0]); }) .catch((err) => { callback(err); }); }); } public emitCloseEvent(statusCode: StatusCode): void { if (!this._closeEventHasBeenEmitted) { debugLog("ClientSession#emitCloseEvent"); this._closeEventHasBeenEmitted = true; this.emit("session_closed", statusCode); } } public createSubscription( options: CreateSubscriptionRequestLike, callback?: ResponseCallback<CreateSubscriptionResponse> ): any { this._defaultRequest(CreateSubscriptionRequest, CreateSubscriptionResponse, options, callback); } /** * @param createSubscriptionRequest * @param callback * * * subscription.on("error', function(err){ ... }); * subscription.on("terminate',function(err){ ... }); * const monitoredItem = await subscription.monitor(itemToMonitor,monitoringParameters,requestedParameters); * monitoredItem.on("changed",function( dataValue) {...}); * */ public async createSubscription2(createSubscriptionRequest: CreateSubscriptionRequestLike): Promise<ClientSubscription>; public createSubscription2( createSubscriptionRequest: CreateSubscriptionRequestLike, callback: (err: Error | null, subscription?: ClientSubscription) => void ): void; public createSubscription2(...args: any[]): any { const createSubscriptionRequest = args[0] as CreateSubscriptionRequestLike; let callback = args[1]; const subscription = new ClientSubscriptionImpl(this, createSubscriptionRequest); // tslint:disable-next-line:no-empty subscription.on("error", (err) => { if (callback) { callback(err); callback = null; } }); subscription.on("started", () => { assert(subscription.session === this, "expecting a session here"); if (callback) { callback(null, subscription); callback = null; } }); } public deleteSubscriptions( options: DeleteSubscriptionsRequestLike, callback?: ResponseCallback<DeleteSubscriptionsResponse> ): any { this._defaultRequest(DeleteSubscriptionsRequest, DeleteSubscriptionsResponse, options, callback); } public setTriggering(request: SetTriggeringRequestOptions, callback?: ResponseCallback<SetTriggeringResponse>): any { this._defaultRequest(SetTriggeringRequest, SetTriggeringResponse, request, callback); } /** */ public transferSubscriptions( options: TransferSubscriptionsRequestLike, callback?: ResponseCallback<TransferSubscriptionsResponse> ): any { this._defaultRequest(TransferSubscriptionsRequest, TransferSubscriptionsResponse, options, callback); } public createMonitoredItems( options: CreateMonitoredItemsRequestLike, callback?: ResponseCallback<CreateMonitoredItemsResponse> ): any { this._defaultRequest(CreateMonitoredItemsRequest, CreateMonitoredItemsResponse, options, callback); } public modifyMonitoredItems( options: ModifyMonitoredItemsRequestLike, callback?: ResponseCallback<ModifyMonitoredItemsResponse> ): any { this._defaultRequest(ModifyMonitoredItemsRequest, ModifyMonitoredItemsResponse, options, callback); } /** * */ public modifySubscription( options: ModifySubscriptionRequestLike, callback?: ResponseCallback<ModifySubscriptionResponse> ): any { this._defaultRequest(ModifySubscriptionRequest, ModifySubscriptionResponse, options, callback); } public setMonitoringMode(options: SetMonitoringModeRequestLike, callback?: ResponseCallback<SetMonitoringModeResponse>): any { this._defaultRequest(SetMonitoringModeRequest, SetMonitoringModeResponse, options, callback); } /** */ public publish(options: PublishRequest, callback: (err: Error | null, response?: PublishResponse) => void): void { this._defaultRequest(PublishRequest, PublishResponse, options, callback); } /** * */ public republish(options: RepublishRequest, callback: (err: Error | null, response?: RepublishResponse) => void): void { this._defaultRequest(RepublishRequest, RepublishResponse, options, callback); } /** * */ public deleteMonitoredItems( options: DeleteMonitoredItemsRequestLike, callback: (err: Error | null, response?: DeleteMonitoredItemsResponse) => void ): void { this._defaultRequest(DeleteMonitoredItemsRequest, DeleteMonitoredItemsResponse, options, callback); } /** * */ public setPublishingMode(publishingEnabled: boolean, subscriptionId: SubscriptionId): Promise<StatusCode>; public setPublishingMode(publishingEnabled: boolean, subscriptionIds: SubscriptionId[]): Promise<StatusCode[]>; public setPublishingMode( publishingEnabled: boolean, subscriptionId: SubscriptionId, callback: (err: Error | null, statusCode?: StatusCode) => void ): void; public setPublishingMode( publishingEnabled: boolean, subscriptionIds: SubscriptionId[], callback: (err: Error | null, statusCodes?: StatusCode[]) => void ): void; /** * @internal */ public setPublishingMode(...args: any[]): any { const publishingEnabled = args[0]; const isArray = Array.isArray(args[1]); const subscriptionIds = isArray ? args[1] : [args[1]]; const callback = args[2]; assert(typeof callback === "function"); assert(publishingEnabled === true || publishingEnabled === false); const options = new SetPublishingModeRequest({ publishingEnabled, subscriptionIds }); this._defaultRequest( SetPublishingModeRequest, SetPublishingModeResponse, options, (err: Error | null, response?: SetPublishingModeResponse) => { /* c8 ignore next */ if (err) { return callback(err); } /* c8 ignore next */ if (!response) { return callback(new Error("Internal Error")); } response.results = response.results || /* c8 ignore next */ []; callback(err, isArray ? response.results : response.results[0]); } ); } /** * */ public translateBrowsePath(browsePath: BrowsePath, callback: ResponseCallback<BrowsePathResult>): void; public translateBr