Jump to content

Recommended Posts

Hi

Background:

I'm diving into this "OOP" stuff right now. My background is NOT CS (Computer Science) but instead electronics and aerospace design. Diving into the deep end of the pool, I'm still in that happy ignorant state of "this is fun" right before I'm realizing I can't swim and that I might drown, or that I can probably stay afloat, but I'm not at all sure what direction I need to swim in to get to shore, or to get to friendly shores... you get the picture I'm sure. (That said, I'm very familiar with "standard" LabVIEW, including RT and FPGA, and have worked full time with LabVIEW for over 2.5 years, I have the CLD and CPI certification for LabVIEW. I currently use LabVIEW 2011.)

Anyway. I've read white papers and articles (How to Mitigate Hardware Obsolescence in Next-Generation Test Systems, LabVIEW Object-Oriented Programming: The Decisions Behind the Design, LabVIEW Object-Oriented Programming FAQ), watched presentations and seminars (Data Communication in LabVIEW Technical Seminar), read forum posts(Techniques for componentizing code), looked at exercises and examples (Object Oriented Design Patterns Technical Manual and Exercises, Software Engineering with LabVIEW, ) and poured over blogs (Worker pool – a design pattern for parallel task execution in LabVIEW, Unlimited parallelism & concurrency with recursive dataflow, Eyes on VI's) and I still find myself still confused on a lot of "core" things (that may only come with experience?). I certainly don't claim to understand all of the linked material; I'm still toweling off from drinking from that fire-hydrant, but I wanted to show that I have done some studying of the topic and figured that if I put at least some of the links up here, it may even help others like me find good links on this OOP beast.

While a lot of this went over my head, I find myself applying and using the term "complecting" and "complect" a lot when thinking about what I want to do and what I don't want to do and for those with time to kill, its a very interesting talk that points out some things regarding "easy" and "simple" that I think is all too commonly forgotten in todays work.. go on.. I'll wait while you watch/listen to it.. you know you want to click it .. go on, its Simple Made Easy its good for you. :)

I'm still in the planning phase, having been blessed with the time to take it slow and do it right (while maintaining some legacy code on the side while I'm incubating this next generation version), so I'm not committed to any particular architecture or method (including OOP). Key is that I (or <we>) will need to support this platform for years while adding new features etc. along the way. So I'm pretty sure that OOP is the right choice.. but how to organize and implement it. Since I can't hope to learn everything in one post here is:

MY QUESTION FOR THIS POST:

How do you know what to put in a class and where to draw the line, both in terms of having too many things in one class, and having to granular (huge amount of classes for the littlest things)??

Example: I have an instrument. Its a peculiar type of instrument with a little bit of a personality (that changes with firmware). Do I create a giant class called myInstrument and create methods for everything I need to do (and get) from the instrument? Or do I make smaller classes for "configure" and "acquire" and gather them all up in another class?

-The instrument communicates over USB or TCP/IP, surely that should be a layer/class of themselves? (class within class).

