There are times when application code needs to be able to process a data set but the programmer cannot determine how large that data set will be at the time of application design.
An example is a function block that maps one set of values to another. This can be useful for several purposes:
- Ballscrew compensation
- Calibration of a sensor, like a thermistor
- Compensating for nonlinearity in test equipment
Function blocks that perform this type of operation may need to use data sets with very different sizes.
Consider a Parker XY cartesian robot with two precision 406XR ballscrew stages. For greater accuracy, the ballscrews (or even the linear encoders if applicable) can be mapped with a laser interferometer on a granite table, providing the customer with a data set that correlates the stage's measurable position to the position indicated by the interferometer (which is much more accurate).
If the stages are different lengths, they will have compensation maps with different numbers of elements. Trying to handle both cases with a fixed-size array could end up being inefficient and clunky.
Parker Automation Manager (PAM) provides an elegant way to handle this. Rather than declare an array input to a function block like this:
alrDataSet : ARRAY [1..5000] OF LREAL;
Consider declaring the input as an input/output array of any undefined size like this:
alrDataSet : ARRAY [*] OF LREAL;
This allows the user to pass in an array of any length as a reference rather than as a standard input. You can do this for arrays of one, two or three dimensions:
alrDataSet : ARRAY [*] OF LREAL; alrDataSet : ARRAY [*,*] OF LREAL; alrDataSet : ARRAY [*,*,*] OF LREAL;
PAM offers the user two operators for finding the size of the array. These are the UPPER_BOUND and LOWER_BOUND operators. These are essential for making sure that the function block does not try to read from or write to an array index that does not exist. The syntax for these operators is given below:
The two arguments are the name of the array and the dimension of the array. The name is simply the name as it was declared in the declaration editor (for instance, alrDataSet like in the example given before). The dimension can be 1, 2 or 3 depending on which dimension of the array you want to measure. The return type for UPPER_BOUND and LOWER_BOUND is always DINT. An example for using the array boundaries is given below.
// Array boundary example. FUNCTION_BLOCK ArrayExample VAR_IN_OUT alrDataSet : ARRAY [*] OF LREAL; END_VAR VAR diIndex : DINT; END_VAR // Modify the values in the data set. FOR diIndex := LOWER_BOUND(alrDataSet,1) TO UPPER_BOUND(alrDataSet,1) DO alrDataSet[diIndex] := diIndex; END_FOR
The example iterates through the first (and only) dimension of alrDataSet and writes the values 1, 2, 3 and so-forth to the array. You could use an array of any reasonable size as an input to this function block (keep in mind processing time and RAM limitations).
Checking for Valid Input
In order to avoid throwing an exception at runtime, it is necessary to make sure that an array is present on the input. There is a simple way to do this. If there is no array present at the input, the array will be equal to zero.
// Verify that the array is valid. IF alrDataSet <> 0 THEN // Array input is OK. ELSE // Array input not present. Put error-handling here. END_IF