BreakArray.g

BreakArray.g — breaks an array point into individual points.

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.

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