Chapter 5. LCOD Implementation

Component Mechanisms

We should consider the inherent requirements for all components.

  • All of the components, public functions, and data should be accessible via a simple interface.

  • We should be able to add/delete/modify our component's actions simply.

  • Any modifications should have little effect on the overall software design.

  • The component stores its own state locally and persistently.

  • The component should initialize itself.

  • Errors are handled by the component.

  • Inputs and Outputs should verify themselves.

Message Sending

Message sending is the means of controlling your component. The calling component will send a message to the called component telling it what to do.

There are various methods you could employ to send messages to your components, but we use enumerated types that are simple and self-documenting. In fact, the biggest bonus when using enumerated types is that you can drop your component on your block diagram, right-click on the input, and pop up a complete reference of the components' actions and attributes.

All About Enumerated Types

The LabVIEW online guide gives a comprehensive overview, however, for those too lazy here's a summary:

  • An enumerated type control can be thought of as a drop-down box

  • Enter additional items by using the labeling tool and pressing Shift-Enter

  • The enumerated type data type is U8, U16, U32, selectable from the Representation palette. The terms U8, U16, and U32 refer to the number of elements the enumerated type can hold (U8 = 256, U16 = 65,536 and U32 = lots and lots)

  • Wiring to a Case Structure makes the case display a string rather than a number

  • Arithmetic operations (except Increment and Decrement) treat enumerated type as their number representation

  • Increment and Decrement rotate at start and end

  • A number is converted to the closest enumeration; out-of-range numbers are set to the last enumeration

101 Things to Do with an Enumerated Type

Well seven actually.

Format into String

Converts the selected enumeration to its text representation (in the example shown in Figure 5.1 the output string would read “Error State”).

Format enumerated type into string.

Figure 5.1. Format enumerated type into string.

This is useful for storing states in a file or database.

Scan from String

Converts the input string to its representative output. In the example shown in Figure 5.2 the output enumeration would be set to “Test 1”, if there is a corresponding list item, or “Error State” if not.

Scan enumerated type from string.

Figure 5.2. Scan enumerated type from string.

This is useful for retrieving states from a file or database.

Typecast

The example in Figure 5.3 converts the number to its enumerated type without making ugly coercion dots (Output String 2 would be set to “Test 2” in this case).

Typecasting enumerated type.

Figure 5.3. Typecasting enumerated type.

Note

Typecasting enumerated type.

Care must be taken to ensure that the numeric is matched to the enumerations representation. If not, it may sit on “Error State”.

Casting and Uncasting

Figure 5.4 shows an enumerated type being cast to or from a string. This is very useful for sending commands through ports. For example, a real-time system could use it as a method of sending commands to and from a PC client to the real-time server using the Transmission Control Protocol/Internet Protocol (TCP/IP) functions.

Casting to a string and casting from a string.

Figure 5.4. Casting to a string and casting from a string.

Case Structure

Gives similar multiple selection functionality to a Select-Case statement in Pascal and Basic, or a Switch in C and Java. Using an enumerated type as in Figure 5.5 improves a system's readability and helps with self-documentation.

Case structure.

Figure 5.5. Case structure.

Firing a Sequence of Events

By putting the enumerated type in an array and allowing a For Loop structure to auto-index, a sequence of events can be driven. In the example shown in Figure 5.6 they go in sequence, but any order could be selected. From this simple structure a wealth of opportunities arise. For example, the array could be generated from a database or file, allowing a completely configurable system in very few steps.

Sequence of events.

Figure 5.6. Sequence of events.

State Machine

Wiring an enumerated type into a While Loop shift register makes an extremely important structure called a state machine. The example shown in Figure 5.7 emulates a Sequence structure, but with added flexibility.

State machine structure.

Figure 5.7. State machine structure.

Consider the following. You've designed your component and created an enumerated type as a control and wired it to the connector. It's now deployed throughout your software. You now need to add some functionality to this component. You pop up and add a command to the enumerated type. If you put this new command anywhere but at the end of the list you will find all your popped up constants broken. If you put the new command at the end you will find ugly coercion dots everywhere. This is aggravating to say the least. The kindly people at National Instruments have given us a solution—Strict Type Definitions.

Strict Type Definitions

You can edit your enumerated type on the Front Panel (highlight control and select Edit>>Customize Control . . ., or set your preferences to “edit a control on a double-click”).

When the Panel shown in Figure 5.8 is displayed, select Strict Type Def. from the drop-down menu and then save in a Controls directory as controlName.ctl.

Strict Type Definition.

Figure 5.8. Strict Type Definition.

Now whenever you create a constant by popping up, it will be an instance of the Strict Type Definition. If you change one you change them all. Here are a few things to be aware of when dealing with Strict Type Definitions.

