HistTest.gHistTest.g — an example script that demonstrates how to access
Historian data.
![]() | |
The code for this and other example scripts can be found in the DataHub distribution archive, typically at this location:
Please refer to Section 3.1, “How to Run a Script” for more information on using scripts. |
/*
* This script demonstrates how to access the DataHub Historian data
* both in raw and processed forms. Generally, a script simply needs
* to create an instance of the Historian class and then make calls
* to the methods of that object. See the documentation for the
* complete list of methods provided.
*
* When you query data from the historian the result is a
* HistoryBuffer instance. This encapsulates the result data in an
* efficient internal representation that is not visible directly to
* the script. In order to use the data you must retrieve the data
* from the HistoryBuffer to produce one or more HistoryValue
* instances. Each HistoryValue contains a value, a timestamp and a
* quality. The timestamp is called "xaxis" because in some cases a
* query could return an y-vs-x result instead of a y-vs-time result.
* The Historian object does not expose a method for a y-vs-x query,
* but the underlying implementation will support it.
*
* A HistoryBuffer holds its data until there are no more references
* to the HistoryBuffer object. This means that a HistoryBuffer
* object that is declared as a local variable in a function will
* only be valid until the function exits (unless the function
* returns the HistoryBuffer object). If you need to hold one or
* more HistoryValue beyond the lifetime of the HistoryBuffer, use
* copyToArray or copyValue to make a copy of the data.
*
* If a history query produces a large amount of data it will be more
* memory efficient to avoid making a copy of the data. You can
* access individual HistoryValue instances within the HistoryBuffer
* using the getValue method. Generally this will take more
* processing time, but can substantially reduce memory use.
*
* Bear in mind that all scripts run in a single thread. If you
* spend a long time processing large historical requests, that will
* disrupt the timing of other scripts.
*
* Statistical queries take substantially longer than raw data
* queries.
*/
require ("Application");
/* Load the functions for Historian access */
require ("HistorianSupport");
/* 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.
*/
class HistTest Application
{
historian;
}
method HistTest.rawQuery(pointname, starttime, duration)
{
local histbuffer, data, datacopy;
local t1, t2, t3, t4;
t1 = nanoclock();
histbuffer = .historian.queryRawData(pointname, starttime,
duration);
t2 = nanoclock();
data = histbuffer.toArray();
t3 = nanoclock();
datacopy = histbuffer.copyToArray();
t4 = nanoclock();
pretty_princ("Raw query produced ",
histbuffer.npoints, " data values\n");
pretty_princ(" Query took ", t2 - t1, " seconds\n");
pretty_princ(" Data access took ", t3 - t2, " seconds\n");
pretty_princ(" Data copy took ", t4 - t3, " seconds\n");
// If we want to use the values outside this method, we need to
// use a copy, not the raw data. The raw data will be cleaned up
// when the histbuffer object is no longer referenced. We could
// also return the histbuffer object from this method, from which
// we could later query the data using toArray().
datacopy;
}
method HistTest.statisticalQuery(pointname, starttime, duration,
aggregation_period_secs, statistic)
{
local histbuffer, data, datacopy;
local t1, t2, t3, t4, t5, t6, t7;
local i, numbad, numbad2;
t1 = nanoclock();
histbuffer = .historian.queryStatistic(pointname,
starttime,
duration,
aggregation_period_secs,
statistic);
t2 = nanoclock();
data = histbuffer.toArray();
t3 = nanoclock();
datacopy = histbuffer.copyToArray();
t4 = nanoclock();
pretty_princ("Statistical query, ", statistic, ", produced ",
histbuffer.npoints, " data values\n");
pretty_princ(" Query took ", t2 - t1, " seconds\n");
pretty_princ(" Data access took ", t3 - t2, " seconds\n");
pretty_princ(" Data copy took ", t4 - t3, " seconds\n");
// There are a few ways to process the data. We can convert the
// data to an array, as we do with data and datacopy. We can
// also walk the data buffer without converting it to an array.
// The next two examples both do the same thing, walking the data
// looking for BAD quality.
numbad = numbad2 = 0;
t5 = nanoclock();
// Loop through the data set, having previously converted the
// buffer data to an array
with value in data do
{
if (value.quality == OPC_QUALITY_BAD)
numbad++;
}
t6 = nanoclock();
// Loop through the data set without converting to an array.
// If the query produces a large amount of data, this will
// certainly be more memory-efficient since a large array does
// not have to be created, but the processing cost of querying
// each value in the data set will be higher.
for (i=histbuffer.npoints-1; i>=0; i--)
{
if (histbuffer.getValue(i).quality == OPC_QUALITY_BAD)
numbad2++;
}
t7 = nanoclock();
pretty_princ(" Loop through array took ",
t6 - t5, " seconds\n");
pretty_princ(" Loop through buffer took ",
t7 - t6, " seconds\n");
pretty_princ(" Found ",
numbad, " (", numbad2, ") Bad quality values\n");
// Print the last value in the data array, just to show
// some results.
if(array_p(data) && length(data) > 0)
{
local result = data[length(data) -1];
pretty_princ(" Query results for most recent value: ",
date_of(result.xaxis), " : ", result.value, "\n\n");
}
}
/* Write the 'main line' of the program here. */
method HistTest.constructor ()
{
.historian = new Historian();
princ ("---------------------------------------\n");
.rawQuery("DataPid:PID1.Mv", nanoclock() - 10, 10);
.rawQuery("DataPid:PID1.Mv", nanoclock() - 100, 100);
.rawQuery("DataPid:PID1.Mv", nanoclock() - 1000, 1000);
.rawQuery("DataPid:PID1.Mv", nanoclock() - 10000, 10000);
princ ("------\n");
.statisticalQuery("DataPid:PID1.Mv",
nanoclock() - 10, 10, 1, "Average");
.statisticalQuery("DataPid:PID1.Mv",
nanoclock() - 100, 100, 1, "StDev");
.statisticalQuery("DataPid:PID1.Mv",
nanoclock() - 1000, 1000, 1, "Minimum");
.statisticalQuery("DataPid:PID1.Mv",
nanoclock() - 10000, 10000, 1, "RegRSquared");
}
/* Any code to be run when the program gets shut down. */
method HistTest.destructor ()
{
}
/* Start the program by instantiating the class. */
ApplicationSingleton (HistTest);