CODESYS - the IEC 61131-3 automation software

Welcome to the official CODESYS Forum
Deutsche Version English version russian version 
It is currently Wed Oct 18, 2017 11:10 pm

All times are UTC+01:00




Post new topic  Reply to topic  [ 12 posts ] 
Author Message
PostPosted: Tue Oct 07, 2014 5:02 am 
Offline
Frequent User
Frequent User

Joined: Fri Sep 02, 2011 8:02 pm
Posts: 255
I have a project that will have hundreds of objects (FBs) implementing different interfaces. I am using __QUERYINTERFACE and looping through the objects to call certain methods. Since I didn't find a way to automatically learn all objects and I don't want to rely on remembering to manually assign drive objects to the drive array and pinch objects to the pinch array, etc... I created an AllObjects array and learn from this array. But! I really do not want to MANUALLY add to this array every time I create a new object - it will be too easy to miss one.

Does anyone know a method to step through var memory and look for objects meeting IDriveClass, IPinchClass, etc?

Simplified and in small scale, example code:
Code:
VAR CONSTANT
  MAX_OBJ: INT:= 4;
END_VAR
VAR
  Roller: ClassRoller;
  Pinch1, Pinch2: ClassPinch;
  Drive1, Drive2: ClassACDrive;
  AllObjects: ARRAY[0..MAX_OBJ] OF IAllObjClass:= [Roller, Pinch1, Pinch2, Drive1, Drive2]; // all object array workaround
  DriveObjects: ARRAY[0..MAX_OBJ] OF IDriveClass;  //array to store drive objects for fast runtime (__queryinterface is slow)
  PinchObjects: ARRAY[0..MAX_OBJ] OF IPinchClass; //array to store pinch objects for fast runtime (__queryinterface is slow)
END_VAR

...init code somewhere...

// learn drive objects
ObjIndex:= 0;
FOR J:= 0 TO MAX_OBJ DO
   IF __QUERYINTERFACE(AllObjects[J], IDriveClass) THEN
      DriveObjects[ObjIndex]:= IDriveClass;
      ObjIndex:= ObjIndex + 1;
   END_IF
END_FOR
TotalDriveObjects:= ObjIndex;

...main code...
For Loop:= 0 to TotalDriveObjects DO
  DriveObjects[Loop].Home();
END_FOR

etc

_________________
Scott Cunningham
KEB America, Inc.
www.kebblog.com
www.kebamerica.com


Top
   
PostPosted: Wed Oct 08, 2014 9:40 am 
Offline

Joined: Thu Aug 04, 2011 11:16 am
Posts: 78
Why do you use a generic interface IAllObjClass when you have to handle the content separetly?

What is the benefit instead of using specific interfaces for each type?


Top
   
PostPosted: Wed Oct 08, 2014 12:30 pm 
Offline
Frequent User
Frequent User

Joined: Fri Sep 02, 2011 8:02 pm
Posts: 255
There will be quite a number of interfaces - at least 20 (I left all of these out of my sample code) and a large number of objects (many hundreds). To avoid using __QUERYINTERFACE during the actual machine operation (too slow doing this with nearly 1000 objects - results in about 2mS of execution time), I must build ahead of time, specific lookup arrays for each interface. Therefore, I create one single large array of all objects, and then build individual arrays from there (FOR loops for each interface compare against the whole list).

Additionally, by having IAllObjClass extend __System.IQueryInterface and requiring all objects to extend IAllObjClass, __QUERYINTERFACE is working.

There is also the possibility that there will be some methods/properties that all objects may have to implement.

_________________
Scott Cunningham
KEB America, Inc.
www.kebblog.com
www.kebamerica.com


Top
   
PostPosted: Thu Oct 09, 2014 3:12 pm 
Offline

Joined: Thu Aug 04, 2011 11:16 am
Posts: 78
scott_cunningham wrote:
There is also the possibility that there will be some methods/properties that all objects may have to implement.


