APIs

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


var async = require("async");
var assert = require("node-opcua-assert");
var _ = require("underscore");
var util = require("util");
var EventEmitter = require("events").EventEmitter;


var read_service = require("node-opcua-service-read");
var call_service = require("node-opcua-service-call");

var AttributeIds = require("node-opcua-data-model").AttributeIds;
var AccessLevelFlag = require("node-opcua-data-model").AccessLevelFlag;
var makeResultMask = require("node-opcua-data-model").makeResultMask;
var BrowseDirection = require("node-opcua-data-model").BrowseDirection;
var NodeClass = require("node-opcua-data-model").NodeClass;
var makeNodeClassMask = require("node-opcua-data-model").makeNodeClassMask;

var ReferenceTypeIds = require("node-opcua-constants").ReferenceTypeIds;
var ObjectTypeIds = require("node-opcua-constants").ObjectTypeIds;

var NodeId = require("node-opcua-nodeid").NodeId;
var makeNodeId = require("node-opcua-nodeid").makeNodeId;
var coerceNodeId = require("node-opcua-nodeid").coerceNodeId;

var StatusCodes = require("node-opcua-status-code").StatusCodes;

var lowerFirstLetter = require("node-opcua-utils").lowerFirstLetter;

var Variant = require("node-opcua-variant").Variant;
var VariantArrayType = require("node-opcua-variant").VariantArrayType;
var DataType = require("node-opcua-variant").DataType;


var ClientSession = require("node-opcua-client").ClientSession;
var ClientSubscription = require("node-opcua-client").ClientSubscription;

var resultMask = makeResultMask("ReferenceType | IsForward | BrowseName | NodeClass | TypeDefinition");

function makeRefId(referenceTypeName) {
    var nodeId = makeNodeId(ReferenceTypeIds[referenceTypeName] || ObjectTypeIds[referenceTypeName]);

    // istanbul ignore next
    if (nodeId.isEmpty()) {
        throw new Error("makeRefId: cannot find ReferenceTypeName + ", referenceTypeName);
    }
    return nodeId;
}
exports.makeRefId = makeRefId;

/**
 * @method convertNodeIdToDataTypeAsync
 *
 * @param session             {ClientSession}
 * @param dataTypeId          {NodeId}
 * @param callback            {Function}
 * @param callback.err        {Error|null}
 * @param callback.dataType   {DataType}
 *
 *  @example
 *
 *      var dataTypeId  ="ns=0;i=11"; // Double
 *      convertNodeIdToDataTypeAsync(session,dataTypeId,function(err,dataType) {
 *          assert(!err && dataType === DataType.Double);
 *      });
 *
 *      var dataTypeId  ="ns=0;i=290"; // Duration => SubTypeOf Double
 *      convertNodeIdToDataTypeAsync(session,dataTypeId,function(err,dataType) {
 *          assert(!err && dataType === DataType.Double);
 *      });
 *
 * see also AddressSpace#findCorrespondingBasicDataType
 */
function convertNodeIdToDataTypeAsync(session, dataTypeId, callback) {

    var nodesToRead = [{
        nodeId: dataTypeId,
        attributeId: AttributeIds.BrowseName
    }];

    session.read(nodesToRead, function (err, unused, dataValues) {

        // istanbul ignore next
        if (err) {
            return callback(err);
        }

        var dataValue = dataValues[0];

        // istanbul ignore next
        if (dataValue.statusCode !== StatusCodes.Good) {
            console.log("convertNodeIdToDataTypeAsync: Cannot read browse name for nodeID ".red + dataTypeId.toString());
            var dataType = DataType.Null;
            return callback(null, dataType);
        }

        var dataTypeName = dataValue.value.value;

        if (dataTypeId.namespace === 0 && DataType.get(dataTypeId.value)) {
            var dataType = DataType.get(dataTypeId.value);
            return callback(null, dataType);
        }

        /// example => Duration (i=290) => Double (i=11)
        // read subTypeOf
        var nodesToBrowse = [{
            // BrowseDescription
            referenceTypeId: makeRefId("HasSubtype"),
            //xx nodeClassMask: makeNodeClassMask("ObjectType"),
            includeSubtypes: false,
            browseDirection: BrowseDirection.Inverse,
            nodeId: dataTypeId,
            resultMask: resultMask
        }];
        session.browse(nodesToBrowse, function (err, results) {
            // istanbul ignore next
            if (err) {
                return callback(err);
            }

            var references = results[0].references;

            if (!references || references.length !== 1) {
                return callback(new Error("cannot find SuperType of " + dataTypeName.toString()));
            }
            var nodeId = references[0].nodeId;
            return convertNodeIdToDataTypeAsync(session, nodeId, callback);
        });
    });
}

