0

I have been trying to gain access to the AXIS_REF_SM3 i.e. Axis of SM3_Basic.MC_MoveAbsolute in order to return the wDriveId of the axis.

Since Axis is declared as a VAR_IN_OUT variable, I am unable to access it outside of the FB using the .Axis syntax since it will cause the compiler to throw a

C0178:  No external access to VAR_IN_OUT parameter 'Axis' of 'MC_MOVEABSOLUTE'."

error which makes sense since VAR_IN_OUT variables are not supposed to be remotely accessible.

I then tried to extend the FB with FUNCTION_BLOCK FB_MoveAbsolute EXTENDS SM3_Basic.MC_MoveAbsolute and write my own method to return the Axis in the PRG calling FB_MoveAbsolute but I am getting the following compiler error

C0283:  Function block 'FB_MoveAbsolute': No override possible on Method MC_MoveAbsolute.FB_Init with access specifier FINAL

instead.

Does anyone know of a better way to get the wDriveId value of the Axis from SM3_Basic.MC_MoveAbsolute?
Or some other way of checking which axis the FB is using?

void
  • 7
  • 3

1 Answers1

1

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:

  1. If you access the variable before setting it:
getVar := wrap.GetSomeVarMethod(); // calling this'll try to access someVar
wrap(someVar := someVar);
  1. 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:

  1. 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.

  1. 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);
Guiorgy
  • 1,405
  • 9
  • 26
  • oh cool, I literally didn't think about just wrapping the FB, I don't suppose there is a way to prevent `FinalBlock` from being used as is with `INTERNAL` or something similar if you don't have access to the library source code right? – void Dec 20 '21 at 13:10
  • See the edit. TL; DR: You can have your wrapper create and fully manage the wrapped object without letting the user touch the wrapped object – Guiorgy Dec 21 '21 at 17:32