Professional OPC
Development Tools

logos

Online Forums

Technical support is provided through Support Forums below. Anybody can view them; you need to Register/Login to our site (see links in upper right corner) in order to Post questions. You do not have to be a licensed user of our product.

Please read Rules for forum posts before reporting your issue or asking a question. OPC Labs team is actively monitoring the forums, and replies as soon as possible. Various technical information can also be found in our Knowledge Base. For your convenience, we have also assembled a Frequently Asked Questions page.

Do not use the Contact page for technical issues.

Persisting data using ADO.NET and QuickOPC.NET

More
19 Sep 2012 19:49 #1032 by support
Your are welcome :-)
One note - the proposed code might be (as side effect) accumulating the added rows (in the Rows property of DataTable) in memory (and not just in the database). This might be an issue if it is meant for long-term logging. If this is an issue, I am sure there will be a solution too - such as clearing the old rows from time to time.

Please Log in or Create an account to join the conversation.

More
19 Sep 2012 19:37 #1031 by jeancg
Great just what was needed, I am very happy this will help me a lot.

I was not following the same logic that you proposed here. I was trying to redefine instantiating class with DataTables and defining my columns DataColumn class using the scheme proposed by Microsoft documentation, but was not working properly.

Thank you so much again.

best regards

Please Log in or Create an account to join the conversation.

More
19 Sep 2012 13:22 #1028 by support
I have made an example that logs data into SQL table columnwise (for 4 items), and merges notificatios with the same timestamp into one a single row. It works with following table definition:

USE QuickOPCExamples;
GO

-- The "ColumnarLog" table stores one item per column.
CREATE TABLE ColumnarLog(
[Timestamp] datetime PRIMARY KEY,
Ramp1Value float NULL,
Ramp1Quality int NULL,
Ramp2Value float NULL,
Ramp2Quality int NULL,
Ramp3Value float NULL,
Ramp3Quality int NULL,
Ramp4Value float NULL,
Ramp4Quality int NULL
);
GO


And here is the actual code:

// LogToSqlEnhanced: Logs OPC Data Access item changes into an SQL database, using a subscription. Item values and qualities
// are stored in their respective columns. Notifications with the same timestamp are merged into a single row.

// The database creation script is in the ExamplesNet\MSSQL\QuickOPCExamples.sql file under the product installation
// directory. The example assumes that the database is already created.

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using OpcLabs.EasyOpc.DataAccess;

