"use strict";
/**
* @module opcua.address_space
*/
const assert = require("node-opcua-assert").assert;
const util = require("util");
const _ = require("underscore");
const NodeClass = require("node-opcua-data-model").NodeClass;
const AttributeIds = require("node-opcua-data-model").AttributeIds;
const DataValue = require("node-opcua-data-value").DataValue;
const DataType = require("node-opcua-variant").DataType;
const Variant = require("node-opcua-variant").Variant;
const StatusCodes = require("node-opcua-status-code").StatusCodes;
const BaseNode = require("./base_node").BaseNode;
const UAVariable = require("./ua_variable").UAVariable;
const SessionContext = require("./session_context").SessionContext;
function UAMethod(options) {
BaseNode.apply(this, arguments);
// assert(this.typeDefinition.value === resolveNodeId("MethodType").value);
this.value = options.value;
this.methodDeclarationId = options.methodDeclarationId;
}
util.inherits(UAMethod, BaseNode);
UAMethod.prototype.nodeClass = NodeClass.Method;
UAMethod.prototype.getExecutableFlag = function (context) {
assert(context instanceof SessionContext);
if (!_.isFunction(this._asyncExecutionFunction)) {
return false;
}
if (this._getExecutableFlag) {
return this._getExecutableFlag(context);
}
return true;
};
UAMethod.prototype.readAttribute = function (context, attributeId) {
assert(context instanceof SessionContext);
const self = this;
const options = {};
switch (attributeId) {
case AttributeIds.Executable:
options.value = {dataType: DataType.Boolean, value: self.getExecutableFlag(context)};
options.statusCode = StatusCodes.Good;
break;
case AttributeIds.UserExecutable:
options.value = {dataType: DataType.Boolean, value: self.getExecutableFlag(context)};
options.statusCode = StatusCodes.Good;
break;
default:
return BaseNode.prototype.readAttribute.call(this, context, attributeId);
}
return new DataValue(options);
};
function default_check_valid_argument(arg) {
return arg.constructor.name === "Argument";
/*
var Argument = require("./_generated_/_auto_generated_Argument").Argument;
return arg instanceof Argument
*/
}
UAMethod.checkValidArgument = default_check_valid_argument;
UAMethod.prototype._getArguments = function (name) {
assert(name === "InputArguments" || name === "OutputArguments");
const argsVariable = this.getPropertyByName(name);
if (!argsVariable) { return [];}
assert(argsVariable instanceof UAVariable);
const args = argsVariable.readValue().value.value;
// a list of extension object
assert(_.isArray(args));
assert(args.length === 0 || UAMethod.checkValidArgument(args[0]) );
return args;
};
UAMethod.prototype.getInputArguments = function () {
return this._getArguments("InputArguments");
};
UAMethod.prototype.getOutputArguments = function () {
return this._getArguments("OutputArguments");
};
/**
* @method bindMethod
* @param async_func {Function}
* @param async_func.inputArguments
* @param async_func.context
* @param async_func.callback {Function}
* @param async_func.callback.err {Error|null}
* @param async_func.callback.callMethodResponse.statusCode {StatusCode}
* @param async_func.callback.callMethodResponse.outputArguments {Variant[]}
*/
UAMethod.prototype.bindMethod = function (async_func) {
assert(_.isFunction(async_func));
const self = this;
self._asyncExecutionFunction = async_func;
};
/**
* @method execute
* @async
* @param context {SessionContext}
* @param inputArguments {null|Variant[]} input arguments as array of variant
* @param callback
* @async
*/
UAMethod.prototype.execute = function (inputArguments, context, callback) {
assert(inputArguments === null || _.isArray(inputArguments));
assert(context instanceof SessionContext);
inputArguments = inputArguments ||[];
inputArguments = inputArguments.map(Variant.coerce);
assert(inputArguments.length === 0 || inputArguments[0] instanceof Variant);
assert(_.isObject(context));
assert(_.isFunction(callback));
const self = this;
// a context object must be provided
if (!context.object) {
context.object = self.parent;
}
assert(context.object instanceof BaseNode);
//xx inputArguments = inputArguments.map(function(a) { return Variant.coerce(a); });
//xx inputArguments.forEach(function(arg){ assert(arg instanceof Variant); });
if (!self._asyncExecutionFunction) {
console.log("Method " + self.nodeId.toString() + " " + self.browseName.toString() + "_ has not been bound");
return callback(null, {statusCode: StatusCodes.BadInternalError});
}
if (!self.getExecutableFlag(context)) {
console.log("Method " + self.nodeId.toString() + " " + self.browseName.toString() + "_ is not executable");
// todo : find the correct Status code to return here
return callback(null, {statusCode: StatusCodes.BadMethodInvalid});
}
// verify that input arguments are correct
// todo :
const inputArgumentResults = [];
const inputArgumentDiagnosticInfos = [];
try {
self._asyncExecutionFunction(inputArguments, context, function (err, callMethodResponse) {
callMethodResponse = callMethodResponse || {};
callMethodResponse.statusCode = callMethodResponse.statusCode || StatusCodes.Good;
callMethodResponse.outputArguments = callMethodResponse.outputArguments || [];
callMethodResponse.inputArgumentResults = inputArgumentResults;
callMethodResponse.inputArgumentDiagnosticInfos = inputArgumentDiagnosticInfos;
// verify that output arguments are correct according to schema
// Todo : ...
const outputArgsDef = self.getOutputArguments();
//xx assert(outputArgsDef.length === callMethodResponse.outputArguments.length,
//xx "_asyncExecutionFunction did not provide the expected number of output arguments");
// to be continued ...
callback(err, callMethodResponse);
});
} catch(err){
console.log("ERR in method handler".red,err.message);
console.error(err.stack);
const callMethodResponse = { statusCode: StatusCodes.BadInternalError};
callback(err, callMethodResponse);
}
};
UAMethod.prototype.clone = function (options,optionalFilter,extraInfo) {
const Namespace = require("./namespace").Namespace;
assert(!options.componentOf || options.componentOf,"trying to create an orphan method ?");
const self = this;
options = options || {};
options = _.extend(_.clone(options),{
methodDeclarationId: self.nodeId
});
options.references = options.references||[];
const addressSpace = self.addressSpace;
Namespace._handle_hierarchy_parent(addressSpace,options.references,options);
const clonedMethod = self._clone(UAMethod,options, optionalFilter, extraInfo);
clonedMethod._asyncExecutionFunction = self._asyncExecutionFunction;
clonedMethod._getExecutableFlag = self._getExecutableFlag;
if (options.componentOf) {
//Xx console.log("Options ",options.componentOf.browseName.toString());
const m = options.componentOf.getMethodByName(clonedMethod.browseName.name);
assert(m);
}
return clonedMethod;
};
exports.UAMethod = UAMethod;