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.
- Forum
- Discussions
- QuickOPC-Classic in .NET
- Reading, Writing, Subscriptions, Property Access
- Persisting data using ADO.NET and QuickOPC.NET
Persisting data using ADO.NET and QuickOPC.NET
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.
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.
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.
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.
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.
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.
Thank you very much.
Please Log in or Create an account to join the conversation.
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.
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.
- Forum
- Discussions
- QuickOPC-Classic in .NET
- Reading, Writing, Subscriptions, Property Access
- Persisting data using ADO.NET and QuickOPC.NET