namespace LogToSqlEnhanced
{
class Program
{
static void Main()
{
const string connectionString =
"Data Source=(local);Initial Catalog=QuickOPCExamples;Integrated Security=true";

Console.WriteLine("Starting up...");
using (var connection = new SqlConnection(connectionString))
{
connection.Open();

// Create all necessary ADO.NET objects.
var adapter = new SqlDataAdapter("SELECT * FROM ColumnarLog", connection);
var dataSet = new DataSet();
adapter.FillSchema(dataSet, SchemaType.Source, "ColumnarLog");
adapter.InsertCommand = new SqlCommandBuilder(adapter).GetInsertCommand();
adapter.UpdateCommand = new SqlCommandBuilder(adapter).GetUpdateCommand();
DataTable table = dataSet.Tables["ColumnarLog"];

Console.WriteLine("Logging for 30 seconds...");
// Subscribe to an OPC item, using an anonymous method to process the notifications.
int[] handles = EasyDAClient.DefaultInstance.SubscribeMultipleItems(
new[]
{
new DAItemGroupArguments("", "OPCLabs.KitServer.2", "Trends.Ramp (1 s)", 1000, null),
new DAItemGroupArguments("", "OPCLabs.KitServer.2", "Trends.Ramp (10 s)", 1000, null),
new DAItemGroupArguments("", "OPCLabs.KitServer.2", "Trends.Ramp (1 min)", 1000, null),
new DAItemGroupArguments("", "OPCLabs.KitServer.2", "Trends.Ramp (10 min)", 1000, null)
},
(_, eventArgs) =>
{
Console.Write(".");
// In this example, we only log valid data. Production logger would also log errors.
if (eventArgs.Vtq != null)
{
// Fill a DataRow with the OPC data, and add it to a DataTable.
DateTime timestamp =
(eventArgs.Vtq.Timestamp < (DateTime) SqlDateTime.MinValue)
? (DateTime) SqlDateTime.MinValue
: eventArgs.Vtq.Timestamp;

DataRow row = dataSet.Tables["ColumnarLog"].Rows.Find(timestamp);
bool adding = (row == null);
if (adding)
{
row = table.NewRow();
row["Timestamp"] = timestamp;
}

switch (eventArgs.ItemDescriptor.ItemId)
{
case "Trends.Ramp (1 s)":
row["Ramp1Value"] = eventArgs.Vtq.Value ?? DBNull.Value;
row["Ramp1Quality"] = (short)eventArgs.Vtq.Quality;
break;
case "Trends.Ramp (10 s)":
row["Ramp2Value"] = eventArgs.Vtq.Value ?? DBNull.Value;
row["Ramp2Quality"] = (short)eventArgs.Vtq.Quality;
break;
case "Trends.Ramp (1 min)":
row["Ramp3Value"] = eventArgs.Vtq.Value ?? DBNull.Value;
row["Ramp3Quality"] = (short)eventArgs.Vtq.Quality;
break;
case "Trends.Ramp (10 min)":
row["Ramp4Value"] = eventArgs.Vtq.Value ?? DBNull.Value;
row["Ramp4Quality"] = (short)eventArgs.Vtq.Quality;
break;
}

if (adding)
table.Rows.Add(row);

// Update the underlying DataSet using an insert command.
adapter.Update(dataSet, "ColumnarLog");
}
}
);
System.Threading.Thread.Sleep(30 * 1000);
Console.WriteLine();

Console.WriteLine("Shutting down...");
EasyDAClient.DefaultInstance.UnsubscribeMultipleItems(handles);
}

Console.WriteLine("Finished.");
}
}
}


I hope this helps.

Please Log in or Create an account to join the conversation.

More
19 Sep 2012 13:21 #1027 by jeancg
I will be very grateful for your help I know this is not part of the scope of support opclabs, but I've been asking for this help because I'm having trouble understanding the workings of QuickOPC with ADO.NET, the last model in the documentation for you is simple and very effective, but not completely given me this.
I tried to seek help from experienced people in ADO.NET to change the model, however they did not understand the workings of QuickOPC help and ended up with no effective answer.

Thank you again.

Please Log in or Create an account to join the conversation.

More
18 Sep 2012 06:39 #1023 by support
I will try to write some example code that does what has been suggested, although it is out of scope of what we do (the goal of the component is to provide the OPC data, but not to do much further with it, because those are the needs that can differ vastly from app to app). Please wait for next post.
It would probably be possible to write some wrapper classes and then use them with Entity Framework, but that seems like an overkill for this kind of task.

Please Log in or Create an account to join the conversation.

More
17 Sep 2012 19:59 #1022 by jeancg
OPC Labs Technical Support wrote:
Hello,
instead of simply inserting a new row each time a notification is received, you can:

look up if there is an existing row with the same timestamp
if yes, place the newly arrived value into its proper column, leaving other data intact, and update the row
if no, place the newly arrived value into its proper column, set he timestamp, and insert as new row.

Best regards

Trying to change the current scheme persistence of data, I'm a little confused by the use of class EasyDAItemChangedEventArgs if it is necessary to use this event to receive notification of OPC items and use the Add method (row) in all documentations ADO.NET I researched I could not change this logic.

I could not create the logic to check the timestamp and fill the column with your itemID and getting the appropriate value type suitable for the type OPC correct, tried to make these changes but have not found the correct syntax for doing so.

I was thinking of using Entity Framework, but I could not pass opc objects to the objects of my class'm researching about it, it would be possible to use this approach with QuickOPC.NET?

Please Log in or Create an account to join the conversation.

