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

如何在C#中合并Opc UA订阅以解决BadTooManySubscriptions错误

解决S7-1200 PLC OPC UA订阅数量超限问题

问题描述

我用C#编写小型HMI程序,需要订阅约200个西门子S7-1200 PLC的标签。目前每个标签单独创建订阅,超过5个就触发Opc.Ua.ServiceResultException: 'BadTooManySubscriptions'错误。我刚接触C#,知道要把订阅合并成一个,但找不到具体实现代码。

以下是当前的错误代码(注释掉5个以外的订阅就能正常获取数据):

try
{
    myClient.AddSubscription("IDODChuck1", "ns=4;i=203", SubCallback);
    myClient.AddSubscription("IDODChuck2", "ns=4;i=204", SubCallback);
    myClient.AddSubscription("HMI_High_Thresh_Chuck1", "ns=4;i=9", SubCallback);
    myClient.AddSubscription("Calculated1VOLTS", "ns=4;i=148", SubCallback);
    myClient.AddSubscription("Calculated2VOLTS", "ns=4;i=150", SubCallback);
    myClient.AddSubscription("Arrow2", "ns=4;i=149", SubCallback);
    myClient.AddSubscription("B", "ns=4;i=150", SubCallback);
    myClient.AddSubscription("HMI_High_Thresh_Chuck1", "ns=4;i=9", SubCallback);
    myClient.AddSubscription("Calculated1VOLTS", "ns=4;i=154", SubCallback);
    myClient.AddSubscription("Chuck1Nominal", "ns=4;i=2", SubCallback);
    myClient.AddSubscription("Chuck2Nominal", "ns=4;i=3", SubCallback);
    myClient.AddSubscription("CurHMIMeasureDiaLPS1", "ns=4;i=4", SubCallback);
    myClient.AddSubscription("CurHMIMeasureDiaLPS2", "ns=4;i=5", SubCallback);
    myClient.AddSubscription("CurHMIMeasureRadLPS1", "ns=4;i=6", SubCallback);
    myClient.AddSubscription("CurHMIMeasureRadLPS2", "ns=4;i=7", SubCallback);
    myClient.AddSubscription("CurrentChuckScreen", "ns=4;i=8", SubCallback);
    myClient.AddSubscription("HMIClmpHighThreshArrowChuck1", "ns=4;i=9", SubCallback);
    myClient.AddSubscription("HMIClmpHighThreshArrowChuck2", "ns=4;i=10", SubCallback);
    myClient.AddSubscription("HMIClmpLowThreshArrowChuck1", "ns=4;i=11", SubCallback);
    myClient.AddSubscription("HMIClmpLowThreshArrowChuck2", "ns=4;i=12", SubCallback);
    myClient.AddSubscription("HMICloseEOSLineChuck1", "ns=4;i=13", SubCallback);
    myClient.AddSubscription("HMICloseEOSLineChuck2", "ns=4;i=14", SubCallback);
    myClient.AddSubscription("HMIClampHighThreshChuck1", "ns=4;i=15", SubCallback);
    myClient.AddSubscription("HMIClampHighThreshChuck2", "ns=4;i=16", SubCallback);
    myClient.AddSubscription("HMIClampLowThreshChuck1", "ns=4;i=17", SubCallback);
    myClient.AddSubscription("HMIClampLowThreshChuck2", "ns=4;i=18", SubCallback);
    myClient.AddSubscription("HMIUnClampThreshChuck1", "ns=4;i=19", SubCallback);
    myClient.AddSubscription("HMIUnClampThreshChuck2", "ns=4;i=20", SubCallback);
    myClient.AddSubscription("PlusClampHighThreshChuck1", "ns=4;i=35", SubCallback);
    myClient.AddSubscription("PlusClampHighThreshChuck2", "ns=4;i=36", SubCallback);
    myClient.AddSubscription("PlusClampLowThreshChuck1", "ns=4;i=37", SubCallback);
    myClient.AddSubscription("PlusClampLowThreshChuck2", "ns=4;i=38", SubCallback);
    myClient.AddSubscription("PlusUnClampThreshChuck1", "ns=4;i=39", SubCallback);
    myClient.AddSubscription("PlusUnClampThreshChuck2", "ns=4;i=40", SubCallback);
    myClient.AddSubscription("ApplyTolereanceLPS1", "ns=4;i=41", SubCallback);
    myClient.AddSubscription("ApplyTolereanceLPS2", "ns=4;i=42", SubCallback);
    myClient.AddSubscription("MinusClampHighThreshChuck1", "ns=4;i=49", SubCallback);
    myClient.AddSubscription("MinusClampHighThreshChuck2", "ns=4;i=50", SubCallback);
    myClient.AddSubscription("MinusClampLowThreshChuck1", "ns=4;i=51", SubCallback);
    myClient.AddSubscription("MinusClampLowThreshChuck2", "ns=4;i=52", SubCallback);
    myClient.AddSubscription("CLOSEEOSONLPS1", "ns=4;i=55", SubCallback);
    myClient.AddSubscription("CLOSEEOSONLPS2", "ns=4;i=56", SubCallback);
    myClient.AddSubscription("ENBLClmpConfChuck1", "ns=4;i=57", SubCallback);
    myClient.AddSubscription("ENBLClmpConfChuck2", "ns=4;i=58", SubCallback);
    myClient.AddSubscription("ENBLUnClmpConfChuck1", "ns=4;i=69", SubCallback);
    myClient.AddSubscription("ENBLUnClmpConfChuck2", "ns=4;i=70", SubCallback);
    myClient.AddSubscription("LPS1Online", "ns=4;i=79", SubCallback);
    myClient.AddSubscription("LPS2Online", "ns=4;i=81", SubCallback);
    myClient.AddSubscription("MinusUnClampThreshChuck1", "ns=4;i=103", SubCallback);
    myClient.AddSubscription("MinusUnClampThreshChuck2", "ns=4;i=104", SubCallback);
    myClient.AddSubscription("ChangeTexttoEOSLPS1", "ns=4;i=123", SubCallback);
    myClient.AddSubscription("ChangeTexttoEOSLPS2", "ns=4;i=124", SubCallback);
    myClient.AddSubscription("HMIClampLightChuck1", "ns=4;i=127", SubCallback);
    myClient.AddSubscription("HMIClampLightChuck2", "ns=4;i=128", SubCallback);
    myClient.AddSubscription("HMIOTLightChuck1", "ns=4;i=131", SubCallback);
    myClient.AddSubscription("HMIOTLightChuck2", "ns=4;i=132", SubCallback);
    myClient.AddSubscription("HMIUnClampLightChuck1", "ns=4;i=133", SubCallback);
    myClient.AddSubscription("HMIUnClampLightChuck2", "ns=4;i=134", SubCallback);
    myClient.AddSubscription("LowClmpHigherthanHighChuck1", "ns=4;i=137", SubCallback);
    myClient.AddSubscription("LowClmpHigherthanHighChuck2", "ns=4;i=138", SubCallback);
    myClient.AddSubscription("ShowCANNOTTEACHmsg", "ns=4;i=139", SubCallback);
    myClient.AddSubscription("ShowTeachUnclamLPS1", "ns=4;i=140", SubCallback);
    myClient.AddSubscription("ShowTeachUnclamLPS2", "ns=4;i=141", SubCallback);
    myClient.AddSubscription("TeachIDODSameTimeLPS1", "ns=4;i=142", SubCallback);
    myClient.AddSubscription("TeachIDODSameTimeLPS2", "ns=4;i=143", SubCallback);
    myClient.AddSubscription("Calculated1VOLTS", "ns=4;i=154", SubCallback);
    myClient.AddSubscription("Calculated2VOLTS", "ns=4;i=155", SubCallback);
    myClient.AddSubscription("RetentiveMemoryMaintUnclmpClampChuck1", "ns=4;i=191", SubCallback);
    myClient.AddSubscription("RetentiveMemoryMaintUnclmpOTChuck1", "ns=4;i=192", SubCallback);
    myClient.AddSubscription("RetentiveMemoryMaintUnclmpClampChuck2", "ns=4;i=193", SubCallback);
    myClient.AddSubscription("RetentiveMemoryMaintUnclmpOTChuck2", "ns=4;i=194", SubCallback);
    myClient.AddSubscription("HMIODCLMPNOMINPUTLPS1", "ns=4;i=195", SubCallback);
    myClient.AddSubscription("HMIODCLMPNOMINPUTLPS2", "ns=4;i=196", SubCallback);
    myClient.AddSubscription("HMIODUNCLMPNOMINPUTLPS1", "ns=4;i=197", SubCallback);
    myClient.AddSubscription("HMIODUNCLMPNOMINPUTLPS2", "ns=4;i=198", SubCallback);
    myClient.AddSubscription("HMIIDCLMPNOMINPUTLPS1", "ns=4;i=199", SubCallback);
    myClient.AddSubscription("HMIIDCLMPNOMINPUTLPS2", "ns=4;i=200", SubCallback);
    myClient.AddSubscription("HMIIDUNCLMPNOMINPUTLPS1", "ns=4;i=201", SubCallback);
    myClient.AddSubscription("HMIIDUNCLMPNOMINPUTLPS2", "ns=4;i=202", SubCallback);
    myClient.AddSubscription("IDODChuck1", "ns=4;i=203", SubCallback);
    myClient.AddSubscription("IDODChuck2", "ns=4;i=204", SubCallback);
}
catch (Exception) { }