In this case I understand the generic interface, but I do not know a good solution for your problem.

The only thing I could imagine is to have a range inside the array for each specific type, e.g. 1-10 are drives, 11-20 the next type,....


Top
   
PostPosted: Thu Oct 09, 2014 7:59 pm 
Offline
Frequent User
Frequent User

Joined: Fri Sep 02, 2011 8:02 pm
Posts: 255
If I put all of the objects into one large array, then it is quite easy to progmatically generate the smaller, specific arrays for each interface (some objects will belong to multiple interfaces due to their functionality):

Code:
//... main object array already populated....
//... now build array for IDrive objects....

// learn drive objects
ObjIndex:= 0;
FOR J:= 0 TO MAX_OBJ DO
   IF __QUERYINTERFACE(AllObjects[J], IDriveClass) THEN
      DriveObjects[ObjIndex]:= IDriveClass;
      ObjIndex:= ObjIndex + 1;
   END_IF
END_FOR
TotalDriveObjects:= ObjIndex;

//... now build array for IPinch objects....
ObjIndex:= 0;
FOR J:= 0 TO MAX_OBJ DO
   IF __QUERYINTERFACE(AllObjects[J], IPinchClass) THEN
      PinchObjects[ObjIndex]:= IPinchClass;
      ObjIndex:= ObjIndex + 1;
   END_IF
END_FOR
TotalPinchObjects:= ObjIndex;

//... now build array for Ixxxx objects....
//... etc


This, of course, takes some time, but no problem at machine power on. Just trying to avoid manually building the main array - but so far, it looks like I will have too!

_________________
Scott Cunningham
KEB America, Inc.
www.kebblog.com
www.kebamerica.com


Top
   
PostPosted: Tue Oct 14, 2014 9:02 pm 
Offline
Frequent User
Frequent User

Joined: Fri Sep 02, 2011 8:02 pm
Posts: 255
Posting solution for those who can use it. Solution requires usage of FB_init() for each Class type. In this way, the arrays of objects are already filled at program start:

...define some interfaces...
Code:
INTERFACE IAllObjects// absolute base class

   METHOD Fb_init : BOOL
   VAR_INPUT
      bInitRetains : BOOL; // Initialization of the retain variables
      bInCopyCode : BOOL; // Instance moved into copy code
   END_VAR

Code:
INTERFACE IConveyor EXTENDS IAllObjects // interface for conveyors

Code:
INTERFACE IDrive EXTENDS IAllObjects // interface for drives


...define global collection arrays...
Code:
GVL_OBJECTS

   VAR_GLOBAL CONSTANT
      MAX_OBJECTS: INT:= 10;
   END_VAR
   VAR_GLOBAL
      CollectionAllObjects: CollectionHandlerAll; // maintains array of all objects
      CollectionDrives: CollectionHandlerDrives; // maintains array of drives
      CollectionConveyors: CollectionHandlerConveyors; // maintains array of conveyors
   END_VAR


...define FB to handle array collections...
Code:
FUNCTION_BLOCK CollectionHandlerAll
VAR_INPUT
END_VAR
VAR_OUTPUT
   TotalObjects: INT; // total objects found - use for FOR/NEXT loop iterations of methods
END_VAR
VAR
   Collection: ARRAY[1..GVL_OBJECTS.MAX_OBJECTS] OF IAllObjects; // object array
END_VAR

   METHOD AddObject
   VAR_INPUT
      Obj   : IAllObjects;
   END_VAR

   TotalObjects:= TotalObjects + 1;
   Collection[TotalObjects]:= Obj;


Code:
FUNCTION_BLOCK CollectionHandlerConveyor
VAR_INPUT
END_VAR
VAR_OUTPUT
   TotalObjects: INT; // total objects found - use for FOR/NEXT loop iterations of methods
END_VAR
VAR
   Collection: ARRAY[1..GVL_OBJECTS.MAX_OBJECTS] OF IConveyor; // object array
