DCAF is built from a collection of software components that can all be used independently. This section will give a high-level overview of each of these components and explain how they compose the framework. There are individual tools for tasks like deterministic tag communication, creating control data abstraction layers, implementing custom timing sources for loop execution, utilizing powerful execution engines, and for building customized configuration editors.
These tools got their start from a project in NI Systems Engineering. This project was kicked off to create tools that help users build more robust software for tasks commonly found in embedded control applications. However, many of these tools also have useful applications outside of embedded control. The DCAF Package Package Dependency Graph shows each of these tools and how they interact with each other. This document will provide an overview for a few of the DCAF 'Support' packages as well as the 'Core' packages.
Tag Bus API
The Tag Bus API allows users to define and access current value data efficiently and without race conditions. Unlike other popular Tag implementations (Current Value Table (CVT), functional global (FGV), Shared Variables, Single Element Queues etc.), the Tag Bus API makes use of a wire for local access scope instead of providing global, reference-based data access. This wire acts as a common interface for many of the other elements of DCAF. Tag Bus provides functionality for instantiating tags, for building tag lookup tables, and provides data accessors for all major LabVIEW data types. Visit LabVIEW Tag Bus Library to learn more details about this API.
Loop Timing Source
The Loop Timing Source is a simple API that defines a set of properties and methods for triggering loop execution. This code allows users to create their own custom timing sources for synchronizing the execution of While Loops with system events or with the arrival of new data. It also ships with a set of implementations similar to what is provided by the LabVIEW Timed Loop in RT. Implementations include a 1kHz source, a 1MHz source, and a 'Scan Engine' source. A timing source has methods for setting clock properties, initialization, and waiting on the clock source.
Configuration Editor Framework (CEF)
The CEF was designed to allow users to quickly build a configuration-based user interface for just about any configuration information. It includes functionality for organizing configuration objects in a hierarchy, displaying custom views of those configuration objects to a user, and saving the object-oriented configuration to a standard or custom file type. New types of configuration objects can also be loaded by the editor as plug-ins. The primary view provided by CEF makes use of a tree control with subpanels, but almost completely abstracts the software for interacting with those elements from developers.
In addition to the highly-customizable configuration editor, the CEF contains a set of classes designed to help developers build configuration APIs for data that can be serialized and deserialized on multiple execution platforms. By default, this class allows a user to create a new configuration object, serialize and deserialize it with migration code, validate a configuration, and give each configuration a readable name and a unique identifier. From this foundation, users can add additional private data and API functions to modify that private data for their specific configuration needs.
Visit Configuration Editor Framework (CEF) to learn more.
DCAF Module Interface or Tag Bus Module (TBM) Parent Class
The Tag Bus API, Loop Timing Source, and CEF are all used to create the DCAF Module Interface. This was designed to provide a common interface for software that generates, modifies, or consumes Tag Bus data. The framework provides two parent classes: Module Runtime and Module Configuration. Both interfaces are very straightforward. The TBM Runtime class provides the following methods for override: Initialize, Input, Process, Process Safe State, Output, Classify Errors, and Close.
In order to prevent memory allocations during execution, the ‘Initialize’ method creates four independent Tag Bus data tables: one for ‘Input’, two for the ‘Process’ functions, and one for ‘Output’. Below is an implementation of the ‘Input’ method for a reusable Scan Engine TBM. This function updates a pre-allocated Tag Bus data table with data from the Scan Engine using data indices calculated in its ‘Initialize’ function.
The ‘Process Safe State’ function is available as a place to define alternative functionality when a system transitions into a fault state.
Finally the ‘Classify Error’ function should be called when any of the other module functions return an error. When called, this function must simply clear the error or bin it into one of four categories: unknown, trivial, recoverable, or critical. Using this approach, error classification can remain module specific and simple for a module developer to implement. Error handling and response can be application specific and can happen external to the module.
Each TBM Runtime class also has a TBM Configuration class paired with it. The configuration class allows developers to create an API that exposes different execution settings of the TBM runtime class, serializes and deserializes that information, and provide the final configuration to the TBM runtime ‘Initialize’ method where it is used to initialize the runtime class.
The configuration class is designed to support execution on any platform, while the runtime class implementation may be platform specific. Separating configuration information from execution code this way allows users to define system behavior on one platform (ex. Windows PC) without loading potentially incompatible runtime code as a dependency. That configuration information can then be converted to a string, and transferred to the intended TBM execution platform (ex. CompactRIO) where the runtime code gets loaded.
The image below shows an example of the Scan Engine TBM’s configuration class API. In addition to the parent methods provided by the CEF configuration class, this implementation added a new functions to add channels to the configuration and to modify the Scan Engine’s scan period. The ‘to string’ method then converts this information to a string for transport using a file, network communication, or other means. This information can then be delivered to the ‘from string’ method where it is converted back into the configuration class object and provided as an input to the Scan Engine runtime ‘Init’ function.
The configuration class is also responsible for returning execution properties of the plug-in. By default there are properties for things like the plug-in alias, the name of the corresponding TBM runtime class, the number of instances that are allowed to run per target, and whether the runtime implementation’s methods are blocking, non-blocking, or deterministic.
Finally, the DCAF Module Interface allows each plug-in to opt in to providing its own execution timing source. This functionality is built from the Loop Timing Source API discussed previously and allows calling code to synchronize the execution of TBM methods with the arrival of fresh data.
Tying all of this back to the three TBM dependencies, the Tag Bus API serves as the interface for sharing data between TBM runtime methods, the CEF provides a parent class for creating configuration APIs that are serializable, and the Loop Timing Source allows TBMs to provide a timing source to calling code so that the their ‘Input’ method can be called as soon as new data is available.
Engine Execution Interface
While exchanging data between two TBMs is fairly straightforward, exchanging data between three or more TBMs is a lot more challenging. To make this easier, a new software component was developed to call TBMs at a specified rate and handle the data exchange between them. This component is called an engine. The engine also handles all state logic and error handling for the collection of TBMs that it executes.
One of the core features of the Engine Execution Interface are its Tag and Mapping APIs. These APIs allow users to connect (Map) data from a TBM method (called a Channel), to an aliased data location (called a Tag) which is accessible by other modules in the engine.
By handling the exchange of data between TBMs in the engine, and by limiting a Tag to a single Channel writer, this API can ensure that there are no race conditions in the software running in the Engine. The image below shows an abstract diagram of how an engine can call TBM methods and connect their Channel data through Tags.
In addition to enforcement of a single writer, the concept of Channels that are defined in a TBM and that get mapped to a Tag in the engine greatly facilitates code reuse. In the image below, the ‘setpoint’ Channel in the ‘Temperature Controller Logic’ TBM is connected to the ‘Setpoint’ Channel in the network ‘UDP’ TBM through a Tag called ‘Setpoint’. The setpoint input for the ‘Temperature Controller Logic’ could instead come from a different source like a local UI or file playback by mapping a different system Channel to the ‘Setpoint’ Tag. However because this change in system behavior represents a simple configuration change, it does not require any changes to the software in the ‘Temperature Controller Logic’ TBM and could likely occur without recompiling the application.
In addition to the Tag and Mapping APIs, the Engine Execution Interface also provides a parent API for launching an engine and forcing state transitions. This API defines methods to Open, Initialize (using a collection of configuration objects), Start, Check for engine errors, Read Module Faults, Read State, Pause, Stop, go to Safe State, and Close an engine. A simple example of how this API could be used is shown below.
These APIs can be used to guide an Engine background process through its various state transitions based on application requirements. The Engine Execution Interface expects the Engine’s state machine complies with the state machine defined in the CANopen protocol specification.
A final point is that the Engine Execution Interface provides tools and guidelines for building an engine, but does not put other constraints on how an engine gets implemented. As a result, similar to TBMs, the framework allows for the creation of multiple engines that a user can choose from. If an engine doesn’t exist to meet a particular use case, users can build their own engines to fill the gap. In addition to simply calling TBMs and exchanging data between them, engines are free to include other features like fault handling, data forcing, execution benchmarking, channel calibration, etc.
DCAF Tag Editor Core
The concept of an engine allows users to develop applications by defining a collection of TBMs to call and by defining how those TBMs exchange data. Programmatically configuring all of this information using the TBM and Engine Execution Interface configuration APIs is possible, but unwieldy. Tag Editor Core provides a CEF-based user interface to abstract away the low-level details of the engine and TBM configuration APIs, making it easier to create, update, visualize, and maintain an application’s configurations.
Tag Editor Core integrates the engine and module frameworks with the CEF by defining a standard file format and means of visualizing engine and module configuration data in the CEF. It also defines a configuration object hierarchy that can be used to add one or more targets to a system, add one or more execution engines to a target, and add one or more TBMs to an engine. Below is an example of a configuration editor built using the tools provided by Tag Editor Core.
Tag Editor Core defines a repository, a file format, a target, a system, and a new CEF Editor Node class for the creation of system, target, engine, and TBM views. For an engine or TBM to be compatible, it must include an implementation of the Editor Node class in addition to its configuration and runtime classes. However with this view, Engine and TBM execution behavior can be defined in an easy-to-use editor, saved to a file, reloaded for future modification, or transferred to a target for execution. Once on the target, a simple API can be used to identify which targets or engines to load. The image below demonstrates how this works.
Like the Engine Execution Interface and the TBM framework, Tag Editor Core only provides a set of rules and guidelines for creating a configuration editor. This means that users have the flexibility of reusing an editor across various projects, or creating a new editor tailored for a particular project.
Implementations, Tools, and Examples
DCAF also includes packages that install templates, examples, and a default configuration editor. These packages also include a number of DCAF plugin modules and the Standard Execution Engine. Visit Getting Started with DCAF to learn more about how to use the shipping example and other tools. Visit DCAF Project Templates to learn more about how to use the shipping templates.