APIs

Show:
"use strict";
const _ = require("underscore");
const assert = require("node-opcua-assert").assert;


const factories = require("node-opcua-factory");
const ec = require("node-opcua-basic-types");
const Variant = require("node-opcua-variant").Variant;
const NodeId = require("node-opcua-nodeid").NodeId;
const StatusCodes = require("node-opcua-status-code").StatusCodes;
const VariantArrayType = require("node-opcua-variant").VariantArrayType;


const UADataType = require("./ua_data_type").UADataType;
const UAMethod = require("./ua_method").UAMethod;

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

exports.Argument = Argument;

function myfindBuiltInType(dataType) {
    return factories.findBuiltInType(dataType.key);
}

const debugLog = require("node-opcua-debug").make_debugLog(__filename);
const doDebug = require("node-opcua-debug").checkDebugFlag(__filename);


function encode_ArgumentList(definition, args, stream) {

    assert(definition.length === args.length);

    assert(_.isArray(definition));
    assert(_.isArray(args));
    assert(definition.length === args.length);
    assert(definition.length >= 0);

    // we only encode arguments by following the definition

    for (let i = 0; i < definition.length; i++) {

        const def = definition[i];
        const value = args[i];

        const encodeFunc = myfindBuiltInType(def.dataType).encode;

        // xx console.log(" cxxxxxxxxxxc ",def);
        // assert((def.valueRank === -1) || (def.valueRank === 0));

        // todo : handle -3 -2
        const isArray = (def.valueRank && ( def.valueRank === 1 || def.valueRank !== -1)) ? true : false;

        if (isArray) {
            ec.encodeArray(value, stream, encodeFunc);
        } else {
            encodeFunc(value, stream);
        }
    }

}
exports.encode_ArgumentList = encode_ArgumentList;

const decode_ArgumentList = function (definition, stream) {

    if (!_.isArray(definition)) {
        throw new Error(
            "This BaseDataType cannot be decoded because it has no definition.\n" +
            "Please construct a BaseDataType({definition : [{dataType: DataType.UInt32 }]});"
        );
    }

    const args = [];
    let value;

    for (let i = 0; i < definition.length; i++) {

        const def = definition[i];

        const decodeFunc = myfindBuiltInType(def.dataType).decode;

        //xx assert(def.valueRank === -1 || def.valueRank==0);
        const isArray = ( def.valueRank === 1 || def.valueRank === -1) ? true : false;

        if (isArray) {
            value = ec.decodeArray(stream, decodeFunc);
        } else {
            value = decodeFunc(stream);
        }
        args.push(value);
    }
    return args;
};

exports.decode_ArgumentList = decode_ArgumentList;

const BinaryStreamSizeCalculator = require("node-opcua-binary-stream").BinaryStreamSizeCalculator;
const binaryStoreSize_ArgumentList = function (description, args) {

    assert(_.isArray(description));
    assert(_.isArray(args));
    assert(args.length === description.length);

    const stream = new BinaryStreamSizeCalculator();
    encode_ArgumentList(description, args, stream);
    return stream.length;
};
exports.binaryStoreSize_ArgumentList = binaryStoreSize_ArgumentList;


function getMethodDeclaration_ArgumentList(addressSpace, objectId, methodId) {

    assert(objectId instanceof NodeId);
    assert(methodId instanceof NodeId);
    // find object in address space
    const obj = addressSpace.findNode(objectId);
    if (!obj) {

        // istanbul ignore next
        if(doDebug) {
            console.warn("cannot find node ",objectId.toString());
        }
        return {statusCode: StatusCodes.BadNodeIdUnknown};
    }
    if (!obj.hasMethods) {
        return {statusCode: StatusCodes.BadNodeIdInvalid};
    }
    let objectMethod = obj.getMethodById(methodId);
    if (!objectMethod) {

        // the method doesn't belong to the object, nevertheless
        // the method can be called
        objectMethod = addressSpace.findNode(methodId);
        if (!objectMethod || !(objectMethod instanceof UAMethod)) {
            return {statusCode: StatusCodes.BadMethodInvalid};
        }
    }

    const methodDeclarationId = objectMethod.methodDeclarationId;

    const methodDeclaration = addressSpace.findNode(methodDeclarationId);
    if (!methodDeclaration) {
        //  return {statusCode: StatusCodes.BadMethodInvalid};
        return {statusCode: StatusCodes.Good, methodDeclaration: objectMethod};
    }
    return {statusCode: StatusCodes.Good, methodDeclaration: methodDeclaration};
}
exports.getMethodDeclaration_ArgumentList = getMethodDeclaration_ArgumentList;

/**
 * @method isArgumentValid
 * @param addressSpace  {AddressSpace}
 * @param argDefinition {Argument}
 * @param arg           {Variant}
 * @return              {Boolean}
 * @private
 */
