是否可以用一些静态参数化来编写功能块?具体来说,我是否可以创建一个具有静态容量的缓冲区,但不同的实例可以具有不同的容量?
理想情况下,我会想象一些常数参数,像这样:
FUNCTION_BLOCK FB_Buffer
VAR_INPUT CONSTANT
StorageSize : DINT;
END_VAR
VAR
Storage : ARRAY [1..StorageSize] OF REAL;
END_VAR
实例化应该是这样的:
FUNCTION_BLOCK FB_Usage
VAR
SmallBuffer : FB_Buffer := (StorageSize := 10);
LargeBuffer : FB_Buffer := (StorageSize := 1000);
END_VAR
假设这是不可能的,什么是最好的方法来管理不同的功能块实例不同的存储大小?
我会把我最不坏的解决方法作为答案。
我被你引用的'静态'变量扔了一点,因为VAR STAT是你想要的单独的东西,并用于使FB的所有实例共享一个共同的元素。
你真正在寻找的是FB_INIT和__NEW的奇迹
你必须管理你自己的数据访问,确保你不会溢出和所有其他危险的事情,但除此之外,这应该按照你发布的答案工作。然后用两个不同长度初始化这段代码就像这样简单:
FUNCTION_BLOCK FB_Usage
VAR
SmallBuffer : fb_DataArray( 100 );
LargeBuffer : fb_DataArray( 10000 );
END_VAR
// Function block for handling a data array that is generated at boot time
FUNCTION_BLOCK fb_DataArray
VAR
pZeroElem : POINTER TO REAL; // Pointer to the head of the array
ArrayLength : UDINT; // Length of the array in elements
END_VAR
// Do something by indexing through ring
METHOD FB_init : BOOL
// Initialisation method for fb_DataArray, allocates memory space to array
VAR
bInitRetains : BOOL; // required
bInCopyCode : BOOL; // required
Length : UDINT; // Number of element in the array
END_VAR
pZeroElem := __NEW( REAL, Length );
// Generate a pointer to the first element of a dataspace that is precisely big enough for <Length> Real elements.
Method FB_exit
// Needs to be called to de-allocate the memory space allocated in fb_init.
VAR
bInCopyCode : BOOL; // Required
END_VAR
IF pZeroElem <> 0 THEN
// Checks if the pointer is valid, then deletes the allocation
__DELETE( pZeroElem );
END_IF
如果你不想用动态内存创建数组,同时你想减少项目中类型的数量,你可以使用条件pragmas
的例子:
//Declaration part of MAIN
PROGRAM MAIN
VAR
{define variant_b}
{IF defined(variant_a)}
conveyor_buffer : ARRAY[1..10] OF INT;
sensor_buffer : ARRAY[1..5] OF BOOL;
{ELSIF defined(variant_b}
conveyor_buffer : ARRAY[1..100] OF INT;
sensor_buffer : ARRAY[1..20] OF BOOL;
{END_IF}
fbConveyor : FB_Conveyor;
END_VAR
//Implementation part of MAIN
fbConveyor(buffer:=conveyor_buffer);
//Declaration part of FB_Conveyor
FUNCTION_BLOCK FB_Conveyor
VAR_IN_OUT
buffer : ARRAY [*] OF INT;
END_VAR
VAR_OUTPUT
END_VAR
VAR
length : DINT;
END_VAR
//Implementation part of FB_Conveyor
length := UPPER_BOUND(buffer,1);
然后将缓冲区作为引用传递给实际使用它们的对象。在这些功能块中,您需要检查上界和下界,以避免出现问题。
如果你不喜欢条件pragmas,但仍然希望你的项目简单明了,你可以在GIT存储库中将变体表示为GIT分支。通过这种方式,您始终知道哪台机器具有哪些功能,并且可以保持干净的结构和架构。另一种策略是利用Beckhoff自动化接口自动创建代码,并按照您决定的结构构建项目。链接如下:https://infosys.beckhoff.com/index.php?content=../content/1031/tc3_automationinterface/242682763.html& id =
通过自动化界面自动生成代码,您再次减少了在机器操作中注入人为错误的可能性,并将复杂性导出到"更高层次"。让你的系统更可靠。
因此,有许多方法可以用来实现可重用的解决方案。即使我知道有许多复杂的机器,如果您的plc架构变得复杂,可能是时候考虑哪些模块和功能可以"外包"了。以坚持KISS原则并确保较低级别的生产。
唯一想到的是创建一个抽象基类FB,并让不同的子类定义具体的存储。仍然有明显的重复和锅炉电镀,但至少比复制粘贴整个FB只是为了改变一个数字要好。
基地声明:
FUNCTION_BLOCK ABSTRACT FB_BufferBase
VAR
Storage : POINTER TO REAL;
StorageSize : DINT;
END_VAR
抽象方法声明:
METHOD ABSTRACT GetStorage
VAR_OUTPUT
ZeroElement : POINTER TO REAL;
ElementCount : DINT;
END_VAR
基体代码:
GetStorage (ZeroElement => Storage, ElementCount => StorageSize);
// Do stuff.
小混凝土声明:
FUNCTION_BLOCK FB_BufferSmall EXTENDS FB_BufferBase
VAR
ConcreteStorage : ARRAY [0..ConcreteStorageSize-1] OF REAL;
END_VAR
VAR CONSTANT
ConcreteStorageSize : DINT := 10;
END_VAR
小具体方法实现:
ZeroElement := ADR(ConcreteStorage[0]);
ElementCount := ConcreteStorageSize;
(FB_BufferLarge
与FB_BufferSmall
相同,只是ConcreteStorageSize
是1000而不是10)
实例化:
FUNCTION_BLOCK FB_Usage
VAR
SmallBuffer : FB_BufferSmall;
LargeBuffer : FB_BufferLarge;
END_VAR