** TLDR;是否可以创建指向方法的指针,存储它们然后调用它们? **
这个问题与这个问题相关:如何在codesys v3中创建指向函数的指针,但我的问题是关于方法的,而且,由于引用的问题已经有5年历史了,其他解决方案可能就在眼前。
下面的代码不起作用,但它给人留下了我想要做什么以及我期望它如何工作的印象。
假设我有一个 FB/类:
Alarm
,其中我想将回调注入到 Alarm.ActivateCallback
中,该回调将在警报激活时使用 Alarm.Activate()
执行。 PLC_PRG 有一个 Alarm
的实例,并希望注入一个方法 PRG.OnAlarm1Activated()
作为回调,目前只需将 PRG 的布尔值设置为 TRUE:
FUNCTION_BLOCK Alarm
VAR_INPUT
ActivateCallback : POINTER TO CallbackDelegate;
END_VAR
IF (ActivateCallback <> 0) THEN
ActivateCallback^();
END_IF;
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
Alarm1Activated := TRUE;
对于上面的代码,我收到以下错误:
可以修改上面的代码来启用所需的功能吗?
作为替代方案,我可以使用接口并注入它们。但我也想知道上面是否也可能。
旁注:CODESYS 帮助说明了以下内容,但也许有解决方法:
... CODESYS 无法在编程系统的应用程序中调用函数指针! ...
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;
}
}
如果您想注入一个 FunctionBlock(本质上是一个类),您可以使用 Interface。
CODESYS 始终将使用接口类型声明的变量视为引用。
如果您想使用指向普通函数的指针,
CODESYS 不提供任何从开发系统中的应用程序内调用函数指针的方法。
因此从对象内部执行回调函数是不可能的。
您必须将函数调用包装在 FunctionBlock 内并将其作为接口传递:
INTERFACE ICallback
METHOD Invoke;
END_INTERFACE;
FUNCTION_BLOCK CallbackExecutor1 IMPLEMENTS ICallback
METHOD Invoke
MyCallbackFunction(); // the function execution is hardcoded
END_METHOD;
END_FUNCTION_BLOCK;
FUNCTION_BLOCK CallbackExecutor2 IMPLEMENTS ICallback
METHOD Invoke
MyProgram.MyCallbackMethod(); // the method execution is hardcoded
END_METHOD;
END_FUNCTION_BLOCK;
FUNCTION_BLOCK CallbackExecutor3 IMPLEMENTS ICallback
VAR
_fbRef: REFERENCE TO MyFB;
END_VAR
METHOD FB_Init: BOOL // constructor
VAR_INPUT
bInitRetains: BOOL;
bInCopyCode: BOOL;
fb_reference: REFERENCE TO MyFB; // to call a method of a FunctionBlock, you need a reference/pointer to an instance of the said FunctionBlock
END_VAR
_fbRef REF= fb_reference;
END_METHOD;
METHOD Invoke
_fbRef.MyCallbackMethod(); // the method execution is hardcoded
END_METHOD;
END_FUNCTION_BLOCK;
FUNCTION_BLOCK FB
VAR
_callback1: ICallback;
_callback2: ICallback;
_callback3: ICallback;
END_VAR
_callback1.Invoke(); // invoke/activate/call where needed
IF TRUE THEN
_callback2.Invoke(); // invoke/activate/call where needed
END_IF;
IF FALSE THEN
_callback3.Invoke(); // invoke/activate/call where needed
END_IF;
METHOD FB_Init: BOOL // constructor
VAR_INPUT
bInitRetains: BOOL;
bInCopyCode: BOOL;
callback1: ICallback; // pass callbacks in the constructor
callback2: ICallback; // pass callbacks in the constructor
callback3: ICallback; // pass callbacks in the constructor
END_VAR
_callback1 := callback1;
_callback2 := callback2;
_callback3 := callback2;
END_METHOD;
END_FUNCTION_BLOCK;
PROGRAM Main_07
VAR
_myfb: MyFB;
callback1: CallbackExecutor1;
callback2: CallbackExecutor2;
callback3: CallbackExecutor3(fb_reference := _myfb);
_fb: FB(callback1 := callback1, callback2 := callback2, callback3 := callback3);
END_VAR
END_PROGRAM
注意:仅仅为了传递回调而创建“一次性”FunctionBlocks 并不是免费的,如果您在资源有限的环境中工作,我建议避免使用此方法。此外,将参数传递给这些回调甚至更加困难,即使不是不可能(至少以通用方式)。
或者为每个回调返回一个布尔值并在调用方检查它们。对于第二个选项,我们可以查看 STANDARD 库中的 TON FunctionBlock:
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
就你而言,也许是这样的:
VAR
obj: MyPOU;
END_VAR
obj( (* inputs *) );
IF (obj.callback1) THEN
Func1();
END_IF
IF (obj.callback2) THEN
Func2();
END_IF