1

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

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

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

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

我将发布我最不坏的解决方法作为回答。

4

3 回答 3

3

您对“静态”变量的引用让我有些震惊,因为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
于 2021-10-20T18:50:52.640 回答
1

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

例子:

//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 自动化接口自动创建代码并按照您决定的结构构建项目。这是链接: https ://infosys.beckhoff.com/index.php?content=../content/1031/tc3_automationinterface/242682763.html&id=

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

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

于 2021-10-22T15:04:05.627 回答
0

唯一想到的是制作一个抽象的基础 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,只是ConcreteStorageSize1000 而不是 10。)

实例化:

FUNCTION_BLOCK FB_Usage
VAR
    SmallBuffer : FB_BufferSmall;
    LargeBuffer : FB_BufferLarge;
END_VAR
于 2021-10-20T09:04:10.717 回答