titanium-as / TitaniumAS.Opc.Client

Open source .NET client library for OPC DA
MIT License
193 stars 94 forks source link

OPC Server gives previous timestamp sometimes #16

Closed pranny closed 6 years ago

pranny commented 6 years ago

I am using this library to build an app. I am using Matrikon OPC Simulator for DA protocol. I noticed that sometimes the values read from OPC Server are from past. It would be like a series of near-real-time values and then there would be a series of values from about 7-8 minutes back. Then again the near-real-time values and so on. This looks pretty weird to me and I am not sure what/where is the issue lying.

The app is a windows service that uses Timer to wake up every 1 second and get the data from OPC Server. It uses locks and timer disabling to make sure that only one thread be running at a time. The data read is interpreted in a custom Class which is then written as JSON files to the hdd.

public partial class OPCDAPollingService : ServiceBase
{
    private System.Timers.Timer timer;
    protected override void OnStart(string[] args)
    {
        var groupState = new OpcDaGroupState
        {
            Culture = CultureInfo.CurrentCulture, //set LCID for group - some OPC servers may be sensitive for this
            IsActive = true, //only active group can be subscribed
            PercentDeadband = 0.0f, //percent deadband
            TimeBias = TimeSpan.Zero,
            UpdateRate = TimeSpan.FromMilliseconds(UpdateRateInMilliseconds),
            UserData = "some userdata" //user data
        };
        Uri url = UrlBuilder.Build(opcServerName);
        server = new OpcDaServer(url);
        server.Connect();
        opcGroup = server.AddGroup(GroupName, groupState);  // add group on OPC Server
        OpcDaItemResult[] results = opcGroup.AddItems(itemDefinitions); // add items on OPC server
        foreach (OpcDaItemResult result in results) //print errors
        {
            if (result.Error.Failed)
            {
                eventLog.WriteEntry("[Deming Capture Service] Error on create item");
            }
            else
            {
                eventLog.WriteEntry(result.Item.ItemId + "...." + result.Item.CanonicalDataType);
            }
        }
        this.timer = new System.Timers.Timer(UpdateRateInMilliseconds);
        this.timer.AutoReset = false;
        this.timer.Elapsed += new System.Timers.ElapsedEventHandler(this.ReadOPCandWrite);
        this.timer.Start();
    }
    protected override void OnStop()
    {
        server.Disconnect();
    }
    private void ReadOPCandWrite(object sender, System.Timers.ElapsedEventArgs e){
        timer.Stop();
        lock (_lock)
        {
            ReadOPCTagValues();

        }
        timer.Start();
    }
    private void ReadOPCTagValues()
    {
        eventLog.WriteEntry(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString() + 
            " @ " + "Beginning OPC Server Query");
        TelemetryDataCaptured tdc = new TelemetryDataCaptured();
        Task<OpcDaItemValue[]> task = opcGroup.ReadAsync(opcGroup.Items);
        task.Wait();
        OpcDaItemValue[] itemValues = task.Result;
        foreach (OpcDaItemValue value in itemValues)
        {
          eventLog.WriteEntry(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString() + " @ " +
                value.Timestamp.ToString()
                );
          TagData tagdata = new TagData(value.Item.ItemId, value.Value.ToString(), value.Timestamp);
          tdc.capturedData.Add(tagdata);
        }

        tdc.WritePortableData(cacheDir);
    }
}

The log generated looks something like below. This is generated from eventLog so it's in reverse chronological order with the latest event on top. Notice that at 8:53:52 the results were correct, but the next instant it changes.

