Jump to content

First post, and first LVOOP program, feedback really appreciated.


Recommended Posts

Hi all, first post and I hope nobody minds that it's quite long. I've finally started to move into LVOOP and I'd really appreciate some help on the first example program I've designed. It's based on the Command/Factory pattern posted by Elijay_K on NI (I've even unashamedly adopted his color schemes for the loops!). I'm a bit wary that my questions will be too basic for this forum, but anyway, here goes...
 
First, a quick description of the application I work with (see 'application.gif'): it's a digital voltage regulator which can be accessed by two interfaces, an I2C PMBus interface and a SVID interface (both are just digital power communication interfaces). In my program I just picked a few simple tasks to do (see 'front_panel.gif'):
 
  1. Read back basic info from the device (part name, FW version, etc). 
  2. Carry out basic control of the regulator, such as setting the output voltage, over-voltage limit and switching it on.
  3. Read and write some internal registers.
  4. Continuously monitor a small sub-set of registers, and update a Vo plot.

 
For the code, I currently have four loops: (1) Event handler to send commands to other loops, (2) PMBus communications loop, (3) SVID communications loop (4) UI Display loop to update front panel indicators (device info, monitored register values, 'power-good'/OVP fault). Note that the SVID loop has no real functionality yet; I just access the device using the PMBus interface.
 
I currently have three parent classses, one for each consumer loop: pmbus_generic.lvclass, svid_general.lvclass, display_generic.lvclass. Then for each child class I just have an Execute method to carry out specific tasks e.g. read/write a register using the PMBus loop, update the plot using the UI Display loop. This brings me to my first question:
 
(Q1) All my low-level functionality is carried out using legacy VI's (non-OOP) that I use as normal in my standard QSM architectures. So I found that I didn't need to define any data in the .ctl clusters for any class at all, yet everything works with no issues. Basically I think I have a dynamic dispatch OOP program which just contains methods but no private data at all. Is this okay? It works and all, but I feel like I'm missing something...
 
(Q2) I just leave my 'UI Update' loop free-running, but this doesn't feel very elegant. In fact I have to 'Stop' that loop directly, and then use that event to stop all the other loops. Is there a more standard (OOP?) way to continuously update front-panel indicators in this sort of program?
 
(Q3) I'd like to add some sort of logging functionality which takes information from both device interface loops- what's a good way of doing that? I'm thinking of using another loop and a related log class which receives data from the other loops. Is this the right approach or is there a better way? I'm assuming I need to be careful about one loop receiving data from two sources?
 
(Q4) For the waveform plot functionality, rather than having it fixed I want a pop-up type window which will display a whole range of waveforms from the application. Do I need to go down the plug-in route here? (i.e. add a plug-in loop and class?). 
 
(Q5) I also want an Options dialog which I can pop up at run-time and change settings. Do I need another loop and class? Perhaps have it as part of any plug-in functionality? Or are there any other elegant config options I can use in OOP? 
 
(Q6) Just thinking, is the pattern I use here even the best choice? Is there a more standard pattern that I can use for what is really just a basic device-control GUI? Ultimately I want to code a much larger app which has lots more of the same functionality as this basic example, so I'd like to get the pattern right from the start. 
 
I've attached a ZIP file of the project, but there's probably a lot of low-level drivers missing; I don't think that matters (hopefully just the basic architecture is all anyone needs).
 
Also, if I'm doing anything in general in the program that's completely idiotic, please point it out! And again, apologies for the length of the post. If it helps I don't really need detailed answers, just pointers in the right direction...

post-40077-0-15066700-1374528555.gif

post-40077-0-70909500-1374528560.jpg

Very Basic UI.zip

Edited by dmurray
Link to comment

dmurray,

 