END_VAR

   METHOD AddObject
   VAR_INPUT
      Obj   : IConveyor;
   END_VAR

   TotalObjects:= TotalObjects + 1;
   Collection[TotalObjects]:= Obj;


Code:
FUNCTION_BLOCK CollectionHandlerDrive
VAR_INPUT
END_VAR
VAR_OUTPUT
   TotalObjects: INT; // total objects found - use for FOR/NEXT loop iterations of methods
END_VAR
VAR
   Collection: ARRAY[1..GVL_OBJECTS.MAX_OBJECTS] OF IDrive; // object array
END_VAR

   METHOD AddObject
   VAR_INPUT
      Obj   : IDrive;
   END_VAR

   TotalObjects:= TotalObjects + 1;
   Collection[TotalObjects]:= Obj;


...define device FBs...
Code:
FUNCTION_BLOCK ClassConveyor IMPLEMENTS IConveyor // conveyor FB
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR

   METHOD Fb_init : BOOL
   VAR_INPUT
      bInitRetains : BOOL; // Initialization of the retain variables
      bInCopyCode : BOOL; // Instance moved into copy code
   END_VAR

   GVL_OBJECTS.CollectionAllObjects.AddObject(THIS^);   // add instance to all objects collection
   GVL_OBJECTS.CollectionConveyors.AddObject(THIS^);   // add instance to conveyor collection


Code:
FUNCTION_BLOCK ClassDrive IMPLEMENTS IDrive // drive FB
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR

   METHOD Fb_init : BOOL
   VAR_INPUT
      bInitRetains : BOOL; // Initialization of the retain variables
      bInCopyCode : BOOL; // Instance moved into copy code
   END_VAR

   GVL_OBJECTS.CollectionAllObjects.AddObject(THIS^);   // add instance to all objects collection
   GVL_OBJECTS.CollectionDrives.AddObject(THIS^);      // add instance to drives collection


...now main program...
Code:
PROGRAM PLC_PRG
VAR
   Drive1, Drive2: ClassDrive;
   Conv1, Conv2: ClassConveyor;
END_VAR


On program start, arrays for all objects, conveyors and drives are automatically built. You must manually code into the ClassXXX.FB_init() which collection arrays you want to have the object, but this is logical. Now it is easy to handle collection method calls as needed. If each drive has a Home() method, a simple FOR loop through the drives collection will call all drives. If all objects have a Reset() method, then a FOR loop through the all objects collection will call all objects. If I add another Conveyor (Conv3) or drive (Drive3) - no change is necessary - automatic object inclusion at project start. All without the slow __QUERYINTERFACE() call during runtime (penalty is RAM usage for holding arrays).

_________________
Scott Cunningham
KEB America, Inc.
www.kebblog.com
www.kebamerica.com


Top
   
PostPosted: Tue May 09, 2017 12:13 pm 
Offline

Joined: Mon Jun 30, 2014 9:35 am
Posts: 55
Scott, it's been some years but I would like to know how you handle online changes.
As I understand it, FB_init only executes after a cold reset.
Doesn't this mean your arrays can be corrupted by online changes? If so, could you make use of fb_exit and fb_reinit somehow?


Top
   
PostPosted: Wed May 10, 2017 12:20 am 
Offline
Frequent User
Frequent User

Joined: Fri Sep 02, 2011 8:02 pm
Posts: 255
Online change has not been a problem when changing the logic. Adding or removing objects is a different story. I never use online change when I add or remove variables/objects.

_________________
Scott Cunningham
KEB America, Inc.
www.kebblog.com
www.kebamerica.com


Top
   
PostPosted: Wed May 10, 2017 4:06 pm 
Offline

Joined: Mon Jun 30, 2014 9:35 am
Posts: 55
Thanks Scott, I do have one more question because I noticed something strange.

If I declare the InterfaceHandler in one GVL and Drive1in another GVL, it doesn't work.
The program runs but it's like the InterfaceHandler code never executes.

