You could try to make a wrapper around the Function Block:
FUNCTION_BLOCK FINAL FinalBlock
VAR_IN_OUT
someVar: DINT;
END_VAR
FUNCTION_BLOCK Wrapper
VAR_IN_OUT
someVar: DINT;
END_VAR
VAR
_wrapped_obj: POINTER TO FinalBlock;
END_VAR
_wrapped_obj^(someVar := someVar);
//////
METHOD FB_Init: BOOL
VAR_INPUT
bInitRetains: BOOL;
bInCopyCode: BOOL;
END_VAR
VAR_IN_OUT
wrappedObj: FinalBlock;
END_VAR
THIS^._wrapped_obj := ADR(wrappedObj);
//////
METHOD GetSomeVarMethod : DINT
GetSomeVarMethod := THIS^.someVar;
//////
METHOD GetWrappedObjP : POINTER TO FinalBlock
GetWrappedObjP := THIS^._wrapped_obj;
PROGRAM SR_Main
VAR
original: FinalBlock;
someVar: DINT := 5;
wrap: Wrapper(original);
getVar: DINT;
END_VAR
wrap(someVar := someVar); // use this instead of "original(someVar := someVar);"
getVar := wrap.GetSomeVarMethod(); // getVar = 5
Edit: If you are going to use references (or pointers), 1 thing to watch out for is invalid reference exceptions. You'll get them in those 2 cases:
- If you access the variable before setting it:
getVar := wrap.GetSomeVarMethod(); // calling this'll try to access someVar
wrap(someVar := someVar);
- You forget that you should use the wrapper instead of the wrapped object:
original(someVar := someVar); // should have called "wrap(someVar := someVar);" instead!
getVar := wrap.GetSomeVarMethod();
To avoid that you can try:
- Check the reference before accessing it:
METHOD GetSomeVarMethod : DINT
IF __ISVALIDREF(THIS^.someVar) THEN
GetSomeVarMethod := THIS^.someVar;
ELSE
GetSomeVarMethod := -1;
END_IF
or
TYPE OPTIONAL
STRUCT
value: DINT;
error: BOOL;
END_STRUCT
END_TYPE
METHOD GetSomeVarMethod : OPTIONAL
IF __ISVALIDREF(THIS^.someVar) THEN
GetSomeVarMethod.value := THIS^.someVar;
ELSE
GetSomeVarMethod.error := TRUE;
END_IF
However, if the user forgets to call wrap(someVar := someVar);
and only ever calls original(someVar := someVar);
then someVar will forever be invalid.
- Instead of passing the object to the wrapper, have the wrapper create and manage the object completely:
FUNCTION_BLOCK Wrapper
VAR_IN_OUT
someVar: DINT;
END_VAR
VAR
// with the 'no_init' attribute, we can let our FB_Init method initialize this object using user given arguments.
// If FinalBlock doesn't have any custom initialization arguments, you can skip this attribute and the FB_init code below.
{attribute 'no_init'}
_wrapped_obj: FinalBlock;
END_VAR
_wrapped_obj(someVar := someVar);
//////
METHOD FB_Init: BOOL
VAR_INPUT
bInitRetains: BOOL;
bInCopyCode: BOOL;
// FinalBlock initialization arguments
END_VAR
THIS^._wrapped_obj.FB_Init( (* initialization arguments *) );
Either way, I'd recommend having good documentation on how to properly use your wrapper and avoid mistakes.
Alternatively, if what you want is to pass the Function Block to a function and access the value of the VAR_IN_OUT variable inside that function, you can just pass that value alongside the Function Block (since if it is VAR_IN_OUT, you should have access to the original anyways):
VAR
original: FinalBlock;
someVar: DINT := 5;
END_VAR
original(someVar := someVar);
getVar := SomeFunction(obj := original, someVar := someVar);
or:
TYPE Bundle :
STRUCT
obj: REFERENCE TO FinalBlock;
someVar: REFERENCE TO DINT;
END_STRUCT
END_TYPE
//////
VAR
original: FinalBlock;
someVar: DINT := 5;
bun: Bundle(obj := original, someVar := someVar);
END_VAR
original(someVar := someVar);
getVar := SomeFunction(bundle := bun);