function convertNodeIdToDataType(dataTypeId) {
    return dataTypeId._dataType;
}

function ProxyBaseNode(proxyManager, nodeId) {

    var self = this;
    /**
     * the object nodeId
     * @property nodeId
     * @type {NodeId}
     */
    self.nodeId = nodeId;

    self.proxyManager = proxyManager;
    assert(self.proxyManager.session, "expecting valid session");
    Object.defineProperty(self, "proxyManager", {
        enumerable: false,
        writable: true
    });
    /**
     * the object's components
     * @property $components
     * @type {Array<ProxyBaseNode>}
     */
    self.$components = [];

    /**
     * the object's properties
     * @property $properties
     * @type {Array<ProxyBaseNode>}
     */
    self.$properties = [];

    /**
     * the object's properties
     * @property $methods
     * @type {Array<ProxyBaseNode>}
     */
    self.$methods = [];

    /**
     * the Folder's elements
     * @property $organizes
     * @type {Array<ProxyBaseNode>}
     */
    self.$organizes = [];

    /**
     * the object's description
     * @property description
     * @type {String}
     */
    self.description = "";
    /**
     * the object's browseName
     * @property browseName
     * @type {String}
     */
    self.browseName = "";
    /**
     * the object's NodeClass
     * @property nodeClass
     * @type {NodeClass}
     */
    self.nodeClass = null;
}

util.inherits(ProxyBaseNode, EventEmitter);


/**
 * get a updated Value of the Variable , by using a ReadRequest
 * @method readValue
 * @param callback {Function}
 * @param callback.err {Error|null}
 * @param callback.dataValue {DataValue}
 */
ProxyBaseNode.prototype.readValue = function (callback) {

    var self = this;
    assert(self.proxyManager);

    var session = self.proxyManager.session;
    assert(session);

    var nodeToRead = {
        nodeId: self.nodeId,
        attributeId: AttributeIds.Value
    };
    self.proxyManager.session.read([nodeToRead], function (err, unused, results) {

        // istanbul ignore next
        if (err) {
            return callback(err);
        }

        var result = results[0];
        var data = result.value;
        callback(null, data);

    });
};

/**
 * set the Value of the Variable, by using a WriteRequest
 * @method writeValue
 * @param dataValue {DataValue}
 * @param callback {Function}
 * @param callback.err {Error|null}
 */
ProxyBaseNode.prototype.writeValue = function (dataValue, callback) {
    var self = this;
    assert(self.proxyManager);

    var session = self.proxyManager.session;
    assert(session);

    var nodeToWrite = {
        nodeId: self.nodeId,
        attributeId: AttributeIds.Value,
        value: dataValue
    };
    self.proxyManager.session.write([nodeToWrite], function (err, results) {

        // istanbul ignore next
        if (err) {
            return callback(err);
        }

        var result = results[0];
        /// console.log("xxxx r=",results.toString());
        if (result !== StatusCodes.Good) {
            callback(new Error(result.toString()));
        } else {
            callback(null);
        }
    });
};

ProxyBaseNode.prototype.toString = function () {

    var str = [];
    var self = this;
    str.push(" ProxyObject ");
    str.push("   browseName     : " + self.browseName.toString());
    str.push("   typeDefinition : " + self.typeDefinition.toString());
    str.push("   $components#   : " + self.$components.length.toString());
    str.push("   $properties#   : " + self.$properties.length.toString());

    return str.join("\n");
};

function ProxyVariable(session, nodeId) {
    ProxyBaseNode.apply(this, arguments);
}
util.inherits(ProxyVariable, ProxyBaseNode);

var ProxyObject = ProxyVariable;

function ObjectExplorer(options) {
    var self = this;
    self.proxyManager = options.proxyManager;
    self.name = options.name;
    self.nodeId = options.nodeId;
    self.parent = options.parent;
}

ObjectExplorer.prototype.$resolve = function (callback) {

    var self = this;

    self.proxyManager.getObject(self.nodeId, function (err, childObj) {

        // istanbul ignore next
        if (err) {
            return callback(err);
        }

        self.parent[self.name] = childObj;
        self.parent.$components.push(childObj);

        callback(null)
    });
};

