如何在派生的FB中覆盖结构

发布于 2025-02-08 06:21:44 字数 880 浏览 1 评论 0 原文

我使用OOP原理为我们的机器设计基本FB时被困。

一台计算机使用类似的模块:

module1 : BaseModuleFB;
module2 : BaseModuleFB;

一个模块(机器的某些部分)需要数据(例如位置,延迟等)的数据(例如位置,延迟等)。该数据将存储在配方中,以便将数据分组为结构中。

FUNCTION_BLOCK BaseModuleFB
VAR
     Data : BaseModuleDataStruct;
END_VAR

现在,我有一台有额外要求的机器。而且还需要一些额外的数据。因此,我扩展了模块:

FUNCTION_BLOCK ModuleWithExtraFunctionFB EXTENDS BaseModuleFB
VAR
     Data : ModuleWithExtraFunctionDataStruct;
END_VAR

上面的代码不会编译,因为可变数据已经在基类中使用。

顺便说一句,数据结构看起来像这样:

TYPE BaseModuleDataStruct:
STRUCT
    position1:INT;
END_STRUCT
END_TYPE

TYPE ModuleWithExtraFunctionDataStruct EXTENDS BaseModuleDataStruct:
STRUCT
    position2:INT;
END_STRUCT
END_TYPE

我想到的另一个选项是创建一个称为 data 的属性。 该属性可以被派生类推荐。

但是这种方法也失败了,因为您无法更改覆盖属性的类型。

也许有人对此有一些好主意? 谢谢。

I'm stuck while designing a basic FB for our machine using OOP principles.

A machine uses different modules like this:

module1 : BaseModuleFB;
module2 : BaseModuleFB;

A module (some part of the machine) needs data to function (like positions, delays, etc.) This data will be stored in a recipe so grouping the data in a struct makes sence.

FUNCTION_BLOCK BaseModuleFB
VAR
     Data : BaseModuleDataStruct;
END_VAR

Now I have a different machine with some extra requirement. And also some extra data is needed. So I extend the module:

FUNCTION_BLOCK ModuleWithExtraFunctionFB EXTENDS BaseModuleFB
VAR
     Data : ModuleWithExtraFunctionDataStruct;
END_VAR

The code above will not compile because the variable Data is already in use in the base class.

The Data structs look like this by the way:

TYPE BaseModuleDataStruct:
STRUCT
    position1:INT;
END_STRUCT
END_TYPE

TYPE ModuleWithExtraFunctionDataStruct EXTENDS BaseModuleDataStruct:
STRUCT
    position2:INT;
END_STRUCT
END_TYPE

Another option I thought of was creating a property called Data.
This property can be overritten by the derived class.

But this approach also failed because you cannot change the type of the overridden property.

Maybe someone has some nice ideas about this?
Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

秋意浓 2025-02-15 06:21:44

为什么不只是将结构的内容移动到功能块中:

FUNCTION_BLOCK BaseModuleFB
VAR
    // instead of "Data : BaseModuleDataStruct;"
    position1: INT;
END_VAR

FUNCTION_BLOCK ModuleWithExtraFunctionFB EXTENDS BaseModuleFB
VAR
    // instead of "Data : ModuleWithExtraFunctionDataStruct;"
    position2: INT;
    // you can access both "position1" and position2" here
END_VAR

如果您需要在结构中具有数据,最简单的方法就是使用多个结构(如 fred ):

FUNCTION_BLOCK BaseModuleFB
VAR
    Data : BaseModuleDataStruct;
END_VAR

FUNCTION_BLOCK ModuleWithExtraFunctionFB EXTENDS BaseModuleFB
VAR
    // Data is already available here.
    Data2 : ModuleWithExtraFunctionDataStruct;
END_VAR

如果您绝对必须在功能块内访问单个结构但是,以一种完全封闭的方式进行操作,但是,您可以将结构定义的责任传递给功能块的用户,并使用接口或指针注入它:

1。对于接口,我们将使用 operator。