Information 9/5/2017 8:53:56 AM OPCDAPollingClient  0   None    4 @ 9/5/2017 8:46:46 AM +00:00
Information 9/5/2017 8:53:56 AM OPCDAPollingClient  0   None    4 @ 9/5/2017 8:46:46 AM +00:00
Information 9/5/2017 8:53:55 AM OPCDAPollingClient  0   None    4 @ Beginning OPC Server Query
Information 9/5/2017 8:53:55 AM OPCDAPollingClient  0   None    8 @ 9/5/2017 8:46:45 AM +00:00
Information 9/5/2017 8:53:55 AM OPCDAPollingClient  0   None    8 @ 9/5/2017 8:46:45 AM +00:00
Information 9/5/2017 8:53:55 AM OPCDAPollingClient  0   None    8 @ Beginning OPC Server Query
Information 9/5/2017 8:53:54 AM OPCDAPollingClient  0   None    4 @ 9/5/2017 8:46:44 AM +00:00
Information 9/5/2017 8:53:54 AM OPCDAPollingClient  0   None    4 @ 9/5/2017 8:46:44 AM +00:00
Information 9/5/2017 8:53:54 AM OPCDAPollingClient  0   None    4 @ Beginning OPC Server Query
Information 9/5/2017 8:53:53 AM OPCDAPollingClient  0   None    4 @ 9/5/2017 8:46:43 AM +00:00
Information 9/5/2017 8:53:53 AM OPCDAPollingClient  0   None    4 @ 9/5/2017 8:46:43 AM +00:00
Information 9/5/2017 8:53:53 AM OPCDAPollingClient  0   None    4 @ Beginning OPC Server Query
Information 9/5/2017 8:53:52 AM OPCDAPollingClient  0   None    8 @ 9/5/2017 8:53:52 AM +00:00
Information 9/5/2017 8:53:52 AM OPCDAPollingClient  0   None    8 @ 9/5/2017 8:53:52 AM +00:00
Information 9/5/2017 8:53:52 AM OPCDAPollingClient  0   None    8 @ Beginning OPC Server Query
Information 9/5/2017 8:53:51 AM OPCDAPollingClient  0   None    5 @ 9/5/2017 8:53:51 AM +00:00
Information 9/5/2017 8:53:51 AM OPCDAPollingClient  0   None    5 @ 9/5/2017 8:53:51 AM +00:00
Information 9/5/2017 8:53:51 AM OPCDAPollingClient  0   None    5 @ Beginning OPC Server Query
Information 9/5/2017 8:53:50 AM OPCDAPollingClient  0   None    8 @ 9/5/2017 8:53:50 AM +00:00
Information 9/5/2017 8:53:50 AM OPCDAPollingClient  0   None    8 @ 9/5/2017 8:53:50 AM +00:00
Information 9/5/2017 8:53:50 AM OPCDAPollingClient  0   None    8 @ Beginning OPC Server Query
Information 9/5/2017 8:53:49 AM OPCDAPollingClient  0   None    4 @ 9/5/2017 8:53:49 AM +00:00
Information 9/5/2017 8:53:49 AM OPCDAPollingClient  0   None    4 @ 9/5/2017 8:53:49 AM +00:00
Information 9/5/2017 8:53:49 AM OPCDAPollingClient  0   None    4 @ Beginning OPC Server Query
Information 9/5/2017 8:53:49 AM OPCDAPollingClient  0   None    5 @ 9/5/2017 8:53:48 AM +00:00
Information 9/5/2017 8:53:49 AM OPCDAPollingClient  0   None    5 @ 9/5/2017 8:53:48 AM +00:00
Information 9/5/2017 8:53:48 AM OPCDAPollingClient  0   None    5 @ Beginning OPC Server Query
Information 9/5/2017 8:53:48 AM OPCDAPollingClient  0   None    8 @ 9/5/2017 8:53:48 AM +00:00
Information 9/5/2017 8:53:48 AM OPCDAPollingClient  0   None    8 @ 9/5/2017 8:53:48 AM +00:00
Information 9/5/2017 8:53:48 AM OPCDAPollingClient  0   None    8 @ Beginning OPC Server Query

Any thoughts on this?

alexey-titov commented 6 years ago

Try to refresh group.

group.RefreshAsync(OpcDaDataSource.Device);

pranny commented 6 years ago

@alexy-titov, Thanks for the response. I don't fully get it. Where should i refresh group? Should I do it after server connection or should i do it every second when my timer function wakes up?

alexey-titov commented 6 years ago

You can refresh group on timer. Also there is another way to acquire data via subscriptions. Please check tests Test_RefreshAsync and Test_IsSubscribed for details.

pranny commented 6 years ago

This does not seem to be working. I replaced Task<OpcDaItemValue[]> task = opcGroup.ReadAsync(opcGroup.Items); with var task = opcGroup.RefreshAsync(OpcDaDataSource.Device); in the ReadOPCTagValues function. It still results in the same issue.

pranny commented 6 years ago

I ended up using OPC Foundation's library which does not have this issue.