Issue
My question is: what are the benefits of using pointers and reference to?
I am new to codesys and in my previous job, I programmed in TIA portal (Siemens) and Sysmac Studio (Omron) and never came across pointers or something similar. I think I understand how they work but not sure when I should be using them myself.
For example, I just received a function block from a supplier:
Why don't they just have an array for input and output?
Solution
First of all, if you have ever used the VAR_IN_OUT
declaration, then you have already used references, since that is equivalent to a VAR
with REFERENCE TO
.
As for the uses, there are mainly 4 that I can think of right now:
Type Punning, which you can also achieve using a
UNION
, but you may not want to have to create a union for every single reinterpretation cast in your code.TL; DR: To save memory and copy execution time. Whenever you pass some data to a function/function block, it gets copied. This is not a big problem if your PLC has enough CPU power and memory, however if you are dealing with especially huge data on a low end PLC, then you may either exceed real time execution constraints, or run out of memory. When you pass a pointer/reference however, no matter how big the data is only the pointer/reference gets copied and passed, which is 4 bytes in 32 bit system, and 8 bytes in a 64 bit one.
In C style languages you'd use pointers/references when you want a function to return multiple values without the hassle of creating a custom structure every time. You can do the same here to, however in CODESYS function can have multiple outputs, for example:
VAR_OUPUT
out1 : INT; (*1st output variable *)
out2 : INT; (*2nd output variable *)
//...
END_VAR
- And finally, as I mentioned at the very beginning, when you want to pass some data that needs to be modified in the function itself, in other words, where you can use
VAR_IN_OUT
you can also use pointers/references. One Special case where you will have to use a pointer is if you have aFunction Block
that receives some data in theFB_Init
(initialization/construction) function and stores it locally. In such case you would have a pointer as a local variable in the function block, and take the address of the variable in theFB_Init
function. Same applies if you have a structure that needs to reference another structure or some data.
PS. There are probably some other uses I missed. One of the main uses in other languages is for dynamic memory allocations, but in CODESYS this is disabled by default and not all PLCs support it, and hardly anyone (that I know) uses it.
EDIT: Though this has been accepted, I want to bring a real life example of us using pointers:
Suppose we want to have a Function Block that calculates the moving average on a given number series. A simple approach would be something like this:
FUNCTION_BLOCK MyMovingAvg
VAR_INPUT
nextNum: INT;
END_VAR
VAR_OUTPUT
avg: REAL;
END_VAR
VAR
window: ARRAY [0..50] OF INT;
currentIndex: UINT;
END_VAR
However, this has the problem that the moving window size is static and predefined. If we wanted to have averages for different window sizes we would either have to create several function blocks for different window sizes, or do something like this:
FUNCTION_BLOCK MyMovingAvg
VAR CONSTANT
maxWindowSize: UINT := 100;
END_VAR
VAR_INPUT
nextNum: INT;
windowSize: UINT (0..maxWindowSize);
END_VAR
VAR_OUTPUT
avg: REAL;
END_VAR
VAR
window: ARRAY [0..maxWindowSize] OF INT;
currentIndex: UINT;
END_VAR
where we would only use the elements of the array from 0 to windowSize
and the rest would be ignored. This however also has the problems that we can't use window sizes more than maxWindowSize
and there's potentially a lot of wasted memory if maxWindowSize
is set high.
There are 2 ways to get a truly general solution:
- Use dynamic allocations. However, as I mentioned previously, this isn't supported by all PLCs, is disabled by default, has drawbacks (you'll have to split you memory into two chunks), is hardly used and is not very CODESYS-like.
- Let the user define the array of whatever size they want and pass the array to our function block:
FUNCTION_BLOCK MyMovingAvg
VAR_INPUT
nextNum: INT;
END_VAR
VAR_OUTPUT
avg: REAL;
END_VAR
VAR
windowPtr: POINTER TO INT;
windowSize: DINT;
currentIndex: UINT;
END_VAR
METHOD FB_Init: BOOL
VAR_INPUT
bInitRetains: BOOL;
bInCopyCode: BOOL;
END_VAR
VAR_IN_OUT // basically REFERENCE TO
window_buffer: ARRAY [*] OF INT; // array of any size
END_VAR
THIS^.windowPtr := ADR(window_buffer);
THIS^.windowSize := UPPER_BOUND(window_buffer, 1) - LOWER_BOUND(window_buffer, 1) + 1;
// usage:
PROGRAM Main
VAR
avgWindow: ARRAY [0..123] OF INT; // whatever size you want!
movAvg: MyMovingAvg(window_buffer := avgWindow);
END_VAR
movAvg(nextNum := 5);
movAvg.avg;
The same principle can be applied to any function block that operates on arrays (for example, we also use it for sorting). Moreover, similarly you may want to have a function that works on any integer/floating number. For that you may use one of the ANY types which is basically a structure that holds a pointer to the first byte of the data, the size of the data (in bytes) and a type enum.
Answered By - Guiorgy Answer Checked By - Katrina (PHPFixing Volunteer)
0 Comments:
Post a Comment
Note: Only a member of this blog may post a comment.