// An interface with a Method/Property that gives the Data structure
// It needs to extend "__System.IQueryInterface" for us to be bale to use the "__QUERYINTERFACE" operator on it later
INTERFACE IDataProvider EXTENDS __System.IQueryInterface
METHOD GetModuleAData : BaseModuleDataStruct

// An interface extension that adds a Method/Property that gives the extended Data structure
INTERFACE IDataProviderB EXTENDS IDataProvider
METHOD GetModuleBData : ModuleWithExtraFunctionDataStruct

FUNCTION_BLOCK BaseModuleFB
VAR
    _dataProvider: IDataProvider;
END_VAR
METHOD FB_Init: BOOL
VAR_INPUT
    bInitRetains: BOOL; // Internal built-in hidden argument. Don't touch!
    bInCopyCode: BOOL;  // Internal built-in hidden argument. Don't touch!
    dataProvider: IDataProvider;
END_VAR
// Inside the FB_Init method: "THIS^._dataProvider := dataProvider;"


FUNCTION_BLOCK ModuleWithExtraFunctionFB EXTENDS BaseModuleFB
VAR
    _dataProviderB: IDataProviderB;
END_VAR
METHOD FB_Init: BOOL
VAR_INPUT
    bInitRetains: BOOL; // Internal built-in hidden argument. Don't touch!
    bInCopyCode: BOOL;  // Internal built-in hidden argument. Don't touch!
    dataProvider: IDataProvider;
    // We will use the __QUERYINTERFACE operator to "cast" IDataProvider to IDataProviderB
END_VAR
VAR
    success: BOOL;
END_VAR
// Inside the FB_Init method:
// success := __QUERYINTERFACE(_dataProvider, _dataProviderB);

然后在基本方法中您应该能够使用 _datapRovider.getModuleadata 以及在扩展方法 _DatapRoviderb.getModulebData 中。


2。对于原始指针,我们只能通过它们传递:

FUNCTION_BLOCK BaseModuleFB
VAR
    pdata: POINTER TO BaseModuleDataStruct;
END_VAR
METHOD SetData: BOOL
VAR_IN_OUT
    data: BaseModuleDataStruct;
END_VAR
// Inside the SetDatamethod: "THIS^.pdata:= ADR(data);"


FUNCTION_BLOCK ModuleWithExtraFunctionFB EXTENDS BaseModuleFB
VAR
    pdata2: POINTER TO ModuleWithExtraFunctionDataStruct;
END_VAR
METHOD SetData: BOOL
VAR_IN_OUT
    data: BaseModuleDataStruct;
END_VAR
// Inside the SetDatamethod: "THIS^.pdata:= ADR(data);"
//                           "THIS^.pdata2:= ADR(data);"
// We are assuming that the user passed data of type "ModuleWithExtraFunctionDataStruct" here!!!
// Care needs to be taken here, otherwise we may get access violations!

在您的第一个运行中,请调用 setData 两个功能块的方法。确保您正在传递正确的数据结构,以避免任何违规访问!

然后,在基本方法中,您应该能够使用 pdata^.position1 和在扩展方法中 pdata2^position2 position2


3。我们还可以将两者结合起来,以直接访问结构,同时保留接口的类型安全性:

INTERFACE IDataProvider EXTENDS __System.IQueryInterface
METHOD GetDataAPtr : POINTER TO BaseModuleDataStruct

INTERFACE IDataProviderB EXTENDS IDataProvider
METHOD GetDataBPtr : POINTER TO ModuleWithExtraFunctionDataStruct

FUNCTION_BLOCK BaseModuleFB
VAR
    pdata: POINTER TO BaseModuleDataStruct;
END_VAR
METHOD FB_Init: BOOL
VAR_INPUT
    bInitRetains: BOOL; // Internal built-in hidden argument. Don't touch!
    bInCopyCode: BOOL;  // Internal built-in hidden argument. Don't touch!
    dataProvider: IDataProvider;
END_VAR
// Inside the FB_Init method: "THIS^.pdata:= dataProvider.GetDataAPtr();"