More
04 Sep 2012 18:35 #1009 by jeancg
Thanks for the reply, I'll try to do this way, post here the results.


Thank you very much.

Please Log in or Create an account to join the conversation.

More
04 Sep 2012 10:00 #1008 by support
Hello,
instead of simply inserting a new row each time a notification is received, you can:

look up if there is an existing row with the same timestamp
if yes, place the newly arrived value into its proper column, leaving other data intact, and update the row
if no, place the newly arrived value into its proper column, set he timestamp, and insert as new row.

Best regards

Please Log in or Create an account to join the conversation.

More
03 Sep 2012 16:32 #1007 by jeancg
Hello again.


I would like to help in a problem I'm having with my requirements of data persistence with the QuickOPC.NET.
At my current data persistence scheme I use SubscribeMultipleItems to instantiate my OPC objects is the scheme proposed by the QuickOPC documentation where I get a column with all the SubscribeMultipleItems and ItemIDs method a value field and another timestamp this scheme works correctly, but I'm trying to do some modifications to this current scheme due to requirements of my project.

Need to change the current schema for my ItemIDs are columns in my database table and no more rows. In my tests I managed to fill my columns with ItemIDs but each value received the method skips to next row to receive the next value of the ItemID getting NULL fields in my table.

Result in the table:






MyColummItemID1
MyColummItemID2
MyColummItemID3
MyColummItemID4
Timestamp
Quality


NULL
NULL
62
NULL
2012-09-02 11:19:46.673
192


NULL
NULL
NULL
99,6405
2012-09-02 11:19:46.673
192


1238527
NULL
NULL
NULL
2012-09-02 11:19:46.673
192


NULL
60,05999
NULL
NULL
2012-09-02 11:19:46.673
192




Expected result:





MyColummItemID1
MyColummItemID2
MyColummItemID3
MyColummItemID4
Timestamp
Quality


1238527
60,05999
62
99,6405
2012-09-02 11:19:46.673
192





My method:
int[] handleTable1 = EasyDAClient.DefaultInstance.SubscribeMultipleItems(
new[]
{
new DAItemGroupArguments("", "MyOPCServer1", "MyItemID1", 1000, null),
new DAItemGroupArguments("", "MyOPCServer1", "MyItemID2", 1000, null),
new DAItemGroupArguments("", "MyOPCServer1", "MyItemID3", 1000, null),
new DAItemGroupArguments("", "MyOPCServer1", "MyItemID4", 1000, null)
},
(_, eventArgs) =>
{
txtLog1.AppendText("*");
if (eventArgs.Vtq != null)
{
table1.Rows.Clear();
DataRow row = table1.NewRow();
if (eventArgs.ItemDescriptor.ItemId == "MyItemID1")
row["MyColummItemID1"] = eventArgs.Vtq.Value;
else if (eventArgs.ItemDescriptor.ItemId == "MyItemID2")
row["MyColummItemID2"] = eventArgs.Vtq.Value;
else if (eventArgs.ItemDescriptor.ItemId == "MyItemID3")
row["MyColummItemID3"] = eventArgs.Vtq.Value;
else if (eventArgs.ItemDescriptor.ItemId == "MyItemID4")
row["MyColummItemID4"] = eventArgs.Vtq.Value;
row["Timestamp"] = (eventArgs.Vtq.Timestamp < (DateTime)SqlDateTime.MinValue)
? (DateTime)SqlDateTime.MinValue
: eventArgs.Vtq.Timestamp.ToLocalTime();
row["Quality"] = (short)eventArgs.Vtq.Quality;
table1.Rows.Add(row);
adapterGeracaoM1.Update(dataSetGeracaoM1, "Table_3");
}
}
);
}

Need to change properly this scheme, this current scheme of persisting the data populating a table field with ItemIDs my very limited when I have to work with reports and left my inconsistent requirements.
Need special attention here to help me find a way to solve this.
Thank you very much.

Please Log in or Create an account to join the conversation.

Moderators: support
Time to create page: 0.101 seconds