我在使用 OOP 原則為我們的機器設計基本 FB 時遇到了困難。
一臺機器使用不同的模塊,如下所示:
module1 : BaseModuleFB;
module2 : BaseModuleFB;
模塊(機器的某些部分)需要資料才能運行(如位置、延遲等)。這些資料將存盤在配方中,因此將資料分組到結構中是有意義的。
FUNCTION_BLOCK BaseModuleFB
VAR
Data : BaseModuleDataStruct;
END_VAR
現在我有一臺不同的機器,有一些額外的要求。而且還需要一些額外的資料。所以我擴展了模塊:
FUNCTION_BLOCK ModuleWithExtraFunctionFB EXTENDS BaseModuleFB
VAR
Data : ModuleWithExtraFunctionDataStruct;
END_VAR
上面的代碼將無法編譯,因為變數 Data 已在基類中使用。
順便說一下,資料結構看起來像這樣:
TYPE BaseModuleDataStruct:
STRUCT
position1:INT;
END_STRUCT
END_TYPE
TYPE ModuleWithExtraFunctionDataStruct EXTENDS BaseModuleDataStruct:
STRUCT
position2:INT;
END_STRUCT
END_TYPE
我想到的另一個選擇是創建一個名為Data. 派生類可以覆寫此屬性。
但是這種方法也失敗了,因為您無法更改被覆寫屬性的型別。
也許有人對此有一些好主意?謝謝。
uj5u.com熱心網友回復:
為什么不直接將結構的內容移動到功能塊中:
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. 對于介面,我們將使用__QUERYINTERFACE運算子。
// 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中。
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在下面的評論中提出的。
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: 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.
如果這讓您煩惱,您可以在編譯器(專案設定)中禁用 C0410 警告。
我在GDrive上上傳了一個示例 PLCOpenXML 檔案,您可以嘗試匯入它并使用它。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/494855.html
上一篇:是否可以在Javascript中在運行時創建新物件?
下一篇:使用自定義混合型別創建Enum
