LabVIEW Development Best Practices Documents

cancel
Showing results for 
Search instead for 
Did you mean: 

Studying the Feasibility of an Agent System Based on LabVIEW Object-Oriented Programming

Introduction

Unlike conventional object-oriented languages such as ANSI C++ or Java, objects in LabVIEW are not treated as entities. Instead, they are handled like any other basic LabVIEW data typeas a passive data object following the dataflow concept [1, 2]. LabVIEW objects flow from data sources to data sinks, and at wire turnoffs, object clones are created and transmitted. Although the dataflow approach achieves intrinsic LabVIEW multithreading ability, it creates problems for classes not automatically cloned together with the object, such as data files, network connections, or physical devices. Unlike with conventional object-oriented languages, LabVIEW objects cannot be active. This means that they cannot start their own threads, as threads are resources that do not clone well.

Given that LabVIEW objects follow the dataflow paradigm, we need to reinvent and reimplement all fundamental object-oriented design patterns [3] in regard to the data flow. It is especially important to simplify treating LabVIEW objects as entities and using them in parallel threads. LabVIEW objects cannot be directly referencedonly indirectly, by using the reference pattern. These are basically queues with a length of one (see the LabVIEW example, ReferenceObject.lvproj). Using the factory design pattern, you can create generic initialized objects of classes specified by a name. Initialization parameters are transmitted in variant attributes. This pattern can replace missing constructors. In an earlier study, these design patterns were combined in an object manager for successful device server implementation [7].

An agent object is supposed to act and communicate autonomously and possibly be mobile. The LabVIEW HGF class library [5, 6] described here focuses on these aspects and includes basic classes that implement the following design patterns: factory, reference, visitor, thread pool, and events. With these patterns, you can generically activate passive data objects that are supposed to be treated as agents.

Agents: Design Patterns and Class Diagrams

Applications must activate LabVIEW objects because they are always passive. To meet the requirements of a generic and scalable approach, even the algorithms (tasks) are pooled into classes. The threadPool design pattern (see the left class diagram in Figure 1) solves this problem. The class HGF_ThreadPool starts threads HGF_ThreadWorker. Those retrieve a class HGF ThreadTask dynamic dispatch VI, while the child classes implement the actual event-driven activity. A LabVIEW object, which should be treated as an entity, can live in such a task, and its wire is no longer accessible from the outside. This effectively prevents unintentional wire turnoffs.

germanbb.png

Figure 1. UML-Class Diagrams


We can communicate with entities we cannot access by wire by using the visitor design pattern (see the right diagram in Figure 1). The NInstrSim class, representing the device simulator [8, 9], derives from HGF_Visitable and HGF_DeviceBase. The device’s basic class defines dynamic dispatch VIs that child classes can overwrite to implement the device-specific activities. In this pattern, HGF_Visitable-VIs are not directly retrieved by the applications, but by visitor classes inherited from HGF_Visitor; for this example, NInstrSim_ReadWaveform. The visitor holds the input and output parameters for the device class subVI in its attributes. The application transfers the visitor to the hosting task using an event. Both class diagrams contain associations to network events (HGF_NetworkEvent) and process variables (HGF_PVBase) such as SharedVariables. The user can alternately transfer a Network-Visitor to a waiting task by a preconfigured event that is triggered by a change in a process variable.

For this study, an agent is considered a combination of a passive object, HGF_Visitable, and an active task, HGF_ThreadTask. In this example, the characteristic of a device agent is abstractly defined by a state machine, HGF_DeviceFSM (see Figure 2, center left), and driven by HGF_TaskDFSM.

Creating a Stationary Device Agent

The sequence in Figure 2 shows the creation of the network event use required objects. A device visitor is associated with a SharedVariable and handed to a task visitor in the form of a parameter, which is transferred via HGF Event to the device agent when the SharedVariable changes. In Figure 2, the HGF_TaskDFSM object, with the initialization parameter for the device object and the SharedVariable event object, is transferred to the ThreadPool and revived by a worker in the background. The loop illustrates the change of the SharedVariables that triggers the visit of the device agent by the device visitor in the background.

The possible actions in state transitions and periodic functions, entry, exit, and do actions, are virtually defined by the class HGF_DeviceBase for all states and can be overwritten by child classes. The device object is created in the transition /createDevice using a factory, initialized, and stored internally as an attribute in the StateData cluster (see Figure 2, center right). As soon as the state machine terminates (STOP/destroyDevice), the device object is destroyed.


During the lifecycle in alive status, the agent switches from NotConfigured over Configure to Configured.Idle.OK. In the Idle status, the device object can periodically monitor the physical device and switch between OK and Fault, if temporary errors arise. During the Idle status, HGF_DeviceVisitor objects can visit the agent. This means that the status temporarily changes to Busy so that the desired actions can be performed; that is, device-class subVIs are called up by visitor objects (accept.vi, shown at the bottom of Figure 2 ). If a severe error occurs, the agent switches to the error status that can only be reset by explicitly triggering the resetError after the error is resolved by a periodically performed dynamic dispatch VI.

germanaaa.png

Figure 2. Device Agent Lifecycle

Conclusion

We proved that LabVIEW agents can be abstractly defined by a combination of a passive data object and task, and then activated by a thread pool and visitors. Child classes implement the actual activities. For mobile agents, an additional task class is necessary to dynamically load LabVIEW classes, implement the required authentication process, take safety aspects into consideration, and permit introspection.


References

[1] LabVIEW Object-Oriented Programming FAQ, http://zone.ni.com/devzone/cda/tut/p/id/3573

[2] LabVIEW Object-Oriented Programming: The Decisions Behind the Design, http://zone.ni.com/devzone/cda/tut/p/id/3574

[3] Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, “Design Patterns - Elements of Reusable Object-Oriented Software,” Addison-Wesley, 1994

[4] H. Brand, D.Beck, “The HGF Base Class Library based on LVOOP,” GSI Scientific Report 2008, page 260, ISSN: 0174-0814, http://www.gsi.de/library/GSI-Report-2009-1

[5] HGF Base Class Library and Design Patterns,

     http://wiki.gsi.de/cgi-bin/view/NIUser/HGFBaseClassLibrary

[6] HGF: Helmholtz, GSI, FAIR

[7] D. Beck, H. Brand, “Control System Design Using LabVIEW Object Oriented Programming,” Proceedings of ICALEPCS07, Knoxville, Tennessee, USA

[8] Instrument Simulator, http://sine.ni.com/nips/cds/view/p/lang/de/nid/10763

[9] NI Virtual Instrument Simulator, http://zone.ni.com/devzone/cda/epd/p/id/1540

This content was written by Frederik Berck and Dr. Holger Brand, GSI Helmholtz Center for Heavy Ion Research GmbH, Darmstadt.

Comments
Proven Zealot
Proven Zealot

That's what the Actor Framework is all about.

https://decibel.ni.com/content/docs/DOC-17193

The framework was released at NI Week 2010, revised substantially over the years since, and is preparing for a big upgrade come August 2012, assuming all goes as planned.

Contributors