One thing that I see here is that there is room for more generalization.  Basically, you have one device which may communicate via two different interfaces: serial and usb if I understand correctly.  I would suspect that the voltage regulator may take several of the same inputs and provide the same outputs (possibly) regardless of the communication method; you will still have to turn things on and off, send voltages, etc its just which low level VIs will be called to accomplish what you want to do.  And I guess this is where the art of OOP comes in - it is not entirely clear to me how to delegate this.  Do you have have commands that are specific to the interface and you simply pass the reference to the interface as well as any data to the command object, or is there a better way of doing this?  Or might this be a terrible way of doing this?  I don't know.

 

Also, I can't remember exactly what Elijah's original demonstration looked like, but I recall that he also included references to controls on the front panel.  While that might have been a good way to implement that there, it doesn't seem entirely clear why you would do this here.  Why not simply have the data relevant to the command passed in with the command itself (i.e. why is that data not part of the class)?  This might simplify your structure a little bit - instead of having two separate classes for turning on and off the regulator, you might simply have a single command instance that has a bool as the primary member and the regulator is turned on or off based on this bool.  This would also make unnecessary the property node calls on the front panel controls.

 

And concerning Elijah's comment:

 

However, your second question leads me to believe that you may actually want to pass information between your loops.  Your UI loop already contains references to front panel items you might want to update upon receive data - ideally, it only updates the necessary component upon receiving new data.  Rather than be free-running, it could respond to commands sent from other loops that contain the new value to be displayed.  As an example, you would have a class to update a graph and the private data could be wafeform data and cursor information - any other loop who had access to the UI queue could then send a command to update the value of this graph.  PS: Rather than increasing the scope of this loop as you add more UI elements, consider having sub-diagrams that load separate UIs with their own dedicated processes)

 

I did something similar to this with a much more complex system and came up with my own architecture in which I had a UI Facade which contained a single subpanel.  The facade then controlled the user view through controls that allowed the user to change that view by dropping in new front panels that were all started in the background when the facade was started.  Each of these front panels had very specific functionality related to discrete pieces of the system.  That being said, there was some overlap in the data retrieved by the facade and the data that was needed for the different front panels.  So each front panel could subscribe to some or all of the data that was received by the facade (observer pattern).  I think in the end, with all the queues and events required for communication, that I ended up with a poor man's Actor Framework.  So, my feeling is, that for something simple, this is an excellent idea.  But, as it gets more complicated, you might consider implementing Elijah's idea with a framework that is already available rather than rolling your own.  But that is probably a step farther than you want to go.

 

I am not sure if I said anything useful (following on someone who knows a lot more about this stuff might not be productive), but I hope it gets you thinking.  And it was nice first go.

 

Cheers, Matt

  • Like 1
Link to comment

Hi Elijah,

Thanks for taking the time to reply. As it happens, just this evening I dug a lot deeper into your code (the Plugin Handler Command class in particular) and saw how you were passing data between loops and other classes, and I had quite a few 'A-ha' moments. Your replies clarified a lot for me too, so I don't have much of a follow-up:

Hey dmurray,

 

Glad to hear you found the command pattern example useful.  It was my first successful application using OO, so I created the manual in the hopes of leading others down a similar path.

 

I took a minute to glance through your code, and one big question jumps out at me: are you actually passing data between these loops?  One of the major reasons to use the command pattern is the encapsulate the data associated with a command inside of a class instead of passing it as a variant.  Given that none of your classes appear to have private data, I can only assume that you aren't actually sending information between the loops.  

 

If my assessment is correct, the use of this pattern may be overkill - though there's nothing necessarily wrong with using it. 

 

However, your second question leads me to believe that you may actually want to pass information between your loops.  

Yes, that was the plan, but I wasn't sure how to go about it, but as mentioned I think understand a lot more now. In fact, even if I wasn't going to share data I'd consider using the methodology as it's such a neat way to code. I'm definitely a fan.

 

Your UI loop already contains references to front panel items you might want to update upon receive data - ideally, it only updates the necessary component upon receiving new data.  Rather than be free-running, it could respond to commands sent from other loops that contain the new value to be displayed.  As an example, you would have a class to update a graph and the private data could be wafeform data and cursor information - any other loop who had access to the UI queue could then send a command to update the value of this graph.  PS: Rather than increasing the scope of this loop as you add more UI elements, consider having sub-diagrams that load separate UIs with their own dedicated processes)

