You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在OPCFoundation.NetStandard.Opc.Ua.Server中编程实现结构类型定义与使用?

OPC UA服务器编程创建结构类型、变量节点及赋值(C#)

1. 定义C#数据类(支持枚举与嵌套结构)

先定义需映射到OPC UA结构类型的C#类,通过DataContractDataMember特性实现序列化支持:

using System.Runtime.Serialization;

// 基础Person类
[DataContract(Name = "Person")]
public class Person
{
    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public int Age { get; set; }
}

// 枚举类型
[DataContract(Name = "Gender")]
public enum Gender
{
    [EnumMember]
    Unknown,
    [EnumMember]
    Male,
    [EnumMember]
    Female
}

// 嵌套结构
[DataContract(Name = "Address")]
public class Address
{
    [DataMember]
    public string Street { get; set; }

    [DataMember]
    public string City { get; set; }
}

// 包含枚举与嵌套结构的复杂类
[DataContract(Name = "Employee")]
public class Employee : Person
{
    [DataMember]
    public Gender Gender { get; set; }

    [DataMember]
    public Address WorkAddress { get; set; }
}

2. 在OPC UA服务器中注册结构类型

在服务器地址空间初始化阶段(重写CreateAddressSpace方法),将自定义结构类型注册到OPC UA服务器:

using Opc.Ua;
using Opc.Ua.Server;

public class CustomNodeManager : NodeManager
{
    public CustomNodeManager(IServerInternal server, ApplicationConfiguration configuration)
        : base(server, configuration)
    {
        NamespaceUris.Add("http://your-custom-namespace.com");
    }

    protected override void CreateAddressSpace(IDictionary<NodeId, INode> addressSpace)
    {
        base.CreateAddressSpace(addressSpace);

        var namespaceIndex = Server.NamespaceUris.GetIndex("http://your-custom-namespace.com");

        // 批量注册自定义类型
        RegisterEncodeableType(typeof(Person), namespaceIndex, addressSpace);
        RegisterEncodeableType(typeof(Gender), namespaceIndex, addressSpace);
        RegisterEncodeableType(typeof(Address), namespaceIndex, addressSpace);
        RegisterEncodeableType(typeof(Employee), namespaceIndex, addressSpace);
    }

    private void RegisterEncodeableType(Type type, ushort namespaceIndex, IDictionary<NodeId, INode> addressSpace)
    {
        var encodeableType = EncodeableType.GetEncodeableType(type);
        if (encodeableType == null)
        {
            encodeableType = new EncodeableType(type, null, namespaceIndex);
            EncodeableType.RegisterEncodeableType(encodeableType);
        }

        var typeNodeId = new NodeId(encodeableType.Name, namespaceIndex);
        if (!addressSpace.ContainsKey(typeNodeId))
        {
            var typeNode = new StructuredTypeNode
            {
                NodeId = typeNodeId,
                BrowseName = new QualifiedName(encodeableType.Name, namespaceIndex),
                DisplayName = new LocalizedText(encodeableType.Name),
                Description = new LocalizedText($"自定义结构类型:{encodeableType.Name}"),
                WriteMask = AttributeWriteMask.None,
                UserWriteMask = AttributeWriteMask.None,
                IsAbstract = false
            };
            AddNode(typeNode);
        }
    }
}

3. 创建结构类型的变量节点

在地址空间中创建使用自定义结构类型的变量节点:

// 在CustomNodeManager的CreateAddressSpace方法中继续添加:
var namespaceIndex = Server.NamespaceUris.GetIndex("http://your-custom-namespace.com");

// 创建Person类型变量节点
var personVariable = new VariableNode
{
    NodeId = new NodeId("PersonVariable", namespaceIndex),
    BrowseName = new QualifiedName("PersonVariable", namespaceIndex),
    DisplayName = new LocalizedText("Person变量"),
    Description = new LocalizedText("Person类型的变量"),
    DataType = new NodeId("Person", namespaceIndex),
    ValueRank = ValueRanks.Scalar,
    AccessLevel = AccessLevels.CurrentRead | AccessLevels.CurrentWrite,
    UserAccessLevel = AccessLevels.CurrentRead | AccessLevels.CurrentWrite,
    MinimumSamplingInterval = 100,
    Historizing = false
};
AddNode(personVariable);

// 创建Employee类型变量节点
var employeeVariable = new VariableNode
{
    NodeId = new NodeId("EmployeeVariable", namespaceIndex),
    BrowseName = new QualifiedName("EmployeeVariable", namespaceIndex),
    DisplayName = new LocalizedText("Employee变量"),
    Description = new LocalizedText("Employee类型的变量"),
    DataType = new NodeId("Employee", namespaceIndex),
    ValueRank = ValueRanks.Scalar,
    AccessLevel = AccessLevels.CurrentRead | AccessLevels.CurrentWrite,
    UserAccessLevel = AccessLevels.CurrentRead | AccessLevels.CurrentWrite,
    MinimumSamplingInterval = 100,
    Historizing = false
};
AddNode(employeeVariable);

4. 为变量节点设置值

创建结构实例并赋值给对应变量节点:

// 设置Person变量值
var personInstance = new Person
{
    Name = "John Doe",
    Age = 30
};
Server.InternalNodeManager.Write(new WriteValue
{
    NodeId = new NodeId("PersonVariable", namespaceIndex),
    AttributeId = Attributes.Value,
    Value = new DataValue(new Variant(personInstance))
});

// 设置Employee变量值(包含枚举与嵌套结构)
var employeeInstance = new Employee
{
    Name = "Jane Smith",
    Age = 28,
    Gender = Gender.Female,
    WorkAddress = new Address
    {
        Street = "123 Main St",
        City = "New York"
    }
};
Server.InternalNodeManager.Write(new WriteValue
{
    NodeId = new NodeId("EmployeeVariable", namespaceIndex),
    AttributeId = Attributes.Value,
    Value = new DataValue(new Variant(employeeInstance))
});

关键注意事项

  • 自定义类的DataContract名称需与OPC UA结构类型名称完全一致
  • 枚举成员必须添加EnumMember特性,否则无法被OPC UA识别
  • 嵌套结构需单独注册,确保服务器类型树能正确解析依赖关系
  • 变量节点的DataType属性必须指向对应结构类型的NodeId

内容的提问来源于stack exchange,提问作者Tobus

火山引擎 最新活动