1

我正在尝试在 .NET Core 3.1 中使用工作服务模板创建 OPCDA C# 客户端。

我的目标是在 OPC DA 触发 DataChange 事件时读取 PLC 标记值。我创建的示例代码在控制台应用程序(.NET Core 3.1)中正确完成了这项工作,但是当我在 Worker Service App(.NET Core 3.1)中使用相同的代码时,我发现它正确连接到 OPC DA 服务器但 DataChange 事件不调用该方法。我尝试添加此代码

public override Task StartAsync(CancellationToken cancellationToken) {}

protected override async Task ExecuteAsync(CancellationToken stoppingToken) {}

但它不会触发事件并且没有错误。

这是我的示例代码:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Opc.Da;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace SampleWorkerService
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;
        private readonly IConfiguration _config;

        public Opc.URL url;
        private Opc.Da.Server server;
        private OpcCom.Factory fact = new OpcCom.Factory();
        private Opc.Da.Subscription groupRead;
        private Opc.Da.SubscriptionState groupState;
        private List<Item> itemsList = new List<Item>();
        string opcTopicName = "TopicName";

        public Worker(ILogger<Worker> logger, IConfiguration configuration)
        {
            _logger = logger;
            _config = configuration;
        }

        public override Task StartAsync(CancellationToken cancellationToken)
        {
                url = new Opc.URL("opcda://localhost/OPC Server");
                server = new Opc.Da.Server(fact, null);
                server.Connect(url, new Opc.ConnectData(new System.Net.NetworkCredential())); 
                //On Debug Server.IsConnected = true meaning server is connected properly
                groupState = new Opc.Da.SubscriptionState();
                groupState.Name = "Group";
                groupState.UpdateRate = 1000;
                groupState.Active = true;
                groupRead = (Opc.Da.Subscription)server.CreateSubscription(groupState);
                groupRead.DataChanged += null;

                Item item = new Item();

                for (int i = 0; i < listTagNamesFrmconfigFile.Count; i++)
                {
                    item = new Item();
                    item.ItemName = $"{opcTopicName}{listTagNamesFrmconfigFile[i].Name}";
                    itemsList.Add(item);
                }

                groupRead.AddItems(itemsList.ToArray()); // On Debug, Item list is added properly

          //groupRead.DataChanged += 
                       new Opc.Da.DataChangedEventHandler(GroupRead_DataChanged); //tried this
          /await Task.Run(() => groupRead.DataChanged += GroupRead_DataChanged); //tried this
          groupRead.DataChanged += GroupRead_DataChanged; 
// This line does not trigger method GroupRead_DataChanged which reads Item Values, THIS IS THE ISSUE (Same Code Works good in Console App)

            return base.StartAsync(cancellationToken);
        }

protected void GroupRead_DataChanged(object subscriptionHandle, object requestHandle, ItemValueResult[] values)
        {
            foreach (ItemValueResult itemValue in values)
            {
                objPLCTagInfo readPLCTagInfo = new objPLCTagInfo();
                readPLCTagInfo.Value = itemValue.Value;
            }
        }

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
             // I tried below commented code, the opcMethods.OPCRead has same code above but does 
                not trigger "groupRead.DataChanged"
            
             //OPCMethods opcMethods = new OPCMethods();

            //while (!stoppingToken.IsCancellationRequested)
            //{
            //    //_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);

            //    opcMethods.OPCRead(_config);

            //    await Task.Delay(1000, stoppingToken).ConfigureAwait(false);
            //}

        // Following code also doesnot trigger "groupRead.DataChanged"
          
            groupRead.DataChanged += (object subscriptionHandle, object requestHandle, 
                                           ItemValueResult[] values) =>
            {
                Console.WriteLine("Event Triggered");
            };

            // await Task.CompletedTask;

            await Task.Delay(1000, stoppingToken).ConfigureAwait(false);
        }

public override Task StopAsync(CancellationToken cancellationToken)
        {
            return StopAsync(cancellationToken);
        }

    }
}

上面的代码适用于控制台应用程序。事件 groupRead.DataChange 不会触发“GroupRead_DataChanged”方法读取数据,没有错误/异常发生:

此代码行和以下方法读取项目值,我试图使其工作

groupRead.DataChanged += GroupRead_DataChanged;

protected void GroupRead_DataChanged(object subscriptionHandle, object requestHandle, ItemValueResult[] values)
    {
        //logic here to read item values
        foreach (ItemValueResult itemValue in values)
            { ... }
    }

我希望我已经解释了我要解决的问题。

感谢您提前提供的时间和解决方案!

4

1 回答 1

0

你需要改变你的方法才能让它发挥作用。您使用后台服务的方式意味着它直接处理并在它有机会使用 EventHandler 之前被处置。

解决这个问题的最简单方法是创建一个“循环”后台服务,以便它保持运行并允许 EventHandler 工作。有关如何实施此链接的示例,请查看此链接:https ://medium.com/@daniel.sagita/backgroundservice-for-a-long-running-work-3debe8f8d25b

TLDR:

public class WorkerHostedService : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stopToken)
    {
      //Do your preparation (e.g. Start code) here
      while (!stopToken.IsCancellationRequested)
      {
        ## YOUR-CODE-HERE ##
      }
      //Do your cleanup (e.g. Stop code) here
    }
}
于 2021-11-08T01:04:39.687 回答