The MacView

Virtual Instrumentation from a Mac perspective

Thursday, June 03, 2010

How to Get All Mac System Colors in LabVIEW

Christina Roger's blog posts over at Eyes on VIs (specifically Secret LabVIEW System Colors and System Colors - Don't Believe Your Eyes) got me looking into the System Colors on the Mac. In doing so I wrote some VIs to query the OS directly (at least on the Mac) what all the system colors are. You can download the VIs at:

Mac System Color VIs

Both the Text Color API and the Brush Color API are very similar. They differ only in the function to call (duh) and the constants passed for what color to get. Here is GetThemeBrushAsColor.vi's block diagram:



My last post talked about converting a list of constants into an enum. I used this method (for both API calls) to create the enum input. For the RGBColor input, I just used an array of three Unsigned 16-bit Integers (Red, Green and Blue in that order in the array). The trickiest part was how to convert three u16s to a LabVIEW color code. LabVIEWs colors are an Unsigned 32-bit integer. The least significant byte is the Blue value. The next most significant byte is the Green value and the next most is the Red value, in other words: 0x00RRGGBB. The most significant byte is where LabVIEW stores magic flags, so we'll keep that zero. To convert from 16-bit values to 8-bit values, we just need the upper half of the 16-bit values. So we use Split Number primitive to get the upper half of the 16-bit values and use Join Numbers to put the colors in the right places.



Then it's just a matter of getting the Call Library Function primitive set up correctly. There you go. Simple call to get all sorts of colors from the OS.

Labels: , , , ,

The views expressed on this website/weblog are mine alone and do not necessarily reflect the views of my employer.


Thursday, May 06, 2010

How To Automate Creating Large Enums

I was creating some VIs to call the OS to get system colors directly when I ran into an annoyance. The OS defines a long list of constants (in this case kThemeBrush* in Appearance.h on the Mac). I wanted an enum in my VI API to make code calling my VI more readable (right click -> create constant on the enum terminal creates an enum constant on the diagram). There were 62 enum values to enter. This was not something I wanted to type one by one into the enum control. So I came up with a way to automate much of the creation.

First I searched for all lines in Appearance.h that contain kThemeBrush and a number. I took those lines and pasted them into a string constant and then wrote some code to parse out the constant name and value (see diagram below).



So what does this diagram do? First I use vi.lib/AdvancedString/Normalize End Of Line.vi to make sure the line endings in the string are the LabVIEW platform line ending (\r on the Mac). I then use Spreadsheet String To Array primitive to parse the string into lines (one of my favorite text parsing tricks in LabVIEW). Then for each line I get the part before the equals (name of the constant) and then I get the part between the equals and the comma (constant value).

I then copy the Ring to my VI, right click on it and select Replace and replace it with an enum. LabVIEW preserves all the strings. However, to get the appropriate value (enums by default use their (zero based) index as their value) I use the output array.

