我正在开发 OPC UA 服务器并尝试在地址空间 OnRunTime 上创建类和实例的功能。我在修改 OPC UA 在运行时删除或添加类时遇到问题。
我实际上是使用 UA ModelCompiler 生成类,将它们添加到我的 UA 服务器项目中,并添加一个节点管理器来加载预定义的节点和实例。
我没有找到一种方法,因此当我更改 ModelDesinger.xml 中的类时,它将在我的项目中自动更改,而无需使用 UA ModelCompiler 重新生成它们。
我相信您可能已经找到了解决方案。但是,对于那些仍在寻找的人,这里有示例代码,演示如何实现这一目标。
我们可以在 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");
}