Array as Type for Interface Property CoDeSys - codesys

I am attempting to implement the Observer design pattern in CoDeSys using structured text. I'm encountering an error where I can't call the ., [], nor [..] on an interface property. Is it possible to have an interface property that is an array? The interface property in question is the following:
PROPERTY observers : ARRAY[0..20] OF IObserver
And the interface structure follows the observer patter like the following.
The implementation of the notifyAllObservers method is the following.
METHOD notifyAllObservers
VAR
i : INT;
END_VAR
FOR i := 0 TO 20 DO
CommandHandler.observers[i].update(CommandHandler.commands);
END_FOR

I think the problem with your CommandHandler.observers[i] is that you don't have any instances of anything implementing the IObserver.
You cannot access your observers property the way you do since it is not an object.
If you delete the observers property, define an observer FB implementing the IObserver and then add an array of observer (FB) inside CommandHandler your notifyAllObservers code will work.

You can't access the array fields that way.
There are several other ways you can achieve what you want in Structured Text:
Since your property is public there is no reason not to declare the array of IObserver as an input field of the Function Block.
If you do so you can access it the way you desire.
Declaration part:
FUNCTION_BLOCK CommandHandler
VAR_INPUT
IObservers : ARRAY[0..19] OF IObserver;
END_VAR
Example call:
fbCommandHandler.IObservers[i].update();
Another way is you access (and modifiy, etc..) your IObservers from inside the update method:
Create a new Interface IObserverArray with an update method:
METHOD update : BOOL
VAR_INPUT
index : INT;
cmd : INT;
END_VAR
Create a new Function Block implementing the IObserverArray itf:
FUNCTION_BLOCK ObserverArray IMPLEMENTS IObserverArray
//declaration part of the update method
METHOD update : BOOL
VAR_INPUT
index : INT;
cmd : INT;
END_VAR
VAR
itfObservers : ARRAY [0..19] OF IObserver;
END_VAR
//imlementation part of the update method
itfObservers[index].update(cmd);
Create a method getObservers() in your CommandHandler Function Block that returns a IObserverArray:
METHOD getObservers : IObserverArray
//imlementation part of the getObservers method
getObservers := aObservers;
Now you just declare aObservers : ObserverArray; in your CommandHandler as a VAR
and call it like this : fbCommandHandler.getObservers().update(5,12);

https://help.codesys.com/api-content/2/codesys/3.5.12.0/en/_cds_operator_new/
_new(8,nLength +1)
Make a list with memcpy(new,old)
__DELETE(old);
Something like that should work!

How did you set up the property observers?
If you just got an array of objects you are not able to access 1 of the array members individually. However if you only use a get action and instead of getting the array of observers, get a reference to an array of observers it will work. like so
PROPERTY PUBLIC observers : REFERENCE TO ARRAY[*Lower_Bound*..*Upper_Bound*] OF *Class to be returned*
In the get action set the following:
observers REF= *object to be returned*
In this way the getter wil return a pointer/reference instead of the object. With the reference you can reach for an individual item in the array.
Hope this helps

Related

How to assign the value to be returned by a method?

I have a method which I want to be returning a value. The declaration is clear to me. But how do I assign the value to be returned inside the method implementation?
I can only think of creating an output variable and use that to propagate the value to the caller. But that is definitely not how I would expect a return value to work:
METHOD M_MyMethod : BOOL
VAR_OUT
bReturnVal : BOOL;
END_VAR
// Do some method things here.
// Then assign the return value.
bReturnVal := bWhatever;
The solution is simple:
M_MyMethod := bWhatever;
Using VAR_OUT is also usefull, if you need to return more than one value and don't want to create dedicated type :)

Assign value to interface element without previous instantiation of the implementation

I have a STRUCT (DUT) which one element is an interface.
STRUCT myStruct
element : iInterf;
END_STRUCT
In declaration session, I have to instantiate first (with same implementation) before assign value to this element.
instance : iInterf_implementation := (some initialization values);
myVar : myStruct := ( element := instance );
Is possible to assign value and instantiate an interface element without create another variable?
Not that I know of. Here's a quote from the CODESYS documentation: CODESYS always treats variables declared with the type of an interface as references. If an interface is essentially a reference, which is essentially a pointer, then you need a variable for the interface to refer/point at.

No matching FB_Init method found when it's in a block

