简体   繁体   中英

Insert to Database with ADO.NET Entity Data Model through OPC UA Client in C#

I am trying to write to a database with the ADO.NET Entity Data Model with a sample client from OPCFoundation/UA-.NETStandard . I'm kind of new to C# and my problem is the lamda expression at:

list.ForEach(i => i.Notification += OnNotification);

When ever a value on the server changes the client will receive the actual value (That's called a subscription). The function below shows the function OnNotification .

private static void OnNotification(MonitoredItem item, MonitoredItemNotificationEventArgs e)
{
    foreach (var value in item.DequeueValues())
    {
        Console.WriteLine("{0}: {1}, {2}, {3}", item.DisplayName, value.Value, value.SourceTimestamp, value.StatusCode);
    }
}

The output of the function looks something like this:

Column01: 4545, 11.12.2017 13:34, Good
Column02: 6767, 11.12.2017 13:34, Good
Column01: 4456, 11.12.2017 13:35, Good 
Column02: 5554, 11.12.2017 13:35, Good 
Column02: 6664, 11.12.2017 13:36, Good
Column01: 6233, 11.12.2017 13:37, Good 
Column02: 5123, 11.12.2017 13:37, Good 

The output is generated when a value is changed and continues until you press a key. Notice that sometimes a column doesn't appear because the value stays the same. In that case the value can be added as NULL to database.

To get the data to the database I thought of just modifying the OnNotification function to something like this:

private static void OnNotification(MonitoredItem item, MonitoredItemNotificationEventArgs e)
    {
        Entities context = new Entities();
        dbName objData = new dbName();

        objData.SourceTimestamp = System.DateTime.Now;

        foreach (var value in item.DequeueValues())
        {
            switch (item.DisplayName)
            {
                case "Column01":
                    objData.Column01 = Convert.ToInt16(value.Value);
                    break;

                case "Column02":
                    objData.Columns02 = Convert.ToInt16(value.Value);
                    break;

                default:
                    Console.WriteLine("Unknown Displayname: {0}", item.DisplayName);
                    break;
            }

        }
        context.dbName.Add(objData);
        context.SaveChanges();

    }

Now I can insert the values to Column01 and Column02 but only on different rows. So my Table in the database looks something like this:

SourceTimestamp  | Column01 | Column02
11.12.2017 13:34 | 4545     | NULL
11.12.2017 13:34 | NULL     | 6767
11.12.2017 13:35 | 4456     | NULL
11.12.2017 13:35 | NULL     | 5554
11.12.2017 13:36 | NULL     | 6664
11.12.2017 13:37 | 6233     | NULL
11.12.2017 13:37 | NULL     | 5123

But I would like to have:

SourceTimestamp  | Column01 | Column02
11.12.2017 13:34 | 4545     | 6767
11.12.2017 13:35 | 4456     | 5554
11.12.2017 13:36 | NULL     | 6664
11.12.2017 13:37 | 6233     | 5123

The problem is the loop of the lambda expression which I cannot control and I am not sure how to handle this problem. I just need to get one subscription message declare them to the column variables and add them to database but it seems kind of tricky to me and I can't seem to solve the problem because of my lack of experience in C#. Suggestions or other solutions are welcome. Thanks in advance.

Ii seems that each 'MonitoredItem' has only one value in the queue for each event. that's why you are getting

Column01: 4545, 11.12.2017 13:34, Good
Column02: 6767, 11.12.2017 13:34, Good
Column01: 4456, 11.12.2017 13:35, Good 
Column02: 5554, 11.12.2017 13:35, Good 
Column02: 6664, 11.12.2017 13:36, Good
Column01: 6233, 11.12.2017 13:37, Good 
Column02: 5123, 11.12.2017 13:37, Good

as output. The MonitoredItem appears to have a unique identifier: CientHandle.

So in your event handler you have to test your data context for an existing dbName object using that identifier and update that object if it exists. If it does not exist then create a new dbName object using the identifier to be updated.

For data logging, I just create a loop, read the values, write them to the database, wait a moment, repeat.

Then all the values have the same timestamp.

ps: Some folks might use RegisterNodes first, but that's not necessary!

pps: Here's a bit of code.

```

        Console.WriteLine("5a - Read values at an interval of 1 second, for 20 seconds.");

        var readValueIds = new ReadValueId[]
        {
            new ReadValueId{ NodeId= NodeId.Parse("i=2258"), AttributeId = Attributes.Value }, // column 0
            new ReadValueId{ NodeId= NodeId.Parse("i=2258"), AttributeId = Attributes.Value }, // column 1
        };

        var cts = new CancellationTokenSource(20000);
        while (!cts.IsCancellationRequested)
        {
            var resp = session.Read(null, 0, TimestampsToReturn.Both, readValueIds, out DataValueCollection results, out DiagnosticInfoCollection infos);
            Console.WriteLine($"ts:{results[0].SourceTimestamp}, c0: {results[0].Value}, c1: {results[1].Value}");
            await Task.Delay(1000);
        }

```

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM