You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OPC UA C#服务端如何与已加载的.NodeSet2.xml文件交互

以下实现基于Traeger OPC UA Toolkit 7.x版本,适配你正在用的Server.NodeSet.Umati.MachineTool示例框架,所有逻辑可以直接在你现有加载完MachineVision NodeSet2的项目里复用。

前置准备

Traeger的NodeSet2加载器会在项目编译时,根据你引入的Opc.Ua.MachineVision.NodeSet2.xml自动生成强类型静态类,包含所有节点的NodeId、BrowseName、类型定义,不需要手动拼NodeId字符串。命名空间索引不要硬编码,统一通过以下方式获取:

// 在服务端启动完成后调用,拿到MachineVision对应的命名空间索引
ushort visionNsIndex = ServerInternal.NamespaceUris.GetIndex(Opc.Ua.MachineVision.Namespaces.OpcUaMachineVision);
1. 节点定位

两种常用方式,优先用强类型定位,避免拼写错误:

// 方式1:强类型直接获取(推荐,编译期可校验)
// 例:获取MachineVisionSystemType定义下的State变量、Trigger方法的NodeId
NodeId stateVarNodeId = Opc.Ua.MachineVision.Variables.MachineVisionSystemType_State;
NodeId triggerMethodNodeId = Opc.Ua.MachineVision.Methods.MachineVisionSystemType_Trigger;
// 你自己实例化的视觉设备对象NodeId,实例化时按你设置的BrowseName生成即可
NodeId visionInstanceNodeId = new NodeId(visionNsIndex, "VisionDevice01");

// 方式2:按相对路径动态查找,适合无强类型的动态节点场景
IList<INode> foundNodes = ServerInternal.NodeManager.Find(
    context: ServerInternal.SystemContext,
    nodesToBrowse: new BrowseDescriptionCollection {
        new BrowseDescription {
            NodeId = ObjectIds.ObjectsFolder,
            BrowseDirection = BrowseDirection.Forward,
            ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences,
            IncludeSubtypes = true,
            NodeClassMask = (uint)NodeClass.All,
            ResultMask = (uint)BrowseResultMask.All
        }
    },
    relativePath: new RelativePath($"{visionNsIndex}:VisionDevice01.{visionNsIndex}:State"),
    throwOnError: false
);
INode targetNode = foundNodes.FirstOrDefault();
2. 节点值读写

不建议每次业务逻辑触发时主动写值,优先给变量绑定读写回调,客户端访问时自动返回最新值、处理写入请求:

if (ServerInternal.NodeManager.FindNode(ServerInternal.SystemContext, stateVarNodeId) is BaseVariableState stateVar)
{
    // 绑定读回调:客户端读取节点值时自动触发
    stateVar.OnSimpleReadValue = (context, node) =>
    {
        // 替换成你自己的业务逻辑:从实际硬件/业务层拿视觉设备实时状态
        return GetVisionDeviceRealtimeState();
    };

    // 绑定写回调:客户端写入节点值时自动触发
    stateVar.OnSimpleWriteValue = (context, node, value) =>
    {
        try
        {
            int targetState = value.GetValueOrDefault<int>();
            // 替换成你自己的业务逻辑:把客户端下发的状态值写到实际硬件
            SetVisionDeviceState(targetState);
            return StatusCodes.Good;
        }
        catch
        {
            return StatusCodes.BadDeviceError;
        }
    };
}

// 主动读写值的接口(适合业务层主动推送状态更新的场景)
// 读
object currentState = ServerInternal.NodeManager.ReadValue(ServerInternal.SystemContext, stateVarNodeId);
// 写
ServerInternal.NodeManager.WriteValue(
    context: ServerInternal.SystemContext,
    nodeId: stateVarNodeId,
    value: new DataValue(new Variant((int)MachineVisionState.Executing))
);
3. 方法调用响应(含输入输出参数处理)

直接给加载生成的方法节点绑定OnCall回调即可,输入输出参数的顺序、类型必须和NodeSet2 XML里的定义完全一致:

if (ServerInternal.NodeManager.FindNode(ServerInternal.SystemContext, triggerMethodNodeId) is MethodState triggerMethod)
{
    triggerMethod.OnCall = (context, method, inputArgs, outputArgs) =>
    {
        // 按NodeSet2定义的顺序读取输入参数
        // 例:Trigger方法输入参数依次为触发模式(Int32枚举)、曝光时间(Float)
        int triggerMode = inputArgs[0].GetValueOrDefault<int>(0);
        float exposure = inputArgs[1].GetValueOrDefault<float>(10.0f);

        try
        {
            // 替换成你自己的业务逻辑:调用硬件触发拍照
            string imgId = TriggerVisionCapture(triggerMode, exposure);
            int costMs = GetLastCaptureCostMs();

            // 按NodeSet2定义的顺序填充输出参数
            // 例:输出参数依次为结果图片ID(String)、处理耗时(Int32)
            outputArgs[0] = new Variant(imgId);
            outputArgs[1] = new Variant(costMs);

            return StatusCodes.Good;
        }
        catch (Exception ex)
        {
            // 业务异常返回对应错误码,客户端可直接捕获
            return StatusCodes.BadResourceUnavailable;
        }
    };
}
4. 事件触发

先确认事件源节点的EventNotifier属性开启了事件订阅权限(NodeSet2加载生成的类型默认带该配置,自定义实例需要手动设置),然后构造事件实例上报即可:

// 开启事件源的事件订阅权限
if (ServerInternal.NodeManager.FindNode(ServerInternal.SystemContext, visionInstanceNodeId) is BaseObjectState visionObj)
{
    visionObj.EventNotifier = EventNotifiers.SubscribeToEvents;
}

// 构造VisionResultReadyEvent事件(对应MachineVision NodeSet2定义的事件类型)
BaseEventState resultEvent = ServerInternal.NodeManager.CreateEvent(
    context: ServerInternal.SystemContext,
    eventTypeId: Opc.Ua.MachineVision.ObjectTypes.VisionResultReadyEventType,
    sourceNode: visionInstanceNodeId,
    sourceName: "VisionDevice01",
    time: DateTime.UtcNow,
    message: new LocalizedText("Vision inspection completed")
);

// 设置事件自定义字段,字段名和NodeSet2里定义的BrowseName一致
resultEvent.SetChildValue(
    ServerInternal.SystemContext,
    Opc.Ua.MachineVision.BrowseNames.VisionResultReadyEventType_ResultId,
    new Variant("RES_20240601_0001")
);
resultEvent.SetChildValue(
    ServerInternal.SystemContext,
    Opc.Ua.MachineVision.BrowseNames.VisionResultReadyEventType_IsPass,
    new Variant(true)
);

// 上报事件,所有订阅对应事件类型的客户端会实时收到
ServerInternal.NodeManager.ReportEvent(ServerInternal.SystemContext, resultEvent);
常见注意事项
  • 枚举类型传值必须转成NodeSet2定义的基础数值类型(默认是Int32),直接传C#枚举对象会触发类型不匹配错误
  • 方法参数顺序必须严格和XML定义一致,参数数量不对会直接返回BadArgumentsMissing错误
  • 不要硬编码命名空间索引,服务端加载节点集顺序变化会导致索引变动,统一用NamespaceUris.GetIndex获取

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 21:15:42