使用OPCLabs订阅OPC DA多项目时,非良好质量值引发读取异常求助
问题分析与解决方案
核心问题原因
你的代码停止继续收集后续值,本质是计数器逻辑与OPC DA的事件推送机制不匹配:
- OPC DA订阅时,服务器仅在初始推送所有项的状态(无论质量好坏),后续仅当项的值、质量或时间戳发生变化时才触发
ItemChanged事件。 - 原代码中,当首次收集完所有70个项(
item_value_read_total ==70但good<70),会重置全局计数器为0,但此时那些已经是Good质量的45个项没有变化,服务器不会再推送它们的事件。后续只能收到剩下25个项的质量更新,item_value_read_total永远到不了70,导致重置和重新收集的逻辑永远无法触发,代码只能等待10秒到期退出。 - 用
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
相关产品推荐
相关产品推荐