Okay, this sounds much better, I'll definitely implement this in some form.

 

 

 

Q3: One loop two sources is fine - in fact, it's what queues are best at.  Even if you weren't using the command pattern, it's common to have multiple producers of commands/data pumping that information into a queue, which should only ever be dequeued in one location (ie: the consumer).  The approach you've described for logging sounds appropriate

Okay, thanks for clarifying. I'm thinking now that I've never really used sharing between loops very well in my usual QSM designs. In one design, for example, I used a crude semaphore method to control access to a global log variable.

 

 

 

Q4: A dialog should be treated like a free running process that has it's own P/C loops on the block diagram.  For a good example of this, see the sample projects and how they invoke an options dialog.

 

Finally, is this the right pattern?  It's fundamentally a producer/consumer queued message handler (P/C-QMH), but we use OO instead of enums with case structures.  As such, it's just as  appropriate a as a regular QMH would be, but you're able to encapsulate the data you pass between the loops using classes (should you choose to do this).

Okay, makes sense. Like I said, even just the neatness of code makes me want to switch to OOP anyway. Thanks again for your help, it's much appreciated.

Derek.

Link to comment

Hi Matt,

Thanks for the reply, there are some good points:

dmurray,

 

One thing that I see here is that there is room for more generalization.  Basically, you have one device which may communicate via two different interfaces: serial and usb if I understand correctly.  I would suspect that the voltage regulator may take several of the same inputs and provide the same outputs (possibly) regardless of the communication method; you will still have to turn things on and off, send voltages, etc its just which low level VIs will be called to accomplish what you want to do.  And I guess this is where the art of OOP comes in - it is not entirely clear to me how to delegate this.  Do you have have commands that are specific to the interface and you simply pass the reference to the interface as well as any data to the command object, or is there a better way of doing this?  Or might this be a terrible way of doing this?  I don't know.

This is a valid point - I accept that my fundamental code doesn't have a good 'OO' feel to it yet. My only exposure to OOP was in my university days a good few years back, and I can't really say I 'got' it then either. Although at the moment I am reading a couple of books on both OOP and design patterns; it's just that time is the enemy!