function readUAStructure(proxyManager, obj, callback) {

    var session = proxyManager.session;

    //   0   Object
    //   1   Variable
    //   2   Method
    var nodeId = obj.nodeId;
    var nodesToBrowse = [

        // Components (except Methods)
        {
            // BrowseDescription
            referenceTypeId: makeRefId("HasComponent"),
            nodeClassMask: makeNodeClassMask("Object | Variable"), // we don't want Method here
            includeSubtypes: true,
            browseDirection: BrowseDirection.Forward,
            nodeId: nodeId,
            resultMask: resultMask
        },
        // Properties
        {
            // BrowseDescription
            referenceTypeId: makeRefId("HasProperty"),
            //nodeClassMask: makeNodeClassMask("Variable"),
            includeSubtypes: true,
            browseDirection: BrowseDirection.Forward,
            nodeId: nodeId,
            resultMask: resultMask
        },

        // Methods
        {
            // BrowseDescription
            referenceTypeId: makeRefId("HasComponent"),
            nodeClassMask: makeNodeClassMask("Method"),
            includeSubtypes: true,
            browseDirection: BrowseDirection.Forward,
            nodeId: nodeId,
            resultMask: resultMask
        },
        // TypeDefinition
        {
            // BrowseDescription
            referenceTypeId: makeRefId("HasTypeDefinition"),
            includeSubtypes: true,
            browseDirection: BrowseDirection.Both,
            nodeId: nodeId,
            resultMask: resultMask

        },
        // FromState
        {
            // BrowseDescription
            referenceTypeId: makeRefId("FromState"),
            includeSubtypes: true,
            browseDirection: BrowseDirection.Forward,
            nodeId: nodeId,
            resultMask: resultMask
        },
        // ToState
        {
            // BrowseDescription
            referenceTypeId: makeRefId("ToState"),
            includeSubtypes: true,
            browseDirection: BrowseDirection.Forward,
            nodeId: nodeId,
            resultMask: resultMask
        },
        // (for folders ) Organizes
        {
            // BrowseDescription
            referenceTypeId: makeRefId("Organizes"),
            includeSubtypes: true,
            browseDirection: BrowseDirection.Forward,
            nodeId: nodeId,
            resultMask: resultMask
        }
    ];


    /**
     * construct a callable method
     *
     * @param obj
     * @param reference
     * @param callback
     */
    function add_method(obj, reference, callback) {

        var name = lowerFirstLetter(reference.browseName.name);

        obj[name] = function functionCaller(inputArgs, callback) {

            assert(_.isFunction(callback));
            // convert input arguments into Variants
            var inputArgsDef = obj[name].inputArguments;


            var inputArguments = inputArgsDef.map(function (arg) {

                var dataType = convertNodeIdToDataType(arg.dataType);

                var arrayType = ( arg.valueRank === 1) ? VariantArrayType.Array : VariantArrayType.Scalar;

                //xx console.log("xxx ",arg.toString());
                var propName = lowerFirstLetter(arg.name);

                var value = inputArgs[propName];
                if (value === undefined) {
                    throw new Error("expecting input argument " + propName);
                }
                if (arrayType === VariantArrayType.Array) {
                    if (!_.isArray(value)) {
                        throw new Error("expecting value to be an Array or a TypedArray");
                    }
                }

                return new Variant({arrayType: arrayType, dataType: dataType, value: value});
            });

            var methodToCall = new call_service.CallMethodRequest({
                objectId: obj.nodeId,
                methodId: reference.nodeId,
                inputArguments: inputArguments
            });

            //xx console.log(" calling ",methodToCall.toString());

            var methodsToCall = [methodToCall];

            session.call(methodsToCall, function (err, result /*, diagInfo */) {


                // istanbul ignore next
                if (err) {
                    return callback(err);
                }

                if (result[0].statusCode !== StatusCodes.Good) {
                    return callback(new Error("Error " + result[0].statusCode.toString()));
                }
                assert(result[0].outputArguments.length === obj[name].outputArguments.length);
                var outputArgs = {};

                var outputArgsDef = obj[name].outputArguments;

                _.zip(outputArgsDef, result[0].outputArguments).forEach(function (pair) {
                    var arg = pair[0];
                    var variant = pair[1];

                    var propName = lowerFirstLetter(arg.name);
                    outputArgs[propName] = variant.value;

                });
                callback(err, outputArgs);

            });
        };


        function extractDataType(arg, callback) {

            if (arg.dataType._dataType) {
                return callback(); // already converted
            }

            convertNodeIdToDataTypeAsync(session, arg.dataType, function (err, dataType) {
                if (!err) {
                    assert(dataType.hasOwnProperty("value"));
                    arg.dataType._dataType = dataType;
                }
                callback(err);
            });
        }


        session.getArgumentDefinition(reference.nodeId, function (err, inputArguments, outputArguments) {

            // istanbul ignore next
            if (err) {
                return callback(err);
            }

            obj[name].inputArguments = inputArguments;
            obj[name].outputArguments = outputArguments;

            async.series([
                function (callback) {
                    async.each(obj[name].inputArguments, extractDataType, callback);
                },
                function (callback) {
                    async.each(obj[name].outputArguments, extractDataType, callback);
                }
            ], callback)
        });

        var methodObj = {
            nodeId: reference.nodeId,
            executableFlag: false,
            browseName: name,
            func: obj[name]
        };
        obj.$methods[name] = methodObj;

        proxyManager._monitor_execution_flag(methodObj, function () {

        });

    }

    function add_component(obj, reference, callback) {

        var name = lowerFirstLetter(reference.browseName.name || "");

        proxyManager.getObject(reference.nodeId, function (err, childObj) {

            // istanbul ignore else
            if (!err) {
                var childObj = new ObjectExplorer({
                    proxyManager: proxyManager,
                    nodeId: reference.nodeId,
                    name: name,
                    parent: obj
                });
                obj[name] = childObj;
                obj.$components.push(childObj);

                childObj.$resolve(callback);
            } else {
                callback(err);
            }
        });
    }

    function addFolderElement(obj, reference, callback) {

        var name = lowerFirstLetter(reference.browseName.name || "");

        var childObj = new ObjectExplorer({
            proxyManager: proxyManager,
            nodeId: reference.nodeId,
            name: name,
            parent: obj
        });
        obj[name] = childObj;
        obj.$organizes.push(childObj);
        childObj.$resolve(callback);
    }

    function add_property(obj, reference, callback) {

        var name = lowerFirstLetter(reference.browseName.name || "");

        obj[name] = new ProxyVariable(proxyManager, reference.nodeId, reference);
        obj.$properties[name] = obj[name];

        callback(null);
    }

    function add_typeDefinition(obj, references, callback) {

        references = references || [];
        if (references.length !== 1) {
            console.log(" cannot find type definition", references.length);
            return callback();
        }
        var reference = references[0];
        assert(!obj.typeDefinition, "type definition can only be set once");
        obj.typeDefinition = reference.browseName.name || "";
        callback();
    }

    function addFromState(obj, reference, callback) {
        proxyManager.getObject(reference.nodeId, function (err, childObj) {
            obj.$fromState = childObj;
            callback(err);
        });
    }

    function addToState(obj, reference, callback) {
        proxyManager.getObject(reference.nodeId, function (err, childObj) {
            obj.$toState = childObj;
            callback(err);
        });
    }


    session.browse(nodesToBrowse, function (err, results) {
        function t(references) {
            return references.map(function (r) {
                return r.browseName.name + " " + r.nodeId.toString();
            });
        }

        // istanbul ignore next
        if (err) {
            return callback(err);
        }

        //xx console.log("Components", t(results[0].references));
        //xx console.log("Properties", t(results[1].references));
        //xx console.log("Methods", t(results[2].references));
        async.series([

            function (callback) {
                async.map(results[0].references, add_component.bind(null, obj), callback);
            },

            function (callback) {
                async.map(results[1].references, add_property.bind(null, obj), callback);
            },

            // now enrich our object with nice callable async methods
            function (callback) {
                async.map(results[2].references, add_method.bind(null, obj), callback);
            },

            // now set typeDefinition
            function (callback) {
                add_typeDefinition.bind(null, obj)(results[3].references, callback);
            },

            //
            function (callback) { // FromState
                // fromState
                var reference = results[4].references ? results[4].references[0] : null;
                // fromState
                if (reference) {
                    return addFromState(obj, reference, callback);
                }
                callback();
            },
            function (callback) { // ToState
                var reference = results[5].references ? results[5].references[0] : null;
                // fromState
                if (reference) {
                    return addToState(obj, reference, callback);
                }
                callback();
            },
            function (callback) { // Organizes
                async.map(results[6].references, addFolderElement.bind(null, obj), callback);
            }

        ], callback);

    });
}