But then we get to things like the "personality" of the instrument: depending on configuration of the instrument, from when I "start" an acquisition to when I can request the data, it could be anywhere from 10 seconds to HOURS. Do I poll the instrument ready flag in a method or outside of the class/object? (inside, yes, right? to protect access to it, or poll it outside, but by using calls to class method?) Do I have methods that other parts of my software can call to "get status" of the instrument? (wouldn't that complect my software un-necessarily since all sorts of places in the code could be structured to depend on the status of the instrument, when in reality all they probably care about is getting updated data at the end with no regards to the status of the instrument at any given time?)

So, then, when the instrument does return data, the data needs to go to all these things, like the file logger (plug-in class?) and the graph history display (another class) only on windows systems, but not cRIO/RT systems, update modbus memory maps (plug-in, some systems, not always) and UPC (plug-in, some systems not always) etc.

How should this sharing of data be done? Other parts polling methods of the instrument class? instrument private method publishing data to named queues? (this last seems like the right choice to me but is it in the spirit of OOP??)

Then (finally), I need to be able to spawn an unknown (at edit time) number of these instruments and I need to be able to run this code on cRIO OR Windows. If I write the instrument class, I think I can spawn copies of it dynamically... should I put the instrument inside of actors (or is the class itself the actor) in the actor framework? Should I use dynamic events to broadcast stops? What about error handling? A class of itself? An actor? what about the complecting that occurs if you do that? (everyone will be calling the error handler from within itself, causing complecting to happen.. what about just having loops push errors and warnings onto a named queue and have a daemon handle it? Can I do that while using OOP? Is the dameon then a class, or is the daemon the architecture and it uses classes inside of it?

As evident by my "question" I am a little overwhelmed when it comes to thinking about what a class/object is and what should live "inside" of it, and what should go around it on the outside. Old habit also keeps screaming QSM QSM QSM and "Action Engines" (aka functional globals) over and over since my brain already knows those tools... I THINK maybe the point is that you define classes and make objects but you tie them all together in "normal" structures like QSM's and possibly even "Action Engines"?

Thank you for your time, and I hope for some encouragement, clarifying questions, guiding hints and tips or just about any response you care to share with me!

Link to comment

You sound overwhelmed.

I'm not a CS nor do I play one on TV, so take any advice with a grain of salt.

How do you know what to put in a class and where to draw the line, both in terms of having too many things in one class, and having to granular (huge amount of classes for the littlest things)??

A class is often taught as a physical thing. The parent is a vehicle with the children being cars, trucks, boats, etc. Taking this to code, a class becomes something like communication, priority queue, language support, analysis, data acquisition, etc. It's something that you would go to the white board to sketch out the view of the system and have a bubble for.

How should this sharing of data be done?

That question will get you more answers than people answering. I'm pretty sure wars have been fought over that as well. I've got my preferences, but it depends on if I need every value, current value, streaming data, etc.

Then (finally), I need to be able to spawn an unknown (at edit time) number of these instruments and I need to be able to run this code on cRIO OR Windows. If I write the instrument class, I think I can spawn copies of it dynamically... should I put the instrument inside of actors (or is the class itself the actor) in the actor framework? Should I use dynamic events to broadcast stops? What about error handling? A class of itself? An actor? what about the complecting that occurs if you do that? (everyone will be calling the error handler from within itself, causing complecting to happen.. what about just having loops push errors and warnings onto a named queue and have a daemon handle it? Can I do that while using OOP? Is the dameon then a class, or is the daemon the architecture and it uses classes inside of it?

I'm not familiar with RT, but there are tricks and techniques that are needed for RT that are not in Windows (at least per my coworker who has done some RT work).

Dynamic loading will work (in Windows) for loading an unknown number of instruments at run-time. From your description, I would expect supervisory code will load-and-launch, shut down, and handle errors passed back from the instruments.

Tim

Link to comment

Yes, I do feel a little overwhelmed. Thanks for taking the time to reply to my

A class is often taught as a physical thing. The parent is a vehicle with the children being cars, trucks, boats, etc. Taking this to code, a class becomes something like communication, priority queue, language support, analysis, data acquisition, etc. It's something that you would go to the white board to sketch out the view of the system and have a bubble for.

I think perhaps what has been causing some of the confusion on my end has been my brains desire to think of the classes and objects as complete programs unto themselves, instead of as "sub-vi" building blocks that together make up a complex program. My opening heading still applies though: where to draw the line (in the sand so to speak)? Using an arbitrary instrument as an example and from your statement, the instrument would be a class, but it would also have other (unrelated) classes "inside" of it, right? So inside some of the methods for the instrument class, I would need to call methods of another class for communication (with children for tcp/ip, modbus, serial or whatever is appropriate for the instrument)? right? Maybe my mind is just buckling from trying to take it all in at once, but this doesn't seem like something I can/should do in the project view?

That question will get you more answers than people answering. I'm pretty sure wars have been fought over that as well. I've got my preferences, but it depends on if I need every value, current value, streaming data, etc.

yeah, I could see that. I might just pick my poison and start traveling down a path and see where it leads. The masochist in me wants to pick the ACTOR framework just because it seems very complicated and feature rich. :)

I'm not familiar with RT, but there are tricks and techniques that are needed for RT that are not in Windows (at least per my coworker who has done some RT work).

Dynamic loading will work (in Windows) for loading an unknown number of instruments at run-time. From your description, I would expect supervisory code will load-and-launch, shut down, and handle errors passed back from the instruments.

Tim

Thanks, I will try to make small proof of concept machines to run on the RT target and see if they behave as expected. Unfortunately, there is quite a bit of work to set up simple proof of concepts in LVOOP.. or rather, it seems that way now since everything is new to me still. I'll report back on progress.. Maybe this thread can evolve into a "one mans journey into the maws of OOP" or something like that!

Thanks again!

Link to comment

I think you badly need the opportunity to use LVOOP for a couple of small projects, in a simple way. First just try using an object or two in place of type-def-clusters. Then a try a parent and two children (your USB or TCP/IP communication might be a good choice). With experience in the basics, you'll be able to get more out of all that reading. There is quite a few examples of people on LAVA trying to develop tools to handle the complex tasks with multiple processes running in parallel, but it would be quite hard to just jump right in without some experience.

Link to comment

... the instrument would be a class, but it would also have other (unrelated) classes "inside" of it, right? So inside some of the methods for the instrument class, I would need to call methods of another class for communication (with children for tcp/ip, modbus, serial or whatever is appropriate for the instrument)?

...

I might just pick my poison and start traveling down a path and see where it leads.

...

The masochist in me wants to pick the ACTOR framework just because it seems very complicated and feature rich. :)

I'm not much ahead of you on the LVOOP curve, but my take is that you need to code something up and see what happens. A "simulated" instrument class may help during early development. Actor Framework or daklu's create.vi and execute.vi loop are very good starting points for encapsulation. I have custom instruments with different evolving capabilities, and I'm having success (so far) grouping their classes all together in create.vi as private data in the instrument's class.

Unfortunately, there is quite a bit of work to set up simple proof of concepts in LVOOP.. or rather, it seems that way now since everything is new to me still.

It's not that much work, after you get used to it.

Link to comment

drjdpowell brings up a good point; you're trying to dive in to the deep end with the 100-lb weights on (do you float? sink? who knows!).

I think perhaps what has been causing some of the confusion on my end has been my brains desire to think of the classes and objects as complete programs unto themselves, instead of as "sub-vi" building blocks that together make up a complex program. My opening heading still applies though: where to draw the line (in the sand so to speak)? Using an arbitrary instrument as an example and from your statement, the instrument would be a class, but it would also have other (unrelated) classes "inside" of it, right? So inside some of the methods for the instrument class, I would need to call methods of another class for communication (with children for tcp/ip, modbus, serial or whatever is appropriate for the instrument)? right? Maybe my mind is just buckling from trying to take it all in at once, but this doesn't seem like something I can/should do in the project view?

Your instrument class could contain everything to communicate TCP, USB, etc. That would lead to a larger single class. Implementing the communication method as a class that is used by the instrument class would make for more reusable and code and help debug as you, for example, know (or at least have certainty) that the TCP communication class works, so any bug must be part of the instrument class.

It sounds like you've got a big application and some specifications, but haven't spent much time with a whiteboard at the 10,000 foot level.

You could put everything in one project. I wouldn't recommend it as it gets highly cumbersome to use. Segmenting out (e.g., "this is the part/project that does error handling and that's all it does") would be my recommendation even if to avoid mental overload

Tim

Link to comment

drjdpowell brings up a good point; you're trying to dive in to the deep end with the 100-lb weights on (do you float? sink? who knows!).

Your instrument class could contain everything to communicate TCP, USB, etc. That would lead to a larger single class. Implementing the communication method as a class that is used by the instrument class would make for more reusable and code and help debug as you, for example, know (or at least have certainty) that the TCP communication class works, so any bug must be part of the instrument class.

It sounds like you've got a big application and some specifications, but haven't spent much time with a whiteboard at the 10,000 foot level.

You could put everything in one project. I wouldn't recommend it as it gets highly cumbersome to use. Segmenting out (e.g., "this is the part/project that does error handling and that's all it does") would be my recommendation even if to avoid mental overload

Tim

Sounds like a good suggestion. I've noticed in tutorials and examples for LVOOP that multiple projects are common.. They still sort of blow my mind. I'm re-working through some NI "draw-along" exercises to pick up a better understanding of this. And yes, the Whiteboard is a great idea. :) As far as the 10k overview, I'm almost done creating pseudo flow charts of the various components and functions that exist in the current code base. I've already spotted several places were things are more complect (intertwined) than they need to be.

