是否可以向功能块添加静态参数?

Is it possible to add static parameters to function blocks?

是否可以编写带有一些静态参数化的功能块?具体来说,我可以制作一个具有静态容量的缓冲区,但不同的实例可以有不同的容量吗?

理想情况下,我会想象一些常量参数,如下所示:

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

假设这不可能,那么管理不同功能块实例的不同存储大小的最佳方法是什么?

我将 post 我最不糟糕的解决方法作为答案。

唯一想到的就是做一个抽象的基础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_BufferLargeFB_BufferSmall 相同,只是 ConcreteStorageSize 是 1000 而不是 10。)

实例化:

FUNCTION_BLOCK FB_Usage
VAR
    SmallBuffer : FB_BufferSmall;
    LargeBuffer : FB_BufferLarge;
END_VAR

您对 'Static' 变量的引用让我有点吃惊,因为 VAR STAT 与您想要的不同,并且用于使 FB 的所有实例共享一个公共元素。

您真正要寻找的是FB_INIT and __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

如果您不想使用动态内存创建数组,同时又想减少项目中的类型数量,您可以使用条件编译指示。

示例:

//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);

然后将缓冲区传递给实际使用它们作为参考的对象。在那些功能块中,您需要检查 UPPER 和 LOWER Bound,以免出现问题。

如果您不喜欢条件编译指示但仍希望您的项目简单明了,您可以在 GIT 存储库中将变体表示为 GIT 个分支。 通过这种方式,您始终知道哪台机器具有哪些功能,并且可以保持干净的结构和体系结构。 另一种策略是使用 Beckhoff 自动化接口自动创建代码并按照您决定的结构构建您的项目。 这是 link: https://infosys.beckhoff.com/index.php?content=../content/1031/tc3_automationinterface/242682763.html&id=

通过使用自动化接口自动生成代码,您再次降低了在机器操作中注入人为错误的可能性,并将复杂性导出到“更高级别”,使您的系统更加可靠。

因此,您可以使用多种方法来实现可重用的解决方案。 尽管我知道那里有很多复杂的机器,但如果您的 plc 架构变得复杂,可能是时候考虑可​​以将哪些模块和功能“外包”到更高级别,以坚持 KISS 原则和安全生产在较低的水平。