If they are put in the same GVL, it works.
It also works if Drive1 is declared in a POU (as in your example code).

I can only assume that this has something to do with the order that Codesys creates the variables and/or initialises them internally.
Do you have any insight to this matter?


Top
   
PostPosted: Thu May 11, 2017 4:35 pm 
Offline
Frequent User
Frequent User

Joined: Wed May 04, 2016 6:00 pm
Posts: 172
I have an ObjectPool too, You can add {attribute 'global_init_slot' := '49995'} at program or GVL init, it ensures that the intialization of the element which has this attribute will be instantiated before all others (The default init slot is 50000).


Top
   
PostPosted: Thu May 18, 2017 11:59 am 
Offline

Joined: Wed Dec 28, 2016 11:00 pm
Posts: 48
@Scott,
Would you be kind enough and post us an archived project?

Thanks in advance!


Top
   
PostPosted: Wed Jun 07, 2017 7:42 pm 
Offline
Frequent User
Frequent User

Joined: Fri Sep 02, 2011 8:02 pm
Posts: 255
Sorry, the project code is not allowed to be shared. Here is finally how I used the __QUERYINTERFACE to add a qualified object (only showing collection specific info):

Code:
FUNCTION_BLOCK FINAL ClassFaultObjectsCollection
VAR
   Collection   : ARRAY[1..GVL_COLLECT.MAX_OBJECTS] OF IFault;
   ObjNames   : ARRAY[1..GVL_COLLECT.MAX_OBJECTS] OF STRING;
   _ArrayOver   : BOOL;
   _BadObj      : INT;
   _DupeObj   : INT;
   _OverObj   : INT;
   _TotalObj   : INT;
END_VAR


(* Add object to the collection.
-----------------------------------------------------------------------
Call this method to add an object to the collection array:
   AddObject(THIS^)

Found below is typical code required.  'Obj' must be converted from an
IAll type to the interface type specific to the collection you are
implementing
*)
METHOD FINAL AddObject
VAR_INPUT
   (* object*)
   Obj   : IAll;
END_VAR
VAR
   Cast: IFault;
   J   : INT;
END_VAR

IF (__QUERYINTERFACE(Obj, Cast)) THEN
   // make sure haven't already added this object
   FOR J:= 1 TO _TotalObj DO
      IF Collection[J] = Cast THEN
         // object pointer matches - dupe!
         _DupeObj:= _DupeObj + 1;
         RETURN;
      END_IF
   END_FOR
   
   IF _TotalObj < GVL_COLLECT.MAX_OBJECTS THEN
      _TotalObj:= _TotalObj + 1;
      Collection[_TotalObj]:= Cast;
      ObjNames[_TotalObj]:= Obj.Instance;
   ELSE
      _ArrayOver:= TRUE;
      _OverObj:= _OverObj + 1;
   END_IF
   
ELSE
   _BadObj:= _BadObj + 1;
END_IF



So if you have a FB that should be added to the collection, it can do it in the FB_INIT of itself (but it must implement the matching interface - or it's a bad object...):

Code:
FUNCTION_BLOCK WantToFault IMPLEMENTS IFault
...
some code
...

METHOD FB_init : bool
VAR_INPUT
   bInitRetains : BOOL; // Initialization of the retain variables
   bInCopyCode : BOOL;  // Instance moved into copy code
   FaultCollector : REFERENCE TO ClassFaultObjectsCollection;
END_VAR

FaultCollector.AddObject(THIS^);

END_FUNCTION_BLOCK



On instantiation, the instance of WantToFault will call the collector's AddObject() method and pass itself to it. The collector will verify it should be an IFault member, and add's it, if necessary.

_________________
Scott Cunningham
KEB America, Inc.
www.kebblog.com
www.kebamerica.com


Top
   
Display posts from previous:  Sort by  
Post new topic  Reply to topic  [ 12 posts ] 

All times are UTC+01:00


Who is online

Users browsing this forum: No registered users and 6 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Limited