class with IOs

elfrosch
2017-02-09
2017-02-20
  • elfrosch - 2017-02-09

    Hey hey,

    I was asking already on german board, but I got no answer...
    http://forum-de.codesys.com/viewtopic.php?f=10&t=5291&sid=505c983d12df32355ae2dcc7304480af

    maybe I've better luck here.

    I tried to implement a cylinder class.

    So I started with an interface:

    INTERFACE ICylinder
    METHOD move2Home: ICylinder
    METHOD move2WorkingPos: ICylinder
    METHOD isInHomePos: BOOL
    METHOD isInWorkingPos: BOOL
    

    then I was implementing a cylinder with two outputs and two inputs

    FUNCTION_BLOCK Cylinder2Out2In IMPLEMENTS ICylinder
    VAR
       DO_Cylinder2WorkingPos: POINTER TO BOOL; (* digital output for move cylinder to working position *)
       DO_Cylinder2HomePos: POINTER TO BOOL; (* digital output for move cylinder to home position *)
       DI_CylinderInHomePos: POINTER TO BOOL; (* digital input for cylinder is in home position *)
       DI_CylinderInWorkingPos: POINTER TO BOOL; (* digital input for cylinder is in working position *)
    END_VAR
    METHOD move2Home: ICylinder
       DO_Cylinder2WorkingPos^ := FALSE;
       DO_Cylinder2HomePos^ := TRUE;
       move2Home := this;
    METHOD move2WorkingPos: ICylinder
       DO_Cylinder2WorkingPos^ := TRUE;
       DO_Cylinder2HomePos^ := FALSE;
       move2Home := this;
    METHOD isInHomePos: BOOL
       isInHomePos := ((DI_CylinderInHomePos^) AND (NOT (DI_CylinderInWorkingPos^)));
    METHOD isInWorkingPos: BOOL
       isInHomePos := ((DI_CylinderInWorkingPos^) AND (NOT (DI_CylinderInHomePos^)));
    METHOD constructor: Cylinder2Out2In
    VAR_INPUT
       doCylinder2WorkingPos: POINTER TO BOOL; (* digital output for move cylinder to working position *)
       doCylinder2HomePos: POINTER TO BOOL; (* digital output for move cylinder to home position *)
       diCylinderInHomePos: POINTER TO BOOL; (* digital input for cylinder is in home position *)
       diCylinderInWorkingPos: POINTER TO BOOL; (* digital input for cylinder is in working position *)
    END_VAR
       DO_Cylinder2WorkingPos := doCylinder2WorkingPos;
       DO_Cylinder2HomePos := doCylinder2HomePos;
       DI_CylinderInHomePos := diCylinderInHomePos;
       DI_CylinderInWorkingPos := diCylinderInWorkingPos;
       constructor := this;  
    

    I know it's not a real cylinder cause there is no error handling, and the position getter are so fine, but it doesn't matter for my questions.
    I point to booleans and a boolean is one byte long, so when my outputs are on QX1.0 and QX1.1 it does not work. Pointer to bit is not possible, cause of word alignment.
    Of corse I can do some thing like pointer to byte address and an integer for the bit address:

    OutByte^.DO_Cylinder2WorkingPos := TRUE;
    

    But from my point of view, it's stupid and the code looks crap, I mean this is a plc and the main cause of a plc is handle IOs.
    The other thing which I don't understand, is why I've to use pointer. I couldn't initialize a reference with a setter.

    I was happy to get a plc with interfaces and classes, but with out IO handling it's useless. I hope somebody knows how to deal with IOs and class.
    I'm grateful for hints or solutions.

    BR

    elfrosch

     
  • TimvH

    TimvH - 2017-02-09

    Why do you want to use pointers to the inputs and outputs?
    Why not use VAR_INPUT and VAR_OUTPUT and connect your actual inputs and outputs to the function block instance?

    Or map your inputs and outputs to variables of your function block instance (local variables).

     
  • josepmariarams - 2017-02-09

    Hi elfrosch

    I think that di/o has not to be inouts of cillinder fb. For me, the io of the cillinder has to be an Ref_cillinder for give orders and ask him (similar to ref_axe)

    The di are declared internally in cillinder fb. Inside di fb we do

    Var
    Byte_8: reference to byte
    End var

    Byte_8 ref= ix0

    And, for example if the di is in the 7th byte 2on bit:

    Mydi:= (byte_8[7] and 16#4)

    Obviously 7th byte and 2ond bit has to be configurable inside fb di using varinput constant or via a file which fb di reads at starting.

     

    Related

    Talk.ru: 7

  • elfrosch - 2017-02-10

    Hi TimvH, hi Josep,

    thanks for your replies...

    TimvH hat geschrieben:
    Why do you want to use pointers to the inputs and outputs?
    Why not use VAR_INPUT and VAR_OUTPUT and connect your actual inputs and outputs to the function block instance?

    Okay this is common way for plc, but then it's useless to use interfaces and classes. Think about I've now a cylinder with two outputs and two inputs, for example the mech. designer decides it's cheaper to use a cylinder with spring for return. If the code is interface driven I don't care, cause I write a new cylinder class with one output and one input and thats it. In your case I've to change all lines of code where I call the cylinder object. Normaly is encapsulation of data one thing what you try to get with oo.

    @Josep
    If I do this: >Zitat:
    Byte_8 ref= ix0
    cylinder, then I've a cylinder for one address byte. In a non chemistry automation you've normaly dozens of cylinders, and I'm honestly too lazy to handle more than one class, also I belive in don't repeat yourself while coding. However while playing with codesys v3, I couldn't inject a reference, so thats the reason why I use pointers.
    If there is no other way then I'll do it with byte and bit address, but then I've ugly constructors.

    Are here 3S developer? Maybe they can tell us what 3S thought about there oo concept, and how to use the oo stuff with IOs the right way.

    BR elfrosch

     
  • josepmariarams - 2017-02-10

    Hi.

    You can extend the problem of oop with io to oop and servo axes.

    Imagine that you have a machine which is made of 10 equals sub elements. Every one of that elements are made of one servo axe and one cillinder.

    You can made on fb which internally has declared one fb which manages the cillinder, but what do you do with axes. You can not work internally with axe1 or axe2...

    One idea could be create an fb which will have an array which points to all axes (obviously this fb has to be an singleton).

    I have another singleton with another array which points to all io map.

    In my cillinder-axe fb i have two varinouts connected to the previous singletons. And I have refs (or pointers) to be used as dio nio or axes.

    To assign a real axe to my internal axe, all my fb has one csv file where I can configure to which physical axe are the internal axe connected.

    At the end all my code is made inside classes. But there has to be one moment in which we have to connect clases attributes to real world elements which are singletons.

     
  • Joan M - 2017-02-12

    Hi elfrosch,

    I've been since 2005 using extensively pointers to bool without problems...

    You can simply use "ptr := ADR(input_variable);" to assign the right bool to the pointer.
    And then "ptr^ := TRUE" will simply assign a TRUE to the bool pointed...

    And yes, this works with inputs and outputs...

    It would be terrible if that would not work.

    Which CodeSys version are you using?

    PS: at the beginning I remember having the problem you mention, but not now.

    That's strange...

     
  • Anonymous - 2017-02-20

    Originally created by: scott_cunningham

    I think your complaint is that a BOOL takes a BYTE space, right? Maybe this helps...

    Take your oop one step further. Instead of your Cylinder directly writing to an IO, make an IO object. In the IO object, include a method to link to your hardware register. Inside, you can simply do:

    FUNCTION_BLOCK DigOutput
    VAR
       Dummy : BOOL;
       HwReg : REFERENCE TO BOOL := Dummy;
    END_VAR
    METHOD LinkRegister
    VAR_INPUT
       Register : REFERENCE TO BOOL;
    END_VAR
    HwReg REF= Register;
    METHOD TurnOn
    HwReg := TRUE;
    

    In your EtherCAT IO Mapping of the IO device, create BOOL variables linked to the hardware registers. Link to these BOOLs:

    VAR
       O1 : DigOutput;
       O2 : DigOutput;
    END_VAR
    O1.LinkRegister(Register := HwOut0);
    O1.TurnOn();
    

    IMG: hdware_link.png

     

Log in to post a comment.