"use strict";
/**
* @module opcua.address_space
*/
var NodeClass = require("node-opcua-data-model").NodeClass;
var NodeId = require("node-opcua-nodeid").NodeId;
var makeNodeId = require("node-opcua-nodeid").makeNodeId;
var resolveNodeId = require("node-opcua-nodeid").resolveNodeId;
var sameNodeId =require("node-opcua-nodeid").sameNodeId;
var assert = require("node-opcua-assert");
var _ = require("underscore");
var Dequeue = require("dequeue");
var utils = require("node-opcua-utils");
var dumpIf = require("node-opcua-debug").dumpIf;
var BaseNode = require("./base_node").BaseNode;
var ReferenceType = require("./referenceType").ReferenceType;
var UAVariable = require("./ua_variable").UAVariable;
var UAVariableType = require("./ua_variable_type").UAVariableType;
var UAObjectType = require("./ua_object_type").UAObjectType;
var UAObject = require("./ua_object").UAObject;
var UAMethod = require("./ua_method").UAMethod;
var UADataType = require("./ua_data_type").UADataType;
var Reference = require("./reference").Reference;
var BrowseResult = require("node-opcua-service-browse").BrowseResult;
var View = require("./view").View;
var BrowseDirection = require("node-opcua-data-model").BrowseDirection;
var QualifiedName = require("node-opcua-data-model").QualifiedName;
var coerceLocalizedText = require("node-opcua-data-model").coerceLocalizedText;
var doDebug = false;
var cetools = require("./address_space_change_event_tools");
var _handle_model_change_event = cetools._handle_model_change_event;
var _handle_delete_node_model_change_event = cetools._handle_delete_node_model_change_event;
var DataTypeIds = require("node-opcua-constants").DataTypeIds;
var VariableTypeIds = require("node-opcua-constants").VariableTypeIds;
/**
* `AddressSpace` is a collection of UA nodes.
*
* var addressSpace = new AddressSpace();
*
*
* @class AddressSpace
* @constructor
*/
function AddressSpace() {
this._nodeid_index = {};
this._aliases = {};
this._objectTypeMap = {};
this._variableTypeMap = {};
this._referenceTypeMap = {};
this._referenceTypeMapInv = {};
this._dataTypeMap = {};
this._private_namespace = 1;
this._internal_id_counter = 1000;
this._constructNamespaceArray();
AddressSpace.registry.register(this);
}
var ObjectRegistry = require("node-opcua-object-registry").ObjectRegistry;
AddressSpace.registry = new ObjectRegistry();
AddressSpace.prototype._constructNamespaceArray = function () {
this._namespaceArray = [];
if (this._namespaceArray.length === 0) {
this.registerNamespace("http://opcfoundation.org/UA/");
}
};
AddressSpace.prototype.getNamespaceUri = function (namespaceIndex) {
assert(namespaceIndex >= 0 && namespaceIndex < this._namespaceArray.length);
return this._namespaceArray[namespaceIndex];
};
AddressSpace.prototype.registerNamespace = function (namespaceUri) {
var index = this._namespaceArray.indexOf(namespaceUri);
if (index !== -1) { return index; }
this._namespaceArray.push(namespaceUri);
return this._namespaceArray.length - 1;
};
AddressSpace.prototype.getNamespaceIndex = function (namespaceUri) {
return this._namespaceArray.indexOf(namespaceUri);
};
AddressSpace.prototype.getNamespaceArray = function () {
return this._namespaceArray;
};
/**
*
* @method add_alias
* @param alias_name {String} the alias name
* @param nodeId {NodeId}
*/
AddressSpace.prototype.add_alias = function (alias_name, nodeId) {
assert(typeof alias_name === "string");
assert(nodeId instanceof NodeId);
this._aliases[alias_name] = nodeId;
};
/**
* find an node by node Id
* @method findNode
* @param nodeId {NodeId|String} a nodeId or a string coerce-able to nodeID, representing the object to find.
* @return {BaseNode}
*/
AddressSpace.prototype.findNode = function (nodeId) {
nodeId = this.resolveNodeId(nodeId);
return this._nodeid_index[nodeId.toString()];
};
AddressSpace.prototype.findMethod = function (nodeId) {
var node = this.findNode(nodeId);
assert(node instanceof UAMethod);
return node;
};
/**
* @property rootFolder
* @type {BaseNode}
*/
Object.defineProperty(AddressSpace.prototype,"rootFolder", {
get: function () {
return this.findNode(this.resolveNodeId("RootFolder"));
}
});
function _registerObjectType(self, node) {
var key = node.browseName.toString();
assert(!self._objectTypeMap[key], " UAObjectType already declared");
self._objectTypeMap[key] = node;
}
function _unregisterObjectType() {}
function _registerVariableType(self, node) {
var key = node.browseName.toString();
assert(!self._variableTypeMap[key], " UAVariableType already declared");
self._variableTypeMap[key] = node;
}
function _registerReferenceType(self, node) {
assert(node.browseName instanceof QualifiedName);
if (!node.inverseName) {
// Inverse name is not required anymore in 1.0.4
//xx console.log("Warning : node has no inverse Name ", node.nodeId.toString(), node.browseName.toString());
node.inverseName = {text: node.browseName.name};
}
var key = node.browseName.toString();
assert(node.inverseName.text);
assert(!self._referenceTypeMap[key], " Node already declared");
assert(!self._referenceTypeMapInv[node.inverseName], " Node already declared");
self._referenceTypeMap[key] = node;
self._referenceTypeMapInv[node.inverseName.text] = node;
}
function _registerDataType(self, node) {
var key = node.browseName.toString();
assert(node.browseName instanceof QualifiedName);
assert(!self._dataTypeMap[key], " DataType already declared");
self._dataTypeMap[key] = node;
}
AddressSpace.prototype._register = function (node) {
assert(node.nodeId instanceof NodeId);
assert(node.nodeId);
assert(node.hasOwnProperty("browseName"));
var indexName = node.nodeId.toString();
if (this._nodeid_index.hasOwnProperty(indexName)) {
throw new Error("nodeId " + node.nodeId.displayText() + " already registered " + node.nodeId.toString());
}
this._nodeid_index[indexName] = node;
if (node.nodeClass === NodeClass.ObjectType) {
_registerObjectType(this, node);
} else if (node.nodeClass === NodeClass.VariableType) {
_registerVariableType(this, node);
} else if (node.nodeClass === NodeClass.Object) {
} else if (node.nodeClass === NodeClass.Variable) {
} else if (node.nodeClass === NodeClass.Method) {
} else if (node.nodeClass === NodeClass.View) {
} else if (node.nodeClass === NodeClass.ReferenceType) {
_registerReferenceType(this, node);
} else if (node.nodeClass === NodeClass.DataType) {
_registerDataType(this, node);
} else {
console.log("Invalid class Name", node.nodeClass);
throw new Error("Invalid class name specified");
}
};
/**
* remove the specified Node from the address space
*
* @method deleteNode
* @param nodeOrNodeId
*
*
*/
AddressSpace.prototype.deleteNode = function (nodeOrNodeId) {
var self = this;
var node =null;
var nodeId;
if (nodeOrNodeId instanceof NodeId) {
nodeId = nodeOrNodeId;
node = this.findNode(nodeId);
// istanbul ignore next
if (!node) {
throw new Error(" deleteNode : cannot find node with nodeId" + nodeId.toString());
}
} else if (nodeOrNodeId instanceof BaseNode) {
node = nodeOrNodeId;
nodeId = node.nodeId;
}
var addressSpace = self;
addressSpace.modelChangeTransaction(function() {
// notify parent that node is being removed
var hierarchicalReferences = node.findReferencesEx("HierarchicalReferences", BrowseDirection.Inverse);
hierarchicalReferences.forEach(function (ref) {
var parent = self.findNode(ref.nodeId);
assert(parent);
parent._on_child_removed(node);
});
function deleteNodePointedByReference(ref) {
var addressSpace = self;
var o = addressSpace.findNode(ref.nodeId);
addressSpace.deleteNode(o.nodeId);
}
// recursively delete all nodes below in the hierarchy of nodes
// TODO : a better idea would be to extract any references of type "HasChild"
var components = node.findReferences("HasComponent", true);
var properties = node.findReferences("HasProperty", true);
// TODO: shall we delete nodes pointed by "Organizes" links here ?
var subfolders = node.findReferences("Organizes", true);
var rf = [].concat(components, properties, subfolders);
rf.forEach(deleteNodePointedByReference);
// delete nodes from global index
var indexName = node.nodeId.toString();
// istanbul ignore next
if (!addressSpace._nodeid_index.hasOwnProperty(indexName)) {
throw new Error("deleteNode : nodeId " + nodeId.displayText() + " is not registered " + nodeId.toString());
}
_handle_delete_node_model_change_event(node);
node.unpropagate_back_references();
if (node.nodeClass === NodeClass.ObjectType) {
_unregisterObjectType(addressSpace, node);
} else if (node.nodeClass === NodeClass.Object) {
} else if (node.nodeClass === NodeClass.Variable) {
} else if (node.nodeClass === NodeClass.Method) {
} else if (node.nodeClass === NodeClass.View) {
} else {
console.log("Invalid class Name", node.nodeClass);
throw new Error("Invalid class name specified");
}
assert(addressSpace._nodeid_index[indexName] === node);
delete addressSpace._nodeid_index[indexName];
node.dispose();
});
};
/**
* resolved a string or a nodeId to a nodeID
*
* @method resolveNodeId
* @param nodeId {NodeId|String}
* @return {NodeId}
*/
AddressSpace.prototype.resolveNodeId = function (nodeId) {
if (typeof nodeId === "string") {
// check if the string is a known alias
var alias = this._aliases[nodeId];
if (alias !== undefined) {
return alias;
}
}
return resolveNodeId(nodeId);
};
var _constructors_map = {
"Object": UAObject,
"ObjectType": UAObjectType,
"ReferenceType": ReferenceType,
"Variable": UAVariable,
"VariableType": UAVariableType,
"DataType": UADataType,
"Method": UAMethod,
"View": View
};
/**
* @method _createNode
* @private
* @param options
*
* @param options.nodeId {NodeId}
* @param options.nodeClass {NodeClass}
* @param options.browseName {String|QualifiedName} the node browseName
* the browseName can be either a string : "Hello"
* a string with a namespace : "1:Hello"
* a QualifiedName : new QualifiedName({name:"Hello", namespaceIndex:1});
* @return {BaseNode}
* @private
*/
AddressSpace.prototype._createNode = function (options) {
assert(typeof options.browseName === "string" || (options.browseName instanceof QualifiedName));
var self = this;
options.description = coerceLocalizedText(options.description);
options.nodeId = self._construct_nodeId(options);
dumpIf(!options.nodeId, options); // missing node Id
assert(options.nodeId instanceof NodeId);
assert(options.nodeClass);
var Constructor = _constructors_map[options.nodeClass.key];
if (!Constructor) {
throw new Error(" missing constructor for NodeClass " + options.nodeClass.key);
}
options.addressSpace = this;
var node = new Constructor(options);
assert(node.nodeId);
assert(node.nodeId instanceof NodeId);
this._register(node);
// object shall now be registered
if (doDebug) {
assert(_.isObject(this.findNode(node.nodeId)));
}
return node;
};
AddressSpace.prototype._findInBrowseNameIndex = function(CLASS,index,browseNameOrNodeId,namespace) {
if (browseNameOrNodeId instanceof NodeId) {
assert(namespace === undefined);
var nodeId = browseNameOrNodeId;
var obj = this.findNode(nodeId);
assert(!obj || obj instanceof CLASS);
return obj;
}
assert(!namespace || namespace >=0);
var browseName = browseNameOrNodeId;
if (namespace) {
browseName = namespace.toString() + ":" + browseName;
}
return index[browseName];
};
/**
* Find the DataType node from a NodeId or a browseName
* @method findDataType
* @param dataType {String|NodeId}
* @param [namespace=0 {Number}] an optional namespace index
* @return {DataType|null}
*
*
* @example
*
* var dataDouble = addressSpace.findDataType("Double");
*
* var dataDouble = addressSpace.findDataType(resolveNodeId("ns=0;i=3"));
*/
AddressSpace.prototype.findDataType = function (dataType,namespace) {
var self = this;
// startingNode i=24 :
// BaseDataType
// +-> Boolean (i=1) {BooleanDataType (ns=2:9898)
// +-> String (i=12)
// +->NumericRange
// +->Time
// +-> DateTime
// +-> Structure
// +-> Node
// +-> ObjectNode
return self._findInBrowseNameIndex(UADataType,this._dataTypeMap,dataType,namespace);
};
var DataType = require("node-opcua-variant").DataType;
/**
* @method findCorrespondingBasicDataType
* @param dataTypeNode
*
*
* @example
*
* var dataType = addressSpace.findDataType("ns=0;i=12");
* addressSpace.findCorrespondingBasicDataType(dataType).should.eql(DataType.String);
*
* var dataType = addressSpace.findDataType("ServerStatus"); // ServerStatus
* addressSpace.findCorrespondingBasicDataType(dataType).should.eql(DataType.ExtensionObject);
*
*/
AddressSpace.prototype.findCorrespondingBasicDataType = function(dataTypeNode) {
var self =this;
assert(dataTypeNode,"expecting a dataTypeNode");
if (typeof dataTypeNode === "string") {
dataTypeNode = this.resolveNodeId(dataTypeNode);
}
if (dataTypeNode instanceof NodeId) {
var _orig_dataTypeNode = dataTypeNode;
dataTypeNode = this.findDataType(dataTypeNode);
if (!dataTypeNode) {
throw Error("cannot find dataTypeNode " + _orig_dataTypeNode.toString());
}
}
assert(dataTypeNode instanceof UADataType);
var id = dataTypeNode.nodeId.value;
var enumerationType = self.findDataType("Enumeration");
if (enumerationType.nodeId === dataTypeNode.nodeId) {
return DataType.Int32;
}
if (dataTypeNode.nodeId.namespace === 0 && DataType.get(id)) {
return DataType.get(id);
}
return this.findCorrespondingBasicDataType(dataTypeNode.subtypeOfObj);
};
/**
*
* @method findObjectType
* @param objectType {String|NodeId}
* @param [namespace=0 {Number}] an optional namespace index
* @return {UAObjectType|null}
*
* @example
*
* var objectType = addressSpace.findObjectType("ns=0;i=58");
* objectType.browseName.toString().should.eql("BaseObjectType");
*
* var objectType = addressSpace.findObjectType("BaseObjectType");
* objectType.browseName.toString().should.eql("BaseObjectType");
*
* var objectType = addressSpace.findObjectType(resolveNodeId("ns=0;i=58"));
* objectType.browseName.toString().should.eql("BaseObjectType");
*/
AddressSpace.prototype.findObjectType = function (objectType,namespace) {
return this._findInBrowseNameIndex(UAObjectType,this._objectTypeMap,objectType,namespace);
};
/**
* @method findVariableType
* @param variableType {String|NodeId}
* @param [namespace=0 {Number}] an optional namespace index
* @return {UAObjectType|null}
*
* @example
*
* var objectType = addressSpace.findVariableType("ns=0;i=62");
* objectType.browseName.toString().should.eql("BaseVariableType");
*
* var objectType = addressSpace.findVariableType("BaseVariableType");
* objectType.browseName.toString().should.eql("BaseVariableType");
*
* var objectType = addressSpace.findVariableType(resolveNodeId("ns=0;i=62"));
* objectType.browseName.toString().should.eql("BaseVariableType");
*/
AddressSpace.prototype.findVariableType = function (variableType,namespace) {
return this._findInBrowseNameIndex(UAVariableType,this._variableTypeMap,variableType,namespace);
};
/**
* returns true if str matches a nodeID, e.g i=123 or ns=...
* @method isNodeIdString
* @param str
* @type {boolean}
*/
function isNodeIdString(str) {
if (typeof str !== "string") {
return false;
}
return str.substring(0, 2) === "i=" || str.substring(0, 3) === "ns=";
}
AddressSpace.isNodeIdString = isNodeIdString;
/**
* @method findReferenceType
* @param refType {String|NodeId}
* @param [namespace=0 {Number}] an optional namespace index
* @return {ReferenceType|null}
*
* refType could be
* a string representing a nodeid : e.g. 'i=9004' or ns=1;i=6030
* a string representing a browse name : e.g 'HasTypeDefinition'
* in this case it should be in the alias list
*
*/
AddressSpace.prototype.findReferenceType = function (refType,namespace) {
// startingNode ns=0;i=31 : References
// References i=31
// +->(hasSubtype) NoHierarchicalReferences
// +->(hasSubtype) HasTypeDefinition
// +->(hasSubtype) HierarchicalReferences
// +->(hasSubtype) HasChild/ChildOf
// +->(hasSubtype) Aggregates/AggregatedBy
// +-> HasProperty/PropertyOf
// +-> HasComponent/ComponentOf
// +-> HasHistoricalConfiguration/HistoricalConfigurationOf
// +->(hasSubtype) HasSubtype/HasSupertype
// +->(hasSubtype) Organizes/OrganizedBy
// +->(hasSubtype) HasEventSource/EventSourceOf
var node;
if (isNodeIdString(refType)) {
refType = resolveNodeId(refType);
}
if (refType instanceof NodeId) {
node = this.findNode(refType);
// istanbul ignore next
if (!(node && (node.nodeClass === NodeClass.ReferenceType))) {
throw new Error("cannot resolve referenceId "+ refType.toString());
}
} else {
assert(_.isString(refType));
if(refType.indexOf(":")>=0) {
var a = refType.split(":");
namespace = a.length === 2 ? parseInt(a[0]) : namespace;
refType = a.length === 2 ? a[1] : refType;
}
node = this._findInBrowseNameIndex(ReferenceType,this._referenceTypeMap,refType,namespace);
assert(!node || (node.nodeClass === NodeClass.ReferenceType && node.browseName.name.toString() === refType));
}
return node;
};
/**
* find a ReferenceType by its inverse name.
* @method findReferenceTypeFromInverseName
* @param inverseName {String} the inverse name of the ReferenceType to find
* @return {ReferenceType}
*/
AddressSpace.prototype.findReferenceTypeFromInverseName = function (inverseName) {
var node = this._referenceTypeMapInv[inverseName];
assert(!node || (node.nodeClass === NodeClass.ReferenceType && node.inverseName.text === inverseName));
return node;
};
/**
* @method normalizeReferenceType
* @param params.referenceType {String|nodeId}
* @param params.isForward {Boolean} default value: true;
* @return {Object} a new reference object with the normalized name { referenceType: <value>, isForward: <flag>}
*/
AddressSpace.prototype.normalizeReferenceType = function (params) {
if (params instanceof Reference) {
// a reference has already been normalized
return params;
}
assert(params.isForward === undefined || typeof params.isForward === "boolean");
// referenceType = Organizes , isForward = true => referenceType = Organizes , isForward = true
// referenceType = Organizes , isForward = false => referenceType = Organizes , isForward = false
// referenceType = OrganizedBy , isForward = true => referenceType = Organizes , isForward = **false**
// referenceType = OrganizedBy , isForward = false => referenceType = Organizes , isForward = **true**
assert((typeof params.referenceType === "string") || (params.referenceType instanceof NodeId));
var obj = this.findReferenceType(params.referenceType);
if (obj) {
params.referenceType = obj.browseName.toString();
} else {
assert(_.isString(params.referenceType) && !isNodeIdString(params.referenceType)," referenceType must be a browseName");
}
params.isForward = utils.isNullOrUndefined(params.isForward) ? true : params.isForward;
var n1 = this.findReferenceType(params.referenceType);
var n2 = this.findReferenceTypeFromInverseName(params.referenceType);
if (!n1 && !n2) {
// unknown type, there is nothing we can do about it yet.
// this case could happen when reading a nodeset.xml file for instance
// when a reference type is being used before being defined.
return new Reference(params);
} else if (n1) {
assert(!n2 || n1.nodeId.toString() === n2.nodeId.toString());
return new Reference(params);
} else {
assert(n2);
// make sure we preserve integrity of object passed as a argument
var new_params = _.clone(params);
new_params.referenceType = n2.browseName.toString();
new_params.isForward = !params.isForward;
return new Reference(new_params);
}
};
AddressSpace.prototype.normalizeReferenceTypes = function(arr) {
if (!arr) {
return arr;
}
assert(_.isArray(arr));
function resolveReferenceNodeId(reference) {
var _nodeId = reference.nodeId;
assert(_nodeId, "missing 'nodeId' in reference");
if (_nodeId && _nodeId.nodeId) {
_nodeId = _nodeId.nodeId;
}
_nodeId = resolveNodeId(_nodeId);
// istanbul ignore next
if (!(_nodeId instanceof NodeId) || _nodeId.isEmpty()) {
throw new Error(" Invalid reference nodeId " + _nodeId.toString() + " " + JSON.stringify(reference,null));
}
reference.nodeId = _nodeId;
}
arr.forEach(resolveReferenceNodeId);
return arr.map(AddressSpace.prototype.normalizeReferenceType.bind(this));
};
/**
* returns the inverse name of the referenceType.
*
* @method inverseReferenceType
* @param referenceType {String} : the reference type name
* @return {String} the name of the inverse reference type.
*
* @example
*
* ```javascript
* addressSpace.inverseReferenceType("OrganizedBy").should.eql("Organizes");
* addressSpace.inverseReferenceType("Organizes").should.eql("OrganizedBy");
* ```
*
*/
AddressSpace.prototype.inverseReferenceType = function (referenceType) {
assert(typeof referenceType === "string");
var n1 = this.findReferenceType(referenceType);
var n2 = this.findReferenceTypeFromInverseName(referenceType);
if (n1) {
assert(!n2);
return n1.inverseName.text;
} else {
assert(n2);
return n2.browseName.toString();
}
};
//----------------------------------------------------------------------------------------------------------------------
AddressSpace.prototype._build_new_NodeId = function () {
var nodeId;
do {
nodeId = makeNodeId(this._internal_id_counter, this._private_namespace);
this._internal_id_counter += 1;
} while(this._nodeid_index.hasOwnProperty(nodeId));
return nodeId;
};
AddressSpace.prototype._coerce_Type = function (dataType, typeMap, typeMapName, finderMethod) {
assert(dataType,"expecting a dataType");
if ( dataType instanceof BaseNode) {
dataType = dataType.nodeId;
}
assert(_.isObject(typeMap));
var self = this;
var nodeId;
if (typeof dataType === "string") {
// resolve dataType
nodeId = self._aliases[dataType];
if (!nodeId) {
// dataType was not found in the aliases database
if (typeMap[dataType]) {
nodeId = makeNodeId(typeMap[dataType], 0);
return nodeId;
} else {
nodeId = resolveNodeId(dataType);
}
}
} else if (typeof dataType === "number") {
nodeId = makeNodeId(dataType, 0);
} else {
nodeId = resolveNodeId(dataType);
}
assert(nodeId instanceof NodeId);
var el = finderMethod.call(this,nodeId);
if (!el) {
// verify that node Id exists in standard type map typeMap
var find = _.filter(typeMap, function (a) {
return a === nodeId.value;
});
/* istanbul ignore next */
if (find.length !== 1) {
throw new Error(" cannot find " + dataType.toString() + " in typeMap " + typeMapName + " L = "+ find.length);
}
}
return nodeId;
};
AddressSpace.prototype._coerce_DataType = function (dataType) {
var self = this;
if (dataType instanceof NodeId) {
//xx assert(self.findDataType(dataType));
return dataType;
}
return self._coerce_Type(dataType, DataTypeIds, "DataTypeIds",AddressSpace.prototype.findDataType);
};
AddressSpace.prototype._coerce_VariableTypeIds = function (dataType) {
return this._coerce_Type(dataType, VariableTypeIds, "VariableTypeIds",AddressSpace.prototype.findVariableType);
};
AddressSpace.prototype._coerceTypeDefinition = function (typeDefinition) {
var self = this;
if (typeof typeDefinition === "string") {
// coerce parent folder to an node
typeDefinition = self.findNode(typeDefinition);
typeDefinition = typeDefinition.nodeId;
}
//xx console.log("typeDefinition = ",typeDefinition);
assert(typeDefinition instanceof NodeId);
return typeDefinition;
};
function isValidModellingRule(ruleName) {
// let restrict to Mandatory or Optional for the time being
return ruleName === null || ruleName === "Mandatory" || ruleName === "Optional";
}
/**
* @method _process_modelling_rule
* @param references {Array<Reference>} the array of references
* @param modellingRule {Reference} the modellling Rule reference.
* @private
*/
AddressSpace._process_modelling_rule = function (references, modellingRule) {
if (modellingRule) {
assert(isValidModellingRule(modellingRule), "expecting a valid modelling rule");
var modellingRuleName = "ModellingRule_" + modellingRule;
//assert(self.findNode(modellingRuleName),"Modelling rule must exist");
references.push({referenceType: "HasModellingRule", nodeId: modellingRuleName});
}
};
/**
* @method _addVariable
* @private
*/
AddressSpace.prototype._addVariable = function (options) {
var self = this;
var baseDataVariableTypeId = self.findVariableType("BaseDataVariableType").nodeId;
assert(options.hasOwnProperty("browseName"));
assert(options.hasOwnProperty("dataType"));
options.historizing = !!options.historizing;
// xx assert(self.FolderTypeId && self.BaseObjectTypeId); // is default address space generated.?
// istanbul ignore next
if (options.hasOwnProperty("hasTypeDefinition")) {
throw new Error("hasTypeDefinition option is invalid. Do you mean typeDefinition instead ?");
}
// ------------------------------------------ TypeDefinition
var typeDefinition = options.typeDefinition || baseDataVariableTypeId;
typeDefinition = self._coerce_VariableTypeIds(typeDefinition);
assert(typeDefinition instanceof NodeId);
// ------------------------------------------ DataType
options.dataType = self._coerce_DataType(options.dataType);
options.valueRank = utils.isNullOrUndefined(options.valueRank) ? -1 : options.valueRank;
assert(_.isFinite(options.valueRank));
assert(typeof options.valueRank === "number");
options.arrayDimensions = options.arrayDimensions || null;
assert(_.isArray(options.arrayDimensions) || options.arrayDimensions === null);
// -----------------------------------------------------
options.minimumSamplingInterval = +options.minimumSamplingInterval || 0;
var references = options.references || [];
references = [].concat(references,[
{referenceType: "HasTypeDefinition", isForward: true, nodeId: typeDefinition}
]);
assert(!options.nodeClass || options.nodeClass === NodeClass.Variable);
options.nodeClass = NodeClass.Variable;
options.references = references;
var variable = self.createNode(options);
assert(variable instanceof UAVariable);
return variable;
};
/**
* add a variable as a component of the parent node
*
* @method addVariable
* @param options
* @param options.browseName {String} the variable name
* @param options.dataType {String} the variable datatype ( "Double", "UInt8" etc...)
* @param [options.typeDefinition="BaseDataVariableType"]
* @param [options.modellingRule=null] the Modelling rule : "Optional" , "Mandatory"
* @param [options.valueRank= -1] {Int} the valueRank
* @param [options.arrayDimensions] {null| Array{Int}}
* @return {Object}*
*/
AddressSpace.prototype.addVariable = function (options) {
var self = this;
assert(arguments.length === 1 ,
"Invalid arguments AddressSpace#addVariable now takes only one argument. Please update your code");
if (options.hasOwnProperty("propertyOf") && options.propertyOf) {
assert(!options.typeDefinition || options.typeDefinition === "PropertyType");
options.typeDefinition = options.typeDefinition || "PropertyType";
} else {
assert(!options.typeDefinition || options.typeDefinition !== "PropertyType");
}
return self._addVariable(options);
};
AddressSpace.prototype._identifyParentInReference = function(references) {
assert(_.isArray(references));
var candidates = references.filter(function(ref){
return (ref.referenceType === "HasComponent" || ref.referenceType === "HasProperty") &&
ref.isForward === false;
});
assert(candidates.length <=1);
return candidates[0];
};
AddressSpace.nodeIdNameSeparator = "-";
function __combineNodeId(parentNodeId,name) {
var nodeId =null;
if (parentNodeId.identifierType === NodeId.NodeIdType.STRING) {
var childName = parentNodeId.value + AddressSpace.nodeIdNameSeparator + name;
nodeId = new NodeId(NodeId.NodeIdType.STRING, childName, parentNodeId.namespace);
}
return nodeId;
}
AddressSpace.prototype._construct_nodeId = function(options) {
var self = this;
var nodeId = options.nodeId;
if (!nodeId) {
// find HasComponent, or has Property reverse
var parentRef = self._identifyParentInReference(options.references);
if (parentRef ) {
assert(parentRef.nodeId instanceof NodeId);
nodeId = __combineNodeId(parentRef.nodeId,options.browseName);
}
}
nodeId = nodeId || self._build_new_NodeId();
if (nodeId instanceof NodeId) {
return nodeId;
}
nodeId = resolveNodeId(nodeId);
assert(nodeId instanceof NodeId);
return nodeId;
};
AddressSpace.prototype._coerceType = function (baseType,topMostBaseType,nodeClass) {
var self = this;
assert(typeof topMostBaseType === "string");
var topMostBaseTypeNode = self.findNode(topMostBaseType);
// istanbul ignore next
if (!topMostBaseTypeNode) {
throw new Error("Cannot find topMostBaseTypeNode " + topMostBaseType.toString());
}
assert(topMostBaseTypeNode instanceof BaseNode);
assert(topMostBaseTypeNode.nodeClass === nodeClass);
if (!baseType) {
return topMostBaseTypeNode;
}
assert(typeof baseType === "string" || baseType instanceof BaseNode);
var baseTypeNode;
if ( baseType instanceof BaseNode) {
baseTypeNode = baseType;
} else {
baseTypeNode = self.findNode(baseType);
}
/* istanbul ignore next*/
if (!baseTypeNode) {
throw new Error("Cannot find ObjectType or VariableType for " + baseType.toString());
}
assert(baseTypeNode);
assert(baseTypeNode.isSupertypeOf(topMostBaseTypeNode));
//xx console.log("baseTypeNode = ",baseTypeNode.toString());
return baseTypeNode;
};
AddressSpace.prototype._addObjectOrVariableType = function (options,topMostBaseType,nodeClass) {
var self = this;
assert(typeof topMostBaseType === "string");
assert(nodeClass === NodeClass.ObjectType || nodeClass === NodeClass.VariableType);
assert(!options.nodeClass);
assert(options.browseName);
assert(typeof options.browseName === "string");
var references = [];
function process_subtypeOf_options(self,options,references) {
// check common misspelling mistake
assert(!options.subTypeOf,"misspell error : it should be 'subtypeOf' instead");
if (options.hasOwnProperty("hasTypeDefinition")) {
throw new Error("hasTypeDefinition option is invalid. Do you mean typeDefinition instead ?");
}
assert(!options.typeDefinition, " do you mean subtypeOf ?");
var subtypeOfNodeId = self._coerceType(options.subtypeOf,topMostBaseType,nodeClass);
assert(subtypeOfNodeId);
references.push({ referenceType: "HasSubtype", isForward: false, nodeId: subtypeOfNodeId });
}
process_subtypeOf_options(self,options,references);
var objectType = this._createNode({
browseName: options.browseName,
nodeClass: nodeClass,
isAbstract: !!options.isAbstract,
eventNotifier: +options.eventNotifier,
references: references
});
objectType.propagate_back_references();
objectType.install_extra_properties();
objectType.installPostInstallFunc(options.postInstantiateFunc);
return objectType;
};
/**
* add a new Object type to the address space
* @method addObjectType
* @param options
* @param options.browseName {String} the object type name
* @param [options.subtypeOf="BaseObjectType"] {String|NodeId|BaseNode} the base class
* @param [options.nodeId] {String|NodeId} an optional nodeId for this objectType, if not provided a new nodeId will be created
* @param [options.isAbstract = false] {Boolean}
* @param [options.eventNotifier = 0] {Integer}
* @param [options.postInstantiateFunc = null] {Function}
*
*/
AddressSpace.prototype.addObjectType = function (options) {
var self = this;
assert(!options.hasOwnProperty("dataType"),"an objectType should not have a dataType");
assert(!options.hasOwnProperty("valueRank"),"an objectType should not have a valueRank");
assert(!options.hasOwnProperty("arrayDimensions"),"an objectType should not have a arrayDimensions");
return self._addObjectOrVariableType(options,"BaseObjectType",NodeClass.ObjectType);
};
/**
* add a new Variable type to the address space
* @method addVariableType
* @param options
* @param options.browseName {String} the object type name
* @param [options.subtypeOf="BaseVariableType"] {String|NodeId|BaseNode} the base class
* @param [options.nodeId] {String|NodeId} an optional nodeId for this objectType, if not provided a new nodeId will be created
* @param [options.isAbstract = false] {Boolean}
* @param [options.eventNotifier = 0] {Integer}
* @param options.dataType {String|NodeId} the variable DataType
* @param [options.valueRank = -1]
* @param [options.arrayDimensions = null] { Array<Int>>
*
*/
AddressSpace.prototype.addVariableType = function (options) {
var self = this;
assert(!options.hasOwnProperty("arrayDimension"),"Do you mean ArrayDimensions ?");
// dataType
options.dataType = options.dataType || "Int32";
options.dataType = self._coerce_DataType(options.dataType);
// valueRank
options.valueRank = utils.isNullOrUndefined(options.valueRank) ? -1 : options.valueRank;
assert(_.isFinite(options.valueRank));
assert(typeof options.valueRank === "number");
// arrayDimensions
options.arrayDimensions = options.arrayDimensions || null;
assert(_.isArray(options.arrayDimensions) || options.arrayDimensions === null);
var variableType = self._addObjectOrVariableType(options,"BaseVariableType",NodeClass.VariableType);
variableType.dataType = options.dataType;
variableType.valueRank = options.valueRank;
variableType.arrayDimensions = options.arrayDimensions;
return variableType;
};
AddressSpace.prototype.addView = function (options) {
var self = this;
assert(arguments.length === 1, "AddressSpace#addView expecting a single argument");
assert(options);
assert(options.hasOwnProperty("browseName"));
assert(options.hasOwnProperty("organizedBy"));
var browseName = options.browseName;
assert(typeof browseName === "string");
var baseDataVariableTypeId = self.findVariableType("BaseDataVariableType").nodeId;
// ------------------------------------------ TypeDefinition
var typeDefinition = options.typeDefinition || baseDataVariableTypeId;
options.references = options.references || [];
options.references.push({referenceType: "HasTypeDefinition", isForward: true, nodeId: typeDefinition});
// xx assert(self.FolderTypeId && self.BaseObjectTypeId); // is default address space generated.?
assert(!options.nodeClass);
options.nodeClass = NodeClass.View;
var view = self.createNode(options);
assert(view instanceof View);
assert(view.nodeId instanceof NodeId);
assert(view.nodeClass === NodeClass.View);
return view;
};
/**
* return true if nodeId is a Folder
* @method _isFolder
* @param addressSpace
* @param folder
* @return {Boolean}
* @private
*/
function _isFolder(addressSpace,folder) {
var self = addressSpace;
var folderType = self.findObjectType("FolderType");
assert(folder instanceof BaseNode);
assert(folder.typeDefinitionObj);
return folder.typeDefinitionObj.isSupertypeOf(folderType);
}
/**
* @method _coerceNode
* @param node
* @return {*}
* @private
*/
AddressSpace.prototype._coerceNode = function (node) {
var self = this;
// coerce to BaseNode object
if (!(node instanceof BaseNode)) {
if (typeof node === "string") {
// coerce parent folder to an object
node = self.findNode(self.resolveNodeId(node)) || node;
}
if (!node || !node.typeDefinition) {
node = self.findNode(node) || node;
if (!node || !node.typeDefinition) {
//xx console.log("xxxx cannot find folder ", folder);
return null;
}
}
}
return node;
};
AddressSpace.prototype._coerceFolder = function (folder) {
var self = this;
folder = self._coerceNode(folder);
// istanbul ignore next
if (folder && !_isFolder(self,folder)) {
throw new Error("Parent folder must be of FolderType " + folder.typeDefinition.toString());
}
return folder;
};
/**
* @method _coerce_parent
* convert a 'string' , NodeId or Object into a valid and existing object
* @param addressSpace {AddressSpace}
* @param value
* @param coerceFunc {Function}
* @private
*/
function _coerce_parent(addressSpace, value, coerceFunc) {
assert(_.isFunction(coerceFunc));
if (value) {
if (typeof value === "string") {
value = coerceFunc.call(addressSpace, value);
}
if (value instanceof NodeId) {
value = addressSpace.findNode(value);
}
}
assert(!value || value instanceof BaseNode);
return value;
}
function _handle_event_hierarchy_parent(addressSpace, references, options) {
options.eventSourceOf = _coerce_parent(addressSpace, options.eventSourceOf, addressSpace._coerceNode);
options.notifierOf = _coerce_parent(addressSpace, options.notifierOf, addressSpace._coerceNode);
if (options.eventSourceOf) {
assert(!options.notifierOf , "notifierOf shall not be provided with eventSourceOf ");
references.push({referenceType: "HasEventSource", isForward: false, nodeId: options.eventSourceOf.nodeId});
} else if (options.notifierOf) {
assert(!options.eventSourceOf , "eventSourceOf shall not be provided with notifierOf ");
references.push({referenceType: "HasNotifier", isForward: false, nodeId: options.notifierOf.nodeId});
}
}
function _handle_hierarchy_parent(addressSpace, references, options) {
options.componentOf = _coerce_parent(addressSpace, options.componentOf, addressSpace._coerceNode);
options.propertyOf = _coerce_parent(addressSpace, options.propertyOf, addressSpace._coerceNode);
options.organizedBy = _coerce_parent(addressSpace, options.organizedBy, addressSpace._coerceFolder);
if (options.componentOf) {
assert(!options.propertyOf);
assert(!options.organizedBy);
assert(addressSpace.rootFolder.objects,"addressSpace must have a rootFolder.objects folder");
assert(options.componentOf.nodeId !== addressSpace.rootFolder.objects.nodeId,"Only Organizes References are used to relate Objects to the 'Objects' standard Object.");
references.push({referenceType: "HasComponent", isForward: false, nodeId: options.componentOf.nodeId});
}
if (options.propertyOf) {
assert(!options.componentOf);
assert(!options.organizedBy);
assert(options.propertyOf.nodeId !== addressSpace.rootFolder.objects.nodeId,"Only Organizes References are used to relate Objects to the 'Objects' standard Object.");
references.push({referenceType: "HasProperty", isForward: false, nodeId: options.propertyOf.nodeId});
}
if (options.organizedBy) {
assert(!options.propertyOf);
assert(!options.componentOf);
references.push({referenceType: "Organizes", isForward: false, nodeId: options.organizedBy.nodeId});
}
}
AddressSpace._handle_hierarchy_parent = _handle_hierarchy_parent ;
function _copy_reference(reference) {
assert(reference.hasOwnProperty("referenceType"));
assert(reference.hasOwnProperty("isForward"));
assert(reference.hasOwnProperty("nodeId"));
assert(reference.nodeId instanceof NodeId);
return {
referenceType: reference.referenceType,
isForward: reference.isForward,
nodeId: reference.nodeId
};
}
function _copy_references(references) {
references = references || [];
return references.map(_copy_reference);
}
function isNonEmptyQualifiedName(browseName) {
if (!browseName) {
return false;
}
if (typeof browseName === "string") {
return browseName.length >= 0;
}
assert(browseName instanceof QualifiedName);
return browseName.name.length > 0;
}
AddressSpace.isNonEmptyQualifiedName = isNonEmptyQualifiedName;
AddressSpace.prototype._collectModelChange = function(view,modelChange) {
//xx console.log("in _collectModelChange", modelChange.verb, verbFlags.get(modelChange.verb).toString());
this._modelChanges.push(modelChange);
};
/**
*
* walk up the hierarchy of objects until a view is found
* objects may belong to multiples views.
* Note: this method doesn't return the main view => Server object.
* @method extractRootViews
* @param node {BaseNode}
* @return {BaseNode[]}
*/
AddressSpace.prototype.extractRootViews = function(node) {
var addressSpace = this;
assert(node.nodeClass === NodeClass.Object || node.nodeClass === NodeClass.Variable);
var visitedMap ={};
var q = new Dequeue();
q.push(node);
var objectsFolder = addressSpace.rootFolder.objects;
assert(objectsFolder instanceof UAObject);
var results = [];
while(q.length) {
node = q.shift();
var references = node.findReferencesEx("HierarchicalReferences",BrowseDirection.Inverse);
var parentNodes = references.map(function(r){
return Reference._resolveReferenceNode(addressSpace,r);
});
parentNodes.forEach(function(parent){
if (sameNodeId(parent.nodeId,objectsFolder.nodeId)) {
return ; // nothing to do
}
if (parent.nodeClass === NodeClass.View) {
results.push(parent);
} else {
var key = parent.nodeId.toString();
if (visitedMap.hasOwnProperty(key)) {
return;
}
visitedMap[key] = parent;
q.push(parent);
}
});
}
return results;
};
AddressSpace.prototype.modelChangeTransaction = function(func) {
var addressSpace = this;
this._modelChangeTransactionCounter = this._modelChangeTransactionCounter || 0;
function beginModelChange(node) {
/* jshint validthis:true */
assert(this);
this._modelChanges = addressSpace._modelChanges || [];
assert(this._modelChangeTransactionCounter >=0);
this._modelChangeTransactionCounter +=1;
}
function endModelChange(node) {
/* jshint validthis:true */
assert(this);
this._modelChangeTransactionCounter -=1;
if (this._modelChangeTransactionCounter === 0) {
if (this._modelChanges.length === 0 ) {
return; // nothing to do
}
//xx console.log( "xx dealing with ",this._modelChanges.length);
// increase version number of participating nodes
var nodes = _.uniq(this._modelChanges.map(function(c) { return c.affected; }));
nodes = nodes.map(function(nodeId) { return addressSpace.findNode(nodeId); });
nodes.forEach(_increase_version_number);
// raise events
if (addressSpace.rootFolder.objects.server) {
var eventTypeNode = addressSpace.findEventType("GeneralModelChangeEventType");
if (eventTypeNode) {
//xx console.log("xx raising event on server object");
addressSpace.rootFolder.objects.server.raiseEvent(eventTypeNode,{
// Part 5 - 6.4.32 GeneralModelChangeEventType
changes: { dataType: "ExtensionObject", value: this._modelChanges }
});
}
}
this._modelChanges = [];
// _notifyModelChange(this);
}
}
beginModelChange.call(this);
try {
func();
}
catch(err) {
throw err;
}
finally {
endModelChange.call(this);
}
};
function _handle_node_version(node,options) {
assert(options);
if (options.nodeVersion) {
assert(node.nodeClass === NodeClass.Variable || node.nodeClass === NodeClass.Object);
var addressSpace = node.addressSpace;
var nodeVersion = addressSpace.addVariable({
propertyOf: node,
browseName: "NodeVersion",
dataType: "String"
});
var initialValue = _.isString(options.nodeVersion) ? options.nodeVersion : "0";
//xx console.log(" init value =",initialValue);
nodeVersion.setValueFromSource({ dataType: "String", value: initialValue });
}
}
function _increase_version_number(node) {
if (node && node.nodeVersion) {
var previousValue = parseInt(node.nodeVersion.readValue().value.value);
node.nodeVersion.setValueFromSource({ dataType: "String", value: (previousValue+1).toString() });
//xx console.log("xxx increasing version number of node ", node.browseName.toString(),previousValue);
}
}
/**
* @method createNode
* @param options
* @param options.nodeClass
* @param [options.nodeVersion {String} = "0" ] install nodeVersion
*
*/
AddressSpace.prototype.createNode = function (options) {
var self = this;
var node = null;
self.modelChangeTransaction(function() {
assert(isNonEmptyQualifiedName(options.browseName));
//xx assert(options.hasOwnProperty("browseName") && options.browseName.length > 0);
assert(options.hasOwnProperty("nodeClass"));
options.references = self.normalizeReferenceTypes(options.references);
var references = _copy_references(options.references);
_handle_hierarchy_parent(self, references, options);
_handle_event_hierarchy_parent(self,references,options);
AddressSpace._process_modelling_rule(references, options.modellingRule);
options.references = references;
node = self._createNode(options);
assert(node.nodeId instanceof NodeId);
node.propagate_back_references();
node.install_extra_properties();
_handle_node_version(node,options);
_handle_model_change_event(node);
});
return node;
};
AddressSpace.prototype.addObject =function(options) {
assert(!options.nodeClass || options.nodeClass === NodeClass.Object);
options.nodeClass = NodeClass.Object;
var typeDefinition = options.typeDefinition || "BaseObjectType";
options.references = options.references ||[];
options.references.push({referenceType: "HasTypeDefinition", isForward: true, nodeId: typeDefinition});
options.eventNotifier = +options.eventNotifier;
//xx options.isAbstract = false,
var obj = this.createNode(options);
assert(obj instanceof UAObject);
assert(obj.nodeClass === NodeClass.Object);
return obj;
};
/**
*
* @method addFolder
* @param parentFolder
* @param options {String|Object}
* @param options.browseName {String} the name of the folder
* @param [options.nodeId] {NodeId}. An optional nodeId for this object
*
* @return {BaseNode}
*/
AddressSpace.prototype.addFolder = function (parentFolder, options) {
var self = this;
if (typeof options === "string") {
options = {browseName: options};
}
assert(!options.typeDefinition, "addFolder does not expect typeDefinition to be defined ");
var typeDefinition = self._coerceTypeDefinition("FolderType");
parentFolder = self._coerceFolder(parentFolder);
options.nodeClass = NodeClass.Object;
options.references = [
{referenceType: "HasTypeDefinition", isForward: true, nodeId: typeDefinition},
{referenceType: "Organizes", isForward: false, nodeId: parentFolder.nodeId}
];
var node = self.createNode(options);
return node;
};
/**
* cleanup all resources maintained by this addressSpace.
* @method dispose
*/
AddressSpace.prototype.dispose = function() {
_.forEach(this._nodeid_index,function(node){
node.dispose();
});
this._nodeid_index = null;
this._aliases = null;
this._objectTypeMap = null;
this._variableTypeMap = null;
this._referenceTypeMap = null;
this._referenceTypeMapInv = null;
this._dataTypeMap = null;
AddressSpace.registry.unregister(this);
if(this._shutdownTask && this._shutdownTask.length > 0) {
throw new Error("AddressSpace#dispose : shutdown has not been called");
}
};
/**
* @method addReferenceType
* @param options
* @param options.isAbstract
* @param options.browseName
* @param options.inverseName
*/
AddressSpace.prototype.addReferenceType = function(options) {
var addressSpace = this;
options.nodeClass = NodeClass.ReferenceType;
options.references = options.references || [];
if (options.subtypeOf) {
assert(options.subtypeOf);
var subtypeOfNodeId = addressSpace._coerceType(options.subtypeOf,"References",NodeClass.ReferenceType);
assert(subtypeOfNodeId);
console.log(subtypeOfNodeId.toString().cyan);
options.references.push({referenceType: "HasSubtype", isForward: false, nodeId: subtypeOfNodeId});
}
var node = addressSpace._createNode(options);
node.propagate_back_references();
return node;
};
/**
* register a function that will be called when the server will perform its shut down.
* @method registerShutdownTask
*/
AddressSpace.prototype.registerShutdownTask = function (task) {
this._shutdownTask = this._shutdownTask || [];
assert(_.isFunction(task));
this._shutdownTask.push(task);
};
AddressSpace.prototype.shutdown = function() {
//xxx console.log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx DO ADDRESSSPACE SHUTDOWN".bgWhite);
var self = this;
if (!self._shutdownTask) {
return;
}
// perform registerShutdownTask
self._shutdownTask.forEach(function (task) {
task.call(self);
});
self._shutdownTask = [];
};
var StatusCodes = require("node-opcua-status-code").StatusCodes;
/**
*
* @method browseSingleNode
* @param nodeId {NodeId|String} : the nodeid of the element to browse
* @param browseDescription
* @param browseDescription.browseDirection {BrowseDirection} :
* @param browseDescription.referenceTypeId {String|NodeId}
* @param [session] {ServerSession}
* @return {BrowseResult}
*/
AddressSpace.prototype.browseSingleNode = function (nodeId, browseDescription, session) {
var addressSpace = this;
// create default browseDescription
browseDescription = browseDescription || {};
browseDescription.browseDirection = browseDescription.browseDirection || BrowseDirection.Forward;
assert(browseDescription.browseDirection);
//xx console.log(util.inspect(browseDescription,{colors:true,depth:5}));
browseDescription = browseDescription || {};
if (typeof nodeId === "string") {
var node = addressSpace.findNode(addressSpace.resolveNodeId(nodeId));
if (node) {
nodeId = node.nodeId;
}
}
var browseResult = {
statusCode: StatusCodes.Good,
continuationPoint: null,
references: null
};
if (browseDescription.browseDirection === BrowseDirection.Invalid) {
browseResult.statusCode = StatusCodes.BadBrowseDirectionInvalid;
return new BrowseResult(browseResult);
}
// check if referenceTypeId is correct
if (browseDescription.referenceTypeId instanceof NodeId) {
if (browseDescription.referenceTypeId.value === 0) {
browseDescription.referenceTypeId = null;
} else {
var rf = addressSpace.findNode(browseDescription.referenceTypeId);
if (!rf || !(rf instanceof ReferenceType)) {
browseResult.statusCode = StatusCodes.BadReferenceTypeIdInvalid;
return new BrowseResult(browseResult);
}
}
}
var obj = addressSpace.findNode(nodeId);
if (!obj) {
// Object Not Found
browseResult.statusCode = StatusCodes.BadNodeIdUnknown;
//xx console.log("xxxxxx browsing ",nodeId.toString() , " not found" );
} else {
browseResult.statusCode = StatusCodes.Good;
browseResult.references = obj.browseNode(browseDescription, session);
}
return new BrowseResult(browseResult);
};
exports.AddressSpace = AddressSpace;
require("./address_space_add_event_type").install(AddressSpace);
require("./address_space_add_method").install(AddressSpace);
require("./address_space_browse").install(AddressSpace);
require("./address_space_construct_extension_object").install(AddressSpace);
require("./ua_two_state_variable").install(AddressSpace);
// State Machines
require("./state_machine/address_space_state_machine").install(AddressSpace);
// DI
require("./address_space_add_enumeration_type").install(AddressSpace);
require("./data_access/address_space_add_AnalogItem").install(AddressSpace);
require("./data_access/address_space_add_MultiStateDiscrete").install(AddressSpace);
require("./data_access/address_space_add_TwoStateDiscrete").install(AddressSpace);
require("./data_access/address_space_add_MultiStateValueDiscrete").install(AddressSpace);
require("./data_access/address_space_add_YArrayItem").install(AddressSpace);
require("./historical_access/address_space_historical_data_node").install(AddressSpace);
//
// Alarms & Conditions
//
require("./alarms_and_conditions/install").install(AddressSpace);