You might have already noticed the error cluster datatype, which is found on the Modern>>Array, Matrix & Cluster palette (Error In and Error Out, shown in Figure 7.64) and can also be found used in the inputs and outputs of many VIs and functions. The error cluster is a special datatype in LabVIEW (a cluster consisting of a status Boolean, code I32, and source string) that is used for propagating information about errors that occur during the execution of LabVIEW code. Errors and error handling are very naturalthere is no reason to be afraid of errors. Rather, we should understand how errors can occur, how to communicate errors, and how to handle errors. Figure 7.64. Error In and Error Out controls found on the Array, Matrix & Cluster paletteSo, what is an error? In simple terms, an error is an event where a function or subVI cannot complete a request because resources are not available or the information (arguments) passed to it are not valid. In LabVIEW, we use dataflow to propagate information about errors inside of an error cluster datatype. Error Cluster DatatypeThe error cluster (shown in Figure 7.65) contains three elements, as follows:
Propagating Errors: Error DataflowYou use the error cluster datatype to store information about an error, and you use dataflow to propagate the error cluster through your block diagram. As you can see the example shown in Figure 7.66, an error cluster passes through the block diagram, connecting the following, in sequence:
Many of LabVIEW's functions and VIs have error in and error out terminals. These are almost always found (if present on the function or VI) on the lower-left and lower-right terminals of the connector pane (respectively). Figures 7.67 and 7.68 show the inputs and outputs of Read From Measurement File and Amplitude and Level Measurements with error in and error out highlighted. Figure 7.67. Read From Measurement File
Figure 7.68. Amplitude and Level Measurements
Generating and Reacting to Errors in SubVIsThe expected behaviors of functions and VIs, with respect to generating and reacting to errors, are the following:
Error Case StructureA subVI can fulfill expected behavior #1, by "wrapping" all of its functional code (its "work") inside of an Error Case Structure, which is simply a case structure that has an error cluster wired to its case selector terminal. As you can see in Figure 7.69, the Case Structure allows the error cluster datatype to be wired to its case selector terminal. When wired in this way, the Case Structure frame names change to "No Error" and "Error." At run-time, if the error cluster does not contain an error, then the "No Error" frame will execute. Conversely, if the error cluster does contain an error, then the "Error" frame will execute. Figure 7.69. An "Error Case Structure" containing an "Error" and "No Error" cases
Figure 7.70 shows how we can use a case structure to conditionally execute our functional code only when there is no "upstream error" (using the dataflow vernacular). Figure 7.70. An "Error Case Structure" used to execute code only when there is no "upstream error"Merge ErrorsBut, what about the situation where you want to do some clean-up work, even if an upstream error has occurred? In this case, you should not wrap your functional code in an Error Case Structure, but rather, merge the error cluster flowing out of your work with the upstream error cluster using Merge Errors.vi, as shown in Figure 7.71. Figure 7.71. Using Merge Errors.vi to combine several error clusters into a single error cluster
An error cluster can contain information about one, and only one, error. (This is a limitation in the data structure that carries a long legacy.) Merge Errors.vi merges several errors cluster inputs into a single error output. However, if there are errors in more than one input, it must choose which of these errors will take top priority and be passed to error out. It uses a top-to-bottom priority scheme, giving error in 1 top priority, then error in 2, and so on. In the example shown in Figure 7.72, it is critical that the upstream error be wired to error in 1 (the top-most error input terminal) to ensure that the upstream error takes priority over any errors that occur inside our subVI. Figure 7.72. Merge Errors.vi
(Programming>>Dialog & User Interface palette). Merges error I/O clusters from different functions. This VI first looks for errors among error in 1, error in 2, and error in 3; then error array in and reports the first error found. If the VI finds no errors, it looks for warnings and returns the first warning found. If the VI finds no warnings, it returns no error. Use merge errors to combine the error clusters of parallel tasks, or tasks that must each execute regardless of upstream errors. Handling Errors in SubVIsIt is a good practice for a subVI to make a reasonable attempt to fulfill its contract (its stated job, per the software design requirements) in every way it can before passing an error up. For example, imagine you are writing a routine that initializes an XY motion stage. (This might actually be the case, for some of our readers.) In order to find the home position (X = 0, Y = 0), the stage is moved at a low velocity toward a position sensor that outputs a digital signal of 1 (TRUE) when the stage reaches the home position. However, because there is a lot of electrical noise coming from the stage motors, the position sensor sometimes fails to report when the stage is at the home position (it sometimes jumps to 0, momentarily). This causes the initialization routine to fail at a rate of about one (1) out of twenty (20) tries, or 5% of the time. When it does fail, it always outputs the same error code. The noisy home position sensor signal has no other effect on the system, except for the occasional error during the homing operation. Knowing what you know about the rate of failure and the type of failure, why not adapt your homing routine to retry (up to a certain number of times) before actually outputting an error message to the calling VI? This would make your code robust: fault tolerant and able to get the job done in spite of minor errors. Wouldn't your end users be much happier if you made this change? Think of all those times they've started an experiment and came back to the lab an hour later expecting to find their experiment completed but, instead, they find that the system has generated that blankity-blank error while trying to home the stage... again! OK, you agree that handling errors in subVIs is a great idea. Because the error cluster is just that, a cluster, you can use the cluster functions such as Unbundle By Name and Bundle By Name to access and modify the error data. For example, you can unbundle the error code. Generating Errors in SubVIsWhen you call a subVI or function and it generates an error, you can either try again (or perhaps try something else) or you can give up and propagate error (passing it downstream or up to the calling VI). But what happens when you want to generate a new error, perhaps because of an invalid input passed down from the calling VI? For this situation, you should use Error Cluster From Error Code.vi to generate a new error (see Figure 7.73). Figure 7.73. Error Cluster From Error Code.vi
Error Cluster From Error Code.vi (Programming>>Dialog & User Interface palette) converts an error or warning code to an error cluster. This VI is useful when you receive a return value from a DLL call or when you return user-defined error codes. For example, Figure 7.74 shows how we might generate an error in a subVI when an invalid argument is passed into the subVI (the TRUE case of the inner Case Structure is shown only for illustration purposes). In this example (which is located on the CD at EVERYONE/CH07/Calculate Square Root with Error.vi), we are calculating the square root of a number x. If x is negative, we will output an error (error code 1, which signifies an argument error) and default output data. Figure 7.74. Calculate Square Root with Error.vi block diagramGiving Up: Displaying Error Messages to the UserIf error conditions cannot be handled by subVIs or in your top-level application, you can "give up" (but, please don't accept defeat too easily) and display an error message to the user. This is the "last resort" of error handling. Figure 7.75 shows an error being passed to Simple Error Handler.vi (found on the Programming>>Dialog & User Interface palette) to display a dialog containing the error information. In this case, we passed a negative number to the subVI shown in Figure 7.74, which is an invalid input. Figure 7.75. Calling Calculate Square Root with Error.vi as a subVIFigure 7.76. Simple Error Handler.vi
Simple Error Handler.vi (Programming>>Dialog & User Interface palette) indicates whether an error occurred. If an error occurred, this VI returns a description of the error and optionally displays a dialog box. This VI calls the General Error Handler VI and has the same basic functionality as General Error Handler but with fewer options. Extra Tips for Error HandlingUse the following tips for successful error handling. Use the Explain Error DialogWhen an error cluster control or indicator contains an error or warning, you can learn more about an error by selecting Explain Error from the cluster's pop-up menu, as shown in Figure 7.77. Figure 7.77. Selecting Explain Error from the pop-up menu of an error cluster to open the Explain Error dialog (shown in Figure 7.78)
This will display the Explain Error dialog, as shown in Figure 7.78. Figure 7.78. Explain Error dialog, which shows a detailed error explanation
Go with the Dataflow: Use Error In and Error Out TerminalsWhen you add error in and error out I/O terminals to your VIs, you allow calling VIs the opportunity to chain the error cluster wire to create dataflow dependencies between subVIs. Also, you are enabling applications to perform error handling, which is a good programming practice. Even if your subVI might not ever generate an error itself, put error I/O terminals on the front panel, and put an Error Case Structure on the block diagram (as you just learned) to allow errors to propagate through the software. Make sure to wire error in to the lower-left terminal and error out to the lower-right terminal of the VI connector panethis is a best practice. Define Your Own Errors: User-Defined Error CodesFor large applications, you might want to explore user-defined error codes, which are specific to your application. The LabVIEW Help documentation describes the process for defining and using this feature. Don't Get Stuck in a Loop: Test Error Status in LoopsIt is almost always a good idea to check for errors inside of loops, so that you can exit the loop if an error occurs. Figure 7.79 shows how this is done. Note that the loop will stop if either the stop button is pressed or an error occurs. Figure 7.79. Checking for errors in a While Loop to exit the loop when there is an errorUse Shift Registers to Pass Errors Through LoopsAlways use shift registers (not tunnels) for passing error clusters through the wall of a loop. This is especially important for a For Loop, as shown in Figure 7.80. Remember, if a For Loop executes zero times, data still passes from the input shift register to the output shift register. Wiring error clusters to shift registers on a For Loop ensures that errors propagate through the For Loop if it executes zero times. Figure 7.80. Using shift registers to pass errors through loops
|