Objects and Array Sizes when Extending and Implementing

michaelADE
2018-10-05
2018-10-11
  • michaelADE - 2018-10-05

    Hey Everyone,

    First time posting, been looking through the forum for a while though.

    I know that you can have arrays of varying size (at compile) if you declare them with constants so i want to create a series of objects(FBs) that have a property which is an array all extending from the one Interface but depending on the object will depend on the size of the array.

    So my logic was

    INTERFACE itfPLCModule
    //This is a property attached to the object not actually in the variable declaration section
    PROPERTY aDigOut : array [0..0] of struct_OutputDig
    
    FUNCTION_BLOCK clsPLCOne IMPLEMENTS itfPLCModule
    VAR CONSTANT
       NUM_DIG_OUTPUTS: INT:= 15;
    END_VAR
    //This is a property attached to the object not actually in the variable declaration section
    PROPERTY aDigOut : array [0..NUM_DIG_OUTPUTS] of struct_OutputDig
    
    FUNCTION_BLOCK clsPLCTwo EXTENDS clsPLCOne 
    VAR CONSTANT
       NUM_DIG_OUTPUTS: INT:= 8;
    END_VAR
    //This is a property attached to the object not actually in the variable declaration section
    PROPERTY aDigOut : array [0..NUM_DIG_OUTPUTS] of struct_OutputDig
    

    However when i compile this I get two different errors
    1. Interface of overridden method '__SetADIGOUT' of interface 'ITFPLCMODULE' doesnt match declaration.
    2. Duplicate definition of variable 'NUM_DIG_OUTPUTS' in function block 'clsPLCTwo' and in base 'clsPLCOne'

    So my question is, is there a way to have varying sizes for arrays when stored as arrays if they are inheriting or extending from the same interface.

    Any advice would be greatly appreciated

     
  • dFx

    dFx - 2018-10-05
    The inherited function block is permitted to overwrite the methods that you have defined in the basic function block. 
    This means that the inherited function block can define a method with the same name, the same inputs and the same output as is defined by the basic function block. 
    

    Codesys help tells you may overwrite methods but I don't see it about variable definition.

    moreover

    The inherited function block is not permitted to have any function block variables with the same name as used for the base function block. 
    The compiler reports this as an error. 
    

    BTW, this is PLC programming, so it's basic about inheritance.

    To get back in your troubles, what is the aim of such interfaces ?

     
  • michaelADE - 2018-10-05

    Hey dFx,

    Thanks so much for your speedy reply. I was worried that it may not have been possible.

    My intention was that in my current project I will be having one master PLC/Screen and then a number of slave IO units that will communicate over can. In the CANOpen_Manager for a single output i have at least four variables or bit that are linked together (xValue, xIsOpen, xIsShort, xIsOvercurrent) so I wanted to group all of the related variables/states into one place. And then some of the physical valves I am driving require two outputs to be active on two different PLC's to be active at the same time. So having them stored in a plc object means that when an output is turned on if a short occures it can easily be determined that its on a certain PLC and also all the health parameters of the plc.
    Its probably overkill but the intention of then having all the PLCs relate back to the same interface is that i can just store all plc's used in the project in an array and can loop through all of the PLC's as desired to check system health of each.
    For reference i have attached a rough UML of what i am trying to do.

    IMG: PLC UML.JPG

     
  • Anonymous - 2018-10-05

    Originally created by: Viacheslav Mezentsev

    You can use pointer to struct as a type for property. This is not safe but will work.

    function_block TFB
    var constant
        MAX_SIZE: int := 100;
    end_var
    var
        _items: array [ 0 .. MAX_SIZE ] of TStruct;
    end_var
    property Items : pointer to TStruct
    [get] Items := adr( _items );
    // ...
    type TStruct :
    struct
        Value: real;
    end_struct
    end_type
    // ...
    program PLC_PRG
    var
        bInit: bool := true;
        r1, r2: real;
        tmp: pointer to TStruct;
        fb: TFB;
    end_var
    if bInit then
        
        tmp := fb.Items;
        tmp[0].Value := 1.5;  
        tmp[1].Value := 10.5;
        bInit := false;    
        
    end_if
    r1 := tmp[0].Value;
    r2 := tmp[1].Value;
    
     

    Related

    Talk.ru: 1

  • josepmariarams - 2018-10-07

    I would create an fb which manages the state, overcurrent, ... Of every output valbe. (Fb_valb)

    I would create an fb with an array of pointer to fb_valb, with method add. This fb has to be instantiate before any fb_valb, there exist an attribute to do that.

    In the fb_init of the fb_valb, add himself to the array of pointers.

    You will have an array of fb_valbs to monitorize its.

    To know wich valb is, use in fb_valb the attribute 'parameterstringof'. And you will have on an string the name of the fb.

    Sent from my Moto G (5S) Plus using Tapatalk

     
  • dFx

    dFx - 2018-10-08

    michaelADE hat geschrieben:
    My intention was that in my current project I will be having one master PLC/Screen and then a number of slave IO units that will communicate over can. In the CANOpen_Manager for a single output i have at least four variables or bit that are linked together (xValue, xIsOpen, xIsShort, xIsOvercurrent) so I wanted to group all of the related variables/states into one place. And then some of the physical valves I am driving require two outputs to be active on two different PLC's to be active at the same time. So having them stored in a plc object means that when an output is turned on if a short occures it can easily be determined that its on a certain PLC and also all the health parameters of the plc.

    Are you talking about remote IOs when saying " two different PLC's" ? or IO units are themselves PLCs ?

    About your issue, understanding you have one PLC and remote IOs (that may be PLCs but used only for IOs), I would process as following :
    1) Declare your full class with maximum size arrays
    2) Declare one (retentive) parameter to know which are in use (or just the last one)(embeded in your class, but not initialized)
    3) Declare each class instance
    4) Address your IOs using your class instance members directly
    5) Force your parameter to the right value (I would recommend by code, this avoid retentive flag)
    6) Don't forget your instance calls (if you did an array of your class, this can be easily looped)

    This feels way more reliable to me than using unmanaged pointers, just because of the complexity of the code, and issues related to online changes.
    Moreover, you cannot resize arrays at runtime (as far as I know). see https://stackoverflow.com/questions/39723966/creat-an-array-in-codesys-with-changeable-size

     
  • michaelADE - 2018-10-11

    Hey Everyone,

    Thanks for your advice,

    I decided to just go with having a pointer to a global array of the various IO parameters in the object.

    Not amazing practice re OO but I also realised that i could map property calls of an object to to the CANopen I/O mapping parameters.

    And by having pointers for the various outputs/inputs that means that if the specific PLC doesnt have a specific output/input type i can have them all point to the same 'empty' array.

    Note:

    FUNCTION_BLOCK clsBasicPLC IMPLEMENTS itfPLCModule
    VAR CONSTANT
       MAX_ANALOG_OUTPUT: DINT:= 1024;
    END_VAR
    VAR
       xSuspendOutputs: BOOL := FALSE;
       pDigOut : POINTER TO ARRAY [0..MAX_DIG_OUT_INDEX] OF struct_OutputDig;
       pAnalogIn : POINTER TO ARRAY [0..MAX_ANA_IN_INDEX] OF struct_InputAnalog;
       pAnalogOut : POINTER TO ARRAY [0..MAX_ANA_OUT_INDEX] OF struct_OutputAnalog;
       pDigIn : POINTER TO ARRAY [0..MAX_DIG_IN_INDEX] OF struct_InputDig;
       pSupplyVoltage : POINTER TO ARRAY [0..MAX_SUPPLY_VOLT_INDEX] OF struct_SupplyVoltage;
       enumPartNumber : enum_PLCPartNumbers;
       sPLCName : STRING;
       iMaxAnaInIndex : INT;
       iMaxAnaOutIndex : INT;
       iMaxDigInIndex : INT;
       iMaxDigOutIndex : INT;
       iMaxSupplyVoltIndex : INT;
       
    END_VAR
    
    Global Variable List
       aSprayHeadPLC_DigOut: ARRAY [0..CR2050_DIG_OUT_INDEX] OF struct_OutputDig:= aCR2050OutputIDs;
       aSprayHeadPLC_SupplyVolt: ARRAY [0..2] OF struct_SupplyVoltage:= aCR20XXSupplyVoltIDs;
       
       //Used as place holders for when a PLC controller doesn't have an input or input of a certain type
       aPLC_NoAnalogOut: ARRAY [0..0] OF struct_OutputAnalog;
       aPLC_NoDigOut: ARRAY [0..0] OF struct_OutputDig;
       aPLC_NoDigIn: ARRAY [0..0] OF struct_InputDig;
       aPLC_NoAnalogIn: ARRAY [0..0] OF struct_InputAnalog;
    
     

Log in to post a comment.