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

使用OPCLabs订阅OPC DA多项目时,非良好质量值引发读取异常求助

问题分析与解决方案

核心问题原因

你的代码停止继续收集后续值,本质是计数器逻辑与OPC DA的事件推送机制不匹配:

  1. OPC DA订阅时,服务器仅在初始推送所有项的状态(无论质量好坏),后续仅当项的值、质量或时间戳发生变化时才触发ItemChanged事件。
  2. 原代码中,当首次收集完所有70个项(item_value_read_total ==70但good<70),会重置全局计数器为0,但此时那些已经是Good质量的45个项没有变化,服务器不会再推送它们的事件。后续只能收到剩下25个项的质量更新,item_value_read_total永远到不了70,导致重置和重新收集的逻辑永远无法触发,代码只能等待10秒到期退出。
  3. 用item_value.Contains("Good")判断质量不可靠,依赖字符串解析容易出错,OPCLabs提供了原生的质量判断属性。

修复方案

1. 重构状态跟踪逻辑

放弃全局计数器,改用数组跟踪每个项是否已获取到Good质量的值,不依赖事件触发次数:

// 新增全局/类级变量
private bool[] _itemsHaveGoodQuality;
private int _totalItemCount; // 提前转换为整数,避免重复转换

2. 初始化状态数组

在subscribe_items方法中完成初始化:

public void subscribe_items(string ip)
{
    item_value_array=new string[100];            
    dAItemGroupArguments.Clear();
    all_items_read = false;

    // 初始化状态数组
    _totalItemCount = Convert.ToInt16(item_no);
    _itemsHaveGoodQuality = new bool[_totalItemCount];
    Array.Fill(_itemsHaveGoodQuality, false);

    for (int u=0; u<100; u++)
    {
        item_value_array[u] = "";                
    }

    for (int i = 0; i < _totalItemCount; i++)
    {
        dAItemGroupArguments.Add(new DAItemGroupArguments(ip, opcServer , item_array[i], 1000, null));
    }          

    using (var client = new EasyDAClient())
    {
        client.ItemChanged += client_Main1_ItemChanged;              
        client.SubscribeMultipleItems(dAItemGroupArguments.ToArray());
        Thread.Sleep(10 * 1000);
        client.UnsubscribeAllItems();                
        Environment.Exit(0);
    }
}

3. 修改事件处理逻辑

直接跟踪单个项的质量状态,实时检查是否所有项都达标:

public void client_Main1_ItemChanged(object sender, EasyDAItemChangedEventArgs e)
{
    try
    {
        if (e.Succeeded)
        {
            // 找到当前项在数组中的索引
            int itemIndex = Array.IndexOf(item_array, e.Arguments.ItemDescriptor.ItemId);
            if (itemIndex == -1) return;

            // 用OPCLabs原生属性判断质量,避免字符串解析错误
            bool isGood = e.Vtq.Quality.IsGood;
            if (isGood)
            {
                item_value_array[itemIndex] = e.Vtq.Value.ToString();
                // 仅当状态从非Good变为Good时更新标记
                if (!_itemsHaveGoodQuality[itemIndex])
                {
                    _itemsHaveGoodQuality[itemIndex] = true;
                }
            }
            else
            {
                // 如果质量变差,重置对应标记
                if (_itemsHaveGoodQuality[itemIndex])
                {
                    _itemsHaveGoodQuality[itemIndex] = false;
                }
            }

            // 检查是否所有项都已获取到Good质量
            bool allGood = _itemsHaveGoodQuality.All(b => b);
            if (allGood)
            {
                // 所有标签质量达标,执行后续处理逻辑...
                
                // 可选:达标后立即取消订阅并退出,无需等待10秒
                var client = sender as EasyDAClient;
                client?.UnsubscribeAllItems();
                Environment.Exit(0);
            }

            // 日志优化:记录当前达标进度
            using (StreamWriter sw = File.AppendText(@"D:\test\opcda_testlog.txt"))
            {
                int currentGoodCount = _itemsHaveGoodQuality.Count(b => b);
                sw.WriteLine($"当前达标项数: {currentGoodCount}/{_totalItemCount} 时间: {DateTime.Now}");
            }
        }
        else
        {
            // 错误处理:记录失败的项和错误信息
            using (StreamWriter sw = File.AppendText(@"D:\test\opcda_testlog.txt"))
            {
                sw.WriteLine($"项 {e.Arguments.ItemDescriptor.ItemId} 订阅失败: {e.ErrorMessage} 时间: {DateTime.Now}");
            }
        }
    }
    catch(Exception ex)
    {
        using (StreamWriter sw = File.AppendText(@"D:\test\opcda_testlog.txt"))
        {
            sw.WriteLine($"事件处理异常: {ex.Message} 时间: {DateTime.Now}");
        }
    }
}

4. 其他优化点

  • 避免重复转换item_no为整数,提前存储为_totalItemCount,减少性能损耗和转换错误。
  • 不要在每次事件触发时都打开/关闭日志文件,可改为批量写入或使用日志库优化IO性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 13:39:55