Thursday, August 23, 2007

Project File Management Enhancements

Continuing my series of posts about the new features of LabVIEW 8.5, here is a short video about the file management enhancements to the LabVIEW Project.

For those of you who aren't familiar with LabVIEW projects, they are a type of file introduced in LabVIEW 8.0. The project file (*.lvproj) does not contain VIs (the way LLBs do), but rather refers to VI files (*.vi) that exist separately on disk.

Labels: , , ,

Thursday, February 01, 2007

What's in a Name? (Libraries)

I want to come back to the topic of cross-linking. LabVIEW 8.0 introduced a feature that is very useful for avoiding cross-linking: project libraries.

It's a slightly misleading name. You don't need to use LabVIEW projects (also new in 8.0) in order to use project libraries. And although LLBs were often referred to as libraries in the past, project libraries have nothing to do with LLBs. (Except that you can put libraries in LLBs... confused yet?)

Most of the time, I just use the term libraries instead of project libraries, and that's what I'll do for the rest of this post.

I've had several people tell me that NI presents new LabVIEW features in terms of what they do, rather than what they're for. So I want to spend a moment to talk about why you might want to use libraries.

The primary intended purpose of libraries is to help you group VIs. There are numerous reasons why you might want to group VIs:
  • shared components
  • toolkits
  • versioning
  • etc.
Generally, these groups consist of VIs that you want to call from outside the group (i.e. the interface) and some supporting subVIs.

In the past, you had few options when you wanted to designate the interface VIs:
  • put them in a higher folder
  • mark them as top-level in an LLB, or
  • give them special names.
There was no way to prevent someone from calling your support VIs directly, though. That meant as soon as you shared your VIs, you had to be careful about changing the connector pane of your support VIs, because caller VIs would break if you did so, and you had no way of knowing who those might be.

