2

** TLDR; Is it possible to create pointers to methods, store them and then call them? **

This question is related to this question: how to create pointer to function in codesys v3, but my question is about methods, and, since the referenced question is +5 years old, other solutions might be at hand.

The code below does not work, but it gives an impression of what I am trying to do, and how I expected it to work.

Let's say I have an FB/class: Alarm, in which I want to inject a call-back into Alarm.ActivateCallback that is to be executed upon activation of the alarm, using Alarm.Activate(). PLC_PRG has an instance of the Alarm and wants to inject a method PRG.OnAlarm1Activated() as the call-back, that, for now, just sets a bool of the PRG to TRUE:

FB Alarm

FUNCTION_BLOCK Alarm
VAR_INPUT
    ActivateCallback : POINTER TO CallbackDelegate;
END_VAR

Alarm.Activate()

IF (ActivateCallback <> 0) THEN
    ActivateCallback^();
END_IF;

PLC_PRG:

PROGRAM PLC_PRG
VAR
    _init : BOOL := TRUE;   
    _activate : BOOL := FALSE;
    _alarm1 : Alarm;
    _alarm1Activated : BOOL;
END_VAR
IF (_init) THEN
    _alarm1.ActivateCallback :=ADR(OnAlarm1Activated);
    _init := FALSE;
END_IF


IF (_activate) THEN
    _alarm1.Activate();
END_IF

PLC_PRG.OnAlarm1Activated():

Alarm1Activated := TRUE;

For the code above, I get the following errors:

  • 'CallbackDelegate' is of type FUNCTION and cannot be instantiated
  • Operation 'Adr' is not possible on 'OnAlarm1Activated'

Can the code above be modified to enable the desired functionality?

As an alternative, I can use interfaces and inject these. But I would also like to know whether the above is possible as well.

Side-note: CODESYS help states the following, but maybe there is a work-around:

... CODESYS offers no possibility to call a function pointer within an application in the programming system! ...

EDIT to ellaborate more on the interface alternative (excuse my C#/CODESYS hybrid semi-code)

public enum AlarmEvent
{
    Activated,
    Cleared,
}

public FB Alarm 
{
    public AlarmHandler : REFERENCE TO IAlarmEventHandler;

    public Activate() {
        AlarmHandler.HandleAlarmEvent(this, AlarmEvent.Activate);
    }
} 

public interface IAlarmEventHandler
{
    public void HandleAlarmEvent(ref Alarm alarm, AlarmEvent event);
}

public FB PLC_PRG : IAlarmEventHandler 
{ 
    Alarm1 : Alarm;

    public FB_Init() {
        Alarm1.AlarmHandler := this;
    }

    public void HandleAlarmEvent(ref Alarm alarm, AlarmEvent event) {
        if (alarm == Alarm1 && event == AlarmEvent.Activated)
            _alarm1Activated = true;
    }
}
Cédric Moers
  • 395
  • 1
  • 10

2 Answers2

2

If you want to inject a FunctionBlock, which is essentially a class, you can use an Interface.

CODESYS always treats variables declared with the type of an interface as references.

If you want to use a pointer to a normal function however,

CODESYS does not provide any means for calling a function pointer from within an application in the development system.

So executing a callback function from within an object is not possible, you'd have to either wrap the function call inside a FunctionBlock and pass it as an Interface, or return a boolean for every callback and check them on the callers side. For the second option, we can look at the TON FunctionBlock from the STANDARD library:

VAR
    timer: TON;
END_VAR

timer(IN := TRUE; PT := T#5S); // 5 second timer

IF (timer.Q) THEN
    // executed when the timer counts 5 seconds
END_IF

In your case, maybe something like this:

VAR
    obj: MyPOU;
END_VAR

obj( (* inputs *) );

IF (obj.callback1) THEN
    Func1();
END_IF

IF (obj.callback2) THEN
    Func2();
END_IF
Guiorgy
  • 1,405
  • 9
  • 26
0

What you want to use is interfaces in CODESYS 3, which are basically method-pointers. You simply make sure that you add a reference to the interface that you want to be called either:

  1. When you create the instance of FB_Alarm (through FB_Init) OR
  2. As an input to the function block.
Jakob
  • 1,288
  • 7
  • 13
  • Interfaces can indeed help in achieving similar results. This is the alternative I mention in my question. (e.g. injecting an `IAlarmEventHandler` into Alarm and implementing it in a calling FB). ```` public interface IAlarmEventHandler { public void HandleAlarmEvent(ref Alarm alarm, AlarmEvent event); } ```` But I would like to know whether the proposed 'method-injection' is possible. – Cédric Moers Mar 06 '23 at 13:54