The front panel of the VI has a Ring (not enum because you can't write the strings to an enum on a running VI). The constant names are written to the Ring values (Strings[]) and we output an array of the constant values. We right click on the array control and select Data Operations -> Copy Data. Then right click on the array constant in the diagram below and select Data Operations -> Paste Data.



This diagram converts the enum values to the constants from the header.

I did this for two separate API calls (Theme Text and Theme Brush), each with their own set of constants. More reliable and much faster than doing it by hand.

Labels: , ,

The views expressed on this website/weblog are mine alone and do not necessarily reflect the views of my employer.


Thursday, April 01, 2010

Threading using Queues


I found something unexpected today. I had written a VI to generate a report of the linkages between files in the LabVIEW distribution. It was a quick and dirty VI that basically did the following:

1. Find all the VIs, Controls, Classes, Libraries, Projects, etc. in the LabVIEW distribution

2. Read the linkage information from the file

3. Write out a report on the file

Here is a simplified version of what I was doing:



With this method, LabVIEW pegged the CPU at 100%. Well, LabVIEW pegged one of my four CPUs at 100%. That's great because I can do other things, but I'd like this to finish as soon as possible. So I broke the three tasks into three separate loops that communicate through Queues. Below is the code:



My code went from using 100% CPU to 250% to 270% CPU.

To use Queues to increase your threading, you need to be able to break up your code into tasks that can be run simultaneously. Usually if you are waiting for I/O, that is a good task. Here I have three disk operations (Directory Listing, Read From File, Write To File). It turns out the slowest of these is reading the linkage information from the file. So much so that the Queue of Paths stays full and the Queue of information to write remains close to empty. To help improve performance in a case like this, you can set a maximum Queue size (on the Path Queue in my case). I set the maximum size to 1,000 Paths. So the Read Link loop would always have no more than 1,000 Paths waiting for it. When it takes a Path from the Queue, the Directory Listing loop wakes up and puts another Path in the Queue.

One trick is to figure out how to end the loops. With many of the LabVIEW APIs, you can loop until the Read operation returns an error, and in the Write loop, just close the reference. With Queues that doesn't work because you may close the Queue with data still in it (1,000 Paths, for instance). So instead we send some invalid data that we would not otherwise send (Not A Path in the Path Queue instance). When each loop finishes stuffing data into the Queue, it puts an invalid element in the Queue. Then the loops that read from the Queue know to stop when they see invalid data.

I hope using this can improve the performance of your VIs. If you have any questions or improvements, please leave them in the comments.

Labels: , , , , ,

The views expressed on this website/weblog are mine alone and do not necessarily reflect the views of my employer.


Tuesday, August 12, 2008

Squeezing Parallelism Out of Your Diagrams with Merge Errors.vi







There are two different ways to run your code on multiple cores in LabVIEW, static and dynamic. We've talked about the dynamic case before, getting a reentrant reference to a VI in a loop and running it. Now I'd like to talk about some simpler methods of squeezing parallelism out of your code.

When I code, I find that I usually, for convenience, chain everything together with error clusters. So I would have one long string of actions, all tied together with an error cluster wire. It's convenient, but cuts down on LabVIEW's ability to run code in parallel. I have come to heavily use the Merge Errors.vi to help me run more code in parallel.

Merge Errors.vi has three error clusters and an array of error clusters as input, and one error cluster as output. So you fork your incoming error wire to each of the sub-VIs that can run in parallel, and then take their output and run them into Merge Errors.vi. If you have more than 3 error clusters to merge, you can also drop a build array node for the remainder.

When ordering is important, make sure the error you want to return to the user is in the first terminal. For instance the code shown below is a "destroy" VI. It must destroy the data, regardless of any errors coming in. This is like the close file primitive. You may have gotten an error while writing to the file, but you still want to close the file. However, we want to return any errors that were passed in to us, even if the destroy resulted in an error.

The bottom line is, use the Merge Errors.vi liberally in your code to handle multiple errors instead of chaining all error clusters and preventing sub-VIs from running in parallel.

Labels: , , ,

The views expressed on this website/weblog are mine alone and do not necessarily reflect the views of my employer.


Friday, August 08, 2008

Simple Preferences VIs

NIWeek was very informative. I got to talk to a lot of customers who use Macs and got a lot of feedback.

I was talking to Travis Pape (pronounced poppy) about his really cool iBike app (LabVIEW built app for Windows and Mac that allows access to iBike computer). We were talking about the difficulties he was having with preference files, and I suggested he simplify the code.

I wrote two VIs that access the OS preference mechanism. They allow you to store key/value pairs for your application. I've posted these VIs at:

http://decibel.ni.com/content/docs/DOC-1832

Another example of using vi.lib/Platform/CFString.llb and CallLibraryFunction nodes. Took about 20 minutes to look up the functions, wire it all up, and then use Polish VIs to add the documentation.

Labels: , , , , , , ,

The views expressed on this website/weblog are mine alone and do not necessarily reflect the views of my employer.


Monday, April 21, 2008

Its The Little Things

First off, talking of little things, my girls are now a year old. For those of you who have twins know, this is about when things start to get easier. The fog lifts and you start to get a life back. I am getting close to completing my work on the next version of LabVIEW. That means I should have more time to post here.

Its the little touches that make the Mac OS a pleasure to use. Since I joined the LabVIEW team back in the LabVIEW 7.0 days, I have tried to add a little special Mac something to the release. Unfortunately, due to several circumstances, I will not be able to do that for the next release of LabVIEW.

Stephen Covey, in his book The 7 Habits of Highly Effective People talks about sharpening the saw. In other words, stop every once in a while and evaluate your efficiency and take some time to become more efficient. We are doing that with the Mac build of LabVIEW, which means the next release will not have the bells and whistles I would like to have in it.

To lament this, I wanted to publicly remind myself of the cool little Mac features I have been able to get into LabVIEW since 7.0. I hope there are also some things in here that you didn't know, and can now be more productive in LabVIEW. (Warning: These features are currently Mac only. Using LabVIEW on Windows may get frustrating after getting used to these features on a Mac.)

Go To LabVIEW Folder

I was fixing a problem with the file dialog box, when I started to get annoyed with trying to find the LabVIEW folder of the LabVIEW I was currently running (I have several versions of LabVIEW scattered all over my hard drive). So I added a button to the Open File dialog (if it treats LLBs as folders) that will allow you to go directly to the currently running LabVIEW's folder.

Horizontal Scrolling

LabVIEW supported the scroll wheel on the mouse on Windows before it was supported on the Mac. One day I got very annoyed that the scroll wheel did nothing in LabVIEW on the Mac, so I added scroll wheel support. Now that only brought us on parity with Windows, so I went a step further and added horizontal scrolling and fast scrolling. When the Mighty Mouse came out, LabVIEW worked in all directions. However, if you do not have a Mighty Mouse, you can scroll horizontally by holding down the shift key and using the scroll wheel. Fast Scrolling support is enabled (horizontal and vertical) by holding down the option key. It will double the scroll speed.

LabVIEW Version on Dock Icon

Note: This appears not to be compatible with Leopard. I don't know that this will be fixed in time for the next release.

If you set "BadgeAppInDock: True" in your LabVIEW preferences file, the version of LabVIEW will show up on the LabVIEW icon in the dock after you launch (you may have to click on it for it to show up). I never got it working quite right (and apparently it doesn't work for Leopard) so we left it in behind a preference setting. The version number on the icon should survive LabVIEW quit, however, it will be cleared after the Dock is restarted.

Flexible Preference File Format

The "style" of preference files for LabVIEW varies between the platforms. For instance, Windows has a "key=value" format and the Mac has a "key: value" format. I made the Mac preferences file format much more flexible, so line endings don't matter (DOS, Mac Classic or UNIX line endings will work), and equals (=) and colon (:) will work. By default, LabVIEW on Mac will write out the "key: value" format, but can read either format.


Command Line Path Strings

This isn't really a Mac specific feature, its more of a Mac necessitated workaround. I added two VIs at /AdvancedString: Command Line String To Path.vi and Path To Command Line String.vi. These VIs look and behave very similar to String to Path and Path to String primitives, and are drop in replacements for them. In fact, on every other platform, they behave exactly the same as their primitive counterparts. On the Mac, however, they convert between Mac classic paths (colon separated) and UNIX paths (slash separated).

The diagram shown here can convert symlinks into the path they represent (the Get File Info primitive does this a lot better). Notice the pink on the Path/String conversion bullets. They are the AdvancedString versions.

Use these VIs whenever you are interacting with the command line. This will make your VIs more portable among platforms.

I know I am forgetting a few other features, but these are the ones that have remained in my memory after my year of no sleep.

Labels: , , ,

The views expressed on this website/weblog are mine alone and do not necessarily reflect the views of my employer.


Thursday, August 16, 2007

Taking advantage of mutliple CPUs

There has been a lot of talk recently about how difficult it is in C/C++ to write good code that efficiently takes advantage of multiple cores/CPUs. That is one of the inherent benefits of LabVIEW, it will automatically split things out onto multiple cores/CPUs if it can figure out that it can.

In the NI-Week keynote, they demonstrated a block diagram with four for-loops executing simultaneously. That is one way to do it. It is simple and gets the job done. However, sometimes you need something that is a little more flexible. To do this, you can use Queues and Open VI by Reference.



This block diagram shows a VI I wrote that generalizes and simplifies the task of running a VI multiple times simultaneously on different sets of data. It takes a path to the VI to run several copies of, two queue references, the names of the queue controls on the VI and the number of copies to start running.



Here is an example of the worker VI. All it takes is two queues, the Data In Queue and the Data Out Queue. It takes a unit of work from the Data In Queue, works on it and puts the results in the Data Out Queue. You may recognize the work being done as the for loop work done in the NI-Week Keynote.

There are, of course, some caveats to great performance and working as expected.

1. Each unit of work must be independent. It may not rely on any other unit of work.

2. There must be no references to uninitialized shift registers, global VIs or references to things that are not protected against being called from multiple threads.

3. The worker VI (and as many sub VIs as possible) must be Reeantrant (File -> VI Properties -> Execution).

4. The Units of work must be fairly hefty. You must have significantly more work done in the VI than the overhead of passing things around in queues.

Using the VI in the keynote on my Mac Pro (4 core), I got 3.6x faster than single core. When I replaced it with the VIs shown above doing the work, I got to 4.9x.

Also of note: on the Mac, you can determine the number of cores in the machine by running the VI at vi.lib/Platform/Miscellaneous.llb/MPProcessors.vi.

The views expressed on this website/weblog are mine alone and do not necessarily reflect the views of my employer.