BreakArray.g — breaks an array point into individual points.
![]() | |
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. |
/*
* Break an array point into individual points. For example, if we
* have a point
* default:myarray = [ 1, 2, 3 ]
* then we would like to create an output like
* default:myarray_0 = 1
* default:myarray_1 = 2
* default:myarray_2 = 3
*
* To set up an array to be broken into individual points,
* call .MonitorArray(). This method takes a point name (of the
* array), a format string and an index offset. The format string
* specifies a suffix to add to the base point name, and the index
* offset determines where to start numbering the suffix.
* Typically index offset is 0 or 1.
*
* For example, to create points like:
* default:myarray_0
* use .MonitorArray("default:myarray", "_%d", 0)
* default:myarray_001
* use .MonitorArray("default:myarray", "_%03d", 1)
* default:myarray[1]
* use .MonitorArray("default:myarray", "[%d]", 1)
*
* This script will automatically respond to changes in the size of
* the array by creating new points as the array expands, or by
* marking existing points as not connected as the array contracts.
*
* This script will create the point hierarchy of the newly generated
* points by splitting the point names on the "splitChars" and using
* each component in the split name as a branch in the hierarchy.
* If you do not want to create the hierarchy, set splitChars to nil.
*/
require ("Application");
class BreakArray Application
{
splitChars = ".";
modeledPoints = new Dictionary();
}
/* Write the 'main line' of the program here. You should only need
* to modify the constructor to match your data points. */
method BreakArray.constructor ()
{
// Delete the calls to .setupTest.
// They are just here to test this script.
.setupTest ("default:test.myarray", [ 1, 2, 3, 4, 5 ]);
.setupTest ("default:test.myarray2", [ 1, 2, 3, 4, 5 ]);
// Add, remove or modify .MonitorArray calls to work with any
// number of array points.
.MonitorArray("default:test.myarray", "rw", "_%03d", 1);
.MonitorArray("default:test.myarray2", "rw", "[%d]", 0);
}
/* ---------------------------------------------------------------- */
// You should not need to modify below this point.
/*
* Create data points for an array using an index and a format string.
* For example, to create points:
* default:myarray_0
* use suffixformat="_%d", indexoffset=0
* default:myarray[1]
* use suffixformat="[%d]", indexoffset=1
* default:myarray_001
* use suffixformat="_%03d", indexoffset=1
*/
method BreakArray.MonitorArray(pointname, accessFlags,
suffixformat, indexoffset)
{
local value;
.OnChange(symbol(pointname), `(@self).Break(this, value,
@accessFlags, @suffixformat, @indexoffset, t));
// If we have a current value,
// break the array for the first time now.
if (!undefined_p(value = eval(symbol(pointname))))
{
.Break(symbol(pointname), value, accessFlags, suffixformat,
indexoffset, nil);
}
}
method BreakArray.Break(pointname, value, accessFlags, suffixformat,
indexoffset, have_previous?=nil)
{
local elementname, elementsym, suffix;
local indx = indexoffset;
local info = PointMetadata(pointname), elementinfo;
local type, curlen, prevlen, i;
local model = new ModelEmitter();
if (array_p(value))
{
// Find the element type
type = info.canonical_type & ~0xfffff000;
// For each value in the array, create a point name for it.
// If the point does not exist, or has an empty canonical
// type, then create the point and set its canonical type
// to the type of the parent array.
with element in value do
{
suffix = format(suffixformat, indx);
elementname = string(pointname, suffix);
elementsym = symbol(elementname);
elementinfo = PointMetadata(elementsym);
if (!elementinfo || elementinfo.canonical_type == 0)
{
// This point has never been created in the DataHub
// Create it and match its canonical type to the
// array type.
datahub_command (format("(create %s 1)",
stringc(elementname)), 1);
datahub_command (format("(set_canonical %s %d 1)",
stringc(elementname), type), 1);
}
if (.splitChars && !.modeledPoints[elementname])
{
.modeledPoints[elementname] = elementname;
model.MapPoint(elementname, .splitChars,
"Empty", accessFlags);
}
datahub_write(elementname, element, nil, info.quality,
info.timestamp);
indx++;
}
}
// Find the previous length. If the array used to be longer then
// we should mark the values that are no longer present as Not
// Connected. If this function is called from within a change
// handler previous is implicitly defined.
if (have_previous)
{
prevlen = (undefined_p(previous) ||
!array_p(previous)) ? 0 : length(previous);
curlen = (array_p(value) ? length(value) : 0);
for (i=curlen; i<prevlen; i++)
{
suffix = format(suffixformat, indx);
elementname = string(pointname, suffix);
elementsym = symbol(elementname);
elementinfo = PointMetadata(elementsym);
if (elementinfo)
{
//princ ("Set array element ", elementname,
" as not connected\n");
datahub_write(elementname, elementinfo.value,
nil, OPC_QUALITY_NOT_CONNECTED,
info.timestamp);
}
indx++;
}
}
model.Emit();
}
/*
* This method is just used to create a test data set.
*/
method BreakArray.setupTest(pointname, value)
{
local model = new ModelEmitter();
model.MapPoint(pointname, ".", "R8 array", "rw");
datahub_command (format("(create %s 1)",
stringc(pointname)), 1);
datahub_command (format("(set_canonical %s \"R8 array\" 1)",
stringc(pointname)), 1);
datahub_write (pointname, value);
model.Emit();
}
/* Any code to be run when the program gets shut down. */
method BreakArray.destructor ()
{
}
/* Start the program by instantiating the class. */
ApplicationSingleton (BreakArray);