如何添加具有子属性的节点并为其设置值?
How to add a node with child properties and set values to them?
我已经根据 OPCFoundation 示例构建了一个 OPC UA 服务器 - https://github.com/OPCFoundation/UA-.NETStandard
还编译了我自己的模型 -
https://github.com/Pro/UA-ModelCompiler/tree/eebf9988940a789a4f36cbef8c5bd6340f1b2f42
在示例中,他们在文件夹中生成了文件,然后为 NodeManger、State 等定制了 类。
我已成功加载生成的 .uanodes 文件,其中包含一些示例节点。但是我无法通过服务器端的代码创建这些节点。测试时,我在函数中进行 - LoadPredefinedNodes(ISystemContext context).
我的结构应该是名为 "Tags" 的对象下方的节点,在该节点中我想在服务器为 运行 时动态添加节点并设置它们的属性。
我在测试时使用 Integration Object 的 OPC UA 客户端。
结构看起来正确,例如:
对象 => 标签 => MyTestTag(属性:名称、说明、值)
根据模型文件定义正确添加了前两个属性
<!-- ### Object Types ###-->
<!-- BaseType -->
<ObjectType SymbolicName="TAG:TagType" BaseType="ua:BaseObjectType" IsAbstract="true" SupportsEvents="true" AccessLevel="ReadWrite">
<Description>Base type for all tags</Description>
<Children>
<Property SymbolicName="TAG:Name" DataType="ua:String" ValueRank="Scalar" ModellingRule="Mandatory" AccessLevel="ReadWrite">
<Description>Name of the tag</Description>
</Property>
<Property SymbolicName="TAG:Description" DataType="ua:String" ValueRank="Scalar" ModellingRule="Optional" AccessLevel="ReadWrite">
<Description>Description of the tag</Description>
</Property>
</Children>
</ObjectType>
<!-- ### Objects ###-->
<Object SymbolicName="TAG:Tags" TypeDefinition="ua:BaseObjectType">
<Description>Contains all instances of tags</Description>
<References>
<Reference IsInverse="true">
<ReferenceType>ua:Organizes</ReferenceType>
<TargetId>ua:ObjectsFolder</TargetId>
</Reference>
</References>
</Object>
第三个是 属性 我尝试从服务器端的代码定义。我添加了值 属性,但我不知道如何写它的值。
protected override NodeStateCollection LoadPredefinedNodes(ISystemContext context)
{
NodeStateCollection predefinedNodes = new NodeStateCollection();
predefinedNodes.LoadFromBinaryResource(context, "Opc.Ua.ValmetData.Tag.Tag.PredefinedNodes.uanodes", this.GetType().GetTypeInfo().Assembly, true);
// The defined ObjectType in the model
NodeState tageTypeNode = predefinedNodes.Find(x => x.SymbolicName == "TagType");
// List node to add the tag to
NodeState tagsNode = predefinedNodes.Find(x => x.SymbolicName == "Tags");
ushort namespaceIndex = Server.NamespaceUris.GetIndexOrAppend(Namespaces.Tag);
TagState tagNode = new TagState(tagsNode);
tagNode.SymbolicName = "MyTestTag";
tagNode.ClearChangeMasks(context, true);
PropertyState ps = new PropertyState(tagNode);
ps.ClearChangeMasks(context, true);
ps.NodeId = new NodeId();
ps.Description = "This is a description";
ps.TypeDefinitionId = tageTypeNode.NodeId;
ps.NumericId = 66;
// ps.NodeClass = NodeClass.Variable;
ps.ReferenceTypeId = ReferenceTypeIds.HasProperty;
ps.BrowseName = new QualifiedName("Value", NamespaceIndex);
ps.DisplayName = ps.BrowseName.Name;
ps.DataType = (uint)BuiltInType.Int32;
// ps.UserAccessLevel = AccessLevels.CurrentReadOrWrite;
// ps.AccessLevel = AccessLevels.CurrentReadOrWrite;
ps.Value = 66;
tagNode.AddChild(ps);
// assign
tagNode.Create(
SystemContext,
new NodeId(tagNode.SymbolicName, namespaceIndex),
new QualifiedName(tagNode.SymbolicName, namespaceIndex),
null,
true);
tagsNode.AddChild(tagNode);
return predefinedNodes;
}
从此代码中,在 Tags 节点中添加了一个名为 MyTestTag 的节点。从模型中添加属性 "Name" 和 "Description"。 "Value" 属性 是由代码添加的。但是,我无法为这些属性中的任何一个设置值。
在客户端中单击 属性 MyTestTag.Value 时,打印错误 "Nullable object must have a value"。
请告诉我哪里做错了。
来自 OPC-UA 的例子有更新变量的代码位,所以我不确定它有什么问题。假设我设置了好的值,代码可能如下所示:
variable.Value = value;
variable.Timestamp = DateTime.UtcNow;
variable.StatusCode = StatusCodes.Good;
variable.ClearChangeMasks(SystemContext, false);
在我的例子中,问题是 nodeId。我变了
ps.NodeId = new NodeId();
// to
ps.NodeId = new NodeId(Guid.NewGuid(), NamespaceIndex);
然后它确实出现在节点树中。
我已经根据 OPCFoundation 示例构建了一个 OPC UA 服务器 - https://github.com/OPCFoundation/UA-.NETStandard
还编译了我自己的模型 - https://github.com/Pro/UA-ModelCompiler/tree/eebf9988940a789a4f36cbef8c5bd6340f1b2f42
在示例中,他们在文件夹中生成了文件,然后为 NodeManger、State 等定制了 类。
我已成功加载生成的 .uanodes 文件,其中包含一些示例节点。但是我无法通过服务器端的代码创建这些节点。测试时,我在函数中进行 - LoadPredefinedNodes(ISystemContext context).
我的结构应该是名为 "Tags" 的对象下方的节点,在该节点中我想在服务器为 运行 时动态添加节点并设置它们的属性。
我在测试时使用 Integration Object 的 OPC UA 客户端。
结构看起来正确,例如: 对象 => 标签 => MyTestTag(属性:名称、说明、值)
根据模型文件定义正确添加了前两个属性
<!-- ### Object Types ###-->
<!-- BaseType -->
<ObjectType SymbolicName="TAG:TagType" BaseType="ua:BaseObjectType" IsAbstract="true" SupportsEvents="true" AccessLevel="ReadWrite">
<Description>Base type for all tags</Description>
<Children>
<Property SymbolicName="TAG:Name" DataType="ua:String" ValueRank="Scalar" ModellingRule="Mandatory" AccessLevel="ReadWrite">
<Description>Name of the tag</Description>
</Property>
<Property SymbolicName="TAG:Description" DataType="ua:String" ValueRank="Scalar" ModellingRule="Optional" AccessLevel="ReadWrite">
<Description>Description of the tag</Description>
</Property>
</Children>
</ObjectType>
<!-- ### Objects ###-->
<Object SymbolicName="TAG:Tags" TypeDefinition="ua:BaseObjectType">
<Description>Contains all instances of tags</Description>
<References>
<Reference IsInverse="true">
<ReferenceType>ua:Organizes</ReferenceType>
<TargetId>ua:ObjectsFolder</TargetId>
</Reference>
</References>
</Object>
第三个是 属性 我尝试从服务器端的代码定义。我添加了值 属性,但我不知道如何写它的值。
protected override NodeStateCollection LoadPredefinedNodes(ISystemContext context)
{
NodeStateCollection predefinedNodes = new NodeStateCollection();
predefinedNodes.LoadFromBinaryResource(context, "Opc.Ua.ValmetData.Tag.Tag.PredefinedNodes.uanodes", this.GetType().GetTypeInfo().Assembly, true);
// The defined ObjectType in the model
NodeState tageTypeNode = predefinedNodes.Find(x => x.SymbolicName == "TagType");
// List node to add the tag to
NodeState tagsNode = predefinedNodes.Find(x => x.SymbolicName == "Tags");
ushort namespaceIndex = Server.NamespaceUris.GetIndexOrAppend(Namespaces.Tag);
TagState tagNode = new TagState(tagsNode);
tagNode.SymbolicName = "MyTestTag";
tagNode.ClearChangeMasks(context, true);
PropertyState ps = new PropertyState(tagNode);
ps.ClearChangeMasks(context, true);
ps.NodeId = new NodeId();
ps.Description = "This is a description";
ps.TypeDefinitionId = tageTypeNode.NodeId;
ps.NumericId = 66;
// ps.NodeClass = NodeClass.Variable;
ps.ReferenceTypeId = ReferenceTypeIds.HasProperty;
ps.BrowseName = new QualifiedName("Value", NamespaceIndex);
ps.DisplayName = ps.BrowseName.Name;
ps.DataType = (uint)BuiltInType.Int32;
// ps.UserAccessLevel = AccessLevels.CurrentReadOrWrite;
// ps.AccessLevel = AccessLevels.CurrentReadOrWrite;
ps.Value = 66;
tagNode.AddChild(ps);
// assign
tagNode.Create(
SystemContext,
new NodeId(tagNode.SymbolicName, namespaceIndex),
new QualifiedName(tagNode.SymbolicName, namespaceIndex),
null,
true);
tagsNode.AddChild(tagNode);
return predefinedNodes;
}
从此代码中,在 Tags 节点中添加了一个名为 MyTestTag 的节点。从模型中添加属性 "Name" 和 "Description"。 "Value" 属性 是由代码添加的。但是,我无法为这些属性中的任何一个设置值。
在客户端中单击 属性 MyTestTag.Value 时,打印错误 "Nullable object must have a value"。
请告诉我哪里做错了。
来自 OPC-UA 的例子有更新变量的代码位,所以我不确定它有什么问题。假设我设置了好的值,代码可能如下所示:
variable.Value = value;
variable.Timestamp = DateTime.UtcNow;
variable.StatusCode = StatusCodes.Good;
variable.ClearChangeMasks(SystemContext, false);
在我的例子中,问题是 nodeId。我变了
ps.NodeId = new NodeId();
// to
ps.NodeId = new NodeId(Guid.NewGuid(), NamespaceIndex);
然后它确实出现在节点树中。