/**
 *
 * @param proxyManager
 * @param nodeId
 * @param options
 * @param callback {Function}
 * @param callback.err
 * @param callback.clientObject
 *
 */
function getObject(proxyManager, nodeId, options, callback) {

    var session = proxyManager.session;

    nodeId = coerceNodeId(nodeId);

    if (nodeId.isEmpty()) {
        return callback(new Error(" Invalid empty node in getObject"));
    }

    var nodesToRead = [
        {
            nodeId: nodeId,
            attributeId: AttributeIds.BrowseName
        },
        {
            nodeId: nodeId,
            attributeId: AttributeIds.Description
        },
        {
            nodeId: nodeId,
            attributeId: AttributeIds.NodeClass
        }
    ];

    function read_accessLevels(clientObject, callback) {
        var nodesToRead = [
            {
                nodeId: nodeId,
                attributeId: AttributeIds.Value
            },
            {
                nodeId: nodeId,
                attributeId: AttributeIds.UserAccessLevel
            },
            {
                nodeId: nodeId,
                attributeId: AttributeIds.AccessLevel
            }
        ];
        session.read(nodesToRead, 1, function (err, node2reads, results) {
            if (results[0].statusCode === StatusCodes.Good) {
                clientObject.dataValue = AccessLevelFlag.get(results[0].value);
            }
            if (results[1].statusCode === StatusCodes.Good) {
                //xx console.log("AccessLevel ", results[3].value.toString())
                clientObject.userAccessLevel = AccessLevelFlag.get(results[1].value.value);
            }
            if (results[2].statusCode === StatusCodes.Good) {
                clientObject.accessLevel = AccessLevelFlag.get(results[2].value.value);
            }
            callback(err);
        });
    }

    var clientObject;

    async.series([

        function (callback) {
            // readAttributes like browseName and references
            session.read(nodesToRead, 1, function (err, node2reads, results) {

                if (!err) {

                    if (results[0].statusCode === StatusCodes.BadNodeIdUnknown) {
                        console.log(" INVALID NODE ", nodeId.toString());
                        return callback(new Error("Invalid Node " + nodeId.toString()));
                    }

                    clientObject = new ProxyObject(proxyManager, nodeId);

                    ///x console.log("xxxx ,s",results.map(function(a){ return a.toString();}));

                    clientObject.browseName = results[0].value.value;
                    clientObject.description = (results[1].value ? results[1].value.value : "");
                    clientObject.nodeClass = NodeClass.get(results[2].value.value);
                    //xx console.log("xxx nodeClass = ",clientObject.nodeClass.toString());

                    if (clientObject.nodeClass === NodeClass.Variable) {
                        return read_accessLevels(clientObject, callback);
                    }
                }
                callback(err);
            });
        },
        function (callback) {

            // install monitored item
            if (clientObject.nodeClass === NodeClass.Variable) {
                //xx console.log("xxxx -> ???", clientObject.nodeId.toString(), clientObject.nodeClass.toString());
                return proxyManager._monitor_value(clientObject, callback);
            }
            callback();
        },


        function (callback) {

            readUAStructure(proxyManager, clientObject, callback);
        }

        //
    ], function (err) {

        // istanbul ignore next
        if (err) {
            return callback(err);
        }

        callback(null, clientObject);
    });
}