Putting a group of VIs in a library conveys numerous benefits, including:
  • Namespacing. The name of a VI in a library is a combination of its file name and the library name (e.g. This lets you use simple VI names (e.g. without cross-linking (assuming your library names are unique).
  • Public/private access scope. Private access scope means that only VIs inside the library can call the VI. Now you can change your subVI connector panes and know for certain that you only have to fix caller VIs inside the library.

OK, so now let's talk more about the technical details. The library (*.lvlib) is not a container file. It is its own kind of LabVIEW document, independent from its member VIs. A VI can be a member of one (and only one) library, but libraries can be members of other libraries.

The library knows which VIs are members of it, and a VI knows which library it is a member of.

If you think about this in terms of VI linkages, you'll notice that this caused a significant change to LabVIEW. Prior to libraries, VIs and subVIs formed a tree of linkages. (SubVIs do not record the VIs that call them). But library/VI linkages go both ways, so linkages no longer form a tree. Instead, they form a graph. This is why, when you save an Untitled library and Untitled VI in it, we have to ask you for the names of both before we can save either.

The namespacing ability of libraries is an extremely powerful tool in avoiding cross-linking. Anytime you make a copy of your VIs, add the copies to a new, uniquely-named library and you'll be certain that you won't accidentally link to them in the future.


Sunday, December 03, 2006

Save As

The Save As dialog in LabVIEW 8.x is hated by some and loved by others. Crystal Drumheller called it the "best feature of LabVIEW 8." But the initial reaction of most people is, "Why does Save As bring up a dialog? Other apps don't do that."

The reason is: other applications don't have linked documents.

When you do "Save As" on a file in Microsoft Word, you aren't affecting any other Word documents. They're all independent.

In LabVIEW, however, you may be changing every other VI, project, and library in memory. Any calling VIs, referring projects, and owning libraries could refer to the original VI or the new one.

And if LabVIEW does the wrong thing, you quickly end up with the cross-linking nightmares I mentioned in my earlier post.

Now, I'm a usability advocate here at NI, and I'm a proponent of simplifying choices to improve usability. But I can't accept doing a catastrophically wrong thing half the time to avoid making the user choose.

The Save As problem was the most difficult UI design project I've ever worked on. Here's some of its story.


Prior to LabVIEW 6, the options were "Save As" and "Save a Copy As." The problem with separate menu items is that you have very little opportunity to explain what they mean inline. How many people knew what "Save a Copy As" did at first glance? From what I've heard, not many.

In LabVIEW 6, these two menu items were combined and a checkbox was added to the bottom of the file dialog:

From feedback I've gotten, I believe that most people didn't even see the checkbox until they got burned by cross-linking. After losing several days of work, people would make sure they knew what that checkbox did and check or uncheck it as needed.

This is, obviously, not good. We don't really want the software equivalent of hazing the uninitiated.

Usage Analysis

People use Save As for two completely different tasks: making a copy of a file and renaming a file.

When you throw in linked files, you find there are really six primary use cases for Save As. Here's an excerpt from the Save As specification:

We have identified the following use cases when doing File»Save As on a VI:
  • Creating a copy of this VI, intending to edit the copy.
    • Variation A: Do not update referencing files and use the clone as a starting point for a new VI.
    • Variation B: Update referencing files to refer to the new copy [This is what 7.x did by default. Example: Changing the functionality of VIs in memory but wanting to leave other callers of the original subVI unchanged].
  • Create a backup of this VI, not intending to edit the backup but intending to continue editing this VI.
  • Change name/location of VI.
  • Copy to new location on disk. (Same name, different folder on disk). [Example: Original VI is on a network drive, saving a copy to my computer].
  • Copying a VI and its subVIs to a new location. [Example: Saving an example VI and its subVIs to a new location as a starting point for new development]
  • Determining the location of a file on disk. [It's a use case orthogonal to the actual purpose of Save As, but it was requested enough that I acknowledge it as a frequent use case of the menu item].


We saw three possible approaches:
  1. Remove functionality. Seriously. As long as you're doing the task that is still possible, it's easier to use. The drawback is that any other ones just become impossible.
  2. Expose all the technical permutations of the operation. Usability specialists tend to frown on this approach, but it can give you the smallest number of options. It just means you have to know what they all do.
  3. Map the choices to tasks that the user is trying to do.

We chose to try the third approach. Hence, a dialog:

You basically have two options in this dialog: do you want to copy the VI, or rename it? If you're making a copy, you have a secondary choice, specifically what VI(s) do you want open afterwards: the new one, the old one, or both?

We intentionally made the default the same as it was in LabVIEW 7.1, so if you never paid attention to the box before, you can click through this dialog and get the same results.

To help avoid cross-linking, we added elements to the dialog when there are callers in memory.
These yellow triangles warn you that you are modifying other files, and give you the ability (via the Referencing files in memory disclosure triangle) to see what those files are.

The dialog looks intimidating, because it has text to explain the choices. But we felt we needed to explain them inline, since they are choices that are not found in other applications.

Further, the dialog can look even bigger if the VI has subVIs, because of use case number 5.

But we believed the ability to copy subVIs while copying a VI to a new location is needed too often to leave it out.

So, in summary, I hope you will give the Save As dialog a chance. Numerous people have told me that, after they took a few minutes to understand the Save As choices, they found the operations they do through Save As to be much easier than in any previous version.


Wednesday, November 15, 2006

VI Linkages and Cross-linking

I want to talk about libraries and projects, but I think first I should set the stage by talking about cross-linking.

VIs are different than most document files because they refer to each other. A VI cannot run unless LabVIEW can find all of its subVIs.

A VI referring to another VI is called a link. When a VI links to a VI other than the one the developer intended it to link to, that's called a cross-link.

Imagine you have a VI with a subVI:

In the beginning, the VIs are on disk. They are are in “Disk World” (with apologies to Terry Pratchett).

When you open the main VI, LabVIEW needs to bring the VIs into “Memory Land.” So it needs to load the main VI and the subVI as well. That means the VI needs to store information about the subVI, particularly where it is on disk.

How does LabVIEW store that information?
  • As an absolute path (e.g. C:\Project1\ Not if there’s any other choice. Imagine how brittle it would be if we always stored absolute paths. If you renamed your Proj1 folder, LabVIEW wouldn't be able to find any subVI of main.VI. However, in some cases, it is not possible to use a relative path (such as when the subVI is on a different network drive), so LabVIEW has to store an absolute path.
  • As a relative path (e.g. .\ Sometimes. In fact, this is how the path is stored in most cases, except when the subVI is in certain “special” locations.
  • As a pseudo path (e.g. \ Sometimes. If the VI is in one of the “special” locations defined by pseudo paths.

In Disk World, a VI remembers its dependencies by path. But once in Memory Land, a VI tracks its dependencies by references to linker identities, which are unique by qualified name (in a given application instance). [I'm not going to talk about application instances in this post, but they have to do with projects. I'm also not going to go into more detail about qualified names now, but they have to do with libraries. In the absence of libraries, you can think of the VI name as the file name].

Why is cross-linking a problem?
  • If you have two copies of and one has a bug fix and another doesn’t, and you accidentally link to the one that doesn’t, you can be quite puzzled as to why your VI is executing incorrectly.
  • If you edit a subVI, thinking that it is part of your current hierarchy when it’s actually loaded from a completely different one, you might save your changes without realizing you’re overwriting a VI you needed for that other hierarchy. If you didn’t keep backups, you could end up losing work as you have to recreate the original subVI when you go back to working on the other set of VIs.
  • If you mass-compile a copy of your VIs, and they're accidentally linked to the VIs you meant to leave in a prior version, you can inadvertently bring all your VIs into the current version.
  • etc.

How does cross-linking arise?
  • When a VI that satisfies the “link to” requirements is already in memory when the referring VI loads. (See section below for more details).
  • Copying files on disk instead of using File>>Save As.
  • Using File>>Save As incorrectly.

How LabVIEW finds a subVI:
  1. Look in memory for a VI of the requested qualified name. If that VI comes from a path other than the one LabVIEW expected, it will post a load warning, but it will still go ahead and use the VI in memory.
  2. Look in memory for a VI that was loaded from the requested path. If the caller expected a VI that wasn’t in a library and we find a VI that’s in a library but otherwise of the correct name, we will use it. However, if we find a VI that’s otherwise of the wrong name, we will not link to it.
  3. If still not found, look at the expected disk location (using the stored relative, pseudo, or absolute path).
  4. If still not found, look for the file in each directory of the VI Search Path.
  5. If still not found, prompt the user.
  6. Again, check memory for the requested name (in case the user selected a file with a different name).

In future posts, I plan to talk more about how to avoid and fix cross-linking.