Running Ecostruxure machine expert which is CodeSys 3.5
I've got the following program structure:
Main: two blocks, Init and Step0 linked by a transition.
In the vars of that main:
VAR
My_Encoder : ENC_REF_M262;
...
END_VAR
So then in my Init block I have My_Encoder.FB_Init(...)
But the error message I get points to the variable declaration in Main which says "C0138 No Matching FB_Init method found fo this instantiation of ENC_REF_M262".
Try using the no_init attribute above your instantiated FB.
VAR
{ attribute 'no_init'}
My_Encoder : ENC_REF_M262;
...
END_VAR
Whenever a function block is instantiated, a matching implementation of the FB_Init method is expected to occur (even inside a wrapping fb).
N.B. You will need to explicitly run the init code (My_Encoder.fb_Init()) somewhere if it has critical functionality
Using the 'no_init' attribute will fix the compile error; however, (IMHO) this may not be the best solution. First, because you'll probably want to initialize the values and secondly because calling FB_Init explicitly, is strongly discouraged. Therefore I would like to explain why the error message occurs as well as giving some insight in how to solve the error.
The error is (AFAIK) due to either one of the two reason listed below.
When you declare and initialize a POU (e.g. an FB between the VAR and END_VAR keywords) and you directly initialize a value to one of it's inputs while this input is not included as VAR_INPUT of the FB_Init of the POU.
I'll clarify this with some code:
VAR
myFunctionBlock: FB_FunctionBlock(myInput:= 1);
END_VAR
.. Above we're initializing input "myInput" of object/instance "myFunctionBlock" with the value 1 and this requires that "myInput" is included in the FB_Init of POU "FB_FunctionBlock":
METHOD FB_init : BOOL
VAR_INPUT
bInitRetains : BOOL;
bInCopyCode : BOOL;
myInput : INT; // Include the input or else you'll get the error
END_VAR
In the VAR_INPUT-block of an FB_Init you can include inputs of the POU to which the FB_Init belongs. When you do this your are required to initialize these inputs when you declare and initialize the POU. (this is probably the case in your example)
Again I'll clarify this with some code:
METHOD FB_init : BOOL
VAR_INPUT
bInitRetains : BOOL;
bInCopyCode : BOOL;
myInput : INT;
END_VAR
.. Above input "myInput" is included in the FB_Init of POU "FB_FunctionBlock" and therefore when we create an object/instance of POU "FB_FunctionBlock" then we're required to initialize the input "myInput".
VAR
myFunctionBlock: FB_FunctionBlock(myInput:= 1);
// Include "myInput:= 1" or else you'll get the error
END_VAR

TwinCat How To Initialize Functionblock with Reference to Global Variable

Hello StackOverflow Community,
i have another question regarding the TwinCat/Beckhoff/Codesys Programming Language, maybe someone is able to help me with this problem.
Here is the Problem:
I want to initalize a functionblock with a reference to some variable. (In this example a simple bool).
Hereby i want to make use of the FB_Init Method.
The Functionblock itself looks something like this:
FUNCTION_BLOCK PUBLIC FB_Ref
VAR
reftoBool : REFERENCE TO BOOL;
END_VAR
The FB_Init Method looks something like this:
METHOD FB_init : BOOL
VAR_INPUT
bInitRetains : BOOL := FALSE;
bInCopyCode : BOOL := FALSE;
reftoBoolIn : REFERENCE TO BOOL;
END_VAR
reftoBool := reftoBoolIn;
The problem is that i can't get the code to work.. i don't know what im doing wrong.
Thanks in advance...
The problem is that you need to use REF= in the body of FB_init, like so:
reftoBool REF= reftoBoolIn;
See documentation here:
https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/136301707.html

Error When Editing Member Variable in Class

I'm trying to use a class in my program.
TStack = Class
Public
constructor Create; Overload;
Procedure Add(Frm:TForm);
Procedure Remove();
Procedure Do_Close;
Private
List : Array[1..Max_Forms] of Rec;
Count : Byte;
End;
Constructor:
constructor TStack.Create;
begin
Self.Count := 0;
end;
Procedure TStack.Add(Frm:TForm);
begin
Inc(Self.Count);
List[Count].Form := #Frm;
List[Count].Width := Frm.Width;
List[Count].Height := Frm.Height;
List[Count].left := Frm.Left;
List[Count].Top := Frm.Top;
end;
I can't change value of Count variable! It cause Run-Time error : Access violation....Write of address 000001E4
What's the problem?!
FOR MORE INFORMATION:
I'm trying to store a pointer to each form in a structure like this :
Rec = Record
Form : ^TForm;
Maximized : Boolean;
Width,
Height,
left,
Top : Integer;
End;
And then
Procedure TStack.Do_Close;
var
i : integer;
MyForm : TForm;
begin
i := .....some code here.......;
MyForm := #List[i].Form;
ShowMessage('I will close '+MyForm.Caption);
MyForm.Close;
end;
AND call constructor like this to initialize 'Count':
Stack.Create;
As described in comments you are attempting to create the object like this:
var
Stack: TStack;
....
Stack.Create;
This is a classic mistake, and one that we've all made. You are calling a method on an uninitialized instance variable.
In order to instantiate a class you need to write this:
Stack := TStack.Create;
On top of that I have the following comments:
Use zero-based indexing for arrays. That's the convention everywhere in Delphi apart from anachronistic strings. And even that is changing in newer versions.
Don't use static arrays for a stack unless you have a good reason for doing so. You'll just run the risk of running out of space. Or allocating more memory than you need. Use a dynamic array.
Rather than a dynamic array, you could use TList<T>.
Even so, one wonders why you are making your own stack class when there is the perfectly good TStack<T>.
You store the address of a local variable in your stack. In TStack.Add you add #Frm into the container. As soon as TStack.Add returns, #Frm is meaningless. That's because Frm is a local variable whose life ends when the function that owns it returns. I think you want to take a copy of Frm.
Picking up item 5 in more detail, your record is declared like this:
Rec = Record
Form : ^TForm;
Maximized : Boolean;
Width,
Height,
left,
Top : Integer;
End;
It is a mistake to use ^TForm. That is a pointer to a variable holding a pointer to an object. That's two levels of indirection, one too many. You must declare the Form field to be of type TForm. I suggest you revise the way Delphi object reference variables work. Delphi classes are what is known as reference types. A variable of type TMyClass where TMyClass is class(...) is already a pointer. The language automatically de-references the pointer when you use the . operator to access members.