Jump to content

Design of object oriented program


Recommended Posts

Hi everybody,

2 years ago, I started a program which uses the producer / consumer - loop design pattern. As the program growed very fast, I now decided to use object oriented programming, but I have now several problems, I hope you can help me.

More concret, I have several data acquisition devices which record images (cameras) or 1D arrays of values (AD card). In producer / consumer design pattern, I can't use these devices in parallel, only a serial data acquisiton is possible. I now found a design pattern called "worker pool" on http://expressionflow.com/2009/11/04/worker-pool/, which seems to be perfectly suited for my approach. I now have to implement the functions of every data device in a class and use dynamic dispatching to call the appropriate "record" function for every class at runtime. So I can use all devices in parallel. Unforunately, NI couldn't tell me if this approach will work as they don't know the design pattern. Somebody already tried it?

As I'm not very familiar with OOP (just made some C++-programs), I now try to figure out:

- How I can realize static member variables? As I have several devices recording images, I like to create a parent class "ImageDevice" and inherit from it...the ImageDevice class should hold the data buffer. But as I only need one image buffer, I like to realize it as static...it's only possible by global variables?

- How I separate my "device control code" from my "front panel control code", that means: If I hit "Record" on the front panel for one device, a boolean indicator should indicate that the recording is in progress... one way to do this would be to create (for the specific device) a member variable which is a reference to my front panel indicator, and on record, I can (in the "record.vi" from the specific device) switch the indicator from false to true...but this would end in a mix up between "device code" and "program code"...how I can handle this?

You see, lots of problems...hope you can help me somehow, as I can't really figure out how to do it the best way...

Thanks in advance,

Christian

Link to comment

Concerning 'static variables': Just to clarify, you want to have a single variable that is shared among all objects? Then you could use an DVR (data value reference) for this. Then use a factory (search for factory design pattern) to create the objects at run-time and initialize them with the same DVR.

To set/unset the 'recording' button, I would use User Events and the objects will fire them as Enter/Exit Action of the recording code section. This is motivated by the fact that your FP code has already an event structure and you don't need an additional loop like when using queues, notifiers, some kind of global for this.

Felix

Link to comment

Ok, thank you for your help! Just had a look for the factory design pattern you mentioned...at the moment, it seems to me that I can handle with this all my data access devices, but so far, I couldn't figure out how to initialize all of my subclasses with the same DVR...It's enough to create a DVR as object member variable, or it's still needed to use something like global variables?

Thank you very much!

Christian

Link to comment

My idea would be to have the DVR initialized in the factory class itself and whenever a new ImageDevice object is created, the DVR is set by the create methode of the factory class. The factory object of course needs to be a singletone (there is plenty of discussions on singletons on lava and ni forums).

Rethinking this, a simpler solution would be to implement the buffer as an 'Action Engine'/'LV2global'. This would be the same concept as I would make the factory a singleton, by using the serialization that 'naturally' takes place at the subVI boundary.

And rethinking again, is it really a design constraint for you to make this buffer 'static'?

Again, one different implementation could be to make the buffer an object itself and make this object a singleton.

Felix

Link to comment

Hi Felix!

Thank you for your reply...As I am a beginner in object oriented programming with Labview (did only some stuff in C++, programming Labview without objectorientation since 6 years now...), I am trying hard to understand your ideas and reading on the design patterns...

First of all, as far as I understand, the factory design pattern let me initialize objects at runtime as needed without loading the class (perfect for plugins?). So this would help me to handle all my different data devices...like camera, scanner, ad-card and so on. Is this right?

I am only trying to implement my devices in classes to use dynamic dispatching which seems to me very useful for what I am trying (i can use a record.vi for every device). So, if I have, let's say, 5 data devices which have totally different settings (meaning I have to expand my front panel for every new device) and I have to implement several different VIs (for example for the AD-Card: channels to record, for the camera somethink like region of interest...), will this pattern be useful for me? If yes, why?

As a second, I understand how to use DVR (only worked with single-element-queues so far), but how I can intialize all my different data devices (camera, scanner..) with the same DVR? If I initialize the camera, the DVR (which is a member variable of my ImageDevice-class), will be created...if I know initialize the scanner, how can I pass the DVR from the camera-object to the scanner-object? Is this meant by "the factory object (the DVR??) needs to be a singleton), that if i initialize my camera and later the scanner, the same buffer is used?

What do you mean, btw, with "serialization that naturally takes place"?