/**
 *
 * @param session
 * @constructor
 */
function UAProxyManager(session) {

    var self = this;
    self.session = session;
    assert(session instanceof ClientSession);
    self._map = {};
    // create a subscription

}


/**
 *
 * @param createSubscriptionRequest
 * @param callback
 * @param callback.err
 * @param callback.subscription
 *
 *
 * subscription.on("error',    function(err){ ... });
 * subscription.on("terminate',function(err){ ... });
 * var monitoredItem = subscription.monitor(itemToMonitor,monitoringParameters,requestedParameters,callback);
 * monitoredItem.on("changed",function( dataValue) {...});
 *
 */
ClientSession.prototype.createSubscription2 = function (createSubscriptionRequest, callback) {
    assert(_.isFunction(callback));
    var self = this;

    var subscription = new ClientSubscription(self, createSubscriptionRequest);
    subscription.on("error", function (err) {

    });
    subscription.on("started", function () {
        callback(null, subscription);
    });
};

UAProxyManager.prototype.start = function (callback) {
    var self = this;

    var createSubscriptionRequest = {
        requestedPublishingInterval: 100,
        requestedLifetimeCount:     6000,
        requestedMaxKeepAliveCount:  100,
        maxNotificationsPerPublish: 1000,
        publishingEnabled: true,
        priority: 10
    };

    self.session.createSubscription2(createSubscriptionRequest, function (err, subscription) {
        self.subscription = subscription;
        self.subscription.on("terminated", function () {
            self.subscription = null;
        });
        callback(err);
    });
};