In this case, there's definitely some commonality between the two interfaces, but they're also quite distinct (if that makes sense). PMBus has a much larger command set and handles most of the comms. SVID is a proprietary Intel interface with a much smaller register footprint in the device. And where they do share functionality, they tend to use different formats (for example a common task would be to read back the regulated load current, but there are subtle differences in how it's done in each I/F). But I'm sure there's a way to break down (combine?) the interfaces into a proper OOP structure; I just don't know enough yet to do it.

I'm also thinking that maybe I need to have some sort of a HAL here (so that I can use the code with different devices?), but again I'm not sure how to go about it. I know there's a good white paper on HAL over on NI that I keep meaning to have a look through. It's just another thing on my learning list.

 

 

Also, I can't remember exactly what Elijah's original demonstration looked like, but I recall that he also included references to controls on the front panel.  While that might have been a good way to implement that there, it doesn't seem entirely clear why you would do this here.  Why not simply have the data relevant to the command passed in with the command itself (i.e. why is that data not part of the class)?  This might simplify your structure a little bit - instead of having two separate classes for turning on and off the regulator, you might simply have a single command instance that has a bool as the primary member and the regulator is turned on or off based on this bool.  This would also make unnecessary the property node calls on the front panel controls.

Again, I just wasn't sure about the right way to go about things, but I understand a bit more now. I'll definitely start using controls as part of the class data now - obviously it makes a lot more sense (especially when it's pointed out to me!). I wasn't happy with all the FP references anyway, and they would have been a bit of a nightmare in the full app I have planned.

 

And concerning Elijah's comment: 

I did something similar to this with a much more complex system and came up with my own architecture in which I had a UI Facade which contained a single subpanel.  The facade then controlled the user view through controls that allowed the user to change that view by dropping in new front panels that were all started in the background when the facade was started.  Each of these front panels had very specific functionality related to discrete pieces of the system.  That being said, there was some overlap in the data retrieved by the facade and the data that was needed for the different front panels.  So each front panel could subscribe to some or all of the data that was received by the facade (observer pattern).  I think in the end, with all the queues and events required for communication, that I ended up with a poor man's Actor Framework.  So, my feeling is, that for something simple, this is an excellent idea.  But, as it gets more complicated, you might consider implementing Elijah's idea with a framework that is already available rather than rolling your own.  But that is probably a step farther than you want to go.

 

I am not sure if I said anything useful (following on someone who knows a lot more about this stuff might not be productive), but I hope it gets you thinking.  And it was nice first go.

 

Cheers, Matt

I really like the Actor Framework but it's definitely beyond me at the moment. My plan over the next few months is to nail the fundamentals as much as possible, particularly some of the fundamental OOP concepts you mention. But I am thinking of using sub-panels in some way in my app; for example there are a lot of waveforms I could show, and I think it might be a good way to learn about sub-panels (I haven't used them before). I haven't even used User Events yet either, so I'm trying to think of a way to work those in without over-complicating things.

But thanks for your reply, it is definitely helpful.

Derek.

Link to comment

Matt raised several excellent points. In particular, he brings up a valid point about the UI loop and the use of references - sometimes the simplest approach is the best one, and it's probably more than adequate in your scenario to simply wire the data to the appropriate terminals as it arrives. I go back and forth on the use of references - it's certainly nice to be able to send commands from anywhere in the system to a single process than then knows how to display them, but allowing data to simply flow to the terminal is often more readable, easier to debug and definitely more performant. I tend to use references when I know I'm going to be using a lot of VI server commands to act upon the UI anyway, especially since it's best to decouple this from other processes.

That being said, not using references will not mitigate the potential nightmare of a very large front panel.  Depending upon how large you intend this panel to become, you should consider relegating logical groups of UI components to subVIs and displaying them in sub panels.  Put generally, when you start thinking about having tabs, start thinking about a sub-panel too.

Matt also brings up a good point about the use of classes to represent the different busses in your system.  The command pattern is a simple way to bridge knowledge of traditional LabVIEW patterns like PC-QMH to classes, but the real power of OOP for LabVIEW users is when you have to represent multiple similar-but-different components of a system, such as hardware, measurements or in your case: busses.  The real trick is determining where in the architecture of your system to abstract functionality such that these devices can be used through a common interface that can automatically take advantage of device-specific functionality under-the-hood.  In your case, the generic command set would be defined by a base class and children would extend this by implementing the unique functionality for the bus they represent.

I recently put together an example of using the Actor Framework to build a measurement and hardware abstraction layer system.  It's a deep dive into Actor Framework, but this blog I wrote on the design decisions I made may still benefit you as you explore HALs.  You might check it out if you have time and see if it yields any insights on how/where you would abstract components of your system.

  • Like 1
Link to comment

I need to think a bit more about reference use so. For now I think I'll use what's easiest, and think about the consequences later!

Re. HALs: The blog post is very helpful. I'll think a bit more on this later, but for now I think a basic hierarchy is as shown below. For example, some devices will have both PMBus and SVID comms, some will be PMBus only, some will be pin-configurable and may need no other comms at all, etc. I also need to think about being able to add future devices and interfaces as simply as possible. In fact the serial-SVID comms approach I use is just prototyping work; I soon hope to add a better USB-type interface.

Or maybe the hierarchy needs to be switched around: device families on the second layer, and interfaces at the third layer? Anyway, there's plenty for me to think about. I'll probably start a separate post when I have something more concrete. For now I really want to get back to looking at the ideas from last night.

This has been very helpful for me, thanks again.

post-40077-0-48843000-1374926960_thumb.p

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