I'll keep learning and trying out things on a smaller scale and I'll update this as I go along. (Is there a "work-blog" type section of the forum, or can I use this thread as such?)

To all of you, thanks for your comments and for even reading my post. I tend to be long winded because I want to give as much information as I can.. not always a good thing though.

Link to comment

I think you badly need the opportunity to use LVOOP for a couple of small projects, in a simple way. First just try using an object or two in place of type-def-clusters. Then a try a parent and two children (your USB or TCP/IP communication might be a good choice). With experience in the basics, you'll be able to get more out of all that reading. There is quite a few examples of people on LAVA trying to develop tools to handle the complex tasks with multiple processes running in parallel, but it would be quite hard to just jump right in without some experience.

This sounds like a great idea. Currently there is an array of "super-cluster" that contains all the information needed for each "channel" by the "engine" in the current software. The engine (in current software) is a queued state machine that is passed an array of all configured/active channels for one instrument. The QSM iterates over the super-clusters passed to it when idle and handles top-level requests for data etc. in between. The super-cluster however, currently contains about 44 various data elements grouped in sub-clusters. Some elements are arrays themselves.

In its current form, since the engine a) configures, b) acquires c) logs data for every configured channel on a single physical instrument d) updates a modbus memory map etc.; the data in the cluster today consists of non-related and loosely coupled elements. I.e. the super secret algorithm for data reduction parameters is used on the data retrieved from the instrument, the data in the "log" cluster is used to create and store the log files (meta data, tags, folders etc.), the instrument sub cluster contains most of what the instrument needs to know etc. So to "decouple" this super cluster, I could create classes for each logical group of data (say, algorithm, instrument, log, sensor, Modbus).