UAProxyManager.prototype.stop = function (callback) {
    var self = this;
    if (self.subscription) {
        self.subscription.terminate();
        self.subscription.once("terminated", function () {
            // todo
            // console.log("xxxx UAProxyManager subscription terminated");
            self.subscription = null;
            callback();
        });

    } else {
        callback(new Error("UAProxyManager already stopped ?"))
    }
};

UAProxyManager.prototype.getObject = function (nodeId, callback, options) {

    var self = this;

    setImmediate(function () {
        options = options || {};
        options.depth = options.depth || 1;

        var key = nodeId.toString();

        if (self._map.hasOwnProperty(key)) {
            return callback(null, self._map[key]);
        }

        getObject(self, nodeId, options, function (err, obj) {

            if (!err) {
                self._map[key] = obj;
            }
            callback(err, obj);
        });
    });
};


UAProxyManager.prototype._monitor_value = function (proxyObject, callback) {

    assert(_.isFunction(callback));
    //xx if (proxyObject.nodeId.toString() !== "ns=0;i=2257") { return;}

    var self = this;

    if (!self.subscription) {
        // some server do not provide subscription support, do not treat this as an error.
        return callback(null); // new Error("No subscription"));
    }

    var itemToMonitor = { // ReadValueId
        nodeId: proxyObject.nodeId,
        attributeId: AttributeIds.Value
    };
    var monitoringParameters = { // MonitoringParameters
        samplingInterval: 0, /* event-based */
        discardOldest: true,
        queueSize: 10
    };
    var requestedParameters = read_service.TimestampsToReturn.Both;

    var monitoredItem = self.subscription.monitor(itemToMonitor, monitoringParameters, requestedParameters, callback);

    // console.log("xxxxxx installing monitored Item",monitoredItem.itemToMonitor.nodeId.toString(),monitoredItem.itemToMonitor.attributeId);
    //xx proxyObject.monitoredItem = monitoredItem;
    Object.defineProperty(proxyObject, "__monitoredItem", {value: monitoredItem, enumerable: false});

    proxyObject.__monitoredItem.on("changed", function (dataValue) {
        proxyObject.dataValue = dataValue;
        proxyObject.emit("value_changed", dataValue);
        // console.log("xxx Value Changed ".red,proxyObject.nodeId.toString() , proxyObject.browseName,proxyObject.dataValue.toString());
    });

};

UAProxyManager.prototype._monitor_execution_flag = function (proxyObject, callback) {

    // note : proxyObject must wrap a method
    assert(_.isFunction(callback));
    assert(proxyObject.nodeId instanceof NodeId);

    var self = this;

    if (!self.subscription) {
        // some server do not provide subscription support, do not treat this as an error.
        return callback(null); // new Error("No subscription"));
    }

    var itemToMonitor = { // ReadValueId
        nodeId: proxyObject.nodeId,
        attributeId: AttributeIds.Executable
    };

    var monitoringParameters = { // MonitoringParameters
        samplingInterval: 0, /* event-based */
        discardOldest: true,
        queueSize: 10
    };
    var requestedParameters = read_service.TimestampsToReturn.None;

    var monitoredItem = self.subscription.monitor(itemToMonitor, monitoringParameters, requestedParameters, callback);

    Object.defineProperty(proxyObject, "__monitoredItem_execution_flag", {value: monitoredItem, enumerable: false});

    proxyObject.__monitoredItem_execution_flag.on("changed", function (dataValue) {
        proxyObject.executableFlag = dataValue.value.value;
        //xx console.log(" execution flag = ", proxyObject.executableFlag , proxyObject.browseName , proxyObject.nodeId.toString());
        //xx proxyObject.emit("execution_flag_changed",proxyObject.executableFlag);
    });
};

exports.UAProxyManager = UAProxyManager;