FUNCTION_BLOCK ModuleWithExtraFunctionFB EXTENDS BaseModuleFB
VAR
    pdata2: POINTER TO ModuleWithExtraFunctionDataStruct;
END_VAR
METHOD FB_Init: BOOL
VAR_INPUT
    bInitRetains: BOOL; // Internal built-in hidden argument. Don't touch!
    bInCopyCode: BOOL;  // Internal built-in hidden argument. Don't touch!
    dataProvider: IDataProvider;
END_VAR
VAR
    success: BOOL;
    _dataProviderB: IDataProviderB;
END_VAR
// Inside the FB_Init method:
// success := __QUERYINTERFACE(_dataProvider, _dataProviderB);
// IF (success) THEN
//     THIS^.pdata2 := _dataProviderB.GetDataBPtr();
// END_IF

然后在基本方法中您应该能够使用 pdata^.position1 和在扩展中方法 pdata2^.position2


4。如果您希望不使用指针,则可以使用引用:

此选项由 Stefan roelofs < /a>在评论下面

INTERFACE IDataProvider EXTENDS __System.IQueryInterface
METHOD GetDataARef : REFERENCE TO BaseModuleDataStruct

INTERFACE IDataProviderB EXTENDS IDataProvider
METHOD GetDataBRef : REFERENCE TO ModuleWithExtraFunctionDataStruct

FUNCTION_BLOCK BaseModuleFB
VAR
    rdata: REFERENCE TO BaseModuleDataStruct;
END_VAR
METHOD FB_Init: BOOL
VAR_INPUT
    bInitRetains: BOOL; // Internal built-in hidden argument. Don't touch!
    bInCopyCode: BOOL;  // Internal built-in hidden argument. Don't touch!
    dataProvider: IDataProvider;
END_VAR
// Inside the FB_Init method: "THIS^.rdata REF= dataProvider.GetDataARef();"


FUNCTION_BLOCK ModuleWithExtraFunctionFB EXTENDS BaseModuleFB
VAR
    rdata2: REFERENCE TO ModuleWithExtraFunctionDataStruct;
END_VAR
METHOD FB_Init: BOOL
VAR_INPUT
    bInitRetains: BOOL; // Internal built-in hidden argument. Don't touch!
    bInCopyCode: BOOL;  // Internal built-in hidden argument. Don't touch!
    dataProvider: IDataProvider;
END_VAR
VAR
    success: BOOL;
    _dataProviderB: IDataProviderB;
END_VAR
// Inside the FB_Init method:
// success := __QUERYINTERFACE(_dataProvider, _dataProviderB);
// IF (success) THEN
//     THIS^.rdata2 REF= _dataProviderB.GetDataBRef();
// END_IF

然后,在您的功能块内部,您可能需要使用 __ ISVALIDREF()运算符来验证铸造/转换是否成功。之后,在基本方法中,您应该能够使用 rdata.position1 和扩展方法 rdata2.position2

注意:您可能会收到许多C0410警告:

C0410:兼容性警告:对类型参考的属性的写入访问访问版本&lt的set-accessor; 3.5.10.0并写入参考,但称为版本&gt; = 3.5.10.0的Get-Accessor并写入值!使用操作员参考=如果要分配参考。

如果这使您烦恼,则可以禁用编译器(项目设置)中的C0410警告。



我在,您可以尝试导入它并与之相处。

Why not just move the contents of the struct into the function block:

FUNCTION_BLOCK BaseModuleFB
VAR
    // instead of "Data : BaseModuleDataStruct;"
    position1: INT;
END_VAR

FUNCTION_BLOCK ModuleWithExtraFunctionFB EXTENDS BaseModuleFB
VAR
    // instead of "Data : ModuleWithExtraFunctionDataStruct;"
    position2: INT;
    // you can access both "position1" and position2" here
END_VAR

If you need to have the data in structures, the simplest way would be to just use several structures (as pointed by Fred):