function isArgumentValid(addressSpace,argDefinition, arg) {

    assert(argDefinition instanceof Argument);
    assert(argDefinition.hasOwnProperty("dataType"));
    assert(argDefinition.hasOwnProperty("valueRank"));
    assert(arg instanceof Variant);

    const argDefDataType = addressSpace.findDataType(argDefinition.dataType);
    const argDataType = addressSpace.findDataType(arg.dataType);

    if (! argDataType) {
        console.log(" cannot find dataType ",arg.dataType);
        return false;
    }
    assert(argDefDataType instanceof UADataType);
    assert(argDataType    instanceof UADataType);

    // istanbul ignore next
    if (doDebug) {
        console.log(" checking argDefDataType ",argDefDataType.toString());
        console.log(" checking argDataType ",argDataType.toString());
    }


    const isArray = (arg.arrayType === VariantArrayType.Array);

    if (argDefinition.valueRank > 0) {

        return isArray;

    } else if (argDefinition.valueRank === -1 ) { // SCALAR
        if (isArray) {
            return false;
        }
    }

    if (argDataType.nodeId.value === argDefDataType.nodeId.value) {
        return true;
    }

    // check that dataType is of the same type (derived )
    return argDefDataType.isSupertypeOf(argDataType);

}

/**
 * @method verifyArguments_ArgumentList
 * @param addressSpace {AddressSpace}
 * @param methodInputArguments {Argument[]}
 * @param inputArguments       {Variant[]}
 * @return statusCode,inputArgumentResults
 */
function verifyArguments_ArgumentList(addressSpace,methodInputArguments, inputArguments) {

    const inputArgumentResults = [];

    if (methodInputArguments.length === 0 && !inputArguments) {
        // it is possible to not provide inputArguments when method  has no arguments
        return {statusCode: StatusCodes.Good};
    }
    if (methodInputArguments.length > 0 && !inputArguments) {
         return {statusCode: StatusCodes.BadArgumentsMissing};
    }
    inputArguments = inputArguments ||[];
    if (methodInputArguments.length > inputArguments.length) {
        // istanbul ignore next
        if (doDebug) {
            console.log("xxxxxxxx verifyArguments_ArgumentList " +
                "\n       The client did  specify too many input arguments for the method.  " +
                "\n        expected : " +methodInputArguments.length + "" +
                "\n        actual   : " + inputArguments.length );
        }
        return {statusCode: StatusCodes.BadInvalidArgument};
    }

    if (methodInputArguments.length < inputArguments.length) {
        // istanbul ignore next
        if (doDebug) {
            console.log("xxxxxxxx verifyArguments_ArgumentList " +
                "\n        The client did not specify all of the input arguments for the method. " +
                "\n        expected : " + methodInputArguments.length + "" +
                "\n        actual   : " + inputArguments.length);
        }
        return {statusCode: StatusCodes.BadArgumentsMissing};
    }

    let errorCount = 0;
    for (let i = 0; i < methodInputArguments.length; i++) {

        const argDefinition = methodInputArguments[i];

        const arg = inputArguments[i];

        // istanbul ignore next
        if (doDebug) {
            console.log("xxxxxxxx verifyArguments_ArgumentList checking argument " + i +
                "\n        expected : " + JSON.stringify(argDefinition) +
                "\n        actual:    " + JSON.stringify(arg));
        }
        if (!isArgumentValid(addressSpace,argDefinition, arg)) {
            // istanbul ignore next
            if (doDebug) {
                console.log("xxxxxxxx verifyArguments_ArgumentList \n" +
                            "         The client did specify a argument with the wrong data type.\n" +
                            "          expected : ".white + argDefinition.dataType + "\n" +
                            "          actual   :".cyan + arg.dataType);
            }
            inputArgumentResults.push(StatusCodes.BadTypeMismatch);
            errorCount += 1;
        } else {
            inputArgumentResults.push(StatusCodes.Good);
        }
    }
    assert(inputArgumentResults.length === methodInputArguments.length);

    const ret = {
        statusCode: errorCount === 0 ? StatusCodes.Good : StatusCodes.BadInvalidArgument,
        inputArgumentResults: inputArgumentResults
    };

    return ret;
}


exports.verifyArguments_ArgumentList = verifyArguments_ArgumentList;


function build_retrieveInputArgumentsDefinition(addressSpace) {
    const the_address_space = addressSpace;
    return function (objectId, methodId) {
        const response = getMethodDeclaration_ArgumentList(the_address_space, objectId, methodId);

        /* istanbul ignore next */
        if (response.statusCode !== StatusCodes.Good) {
            console.log(" StatusCode  = " + response.statusCode.toString());
            throw new Error("Invalid Method " + response.statusCode.toString() + "ObjectId= " + objectId.toString() + "Method Id =" + methodId.toString());
        }
        const methodDeclaration = response.methodDeclaration;
        // verify input Parameters
        const methodInputArguments = methodDeclaration.getInputArguments();
        assert(_.isArray(methodInputArguments));
        return methodInputArguments;
    };
}
exports.build_retrieveInputArgumentsDefinition = build_retrieveInputArgumentsDefinition;

function convertJavaScriptToVariant(argumentDefinition, values) {

    assert(argumentDefinition.length === values.length);
    assert(_.isArray(argumentDefinition));
    assert(_.isArray(values));

    return _.zip(values, argumentDefinition).map(function (pair) {

        const value = pair[0];
        const arg = pair[1];
        const variant = _.extend({}, arg);
        variant.value = value;
        return new Variant(variant);
    });
}
exports.convertJavaScriptToVariant = convertJavaScriptToVariant;