function __validateDataChangeFilter(filter,itemToMonitor,node) { assert(itemToMonitor.attributeId === AttributeIds.Value); assert(filter instanceof subscription_service.DataChangeFilter); if (!(node instanceof UAVariable)) { return StatusCodes.BadNodeIdInvalid; } assert(node instanceof UAVariable); // if node is not Numerical=> DataChangeFilter assert(node.dataType instanceof NodeId); var dataType = node.addressSpace.findDataType(node.dataType); var dataTypeNumber = node.addressSpace.findDataType("Number"); if (!dataType.isSupertypeOf(dataTypeNumber)) { return StatusCodes.BadFilterNotAllowed; } if (filter.deadbandType === subscription_service.DeadbandType.Percent) { if (filter.deadbandValue < 0 || filter.deadbandValue > 100) { return StatusCodes.BadDeadbandFilterInvalid; } // node must also have a valid euRange if (!node.euRange) { console.log(" node has no euRange ! DeadbandPercent cannot be used on node "+ node.nodeId.toString()); return StatusCodes.BadMonitoredItemFilterUnsupported; } } return StatusCodes.Good; }
function _decode_member_(value, field, stream, options) { var tracer = options.tracer; var cursor_before = stream.length; var fieldType = field.fieldType; if (field.category === "basic") { value = field.schema.decode(stream); tracer.trace("member", options.name, value, cursor_before, stream.length, fieldType); } else if (field.category === "enumeration") { value = field.schema.decode(stream); tracer.trace("member", options.name, value, cursor_before, stream.length, fieldType); } else { assert(field.category === "complex"); assert(_.isFunction(field.schema)); var Constructor = field.schema; value = new Constructor(); value.decode_debug(stream, options); } return value; }
var makeNodeId = function makeNodeId(value, namespace) { value = value || 0; namespace = namespace || 0; var identifierType = NodeIdType.NUMERIC; if (typeof value === "string") { // 1 2 3 // 012345678901234567890123456789012345 // "72962B91-FA75-4AE6-8D28-B404DC7DAF63" if (isValidGuid(value)) { identifierType = NodeIdType.GUID; } else { identifierType = NodeIdType.STRING; // detect accidental string of form "ns=x;x"; assert(value.indexOf("ns=") === -1, " makeNodeId(string) ? did you mean using coerceNodeId instead? "); } } else if (value instanceof Buffer) { identifierType = NodeIdType.BYTESTRING; } var nodeId = new NodeId(identifierType, value, namespace); assert(nodeId.hasOwnProperty("identifierType")); return nodeId; };
AddressSpace.prototype.addState = function (component, stateName, stateNumber, isInitialState) { var addressSpace = this; isInitialState = !!isInitialState; assert(component instanceof UAObjectType); assert(_.isString(stateName)); assert(_.isBoolean(isInitialState)); var initialStateType = addressSpace.findObjectType("InitialStateType"); var stateType = addressSpace.findObjectType("StateType"); var state; if (isInitialState) { state = initialStateType.instantiate({ browseName: stateName, componentOf: component }); } else { state = stateType.instantiate({ browseName: stateName, componentOf: component }); } // ensure state number is unique state.stateNumber.setValueFromSource({ dataType: DataType.UInt32, value: stateNumber }); return state; };
function NumericRange(value, second_value) { var self = this; function _construct_from_string(value) { var nr = construct_numeric_range_from_string(value); self.type = nr.type; self.value = nr.value; } function _construct_from_values(value, second_value) { if (_.isUndefined(second_value)) { self._set_single_value(value); } else { if (!_.isFinite(second_value)) { throw new Error(" invalid second argument, expecting a number"); } self._set_range_value(value, second_value); } } function _construct_from_array(value) { assert(value.length === 2); if (_.isFinite(value[0])) { if (!_.isFinite(value[1])) { throw new Error(" invalid range in " + value); } self._set_range_value(value[0], value[1]); } } function _construct_from_NumericRange(value) { self.value = _.clone(value); self.type = value.type; } assert(!value || !(value instanceof NumericRange), "use coerce to create a NumericRange"); if (typeof value === "string") { _construct_from_string(value); } else if (_.isFinite(value) && !_.isUndefined(value)) { _construct_from_values(value, second_value); } else if (_.isArray(value)) { _construct_from_array(value); } else if (value instanceof NumericRange) { _construct_from_NumericRange(value); } else { this.value = "<invalid>"; this.type = NumericRangeType.Empty; } assert((this.type !== NumericRangeType.ArrayRange) || _.isArray(this.value)); }
UADataType.prototype._getDefinition = function () { var self = this; var definition = []; if (self.enumStrings) { var enumStrings = self.enumStrings.readValue().value.value; assert(_.isArray(enumStrings)); definition = enumStrings.map(function(e,index){ return { value: index, name: e.text }; }); } else if (self.enumValues) { assert(self.enumValues,"must have a enumValues property"); var enumValues = self.enumValues.readValue().value.value; assert(_.isArray(enumValues)); definition = _.map(enumValues,function(e) { return { name: e.displayName.text, value: e.value[1]}; }); } // construct nameIndex and valueIndex var indexes = { nameIndex: {}, valueIndex: {} }; definition.forEach(function(e){ indexes.nameIndex[e.name] =e; indexes.valueIndex[e.value] =e; }); return indexes; };
NumericRange.overlap = function (nr1, nr2) { nr1 = nr1 || empty; nr2 = nr2 || empty; assert(nr1 instanceof NumericRange); assert(nr2 instanceof NumericRange); if (NumericRangeType.Empty === nr1.type || NumericRangeType.Empty === nr2.type) { return true; } if (NumericRangeType.SingleValue === nr1.type && NumericRangeType.SingleValue === nr2.type) { return nr1.value === nr2.value; } if (NumericRangeType.ArrayRange === nr1.type && NumericRangeType.ArrayRange === nr2.type) { // +-----+ +------+ +---+ +------+ // +----+ +---+ +--------+ +---+ var l1 = nr1.value[0]; var h1 = nr1.value[1]; var l2 = nr2.value[0]; var h2 = nr2.value[1]; return _overlap(l1, h1, l2, h2); } console.log(" NR1 = ", nr1.toEncodeableString()); console.log(" NR2 = ", nr2.toEncodeableString()); assert(false, "not implemented yet "); // TODO };
NodeCrawler.prototype._push_task = function (name, task) { var self = this; assert(_.isFunction(task.func)); task.comment = "P:" + name; assert(task.func.length === 2); self.q.push(task); };
OPCUAClient.prototype.withSubscription = function (endpointUrl, subscriptionParameters, innerFunc, callback) { assert(_.isFunction(innerFunc)); assert(_.isFunction(callback)); this.withSession(endpointUrl, function (session, done) { assert(_.isFunction(done)); const subscription = new ClientSubscription(session, subscriptionParameters); try { innerFunc(session, subscription, function () { subscription.terminate(function (err) { done(err); }); }); } catch (err) { console.log(err); done(err); } }, callback); };
global.setInterval = function (func, delay) { assert(arguments.length === 2); assert(delay !== undefined); assert(_.isFinite(delay)); if (delay <= 10) { throw new Error("GLOBAL#setInterval called with a too small delay = " + delay.toString()); } // increase number of pending timers self.setIntervalCallCount += 1; var key = self.setIntervalCallCount; var intervalId = self.setInterval_old(func, delay); self.interval_map[key] = { intervalId: intervalId, disposed: false, stack: get_stack() }; if (trace) { console.log("setInterval \n", get_stack().red, "\n"); } return key; };
this._activateSession(session, function (err) { if (!err) { if (old_client !== self) { // remove session from old client: if (old_client) { old_client._removeSession(session); assert(!_.contains(old_client._sessions, session)); } self._addSession(session); assert(session._client === self); assert(!session.closed,"session should not vbe closed"); assert(_.contains(self._sessions, session)); } } else { // istanbul ignore next if (doDebug) { console.log("reactivateSession has failed !".red.bgWhite, err.message); } } callback(err); });
ChunkManager.prototype.write = function (buffer, length) { length = length || buffer.length; assert(buffer instanceof Buffer || (buffer === null)); assert(length > 0); var l = length; var input_cursor = 0; while (l > 0) { assert(length - input_cursor !== 0); if (this.cursor === 0) { this._push_pending_chunk(false); } // space left in current chunk var space_left = this.maxBodySize - this.cursor; var nb_to_write = Math.min(length - input_cursor, space_left); if (buffer) { buffer.copy(this.chunk, this.cursor + this.dataOffset, input_cursor, input_cursor + nb_to_write); } input_cursor += nb_to_write; this.cursor += nb_to_write; if (this.cursor >= this.maxBodySize) { this._postprocess_current_chunk(); } l -= nb_to_write; } };
/** * @method extractEventFields * extract a array of eventFields from a event node, matching the selectClauses * @param selectClauses * @param eventData : a pseudo Node that provides a browse Method and a readValue(nodeId) */ function extractEventFields(selectClauses,eventData) { assert_valid_event_data(eventData); assert(_.isArray(selectClauses)); assert(selectClauses.length===0 || selectClauses[0] instanceof SimpleAttributeOperand); return selectClauses.map(extractEventField.bind(null, eventData)); }
ClientSecureChannelLayer.prototype._construct_security_header = function () { var self = this; assert(self.hasOwnProperty("securityMode")); assert(self.hasOwnProperty("securityPolicy")); self.receiverCertificate = self.serverCertificate; var securityHeader = null; switch (self.securityMode.value) { case MessageSecurityMode.SIGN.value: case MessageSecurityMode.SIGNANDENCRYPT.value: assert(self.securityPolicy !== SecurityPolicy.None); // get the thumbprint of the client certificate var thumbprint = self.receiverCertificate ? crypto_utils.makeSHA1Thumbprint(self.receiverCertificate) : null; securityHeader = new AsymmetricAlgorithmSecurityHeader({ securityPolicyUri: securityPolicy_m.toURI(self.securityPolicy), senderCertificate: self.getCertificateChain(), // certificate of the private key used to sign the message receiverCertificateThumbprint: thumbprint // thumbprint of the public key used to encrypt the message }); break; default: /* istanbul ignore next */ assert(false, "invalid security mode"); } //xx console.log("xxxx security Header",securityHeader.toJSON()); //xx console.log("xxxx receiverCertificate",self.receiverCertificate.toString("base64").cyan); self.securityHeader = securityHeader; };
function verifySignature(receiverCertificate, receiverNonce, signature, senderCertificate, securityPolicy) { if (securityPolicy === SecurityPolicy.None) { return true; } var crypto_factory = getCryptoFactory(securityPolicy); if (!crypto_factory) { return false; } assert(receiverNonce instanceof Buffer); assert(receiverCertificate instanceof Buffer); assert(signature instanceof SignatureData); assert(senderCertificate instanceof Buffer); if (!(signature.signature instanceof Buffer)) { // no signature provided return false; } assert(signature.signature instanceof Buffer); // This parameter is calculated by appending the clientNonce to the clientCertificate var buffer = Buffer.concat([receiverCertificate, receiverNonce]); return crypto_factory.asymmetricVerify(buffer, signature.signature, senderCertificate); }
function _install_security_token_watchdog() { /* jshint validthis: true */ var self = this; // // install timer event to raise a 'lifetime_75' when security token is about to expired // so that client can request for a new security token // note that, for speedup in test, // it is possible to tweak this interval for test by specifying a tokenRenewalInterval value // var liveTime = self.securityToken.revisedLifeTime; assert(liveTime && liveTime > 20); var timeout = self.tokenRenewalInterval || liveTime * 75 / 100; timeout = Math.min(timeout, liveTime * 75 / 100); if (doDebug) { debugLog(" time until next security token renal = ".red.bold, timeout, "( lifefime = ", liveTime + ")"); } assert(self._securityTokenTimeoutId === null); self._securityTokenTimeoutId = setTimeout(function () { self._securityTokenTimeoutId = null; _on_security_token_about_to_expire.call(self); }, timeout); }
OPCUAClient.prototype._closeSession = function (session, deleteSubscriptions, callback) { var self = this; assert(_.isFunction(callback)); assert(_.isBoolean(deleteSubscriptions)); // istanbul ignore next if (!self._secureChannel) { return callback(new Error("no channel")); } assert(self._secureChannel); var request = new CloseSessionRequest({ deleteSubscriptions: deleteSubscriptions }); if (!self._secureChannel.isValid()) { return callback(); } session.performMessageTransaction(request, function (err, response) { if (err) { //xx console.log("xxx received : ", err, response); //xx self._secureChannel.close(function () { //xx callback(err, null); //xx }); callback(err, null); } else { callback(err, response); } }); };
/** * Construct a node ID * * @class NodeId * @param {NodeIdType} identifierType - the nodeID type * @param {Number|String|GUID|Buffer} value - the node id value. The type of Value depends on identifierType. * @param {Number} namespace - the index of the related namespace (optional , default value = 0 ) * @example * * ``` javascript * var nodeId = new NodeId(NodeIdType.NUMERIC,123,1); * ``` * @constructor */ function NodeId(identifierType, value, namespace) { /** * @property identifierType * @type {NodeIdType} */ this.identifierType = NodeIdType.get(identifierType.value); assert(this.identifierType); /** * @property value * @type {*} */ this.value = value; /** * @property namespace * @type {Number} */ this.namespace = namespace || 0; // namespace shall be a UInt16 assert(this.namespace >= 0 && this.namespace <= 0xFFFF); assert(this.identifierType !== NodeIdType.NUMERIC || (this.value >= 0 && this.value <= 0xFFFFFFFF)); assert(this.identifierType !== NodeIdType.GUID || isValidGuid(this.value)); assert(this.identifierType !== NodeIdType.STRING || typeof this.value === "string"); }
UAStateMachine.prototype.findTransitionNode = function(fromStateNode,toStateNode) { var self = this; var addressSpace = self.addressSpace; fromStateNode = self._coerceNode(fromStateNode); if (!fromStateNode) { return null; } toStateNode = self._coerceNode(toStateNode); assert(fromStateNode instanceof UAObject); assert(toStateNode instanceof UAObject); var stateType = addressSpace.findObjectType("StateType"); assert(fromStateNode.typeDefinitionObj.isSupertypeOf(stateType)); assert(toStateNode.typeDefinitionObj.isSupertypeOf(stateType)); var transitions = fromStateNode.findReferencesAsObject("FromState",false); transitions = transitions.filter(function(transition){ assert(transition.toStateNode instanceof UAObject); return transition.toStateNode === toStateNode; }); if (transitions.length ===0 ) { // cannot find a transition from fromState to toState return null; } assert(transitions.length === 1); return transitions[0]; };
function difference(v1,v2,absoluteDeadband) { assert(_.isFinite(absoluteDeadband)); if (v1.arrayType === VariantArrayType.Array) { if (v1.dataType !== v2.dataType) { return true; } if (v1.value.length !== v2.value.length) { return true; } var n = v1.value.length; var i =0; for (i=0;i<n;i++) { if (_differenceScalar(v1.value[i],v2.value[i],v1.dataType,absoluteDeadband)) { return true; } } return false; } else { assert(v1.arrayType === VariantArrayType.Scalar); assert(v1.dataType === v2.dataType); return _differenceScalar(v1.value,v2.value,v1.dataType,absoluteDeadband); } }
UAAcknowledgeableConditionBase.prototype._confirm_branch = function _confirm_branch(eventId,comment,branch,message) { assert(typeof(message) === "string"); assert(comment instanceof LocalizedText); var conditionNode = this; //xx var eventId = branch.getEventId(); assert(branch.getEventId().toString("hex") === eventId.toString("hex")); branch.setConfirmedState(true); branch.setRetain(false); branch.setComment(comment); conditionNode._raiseAuditConditionCommentEvent(message,eventId,comment); conditionNode._raiseAuditConditionConfirmEvent(branch); conditionNode.raiseNewBranchState(branch); /** * @event confirmed * @param eventId * @param comment * @param eventId * raised when the alarm branch has been confirmed */ conditionNode.emit("confirmed",eventId,comment,branch); };
/** * * @class HistoryReadRequest * @constructor * @extends BaseUAObject * @param options {Object} * @param [options.requestHeader] {RequestHeader} * @param [options.historyReadDetails] {ExtensionObject} Maximum age of the value to be read in milliseconds * @param [options.timestampsToReturn = 3] {TimestampsToReturn} An enumeration that specifies the Timestamps to be returned for each requested Variable Value Attribute. * @param [options.releaseContinuationPoints] {Boolean} * @param [options.nodesToRead] {HistoryReadValueId[]} List of Nodes and their Attributes to read. For each entry in this list, a StatusCode is returned, and if it indicates success, the Attribute Value is also returned. */ function HistoryReadRequest(options) { options = options || {}; /* istanbul ignore next */ if (schema_helpers.doDebug) { check_options_correctness_against_schema(this,schema,options); } var self = this; assert(this instanceof BaseUAObject); // ' keyword "new" is required for constructor call') resolve_schema_field_types(schema); BaseUAObject.call(this,options); if (options === null) { BaseUAObject.call(this,options); self.requestHeader = null; /* new RequestHeader(null); */ self.nodesToRead = null; /* null array */ return ; } /** * * @property requestHeader * @type {RequestHeader} */ self.requestHeader = new RequestHeader( options.requestHeader); /** * Maximum age of the value to be read in milliseconds * @property historyReadDetails * @type {ExtensionObject} */ self.historyReadDetails = initialize_field(schema.fields[1], options.historyReadDetails); /** * An enumeration that specifies the Timestamps to be returned for each requested Variable Value Attribute. * @property timestampsToReturn * @type {TimestampsToReturn} * @default 3 */ self.setTimestampsToReturn(initialize_field(schema.fields[2], options.timestampsToReturn)); /** * * @property releaseContinuationPoints * @type {Boolean} */ self.releaseContinuationPoints = initialize_field(schema.fields[3], options.releaseContinuationPoints); /** * List of Nodes and their Attributes to read. For each entry in this list, a StatusCode is returned, and if it indicates success, the Attribute Value is also returned. * @property nodesToRead * @type {HistoryReadValueId[]} */ self.nodesToRead = []; if (options.nodesToRead) { assert(_.isArray(options.nodesToRead)); self.nodesToRead = options.nodesToRead.map(function(e){ return new HistoryReadValueId(e); } ); } // Object.preventExtensions(self); }
function callConditionRefresh(subscription,callback) { var the_session = subscription.publish_engine.session; var subscriptionId = subscription.subscriptionId; assert(_.isFinite(subscriptionId),"May be subscription is not yet initialized"); assert(_.isFunction(callback)); var conditionTypeNodeId = resolveNodeId("ConditionType"); var browsePath = [ makeBrowsePath(conditionTypeNodeId,".ConditionRefresh") ]; var conditionRefreshId = resolveNodeId("ConditionType_ConditionRefresh"); //xx console.log("browsePath ", browsePath[0].toString({addressSpace: server.engine.addressSpace})); async.series([ // find conditionRefreshId function (callback) { the_session.translateBrowsePath(browsePath, function (err, results) { if(!err ) { // istanbul ignore else if (results[0].targets.length > 0){ conditionRefreshId = results[0].targets[0].targetId; } else { // cannot find conditionRefreshId console.log("cannot find conditionRefreshId",results[0].toString()); err = new Error(" cannot find conditionRefreshId"); } } callback(err); }); }, function (callback) { var methodsToCall = [{ objectId: conditionTypeNodeId, methodId: conditionRefreshId, inputArguments: [ new Variant({ dataType: DataType.UInt32, value: subscriptionId }) ] }]; the_session.call(methodsToCall,function(err,results) { if (err) { return callback(err); } // istanbul ignore next if (results[0].statusCode !== StatusCodes.Good) { return callback(new Error("Error " + results[0].statusCode.toString())); } callback(); }); } ],callback); }
function verify_message_chunk(message_chunk) { assert(message_chunk); assert(message_chunk instanceof Buffer); var header = readMessageHeader(new BinaryStream(message_chunk)); if (message_chunk.length !== header.length) { throw new Error(" chunk length = " + message_chunk.length + " message length " + header.length); } }
UATwoStateVariable.prototype.getValueAsString = function TwoStateVariable_getValue() { var node = this; var dataValue = node.readValue(); assert(dataValue.statusCode === StatusCodes.Good); assert(dataValue.value.dataType === DataType.LocalizedText); return dataValue.value.value.text.toString(); };
/*= * * @param arr * @param maxNode * @private * @return {*} */ function _fetch_elements(arr, maxNode) { assert(_.isArray(arr)); assert(arr.length > 0); var high_limit = ( maxNode <= 0) ? arr.length : maxNode; var tmp = arr.splice(0, high_limit); assert(tmp.length > 0); return tmp; }
ClientSubscription.prototype._remove = function (monitoredItem) { var self = this; var clientHandle = monitoredItem.monitoringParameters.clientHandle; assert(clientHandle); assert(self.monitoredItems.hasOwnProperty(clientHandle)); monitoredItem.removeAllListeners(); delete self.monitoredItems[clientHandle]; };
UATwoStateVariable.prototype.getValue = function TwoStateVariable_getValue() { var node = this; var dataValue = node.id.readValue(); assert(dataValue.statusCode === StatusCodes.Good); assert(dataValue.value.dataType === DataType.Boolean); return dataValue.value.value; };
OPCUADiscoveryServer.prototype._on_RegisterServerRequest = function (message, channel) { var server = this; var request = message.request; assert(request._schema.name === "RegisterServerRequest"); assert(request instanceof RegisterServerRequest); function sendError(statusCode) { console.log("_on_RegisterServerRequest error".red, statusCode.toString()); var response = new RegisterServerResponse({responseHeader: {serviceResult: statusCode}}); return channel.send_response("MSG", response, message); } // check serverType is valid if (!_isValideServerType(request.server.serverType)) { return sendError(StatusCodes.BadInvalidArgument); } // BadServerUriInvalid // TODO // BadServerNameMissing if (request.server.serverNames.length === 0) { return sendError(StatusCodes.BadServerNameMissing); } // BadDiscoveryUrlMissing if (request.server.discoveryUrls.length === 0) { return sendError(StatusCodes.BadDiscoveryUrlMissing); } var key = request.server.serverUri; if (request.server.isOnline) { console.log(" registering server : ".cyan, request.server.serverUri.yellow); server.registered_servers[key] = request.server; // prepare serverInfo which will be used by FindServers var serverInfo = {}; serverInfo.applicationUri = request.server.serverUri; serverInfo.applicationType = request.server.serverType; serverInfo.productUri = request.server.productUri; serverInfo.applicationName = request.server.serverNames[0]; // which one shall we use ? serverInfo.gatewayServerUri = request.server.gatewayServerUri; // XXX ?????? serverInfo.discoveryProfileUri = serverInfo.discoveryProfileUri; serverInfo.discoveryUrls = request.server.discoveryUrls; server.registered_servers[key].serverInfo = serverInfo; } else { if (key in server.registered_servers) { console.log(" unregistering server : ".cyan, request.server.serverUri.yellow); delete server.registered_servers[key]; } } var response = new RegisterServerResponse({}); channel.send_response("MSG", response, message); };
self._performMessageTransaction(msgType, msg, function (error, response) { if (response && response.responseHeader.serviceResult !== StatusCodes.Good) { error = new Error(response.responseHeader.serviceResult.toString()); } if (!error) { /* istanbul ignore next */ if (false && doDebug) { debugLog(response.toString()); } assert(response instanceof OpenSecureChannelResponse); //xx assert(!is_initial || self.securityToken.secureChannelId === response.securityToken.secureChannelId); // todo : verify that server certificate is valid // A self-signed application instance certificate does not need to be verified with a CA. // todo : verify that Certificate URI matches the ApplicationURI of the server self.securityToken = response.securityToken; assert(self.securityToken.tokenId > 0 || msgType === "OPN", "_sendSecureOpcUARequest: invalid token Id "); assert(response.hasOwnProperty("serverNonce")); self.serverNonce = response.serverNonce; if (self.securityMode !== MessageSecurityMode.NONE) { // verify that server nonce if provided is at least 32 bytes long /* istanbul ignore next */ if (!self.serverNonce) { console.log(" client : server nonce is invalid !"); return callback(new Error(" Invalid server nonce")); } // This parameter shall have a length equal to key size used for the symmetric // encryption algorithm that is identified by the securityPolicyUri. if (self.serverNonce.length !== self.clientNonce.length) { console.log(" client : server nonce is invalid !"); return callback(new Error(" Invalid server nonce length")); } } var cryptoFactory = self.messageBuilder.cryptoFactory; if (cryptoFactory) { assert(self.serverNonce instanceof Buffer); self.derivedKeys = cryptoFactory.compute_derived_keys(self.serverNonce, self.clientNonce); } var derivedServerKeys = self.derivedKeys ? self.derivedKeys.derivedServerKeys : null; self.messageBuilder.pushNewToken(self.securityToken, derivedServerKeys); _install_security_token_watchdog.call(self); self._isOpened = true; } callback(error); });