FUNCTION_BLOCK BaseModuleFB
VAR
    Data : BaseModuleDataStruct;
END_VAR

FUNCTION_BLOCK ModuleWithExtraFunctionFB EXTENDS BaseModuleFB
VAR
    // Data is already available here.
    Data2 : ModuleWithExtraFunctionDataStruct;
END_VAR

If you absolutely must access a single structure inside your function block, there is no simple way to do it in a completely enclosed way, however, you could pass the responsibility of defining the struct to the user of the function block and inject it either by using interfaces or pointers:

1. For interfaces, we will use the __QUERYINTERFACE operator.

// An interface with a Method/Property that gives the Data structure
// It needs to extend "__System.IQueryInterface" for us to be bale to use the "__QUERYINTERFACE" operator on it later
INTERFACE IDataProvider EXTENDS __System.IQueryInterface
METHOD GetModuleAData : BaseModuleDataStruct

// An interface extension that adds a Method/Property that gives the extended Data structure
INTERFACE IDataProviderB EXTENDS IDataProvider
METHOD GetModuleBData : ModuleWithExtraFunctionDataStruct

FUNCTION_BLOCK BaseModuleFB
VAR
    _dataProvider: IDataProvider;
END_VAR
METHOD FB_Init: BOOL
VAR_INPUT
    bInitRetains: BOOL; // Internal built-in hidden argument. Don't touch!
    bInCopyCode: BOOL;  // Internal built-in hidden argument. Don't touch!
    dataProvider: IDataProvider;
END_VAR
// Inside the FB_Init method: "THIS^._dataProvider := dataProvider;"


FUNCTION_BLOCK ModuleWithExtraFunctionFB EXTENDS BaseModuleFB
VAR
    _dataProviderB: IDataProviderB;
END_VAR
METHOD FB_Init: BOOL
VAR_INPUT
    bInitRetains: BOOL; // Internal built-in hidden argument. Don't touch!
    bInCopyCode: BOOL;  // Internal built-in hidden argument. Don't touch!
    dataProvider: IDataProvider;
    // We will use the __QUERYINTERFACE operator to "cast" IDataProvider to IDataProviderB
END_VAR
VAR
    success: BOOL;
END_VAR
// Inside the FB_Init method:
// success := __QUERYINTERFACE(_dataProvider, _dataProviderB);

Then in the base methods you should be able to use _dataProvider.GetModuleAData and in the extended methods _dataProviderB.GetModuleBData.


2. For raw pointers, we can just pass them:

FUNCTION_BLOCK BaseModuleFB
VAR
    pdata: POINTER TO BaseModuleDataStruct;
END_VAR
METHOD SetData: BOOL
VAR_IN_OUT
    data: BaseModuleDataStruct;
END_VAR
// Inside the SetDatamethod: "THIS^.pdata:= ADR(data);"


FUNCTION_BLOCK ModuleWithExtraFunctionFB EXTENDS BaseModuleFB
VAR
    pdata2: POINTER TO ModuleWithExtraFunctionDataStruct;
END_VAR
METHOD SetData: BOOL
VAR_IN_OUT
    data: BaseModuleDataStruct;
END_VAR
// Inside the SetDatamethod: "THIS^.pdata:= ADR(data);"
//                           "THIS^.pdata2:= ADR(data);"
// We are assuming that the user passed data of type "ModuleWithExtraFunctionDataStruct" here!!!
// Care needs to be taken here, otherwise we may get access violations!

In your Program on the first run call the SetData methods for both Function Blocks. Make sure you are passing the correct data struct to avoid any access violations!

Then in the base methods you should be able to use pdata^.position1 and in the extended methods pdata2^position2.


3. We could also combine the two for direct access to the structure while retaining the type safety of interfaces:

INTERFACE IDataProvider EXTENDS __System.IQueryInterface
METHOD GetDataAPtr : POINTER TO BaseModuleDataStruct

INTERFACE IDataProviderB EXTENDS IDataProvider
METHOD GetDataBPtr : POINTER TO ModuleWithExtraFunctionDataStruct