Now, I need all of this data to be conveniently stored to file. Currently, all configuration data for all channels on all instruments are stored in a single binary file. On startup, a parser reads the binary file and sorts out one array for each defined instrument. These sub-arrays are then passed to dynamically spawned "engines", one for each instrument. Where my thinking breaks down on making this an LVOOP implementation is this: How can I group and organize all these things as different classes such that all the parts of the engine can get to the information it needs (i.e. log info cluster for instrument 1, channel 2)? As stated above, it currently does so by reading a FG by passing in what instrument number it is and the config/channel index it wants.

Hummm. I could have a class "configuration", with methods "read file", "write file", "set" and "get"? Then I could make children of that class, one for each logical group? Each child would build and maintain arrays (or some such) in private data space of all "configuration elements", then the engine would access the data by calling override versions of the parent get's for each child type as required and an init routine (prior to the launch of the engine(s)) could call the parent "read file"? or something to that effect.

I'm already seeing a couple of issues with this, since doing this, the parent won't know the data structure of the children so it can't read/write a single file, but if I do this at the child level, then I end up with one file per child, right?

Wait, don't tell me, I need to start with something simpler.. :) -I don't have anything against simple per se, its just all my readily to mind examples belong to this project which has a lot of complexity to it.

Link to comment

Hummm. I could have a class "configuration", with methods "read file", "write file", "set" and "get"? Then I could make children of that class, one for each logical group? Each child would build and maintain arrays (or some such) in private data space of all "configuration elements", then the engine would access the data by calling override versions of the parent get's for each child type as required and an init routine (prior to the launch of the engine(s)) could call the parent "read file"? or something to that effect.

I see two approaches. One is there is a generic configuration class that reads data of a particular format, but doesn't care what that data is (akin to the XML or INI routines). The other is the parents have a dynamic dispatch configuration read and write VIs that are overridden by the children. The child would call the parent routine then perform its own configuration read/write.

Wait, don't tell me, I need to start with something simpler.. :) -I don't have anything against simple per se, its just all my readily to mind examples belong to this project which has a lot of complexity to it.

You have something simple in finding some very fundamental items such as, say, TCP communication or log files.

Tim

Link to comment

You have something simple in finding some very fundamental items such as, say, TCP communication or log files.

Thanks Tim, I will try the log file class:

Just one question there: it needs information from the (not yet developed) configuration class/cluster. Should this data be passed as an argument into the method(s) along with data to update the log file with, or should this information be retrieved by calling a method of the not yet developed configuration class internally? It seems it would be cleaner to obtain this inside of the object. Is that the correct way to think of it?

Link to comment

Thanks Tim, I will try the log file class:

Just one question there: it needs information from the (not yet developed) configuration class/cluster. Should this data be passed as an argument into the method(s) along with data to update the log file with, or should this information be retrieved by calling a method of the not yet developed configuration class internally? It seems it would be cleaner to obtain this inside of the object. Is that the correct way to think of it?

I set my version of log-file object up as a generic file, so I passed in file name and root path to the class. I also set them up to be accessible as parameters, which is very useful for diagram space when you have lots of settings for the class. I can reuse the log file class by setting it up this way. You may have design requirements that would promote another path.

Tim

Edited by Tim_S
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.