Welcome to our new forum
All users of the legacy CODESYS Forums, please create a new account at account.codesys.com. But make sure to use the same E-Mail address as in the old Forum. Then your posts will be matched. Close

FB Derivation Within a Library

2016-02-09
2016-02-12
  • Andrew Hollom - 2016-02-09

    Hello,

    I'm developing a library and am trying to use inheritance where appropriate. An example of the sort of thing I'm trying to do is shown below.

    FUNCTION BLOCK INTERNAL BaseClass
      VAR_IN_OUT
        i0: T1;
      END_VAR
      VAR_IN
        i1: BOOL;
        i2: BOOL;
        i3: T2;
        i4: T3;
      END_VAR
      VAR_OUT
        o0: BOOL;
        o1: BOOL;
        o2: BOOL;
        o3: BOOL;
        o4: BOOL;
        o5: WORD;
      END_VAR
      // Define common logic here using I/O variables and methods/properties overridden by derived classes.
    END_FUNCTION_BLOCK
    FUNCTION BLOCK DerivedClass EXTENDS BaseClass
      VAR_IN
        i5: LREAL;
        i6: LREAL;
        i7: LREAL;
        i8: LREAL;
      END_VAR
      SUPER^(i0 := i0);  // Call the base class's logic (using overridden methods/properties)
    END_FUNCTION_BLOCK
    
    ```This almost works, but when I use it thus:
    

    VAR
      fb: DerivedClass;
      x: T1;
      y: BOOL;
    END_VAR
    fb(i0 := x, i1 := TRUE, i5 := 100.0, i6 := 100.0, i7 := 10.0, o0 => y);

    ```
    I get 'Cannot access internal variable BaseClass.i0' (and the same for 'o0'). The problem is that I have made the base class internal, as I want a user of the library to be neither aware of the base class's existence nor able to use it. The problem seems to be that the INTERNAL qualifier on the base class FB seems to make all its I/O variables inaccessible when the derived class is used. If I remove the INTERNAL qualifier, all is well, but the user can see the base class in the auto-complete pop-up.

    If I do the above, but include it directly in a PRG, then all is fine, it's just when it is in a library that the problem with INTERNAL arises.

    I have tried placing {attribute 'hide'} ahead of the base class's definition, which serves well from the point of view of stopping the base class from appearing in auto-complete pop-ups, but it is still possible to use the base class if you know its name.

    Can anyone advise on the best way to hide base classes in a library from the user, but not in such a way that stops the base class's I/O variables from being accessed?

    Finally, is the way that the INTERNAL qualifier hides its I/O variables correct, i.e. should it just make the FB for internal use only?

    Thanks in advance for your help.

    Andrew.

     
  • Anonymous - 2016-02-10

    Originally created by: scott_cunningham

    The keyword INTERNAL means that your derived class can access the base class within it's code, but you cannot access the base class from outside (which is what your are doing when you call your derived class). You will need to add properties to your derived class to pass on the values to the base class.

     
  • Andrew Hollom - 2016-02-10

    Thanks for the help Scott.

    This behaviour depends on how you look at it. One way is that the derived class inherits the I/O variables, so they should be usable because the derived class is not internal. The other way is that the derived class does not inherit the parent class's I/O variables, such that when you use them with an instance of the derived class, you are actually interfacing directly with the parent class, which is not possible because it is internal. The latter way appears to match how it's implemented.

    The behaviour as-is is akin to the base class's I/O variables having a notional PRIVATE or PROTECTED attribute, making them accessible in only the base class or base and parent class respectively, but not accessible outside the derived class. Personally, I don't find this semantic interpretation too useful.

    This is especially true in my situation since the base class has a VAR_IN_OUT variable (a structure), which the compiler insists I initialise when I call an instance of the derived class, so I cannot even rename all the base class's I/O variables (and duplicate them in the derived class) and assign them when I call SUPER^() from the derived class. For example, issuing SUPER^(i0base := i0, i1base := i1, i2base := i2, i3base := i3, i4base := i4, o0base => o0, o1base => o1, o2base => o2, o3base => o3, o4base => o4, o5base => o5) in the derived class's code body seems okay, but when the derived class's instance is called, the compiler insists that 'i0base' needs assigning in that call, but this cannot be done because it is INTERNAL. This makes using an FB derived from an INTERNAL FB that has a VAR_IN_OUT I/O variable impossible! Consequently, doing such should either result in a compilation error or the semantics of how the base class's I/O variables are interpreted needs changing. I don't even think using properties as you suggested will work, as the compiler will still insist that the VAR_IN_OUT I/O variable of the base class be initialised, even though it won't allow me to do so.

    Thanks again for your help,

    Andrew.

     
  • Anonymous - 2016-02-10

    Originally created by: scott_cunningham

    For reasons you mention (and others), I try to avoid VAR_IN_OUTs. I will use either a pointer or REFERENCE TO instead to achieve the link. Maybe you could use the following little trick:

    FB_BASE
    - Prop1
    - Prop2

    FB_BIGGER
    FbBase : FB_BASE
    - PropA
    - PropB

    PROPERTY ProbB REFERENCE TO FB_BASE

    Only define a GET (PropB := FbBase).

    (This is from memory - it was something like this).

    Then, when you can do: FbBig.PropB.Prop1 and FbBig.PropB.Prop2

    This is different to what you were originally hoping for (FbBig.Prop1 and FbBig.Prop2), however.

     
  • singleton - 2016-02-12

    This is finally the concept of OOP. If you want to access any data of the base FB, use properties or methods inside the derived FB and "forward" or whatever you want to do with the data to the base FB. It's not perfect inside CoDeSys, but I guess it's the best on the market, at least inside automation business

     

Log in to post a comment.