public NodeId CreateVariable(
NodeId parentId,
NodeId referenceTypeId,
NodeId nodeId,
QualifiedName browseName,
VariableAttributes attributes,
ExpandedNodeId typeDefinitionId)
{
try
{
m_lock.Enter();
// check browse name.
if (QualifiedName.IsNull(browseName))
{
throw new ServiceResultException(StatusCodes.BadBrowseNameInvalid);
}
// user default type definition.
if (NodeId.IsNull(typeDefinitionId))
{
typeDefinitionId = VariableTypes.BaseDataVariableType;
}
// find type definition.
IVariableType variableType = GetManagerHandle(typeDefinitionId) as IVariableType;
if (variableType == null)
{
throw ServiceResultException.Create(StatusCodes.BadTypeDefinitionInvalid, "Type definition '{0}' does not exist or is not an VariableType.", typeDefinitionId);
}
// check if node id exists.
if (!NodeId.IsNull(nodeId))
{
if (m_nodes.Exists(nodeId))
{
throw ServiceResultException.Create(StatusCodes.BadNodeIdExists, "NodeId '{0}' already exists.", nodeId);
}
}
// create a unique id.
else
{
nodeId = CreateUniqueNodeId();
}
// find parent.
ILocalNode parent = null;
if (!NodeId.IsNull(parentId))
{
parent = GetManagerHandle(parentId) as ILocalNode;
if (parent == null)
{
throw ServiceResultException.Create(StatusCodes.BadParentNodeIdInvalid, "Parent node '{0}' does not exist.", parentId);
}
// validate reference.
ValidateReference(parent, referenceTypeId, false, NodeClass.Variable);
}
// verify instance declarations.
ILocalNode instanceDeclaration = FindInstanceDeclaration(parent, browseName);
if (instanceDeclaration != null)
{
if (instanceDeclaration.NodeClass != NodeClass.Variable)
{
throw ServiceResultException.Create(
StatusCodes.BadNodeClassInvalid,
"The type model requires that node with a browse name of {0} have a NodeClass of {1}.",
browseName,
instanceDeclaration.NodeClass);
}
if (!m_server.TypeTree.IsTypeOf(typeDefinitionId, instanceDeclaration.TypeDefinitionId))
{
throw ServiceResultException.Create(
StatusCodes.BadNodeClassInvalid,
"The type model requires that node have a type definition of {0}.",
instanceDeclaration.TypeDefinitionId);
}
}
// get the variable.
IVariable variable = instanceDeclaration as IVariable;
// create node.
VariableNode node = new VariableNode();
// set defaults from type definition.
node.NodeId = nodeId;
node.NodeClass = NodeClass.Variable;
node.BrowseName = browseName;
node.DisplayName = browseName.Name;
node.Description = null;
node.WriteMask = 0;
node.UserWriteMask = 0;
node.Value = (variable == null)?new Variant(Utils.Clone(variableType.Value)):Variant.Null;
node.DataType = variableType.DataType;
node.ValueRank = variableType.ValueRank;
node.ArrayDimensions = new UInt32Collection(variableType.ArrayDimensions);
node.AccessLevel = AccessLevels.CurrentReadOrWrite;
node.UserAccessLevel = node.AccessLevel;
node.MinimumSamplingInterval = MinimumSamplingIntervals.Indeterminate;
node.Historizing = false;
// set defaults from instance declaration.
if (variable != null)
{
node.DisplayName = variable.DisplayName;
node.Description = variable.Description;
node.WriteMask = (uint)variable.WriteMask;
node.UserWriteMask = (uint)variable.UserWriteMask;
node.Value = new Variant(Utils.Clone(variable.Value));
node.DataType = variable.DataType;
node.ValueRank = variable.ValueRank;
node.ArrayDimensions = new UInt32Collection(variable.ArrayDimensions);
node.AccessLevel = variable.AccessLevel;
node.UserAccessLevel = variable.UserAccessLevel;
node.MinimumSamplingInterval = variable.MinimumSamplingInterval;
node.Historizing = variable.Historizing;
}
// update attributes.
UpdateAttributes(node, attributes);
// Value
if (attributes != null && (attributes.SpecifiedAttributes & (uint)NodeAttributesMask.Value) != 0)
{
node.Value = attributes.Value;
}
// DataType
if (attributes != null && (attributes.SpecifiedAttributes & (uint)NodeAttributesMask.DataType) != 0)
{
if (!m_server.TypeTree.IsTypeOf(attributes.DataType, variableType.DataType))
{
throw ServiceResultException.Create(
StatusCodes.BadNodeClassInvalid,
"The type definition requires a DataType of {0}.",
variableType.DataType);
}
if (variable != null)
{
if (!m_server.TypeTree.IsTypeOf(attributes.DataType, variable.DataType))
{
throw ServiceResultException.Create(
StatusCodes.BadNodeClassInvalid,
"The instance declaration requires a DataType of {0}.",
variable.DataType);
}
}
node.DataType = attributes.DataType;
}
// ValueRank
if (attributes != null && (attributes.SpecifiedAttributes & (uint)NodeAttributesMask.ValueRank) != 0)
{
if (!ValueRanks.IsValid(attributes.ValueRank, variableType.ValueRank))
{
throw ServiceResultException.Create(
StatusCodes.BadNodeClassInvalid,
"The type definition requires a ValueRank of {0}.",
variableType.ValueRank);
}
if (variable != null)
{
if (!ValueRanks.IsValid(attributes.ValueRank, variable.ValueRank))
{
throw ServiceResultException.Create(
StatusCodes.BadNodeClassInvalid,
"The instance declaration requires a ValueRank of {0}.",
variable.ValueRank);
}
}
node.ValueRank = attributes.ValueRank;
}
// ArrayDimensions
if (attributes != null && (attributes.SpecifiedAttributes & (uint)NodeAttributesMask.ArrayDimensions) != 0)
{
if (!ValueRanks.IsValid(attributes.ArrayDimensions, node.ValueRank, variableType.ArrayDimensions))
{
throw ServiceResultException.Create(
StatusCodes.BadNodeClassInvalid,
"The ArrayDimensions do not meet the requirements for the type definition: {0}.",
variableType.NodeId);
}
if (variable != null)
{
if (!ValueRanks.IsValid(attributes.ArrayDimensions, node.ValueRank, variable.ArrayDimensions))
{
throw ServiceResultException.Create(
StatusCodes.BadNodeClassInvalid,
"The ArrayDimensions do not meet the requirements for the instance declaration: {0}.",
variable.ValueRank);
}
}
node.ArrayDimensions = attributes.ArrayDimensions;
}
// AccessLevel
if (attributes != null && (attributes.SpecifiedAttributes & (uint)NodeAttributesMask.AccessLevel) != 0)
{
node.AccessLevel = attributes.AccessLevel;
}
// AccessLevel
if (attributes != null && (attributes.SpecifiedAttributes & (uint)NodeAttributesMask.UserAccessLevel) != 0)
{
node.UserAccessLevel = attributes.UserAccessLevel;
}
// MinimumSamplingInterval
if (attributes != null && (attributes.SpecifiedAttributes & (uint)NodeAttributesMask.MinimumSamplingInterval) != 0)
{
node.MinimumSamplingInterval = attributes.MinimumSamplingInterval;
}
// Historizing
if (attributes != null && (attributes.SpecifiedAttributes & (uint)NodeAttributesMask.Historizing) != 0)
{
node.Historizing = attributes.Historizing;
}
// add references with parent.
if (parent != null)
{
AddReference(parent, referenceTypeId, false, node, true);
}
// add type definition.
AddReference(node, ReferenceTypeIds.HasTypeDefinition, false, variableType, false);
// add to address space.
AddNode(node);
// apply modelling rules.
NodeFactory factory = new NodeFactory(m_nodes);
IList<ILocalNode> nodesToAdd = factory.ApplyModellingRules(node, variableType.NodeId, ref m_lastId, 1);
// add the nodes.
foreach (Node nodeToAdd in nodesToAdd)
{
AddNode(nodeToAdd);
}
// add references with parent.
if (parent != null)
{
AddReference(parent, referenceTypeId, false, node, true);
}
// find the top level parent that must be used to apply the modelling rules.
if (instanceDeclaration != null)
{
ILocalNode toplevelParent = FindTopLevelModelParent(parent);
// add modelling rule.
AddReference(node, ReferenceTypeIds.HasModelParent, false, parent, true);
// update the hierarchy.
nodesToAdd = factory.ApplyModellingRules(toplevelParent, (NodeId)toplevelParent.TypeDefinitionId, ref m_lastId, 1);
// add the nodes.
foreach (Node nodeToAdd in nodesToAdd)
{
AddNode(nodeToAdd);
}
}
// return the new node id.
return node.NodeId;
}
finally
{
m_lock.Exit();
}
}