HistTest.g

HistTest.g — an example script that demonstrates how to access Historian data.

Code

[Note]

The code for this and other example scripts can be found in the DataHub distribution archive, typically at this location:

C:\Program Files\Cogent\Cogent DataHub\scripts\

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);