APIs

Show:
"use strict";
/**
 * @module opcua.client
 */

const assert = require("node-opcua-assert").assert;
const _ = require("underscore");
const async = require("async");
//
const resolveNodeId = require("node-opcua-nodeid").resolveNodeId;
const Variant = require("node-opcua-variant").Variant;
const DataType = require("node-opcua-variant").DataType;
const StatusCodes = require("node-opcua-status-code").StatusCodes;

const LocalizedText = require("node-opcua-data-model").LocalizedText;
const NodeId = require("node-opcua-nodeid").NodeId;
const coerceNodeId = require("node-opcua-nodeid").coerceNodeId;
assert(_.isFunction(coerceNodeId));

const ClientSession = require("../client_session").ClientSession;

const makeBrowsePath = require("node-opcua-service-translate-browse-path").makeBrowsePath;

const call_service = require("node-opcua-service-call");
const CallMethodRequest = call_service.CallMethodRequest;



function callConditionRefresh(subscription,callback) {

    const the_session    = subscription.publish_engine.session;
    const subscriptionId = subscription.subscriptionId;

    assert(_.isFinite(subscriptionId),"May be subscription is not yet initialized");
    assert(_.isFunction(callback));

    const conditionTypeNodeId = resolveNodeId("ConditionType");

    const browsePath = [
        makeBrowsePath(conditionTypeNodeId,".ConditionRefresh")
    ];
    let 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 && 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) {

            const 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);
}
exports.callConditionRefresh = callConditionRefresh;

ClientSession.prototype.disableCondition = function(){

};

ClientSession.prototype.enableCondition = function(){

};


/**
 * @method addCommentCondition
 * The AddComment Method is used to apply a comment to a specific state of a Condition instance.
 * Normally, the NodeId of the object instance as the ObjectId is passed to the Call Service.
 * However, some Servers do not expose Condition instances in the AddressSpace. Therefore all Servers
 * shall also allow Clients to call the AddComment Method by specifying ConditionId as the ObjectId.
 * The Method cannot be called with an ObjectId of the ConditionType Node.
 *
 * Notes:
 * Comments are added to Event occurrences identified via an EventId. EventIds where the related EventType
 * is not a ConditionType (or subtype of it) and thus does not support Comments are rejected.
 * A ConditionEvent – where the Comment Variable contains this text – will be sent for the identified
 * state. If a comment is added to a previous state (i.e. a state for which the Server has created a
 * branch), the BranchId and all Condition values of this branch will be reported/.
 *
 * @param conditionId {NodeId}
 * @param eventId     {Buffer}
 * @param comment     {String|LocalizedText}
 * @param callback    {Function}
 * @param callback.err {Error|null}
 */
ClientSession.prototype.addCommentCondition = function(conditionId,eventId,comment,callback){
    this._callMethodCondition("AddComment",conditionId,eventId,comment,callback);
};



/**
 * @method findMethodId
 * @param nodeId
 * @param methodName
 * @param callback
 */
ClientSession.prototype.findMethodId = function(nodeId,methodName,callback) {

    const session =this;
    const browsePath = makeBrowsePath(nodeId, "/"+methodName);
    let methodId;
    session.translateBrowsePath(browsePath, function (err, results) {

        // istanbul ignore else
        if (results.targets.length > 0){
            methodId = results.targets[0].targetId;
            return callback(null,methodId);
        } else {
            // cannot find objectWithMethodNodeId
            console.log("cannot find "  + methodName + " Method",results.toString());
            err = new Error(" cannot find " + methodName + " Method");
        }
        callback(err);
    });

};



ClientSession.prototype._callMethodCondition = function(methodName,conditionId,eventId,comment,callback) {
    const session = this;
    conditionId = coerceNodeId(conditionId);
    assert(conditionId instanceof NodeId);
    assert(eventId instanceof Buffer);
    assert(typeof(comment) === "string" || comment instanceof LocalizedText);

    comment = LocalizedText.coerce(comment);

    let methodId;

    async.series([
        function (callback) {
            session.findMethodId(conditionId, methodName, function (err, _methodId) {
                if (!err) {
                    methodId = _methodId;
                }
                callback(err);
            });
        },
        function (callback) {
            const methodToCalls = [];
            methodToCalls.push(new CallMethodRequest({
                objectId: conditionId,
                methodId: methodId,
                inputArguments: [
                    /* eventId */ new Variant({dataType: "ByteString", value: eventId}),
                    /* comment */ new Variant({dataType: "LocalizedText", value: comment})
                ]
            }));

            session.call(methodToCalls, function (err, results) {
                if (err) {
                    return callback(err);
                } 
                callback(err, results[0]);
            });
        }
    ], function (err, results) {
        if (err) {
            return callback(err);
        }
        const call_results = results[1];
        callback(err, call_results.statusCode);
    });
};

/**
 * @method confirmCondition
 * from Spec 1.03 Part 9 : page 27
 *    The Confirm Method is used to confirm an Event Notifications for a Condition instance state
 *    where ConfirmedState is FALSE.
 *    Normally, the NodeId of the object instance as the ObjectId is passed to the Call Service.
 *    However, some Servers do not expose Condition instances in the AddressSpace.
 *    Therefore all Servers shall also allow Clients to call the Confirm Method by specifying ConditionId as the ObjectId.
 *    The Method cannot be called with an ObjectId of the AcknowledgeableConditionType Node.
 * @param conditionId
 * @param eventId
 * @param comment
 * @param callback
 */
ClientSession.prototype.confirmCondition = function(conditionId,eventId,comment,callback){
    // ns=0;i=9113 AcknowledgeableConditionType#Confirm
    // note that confirm method is Optionals on condition
    this._callMethodCondition("Confirm",conditionId,eventId,comment,callback);
};

/**
 * @class ClientSession
 * @method acknowledgeCondition
 *
 * from Spec 1.03 Part 9 : page 27
 *   The Acknowledge Method is used to acknowledge an Event Notification for a Condition
 *   instance state where AckedState is false.
 *   Normally, the NodeId of the object instance as the ObjectId is passed to the Call Service.
 *   However, some Servers do not expose Condition instances in the AddressSpace.
 *   Therefore all Servers shall also allow Clients to call the Acknowledge Method by specifying ConditionId as the ObjectId.
 *   The Method cannot be called with an ObjectId of the AcknowledgeableConditionType Node.
 *
 *   A Condition instance may be an Object that appears in the Server Address Space. If this is
 *   the case the ConditionId is the NodeId for the Object.
 *
 *   The EventId identifies a specific Event Notification where a state to be acknowledged was
 *   reported. Acknowledgement and the optional comment will be applied to the state identified
 *   with the EventId. If the comment field is NULL (both locale and text are empty) it will be
 *   ignored and any existing comments will remain unchanged. If the comment is to be reset, an
 *   empty text with a locale shall be provided.
 *   A valid EventId will result in an Event Notification where AckedState/Id is set to TRUE and the
 *   Comment Property contains the text of the optional comment argument. If a previous state is
 *   acknowledged, the BranchId and all Condition values of this branch will be reported.
 *
 * @param conditionId {NodeId}
 * @param eventId     {Buffer}
 * @param comment     {String|LocalizedText}
 * @param callback    {Function}
 * @param callback.err {Error|null}
 */
ClientSession.prototype.acknowledgeCondition = function(conditionId,eventId,comment,callback){
    // ns=0;i=9111 AcknowledgeableConditionType#Acknowledge
    this._callMethodCondition("Acknowledge",conditionId,eventId,comment,callback);
};