Using a scalable architecture can help you easily expand your application and reuse your own development efforts when you want to add new features. Once you create a good scalable architecture for your company, you can create templates that you and others can reuse for future projects. This document goes over some useful scaleable design patterns that a developer can use in their application architecture. By no means is this a comprehensive list, and no one architecture should be considered as the "best", however it may be helpful to start with one of these design patterns, or variations on them. A good architecture is one that fits best for the application and can be a combination of the design patterns below. Examples of each design pattern described here are attached at the end of the document.
There are three core design patterns that can help start a foundation for a good overall architecture. Each of these alone may not be ideal, but you can combine and expand them to create the perfect architecture
The state machine is one of the fundamental design patterns LabVIEW developers frequently use to build applications quickly. You can use the state machine architecture to implement complex decision-making algorithms represented by state diagrams or flow charts.
State machines are used in applications where distinguishable states exist. Each state can lead to one or multiple states, and can also end the process flow. A State Machine relies on user input or in-state calculation to determine which state to transition to.
A basic LabVIEW state machine consists of these primary components:
Find more information about the state machine architecture here.
The event handler design pattern provides a powerful and efficient architecture for handling user interaction with LabVIEW. Use the event handler for detecting when events occur such as a user changing the value of a control, moving or clicking the mouse, or pressing a key, etc.
The standard event handler template consists of an Event structure contained in a While Loop. Configure the Event structure to have one case for each category of event you want to detect. Each event case contains the handling code that executes immediately after an event occurs,
Refer to the LabVIEW help for more information about the Event structure.
Use the Producer/Consumer design pattern for data sharing between multiple loops running at different rates. The Producer/Consumer pattern’s parallel loops break down into two categories; those that produce data, and those that consume the data produced. Data queues communicate data between loops in the Producer/Consumer design pattern. These queues offer the advantage of data buffering between producer and consumer loops.
More information on the Producer/Consumer architecture can be found here.
The Queued Message Handler (QMH) design pattern is a combination of producer/consumer, event handler and state machine architectures together. The producer loop contains an event structure that sends messages to the consumer loop. The consumer receives and processes the messages in a state machine. A message can be triggered by UI events or from other states in the state machine. The QMH can also be designed to provide feedback from the consumer to the producer using User Events.
The functional global variable (also known as FGV, LV2 Style global, Action Engine, etc) is a design element and data storage method that has been used in LabVIEW since LabVIEW 2. The FGV stores data in an unintialized shift register and allows you to access data anywhere within an application. Aside from storing data, you can also perform multiple different tasks or actions on this data, allowing a very customized control of data being transferred as well as protecting you from race conditions.
The FGV in LabVIEW consists of a while loop with an uninitialized shift register, and an input which decides the action. The while loop usually only runs once and can contain other controls and indicators to get and set the data within it.
A basic FGV is shown below and contains Data in and Data Out and two actions "Set" and "Get"
In a larger application, you can create a more powerful FGV that performs multiple module functions. You can use the FGV to store references, shared data to use throughout the application, etc. You can also use it to encapsulate low level functionality from the user and fully contain all complicated aspects of the module within one VI. One example of this is a File I/O Module as shown below. The VI stores the file reference between successive calls and performs 4 different actions (Open, Read, Write and Close) within a single VI.
A subpanel in LabVIEW is a container which you can use to dynamically call any VI front panel from another VI's front panel. Using a subpanel as your main architecture helps create a scalable architecture because you can easily add another VI which is called form the main application with little or no code change. It is flexible because the UI can be anything that fits within a subpanel. It's modular because a developer can independently cod and test each VI outside of the main architecture. Finally the architecture can be very simple as subpanel VIs may not need to communicate to the main VI and can run by themselves.
The below example scans a certain directory for any plugin VIs with the name SubVI. It then will populate a text ring with available plugins and lets a user choose between which subpanel they choose to display on the main VI. This architecture allows a developer to add new modules without a single change to the main VI.
In some situations, an application architecture needs to be able to launch an undetermined number of processes and asynchronously retrieve the results. We can do this using asynchronous calls. This allows to dynamically run any number of VIs in parallel, which is particularly useful for background tasks like Auto-save or Data archiving.
In the below example, the main VI calls a process VI asynchronously a certain number of times. Each VI runs on it's own and returns data to the calling VI whenever it is finished. The calling VI can continue running other code if needed and not wait on the process VI to finish.
Object oriented programming (OOP) is an advanced design technique added to LabVIEW in version 8.2. LabVIEW object-oriented programming uses concepts from other object-oriented programming languages such as C++ and Java, including class structure, encapsulation, and inheritance. You can use these concepts to create code that is easier to maintain and modify without affecting other sections of code within the application.
There are many common Object Oriented design patterns that can be applied to LabVIEW OOP. These are described in great deal in the document Applying Common Object-Oriented (OO) Design Patterns to LabVIEW.