This tutorial demonstrates how to find a particular row and update it, as well as write new rows, depending on the point.
![]() | |
This tutorial uses the same DSN, database, and table as Tutorial 2. If you haven't done Tutorial 2 yet, please review Getting Started in that section to see how to set up your system for this tutorial. |
![]() | |
Although DataHub Scripting is included with every Cogent DataHub license, ODBC scripting requires that the Cogent DataHub Database feature be licensed as well. |
![]() | |
The code for this and other example scripts can be found in the DataHub distribution archive, typically at this location:
Please refer to Chapter 3, An Explanation of the Tutorial Code for more information about the code. |
ODBCTutorial3.g
/* All user scripts should derive from the base "Application" class */
require ("Application");
/* Get the Gamma library functions and methods for ODBC and/or
* Windows programming. Uncomment either or both. */
//require ("WindowsSupport");
require ("ODBCSupport");
require ("Time");
require ("Quality");
/* Applications share the execution thread and the global name
* space, so we create a class that contains all of the functions
* and variables for the application. This does two things:
* 1) creates a private name space for the application, and
* 2) allows you to re-load the application to create either
* a new unique instance or multiple instances without
* damaging an existing running instance.
*/
/*
* This application assumes that the table specified by the
* "tablename" member variable exists in the DSN specified by the
* "DSN" member variable below.
* The table consists of at least the following columns:
* ID - integer, identity, non-null, counter
* PTVALUE - real, non-null
* PTTIME - datetime, null
* PTNAME - text string, null
* PTQUALITY - text string, null
* Any other columns in this table must be allowed to take on a
* NULL value.
*/
class ODBCTutorial3 Application
{
/* User-defined values, may be changed as needed. */
DSN = "DataHubTest";
user = "test";
password = "test";
tablename = "datatable";
/* These values get defined by the program.*/
conn;
env;
tableclass;
}
/* Connect to the DSN and create a class that maps the table. */
method ODBCTutorial3.Connect ()
{
/* Create the ODBC environment and connection */
.env = ODBC_AllocEnvironment();
.conn = .env.AllocConnection();
/* Attempt the connection. */
ret = .conn.Connect (.DSN, .user, .password);
if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO)
error (.conn.GetDiagRec());
/* Create a class from the table */
.tableclass = .conn.ClassFromTable (#DataEntry, nil, .tablename);
/* Set the primary key. This is redundant for MS-SQL and MYSQL
since they can figure it out themselves, but Access requires
it. */
mykey = .conn.SetPrimaryKey (.tableclass, "ID");
}
/* Fill a database record with new information from a point change */
method ODBCTutorial3.FillRecord (record, sym, newvalue)
{
local timestamp;
timestamp = localtime (PointGetUnixTime(sym));
timestamp = format ("%d-%02d-%02d %02d:%02d:%02d",
timestamp.year + 1900, timestamp.mon + 1,
timestamp.mday, timestamp.hour, timestamp.min,
timestamp.sec);
record.PTNAME = string (sym);
record.PTVALUE = number (newvalue);
record.PTTIME = timestamp;
record.PTQUALITY = GetQualityName(PointMetadata(sym).quality);
record;
}
/* Write a new record into the database based on a point change. */
method ODBCTutorial3.AddRecord (sym, newvalue)
{
local record = new DataEntry();
.FillRecord (record, sym, newvalue);
.conn.Insert (record);
record;
}
/* Write a data point into a field of a record. This is called
from a DataHub point change event. This method will replace
an existing record that is cached with the point at startup. If
there was no existing row in the database, this will create one
and then update it in subsequent calls. */
method ODBCTutorial3.UpdateRecord (sym, newvalue)
{
local record = getprop (sym, #dbrecord);
if (!record)
{
record = .AddRecord (sym, newvalue);
setprop (sym, #dbrecord, record);
}
else
{
.FillRecord (record, sym, newvalue);
.conn.Update (record);
}
}
/* Find an existing record in the database for this point. If it
exists, associate the record with the point. */
method ODBCTutorial3.GetExistingRecord (sym, klass)
{
local result = .conn.QueryToClass (klass, string
("SELECT * FROM ",
klass.__table,
" WHERE PTNAME = '",
sym, "'"));
if (array_p(result))
setprop (sym, #dbrecord, result[0]);
}
/* Start updating the database whenever a point changes. If the
overwrite argument is non-nil or absent, then this method will
cause an existing record in the database to be overwritten each
time. If overwrite is nil, every point change will create a
new row in the database. */
method ODBCTutorial3.WatchPoint (sym, tableclass, overwrite?=t)
{
/* Grab an existing record for this point if it exists */
.GetExistingRecord (sym, tableclass);
if (overwrite)
.OnChange (sym, `(@self).UpdateRecord (this, value));
else
.OnChange (sym, `(@self).AddRecord (this, value));
}
/* The mainline. Connect to the database and begin storing data from
the DataHub program into the database. */
method ODBCTutorial3.constructor ()
{
local ret;
/* Connect to the DSN. */
.Connect();
/* Register points that we want to save. The WatchPoint method
takes an optional third arguement. If it is nil, every point
change will add a row to the table. If it is absent or
non-nil, then every point change overwrites the existing row
in the table for that point. */
.WatchPoint (#$DataSim:Square, .tableclass, nil);
.WatchPoint (#$DataSim:Sine, .tableclass);
}
/* Any code to be run when the program gets shut down. */
method ODBCTutorial3.destructor ()
{
}
/* Start the program by instantiating the class. If your
* constructor code does not create a persistent reference to
* the instance (self), then it will be destroyed by the
* garbage collector soon after creation. If you do not want
* this to happen, assign the instance to a global variable, or
* create a static data member in your class to which you assign
* 'self' during the construction process. ApplicationSingleton()
* does this for you automatically. */
ApplicationSingleton (ODBCTutorial3);
There are several ways you can modify the code. These modifications are made in
the ODBCTutorial2.constructor method, towards the end of the
script.
Overwrite or add rows. There are two ways the ODBC database can receive the data: by
overwriting old values with new values in a single row, or by adding a
new row for each new value. These are determined by the last argument in
the .WatchPoint function.:
.WatchPoint (#$DataSim:Square, tableclass, nil);
.WatchPoint (#$DataSim:Sine, tableclass);The default is to overwrite values. This is what happens for values
pertaining to DataSim:Sine. To have the DataHub instance
write a new line for each change, you can add a final argument,
nil, such as in DataSim:Square
above.
Add more DataSim points. To add other points from DataSim, use this format for new rows:
.WatchPoint (#$DataSim:pointname, tableclass, nil);or this format for overwriting rows:
.WatchPoint (#$DataSim:pointname, tableclass);Where pointname is the name of a point in
DataSim.
Add your own points You can add your own points using this syntax:
.WatchPoint (#$domain:pointname, tableclass, nil); .WatchPoint (#$domain:pointname, tableclass);
where domain is the domain that the point is
in, and pointname is the name of the
point.