OPC UA 类在运行时添加、删除和修改

问题描述 投票:0回答:1

我正在开发 OPC UA 服务器并尝试在地址空间 OnRunTime 上创建类和实例的功能。我在修改 OPC UA 在运行时删除或添加类时遇到问题。

我实际上是使用 UA ModelCompiler 生成类,将它们添加到我的 UA 服务器项目中,并添加一个节点管理器来加载预定义的节点和实例。

我没有找到一种方法,因此当我更改 ModelDesinger.xml 中的类时,它将在我的项目中自动更改,而无需使用 UA ModelCompiler 重新生成它们。

opc-ua opc node-opcua
1个回答
0
投票

我相信您可能已经找到了解决方案。但是,对于那些仍在寻找的人,这里有示例代码,演示如何实现这一目标。

我们可以在 CustomNodeManager 类中定义自定义方法,以便在运行时从 UA 客户端动态创建和实例化 UA 服务器信息模型中的对象。下面是一个示例实现:

public class MyCustomNodeManager : CustomNodeManager2
{ 
    public MyCustomNodeManager(IServerInternal server, ApplicationConfiguration configuration)
        : base(server, configuration, "http://yournamespace.com/fullurl")
    {
        SystemContext.NodeIdFactory = this;
        SetNamespaces(new[] { "http://yournamespace.com/fullurl" });
    }

    public override void CreateAddressSpace(IDictionary<NodeId, IList<IReference>> externalReferences)
    {
        lock (Lock)
        {
            base.CreateAddressSpace(externalReferences);
            
            // Define the method for creating a new class.
            MethodState addClassMethod = new MethodState(process)
            {
                NodeId = new NodeId(Guid.NewGuid(), GetNamespaceIndex),
                BrowseName = new QualifiedName("Create a New Class Type", GetNamespaceIndex),
                DisplayName = "Create a New Class Type",
                ReferenceTypeId = ReferenceTypeIds.HasComponent,
                UserExecutable = true,
                Executable = true
            };

            // Configure input arguments.
            addClassMethod.InputArguments = new PropertyState<Argument[]>(addClassMethod)
            {
                NodeId = new NodeId(Guid.NewGuid(), NSMethods),
                BrowseName = BrowseNames.InputArguments,
                DisplayName = "Input Arguments",
                TypeDefinitionId = VariableTypeIds.PropertyType,
                ReferenceTypeId = ReferenceTypeIds.HasProperty,
                DataType = DataTypeIds.Argument,
                ValueRank = ValueRanks.OneDimension,
                Value = new[]
                {
                    new Argument { Name = "Class ID", Description = "NodeID for the new class", DataType = DataTypeIds.String, ValueRank = ValueRanks.Scalar },
                    new Argument { Name = "Parent ID", Description = "Set to 0 for the default location; otherwise, provide a parent ID", DataType = DataTypeIds.String, ValueRank = ValueRanks.Scalar },
                    new Argument { Name = "Name", Description = "Name of the new class", DataType = DataTypeIds.String, ValueRank = ValueRanks.Scalar }
                }
            };

            // Configure output arguments.
            addClassMethod.OutputArguments = new PropertyState<Argument[]>(addClassMethod)
            {
                NodeId = new NodeId(Guid.NewGuid(), NSMethods),
                BrowseName = BrowseNames.OutputArguments,
                DisplayName = "Output Arguments",
                TypeDefinitionId = VariableTypeIds.PropertyType,
                ReferenceTypeId = ReferenceTypeIds.HasProperty,
                DataType = DataTypeIds.Argument,
                ValueRank = ValueRanks.OneDimension,
                Value = new[]
                {
                    new Argument { Name = "Output1", Description = "Class ID of the created class", DataType = DataTypeIds.UInt32, ValueRank = ValueRanks.Scalar }
                }
            };

            process.AddChild(addClassMethod);
            addClassMethod.OnCallMethod = new GenericMethodCalledEventHandler(MakingEquipmentClass);
        }
    }

    public ServiceResult MakingEquipmentClass(
        ISystemContext context,
        MethodState method,
        IList<object> inputArguments,
        IList<object> outputArguments)
    {
        if (inputArguments.Count < 3) return StatusCodes.BadArgumentsMissing;

        // Validate input arguments.
        string? equipNum = inputArguments[0] as string;
        string? parentNum = inputArguments[1] as string;
        string? equipName = inputArguments[2] as string;

        if (equipNum == null || parentNum == null || equipName == null)
            return StatusCodes.BadTypeMismatch;

        // Set the parent node ID.
        NodeId parentID = string.IsNullOrWhiteSpace(parentNum)
            ? new NodeId(5034, GetNamespaceIndex)
            : new NodeId(parentNum, GetNamespaceIndex);

        NodeId equipID = new NodeId(equipNum, GetNamespaceIndex);

        // Create and register the new class.
        BaseObjectTypeState newClass = new BaseObjectTypeState
        {
            NodeId = equipID,
            BrowseName = new QualifiedName(equipName, GetNamespaceIndex),
            DisplayName = equipName
        };
        newClass.AddReference(ReferenceTypeIds.HasSubtype, true, parentID);
        Find(parentID).AddReference(ReferenceTypeIds.HasSubtype, false, newClass.NodeId);
        AddPredefinedNode(SystemContext, newClass);

        outputArguments[0] = equipNum;
        return ServiceResult.Good;
    }

    public ushort GetNamespaceIndex => Server.NamespaceUris.GetIndexOrAppend("http://yournamespace.com/fullurl");
}
© www.soinside.com 2019 - 2024. All rights reserved.