Bad Things

  • If you create a subVI by selecting it and using the Create subVI, the new Front Panel objects are not linked to the Strict Type Definition.

  • Similarly, popping up on a tunnel in a Case Structure does not create a tunnel.

  • And popping up from any of the built-in functions (Scan From String, Unflatten From String, etc.) doesn't create them either.

Good Things

  • Duplicating Cases and Sequences does preserve the link to the Strict Type Definitions.

  • Cutting and Pasting also preserves the link to the Strict Type Definitions.

Prior to LabVIEW 6 you didn't get a Strings[] attribute for a Strict Type Defined Enumerated Type, but you did for a normal enumerated type. This was overcome by looping around each element in the enumeration and creating the element content array from that loop.

We were a bit worried about the robustness of using Strict Type Definitions, for example, if we changed one how would this ripple through our software. In practice they have proven to be remarkably immune to change, invariably coping with anything that we could throw at them. Everything discussed here about Strict Type Definitions is also applicable to normal Type Definitions.

LabVIEW has the tools to define a flexible command and attribute message sending mechanism. Oh so powerful! Oh so underutilized!

Persistent Local Storage

We want our component to keep ahold of its own state after it has been active. This data should be considered private, hidden from outside of the component and only accessible by the commands sent to the component.

There is only one efficient private method of storing data within a VI in LabVIEW and this is by using shift registers on a For Loop or While Loop structure. Figure 5.9 illustrates their use.

Shift registers.

Figure 5.9. Shift registers.

Shift registers are local variables that are primarily used for transferring values from one iteration of the loop to another. They have an important characteristic that we use, and that is persistence. By persistence we mean that the data held within the shift register is kept even after a VI has been run. This gives our components independence, which is essential for loose coupling, and data privacy, which is essential for information hiding.

Let's put it all together and make a dummy component structure.

The Basic Structure of a Component

As you progress through the book you'll notice a common look and feel to all the components. We'll now build a template from scratch for a simple component structure and explain the parts.

One of the simplest structures would be an enumerated type wired to Case Structure. We'll wire the enumerated type through the shift register of a While Loop. This will allow us to write a bit of self-initializing code. Figure 5.10 illustrates this.

  • The component should initialize itself.

Basic component.

Figure 5.10. Basic component.

The structure of the component allows it to auto-initialize using the very handy “First Call?” function found in the Advanced>>Synchronization Palette. The “First Call?” function is the small, round effort on the left of the While Loop. The first time a subVI is called from another VI it returns a TRUE, which causes the Initialize element of the enumerated type to be passed to the Case Structure. In the Initialize case would be file loads, array size setting, instrument setup, or any other Initialization duties you can think of. From then on the “First Call?” function passes FALSE. Be aware that if you run the Basic Component VI from its Front Panel it will always return a “First Call?” of TRUE. The Initialize case then passes the Command enumerated type to the shift register and iterates another time.

If your component doesn't need initializing, just leave that particular command out.

  • Errors are handled by the component.

  • Inputs and Outputs should verify themselves.

As seen in Figure 5.11, for this template all the cases either Finish and exit or just exit. The UnderRange and OverRange Cases protect the bounds of the enumerated type, and they should be used to throw an error complaining that they've been called. For this example the error-trapping consists purely of passing the errors through. There is no reason that the error conditions shouldn't be separate cases or that part of the Finish case has the capability to check for errors and act accordingly.

Component states.

Figure 5.11. Component states.

The Finish case could be used for postcondition checking, and you could even employ a Start case to check for preconditions. Pre- and postconditions are described in detail in Chapter 6 on complementary techniques.

  • We should be able to add/delete/modify our component's actions simply.

  • Any modifications should have little effect on the overall software design.

To add new functionality to the component simply add the function name to the enumerated type and duplicate or add a new case to the Case Structure. All you need to do then is add the new actions. When deleting functionality it is worth removing the obsolete cases before removing the elements from the enumerated type.

  • All of the components, public functions, and data should be accessible via a simple interface.

The public interface to the component is shown in Figure 5.12, and you must agree that it is very simple. This component is obviously very simple because it has no real inputs and outputs. That aside, if you want to access any of the functions of the component all you need to do is right-click on the Command input, and a Strictly Typed enumerated constant will be created, listing all of the functionality available for the component.

  • The component stores its own state locally and persistently.

Basic component public interface.

Figure 5.12. Basic component public interface.

Our example only uses its shift registers to contain data when it is running; it has no need to store any local data between calls. If it did, as does the stack-queue component described later in the book, then you would see one or more shift registers initialized by the Initialize case.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.90.54