If it's really a design constraint to make this buffer static? Let me say...the only thing I'd like to do is to know where I can find my data...if I use the camera or the scanner, there shoudn't be separate buffers...as all saving, displaying and manipulation-functions are already implemented, I don't like to distinguish where my data comes from. The saving, for example, is the same routine for scanner as it is for camera...so I want to use the same data buffer.

To make the buffer an object itself would be a good method, but I think I would then have to pass this object to all created data devices, so that they can write into this buffer if i call the "record.vi". But I think this would be a very easy solution...create the data buffer on start, and on initialization of a data device, I can pass the buffer to a member variable.

By the way: How objects are passed in LabView? I started using queues to do a "by reference" call, as I have to transfer large amounts of data...if I now use classes and pass these classes into my program, the class data is handled by reference or by value? Meaning: If i have one wire which goes parallel to 2 sub-vis, if the upper subvi changes something in the class, it will simultaneously change in the lower subvi?

Thank you again for all of your help!

Christian

My idea would be to have the DVR initialized in the factory class itself and whenever a new ImageDevice object is created, the DVR is set by the create methode of the factory class. The factory object of course needs to be a singletone (there is plenty of discussions on singletons on lava and ni forums).

Rethinking this, a simpler solution would be to implement the buffer as an 'Action Engine'/'LV2global'. This would be the same concept as I would make the factory a singleton, by using the serialization that 'naturally' takes place at the subVI boundary.

And rethinking again, is it really a design constraint for you to make this buffer 'static'?

Again, one different implementation could be to make the buffer an object itself and make this object a singleton.

Felix

Link to comment

Thank you for your reply...As I am a beginner in object oriented programming with Labview (did only some stuff in C++, programming Labview without objectorientation since 6 years now...), I am trying hard to understand your ideas and reading on the design patterns...

I'm not much ahead of you. For OOP design patterns, I normally use wikipedia (in lack of a text book).

First of all, as far as I understand, the factory design pattern let me initialize objects at runtime as needed without loading the class (perfect for plugins?). So this would help me to handle all my different data devices...like camera, scanner, ad-card and so on. Is this right?

My idea of using a factory was linked to the DVR concept. Using a DVR as private data requires an initialization method which you would call in the factory.

if I know initialize the scanner, how can I pass the DVR from the camera-object to the scanner-object? Is this meant by "the factory object (the DVR??) needs to be a singleton), that if i initialize my camera and later the scanner, the same buffer is used?

A factory is most of the time implemented as singleton.

The DVR is a reference. If all your objects have the same reference, they share the data. You just need to pass the DVR to every object you create in the factory, then they all share the buffer. Working with DVRs is very similar to single element queues (SEQ).

What do you mean, btw, with "serialization that naturally takes place"?

If you don't set the VI as reentrant, it can only run when it isn't running in an other section of your code. This is a very elegant way of protecting against concurrent access to data. In all other situations you need to explicitly state this, using semaphores around globals, the dequeue/enqueue operation of SEQ, the Inplace Structure for DVRs.

If it's really a design constraint to make this buffer static? Let me say...the only thing I'd like to do is to know where I can find my data...if I use the camera or the scanner, there shoudn't be separate buffers...as all saving, displaying and manipulation-functions are already implemented, I don't like to distinguish where my data comes from. The saving, for example, is the same routine for scanner as it is for camera...so I want to use the same data buffer.

Why don't you just use an array as private data in your parent class and implement the data access methods (Get/Set). All code that is the same for every device (like save) will be implemented in the parent class using these data access methods.

All device specific code goes in the child classes, like 'record' and use the parent methods to place the data in the array.

(As a side note, you can override the Data Access methods in a child class, the parent calls to these methods like in the save will 'dynamic dispatch' to the child methods)

By the way: How objects are passed in LabView? I started using queues to do a "by reference" call, as I have to transfer large amounts of data...if I now use classes and pass these classes into my program, the class data is handled by reference or by value? Meaning: If i have one wire which goes parallel to 2 sub-vis, if the upper subvi changes something in the class, it will simultaneously change in the lower subvi?

Most of the time a by-value implementation should be good. Then you treat the object like a cluster.

If you need to access the same data in parallel loops, you need a by ref implementation. There are 2 diffrent ways to do it:

a) place the class data in an DVR, the private data is then the DVR. You can mix this with by-value data (for example only place the buffer in the DVR).

b) place the object in an DVR. Then wrap all methods of the class inside inplece element structures. You can mix it with by-val, too (direct calls of the class methods, e.g. in the initialization part before you have the parallel loops).

Felix

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.