3.5 Instruction List Programming
3.5.2 Program Organization Units (POUs)
}
3.5.2 Program Organization Units (POUs)
Program Organization Units are special categories of DataTypes which include program logic as well. POUs are blocks of code with a calling interface. The fields of a POU form its calling interface and each field is associated with an interface type (as discussed in the previous section) which decides whether the field is INPUT/OUTPUT or INOUT.
There are three types of POUs: Functions (FN), Function Blocks (FBs) and PROGRAMS. Some of the IL specific rules about these POUs are re-stated here briefly for completeness. For further details, please refer the attached book.
Functions are stateless i.e every invocation of an Function with the same input produces the same output. Functions cannot have fields with interface type VAR. Function Blocks and Programs are stateful operations. Each FB, PRO-GRAM is like a class and variables of their type are like objects of the class. Invocations of a variable of type FB or PROGRAM can store state which may be used by the logic during its next invocation.
Here, we illustrate how POUs can be written and how then can invoke other POUs to perform various tasks. We used the PID control example givenhere. The first POU we look at is a Function Block called DivideFB which takes a Dividend, Divisor as inputs and returns a Quotient and Reminder. It also sets an error flag if the divisor is 0:
pou_var {
(continues on next page)
3.5. Instruction List Programming 19
(continued from previous page)
insn: "Error: LD 0"
insn: "ST Quotient"
The fields Dividend, Divisor, Quotient, DivRem and DivError together form the Calling Interface of the FB. Divided and Divisor have intf_type set to VAR_INPUT which means that a POU which calls a variable of type DivideFB, needs to set/assign values to these two fields. The FB inturn outputs three fields Quotient, DivRem and DivError which can be read by the calling POU as we show next. We do not explain the logic of the FB here but we highly encourage the reader to do so.
Note: One caveat needs to pointed out with respect to Labels embedded inside instructions: The labels should be inline with the instruction i.e. there cannot be a Label with no associated instruction in the same line. In the above example “Error: LD 0” cannot be split into two separate lines with one line containing the label “Error” and the other line containing the instruction “LD 0”.
In the next step, we look at one other FB and a PROGRAM which invokes both. The GetControlInputFB given below has 4 INPUT, 1 INOUT, 1 OUTPUT and 5 TEMP fields. The TEMP fields are local variables with constant values.
We omit its code here for brevity.:
pou_var {
(continues on next page)
OpenSCADA Documentation, Release 1.0
(continued from previous page) field_name: "integral"
In the calling interface of the PID_CONTROL program, notice two fields “div” and “get_control” whose datatypes are the two previously defined Function Blocks. This is an example of an instantiation of a POU in IL. The fields
“div” and “get_control” are objects of type “DivideFB” and “GetControlInputFB” respectively. These objects can be invoked/called within the POU to execute their embedded logic. Also notice, two other fields “current_theta” and
3.5. Instruction List Programming 21
“force” which are declared as VAR_EXTERNAL. These are global variables which are defined at the resource level in the resource specificationfile:
pou_var {
(continues on next page)
OpenSCADA Documentation, Release 1.0
(continued from previous page) field_name: "curr_time"
# If not started, store prev_time, prev_error insn: "LD started"
insn: "CAL div(Dividend:= current_theta, Divisor:= two_pi)"
insn: "LD div.DivRem"
insn: "else1: LD tmp"
insn: "ST prev_error"
(continues on next page)
3.5. Instruction List Programming 23
(continued from previous page)
insn: "CAL div(Dividend:= current_theta, Divisor:= two_pi)"
insn: "LD div.DivRem"
insn: "else: LD tmp"
insn: "ST curr_error"
insn: "CAL get_control(time_delta:= time_delta, curr_error:= curr_error, prev_
˓→error:= prev_error, integral:= prev_integral)"
insn: "LD get_control.force"
The DivideFB object is called in the line:
CAL div(Dividend:= current_theta, Divisor:= two_pi)
Its INPUT variables are set to current_theta and two_pi which are both fields in the calling POU i.e PID_CONTROL.
After the invocation, one of its output is read and subsequently stored in another variable called tmp:
LD div.DivRem ST tmp
Instead, this can also be accomplished in one line as follows:
CAL div(Dividend:= current_theta, Divisor:= two_pi, DivRem=>tmp) Similarly, the GetControlInputFB object is called in the line:
CAL get_control(time_delta:= time_delta, curr_error:= curr_error, prev_error:= prev_
˓→error, integral:= prev_integral)
Its INPUT and INOUT variables are set during the invocation and its output field is read in the next line:
LD get_control.force
On the contrary to Function Blocks and Programs, Functions cannot be instantiated with variables. Functions like TIME_TO_REAL and REAL_TO_INT (which are SFCs both used in these FBs and PROGRAM) are simply invoked as:
OpenSCADA Documentation, Release 1.0
FUNCTION_NAME operand1, operand2, ..., operandN
and the result of the Function can be loaded from the Accumulator after its invocation. Functions in OpenSCADA can return only one value and the specified operands are mapped to corresponding fields of the function in the same order.
3.5.2.1 Supported Instructions
OpenSCADA ships with the following IL instructions. Some of these instructions can take multiple operands or a variable number of operands. For further details on each instruction, please consult the included references:
Instructions
ADD GE LT NOT
AND GT MOD OR
DIV LD MUL SHL
EQ LE NE SHR
ST SUB XOR CAL
JMP
3.5.2.2 Supported System Functions
OpenSCADA ships with the following System Functions (SFCs). Some of these SFCs can take multiple operands or a variable number of operands. For further details on each instruction, please consult the included references:
System Functions (SFCs)
ABS ACOS ASIN ANY_TO_ANY
ASIN ATAN COS EXP
GTOD LIMIT LN LOG
MAX MIN MUX SEL
SIN SQRT TAN
Note: The ANY_TO_ANY SFC is not actually a single SFC but a set of SFCs which are used for type casting. For example the TIME_TO_REAL and REAL_TO_INT SFCs used in the previous example are a part of this class of SFCs. For a full list of allowed type casting SFCs, consult the included references.
3.5.2.3 Supported System Function Blocks
OpenSCADA ships with following System Function Blocks (SFBs). For further details on each instruction, please consult the included references:
System Function Blocks (SFBs)
SR RS R_TRIG F_TRIG
CTU CTD CTUD TP
TON TOFF
3.5. Instruction List Programming 25