FUNCTION_BLOCK BaseModuleFB
VAR
    pdata: POINTER TO BaseModuleDataStruct;
END_VAR
METHOD FB_Init: BOOL
VAR_INPUT
    bInitRetains: BOOL; // Internal built-in hidden argument. Don't touch!
    bInCopyCode: BOOL;  // Internal built-in hidden argument. Don't touch!
    dataProvider: IDataProvider;
END_VAR
// Inside the FB_Init method: "THIS^.pdata:= dataProvider.GetDataAPtr();"


FUNCTION_BLOCK ModuleWithExtraFunctionFB EXTENDS BaseModuleFB
VAR
    pdata2: POINTER TO ModuleWithExtraFunctionDataStruct;
END_VAR
METHOD FB_Init: BOOL
VAR_INPUT
    bInitRetains: BOOL; // Internal built-in hidden argument. Don't touch!
    bInCopyCode: BOOL;  // Internal built-in hidden argument. Don't touch!
    dataProvider: IDataProvider;
END_VAR
VAR
    success: BOOL;
    _dataProviderB: IDataProviderB;
END_VAR
// Inside the FB_Init method:
// success := __QUERYINTERFACE(_dataProvider, _dataProviderB);
// IF (success) THEN
//     THIS^.pdata2 := _dataProviderB.GetDataBPtr();
// END_IF

Then in the base methods you should be able to use pdata^.position1 and in the extended methods pdata2^.position2.


4. If you'd prefer to not use pointers, you can use references instead:

This option was proposed by Stefan Roelofs in a comment below.

INTERFACE IDataProvider EXTENDS __System.IQueryInterface
METHOD GetDataARef : REFERENCE TO BaseModuleDataStruct

INTERFACE IDataProviderB EXTENDS IDataProvider
METHOD GetDataBRef : REFERENCE TO ModuleWithExtraFunctionDataStruct

FUNCTION_BLOCK BaseModuleFB
VAR
    rdata: REFERENCE TO BaseModuleDataStruct;
END_VAR
METHOD FB_Init: BOOL
VAR_INPUT
    bInitRetains: BOOL; // Internal built-in hidden argument. Don't touch!
    bInCopyCode: BOOL;  // Internal built-in hidden argument. Don't touch!
    dataProvider: IDataProvider;
END_VAR
// Inside the FB_Init method: "THIS^.rdata REF= dataProvider.GetDataARef();"


FUNCTION_BLOCK ModuleWithExtraFunctionFB EXTENDS BaseModuleFB
VAR
    rdata2: REFERENCE TO ModuleWithExtraFunctionDataStruct;
END_VAR
METHOD FB_Init: BOOL
VAR_INPUT
    bInitRetains: BOOL; // Internal built-in hidden argument. Don't touch!
    bInCopyCode: BOOL;  // Internal built-in hidden argument. Don't touch!
    dataProvider: IDataProvider;
END_VAR
VAR
    success: BOOL;
    _dataProviderB: IDataProviderB;
END_VAR
// Inside the FB_Init method:
// success := __QUERYINTERFACE(_dataProvider, _dataProviderB);
// IF (success) THEN
//     THIS^.rdata2 REF= _dataProviderB.GetDataBRef();
// END_IF

Then, inside your Function Blocks you may want to use the __ISVALIDREF() operator to validate that the cast/conversion was successful. After that in the base methods you should be able to use rdata.position1 and in the extended methods rdata2.position2.

NOTE: You may get many C0410 warnings:

C0410: COMPATIBILITY WARNING: A write Access to a Property of type REFERENCE calls the SET-Accessor for versions < 3.5.10.0 and writes the reference, but calls the GET-Accessor for versions >= 3.5.10.0 and writes the value! Use the operator REF= if you want to assign the reference.

If this annoys you, you may disable the C0410 warning in your compiler (Project Settings).



I uploaded an example PLCOpenXML file on GDrive, you can try importing it and play around with it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文