解决方案:合并订阅为单个订阅下的多个监控项

1. 定义标签信息类(可选,方便管理)

先创建一个类存储标签名称和节点ID,便于批量管理:

public class TagInfo
{
    public string Name { get; set; }
    public string NodeId { get; set; }
}

2. 批量创建监控项并添加到单个订阅

核心思路是创建单个Subscription,然后向其中添加多个MonitoredItem,示例代码如下:

try
{
    // 1. 整理所有需要订阅的标签列表
    var tags = new List<TagInfo>
    {
        new TagInfo { Name = "IDODChuck1", NodeId = "ns=4;i=203" },
        new TagInfo { Name = "IDODChuck2", NodeId = "ns=4;i=204" },
        new TagInfo { Name = "HMI_High_Thresh_Chuck1", NodeId = "ns=4;i=9" },
        new TagInfo { Name = "Calculated1VOLTS", NodeId = "ns=4;i=148" },
        new TagInfo { Name = "Calculated2VOLTS", NodeId = "ns=4;i=150" },
        // 补充剩余所有标签...
        new TagInfo { Name = "HMIIDUNCLMPNOMINPUTLPS2", NodeId = "ns=4;i=202" }
    };

    // 2. 创建单个订阅
    var subscription = new Subscription(myClient.Session)
    {
        PublishingInterval = 500, // 发布间隔,根据需求调整
        Enabled = true
    };

    // 3. 为每个标签创建监控项并加入订阅
    foreach (var tag in tags)
    {
        var monitoredItem = new MonitoredItem(subscription.DefaultItem)
        {
            DisplayName = tag.Name,
            StartNodeId = new NodeId(tag.NodeId)
        };
        // 绑定回调,传递标签名称以便区分数据
        monitoredItem.Notification += (sender, e) => SubCallback(tag.Name, e);
        subscription.AddItem(monitoredItem);
    }

    // 4. 激活订阅
    myClient.Session.AddSubscription(subscription);
    subscription.Create();
}
catch (Exception ex)
{
    // 建议打印错误信息方便调试
    Console.WriteLine($"订阅创建失败:{ex.Message}");
}

3. 调整回调函数(适配多标签)

修改原回调函数,支持区分不同标签的数据更新:

private void SubCallback(string tagName, NotificationEventArgs e)
{
    foreach (var value in e.NotificationValue)
    {
        if (value is DataValue dataValue)
        {
            Console.WriteLine($"标签{tagName}更新:{dataValue.Value}");
            // 根据标签名称处理对应的数据更新逻辑
        }
    }
}

关键说明

  • 单个订阅+多监控项:OPC UA规范中,一个订阅可包含多个监控项,这是批量订阅的正确方式,能规避PLC的订阅数量限制。
  • S7-1200限制:西门子S7-1200的OPC UA服务器默认对客户端订阅数量限制较严(通常为5个),必须合并成单个订阅下的多监控项。
  • 清理重复标签:原代码存在重复订阅的标签(如多次订阅IDODChuck1),建议先去重,避免资源浪费。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 21:25:54