View on GitHub

Chapter 4
Component Structure

4.1 Introduction

This chapter discusses more advanced topics related to Component development. Prior to reading this chapter, familiarize yourself with the information in Chapter 3.

4.2 Auto-Generated Component Files

The REDHAWK IDE provides a tool for auto-generating Component code for C++, Python, or Java languages. This process takes care of REDHAWK compliance and allows the developer to insert their own custom processing algorithm for working with the data sent/received by the Component. The following section provides a brief description of the generated files. Note that some files may be freely modified, while other files should not be modified. For more information on using the REDHAWK IDE, refer to the REDHAWK IDE chapters of this document (Starting at Chapter 14).

Modifying particular files is discouraged for two reasons:

  1. If a user regenerates the Component using the IDE (for example, to add a Port), particular files are overwritten by the code generators.

    • Files whose modification is not discouraged (e.g., componentName.cpp) are not affected by such an action.
    • The REDHAWK IDE provides the option of not rewriting particular files.

  2. Modification of files implementing REDHAWK interfaces may impact compatibility with other REDHAWK modules.

The word componentName is replaced with the Component name provided during Component creation.

4.2.1 Files Generated for All Components

4.2.1.1 Build-Related Files

The code generators create the following files for building and installing the Component using Autotools:

4.2.1.2 Component XML Descriptors

4.2.1.3 Unit Tests File

4.2.2 Files Generated for C++ Components

4.2.2.1 Files where your implementation-specific code should be

4.2.2.2 Files that you should not modify

If these files are modified, then your ability to regenerate the component is affected.

4.2.3 Files Generated for Python Components

4.2.3.1 Files where your implementation-specific code should be

4.2.3.2 Files that you should not modify

If these files are modified, then your ability to regenerate the component is affected.

4.2.4 Files Generated for Java Components

4.2.4.1 Files where your implementation-specific code should be

4.2.4.2 Files that you should not modify

If these files are modified, then your ability to regenerate the component is affected.

4.2.4.3 Transitioning Java Components from version 1.8 to later versions

If componentName.java is not regenerated when migrating from 1.8 to later versions, the Component does not inherit from the base class, so it does not take advantage of the new code pattern. In addition, in order for the Component to build, the method configureOrb must be added to componentName.java because the base class makes a call on that method.

If you want to inherit from the base class and take advantage of the new pattern, save off the custom main class Java file, regenerate the main Java class to get the new code pattern, and for example, move source from the run() method to the serviceFunction() method to integrate the custom code in the 1.8 main class into the new main class.

If componentName.java is regenerated, any custom code added to it is removed.

Add this class to the componentName.java right before the last line which is "}"

4.3 Auto-Generated Component Methods

This section provides an overview of noteworthy methods provided in the auto-generated Component files. In some cases, the names of the methods vary by language.

4.3.1 serviceFunction()

The core functionality of a Component resides in the serviceFunction() method in C++, the process() method in Python, and the serviceFunction() method in Java. The serviceFunction() is invoked on a recurring basis after start() is called on the Component’s base class.

4.3.2 constructor()

This is the Component/Device constructor. When this function is invoked, properties of kind Property are initialized to their default or overloaded state.

4.4 Base Component Members

This section provides an overview of members available to the Component class. There are four kinds of members: ports, properties, domain awareness, and network interface.

4.4.1 Ports

Data flow into and out of Components is accomplished through the use of Ports. Ports are described as being either a provides (input) or uses (output) Port. This naming convention is often viewed as counter-intuitive, so an explanation is in order. Ports are RPC interfaces to a Component. An input Port, therefore, provides functionality that can be used by an output Port.

REDHAWK contains a variety of standardized interfaces that facilitate interoperability. These interfaces are implemented by Ports. When a Port is selected in the Component generation wizard in the REDHAWK IDE, code to implement these interfaces is automatically generated.

Irrespective of direction, a Port is accessed as a member of the Component’s base class. Assuming that a Port called “myport” of any interface exists in the Component, it is accessed in the following ways in C++, Python, and Java, respectively.

See Section 5.6 for details on how to use ports for sending or receiving data.

4.4.2 Properties

Much like Ports, Properties are available to the Component through generated members to for the Component’s base class. The Property is found through the Property’s name (if it has one) or its id. For example, if a Property is defined with an id of “foo” and a name of “abc”, it would can be accessed in the following ways in C++, Python, and Java, respectively,

If the Property does not have a name defined, then it would be accessed in the following way in C++, Python, and Java, respectively,

Note that no automated check is performed on the code generation to avoid a name collision between properties or ports.

4.4.3 Domain Awareness

Each Component has two members that provide a reference to the Domain and Application in which the Component is operating. To retrieve the Domain Manager and Application, access the member functions getDomainManager() and getApplication(), which return a DomainManagerContainer and ApplicationContainer, respectively. DomainManagerContainer has the member getRef(), which returns the CORBA pointer to the Domain Manager object. ApplicationContainer has the member getRef(), which returns the CORBA pointer to the Application object.

In the case of Devices, instead of getApplication(), the base class contains getDeviceManager(), which returns a DeviceManagerContainer. The DeviceManagerContainer has the member getRef(), which returns the CORBA pointer to the Device Manager object.

4.4.4 Network Interface

If a Component contains a dependency against any member of GPP’s nic_allocation allocation property, then the framework will, upon deployment, make sure that those network resources are made available to the Component. Whichever NIC statisfies the allocation requirement is fed to the Component, and it is made available to the developer through the getNetwork() member, which returns a NetworkContainer. NetworkContainer has the member function getNic(), which is the string name of the NIC that satisfied the requirement (i.e.: eth0).

Note that if the network dependency is declared for any one Component, that Component’s deployment is made to the core(s) that are closest to the NIC on the processor. This happens automatically with no need for additional input from the deployer.

4.5 Component Implementations

Components may specify particular dependencies such as Operating System (OS), processor architecture, or required Device Properties (e.g., processor speed or memory capacity). Setting these dependencies ensures that a Component is deployed to an appropriate Device at runtime.

While REDHAWK supports multiple implementations for a single Component, it can be confusing, especially when debugging a system. Except for some limited scenarios, it is recommended that developers associate a single implementation with each Component

4.6 Managing and Defining Properties

Properties are defined by their structure, kind, and type. The four different Property structures include:

Three commonly used kinds of Properties in REDHAWK include:

The Property’s type corresponds with basic programming language primitive types such as floats, long integers, booleans, etc. Additionally, numeric types can be complex.

Through the use of generated code and the REDHAWK libraries, manipulation of Properties uses fundamental types provided by C++, Python, or Java, as seen in Section 4.4.2. For example, a simple sequence, complex-float Property is manipulated via a std::vector< std::complex<float> > variable in C++ and a list of Python complex objects in Python. Generated Component code provides a class data field representing each Property for that Component.

The primitive data types supported for simple Properties/simple sequence Properties are: boolean, octet, float, double, short, ushort, long, longlong, ulong, ulongong, string, objref, and char.

simple and simple sequence Properties with the following primitive data types can also be marked as complex values: boolean, octet, float, double, short, ushort, long, longlong, ulong, and ulongong.

Each Component implements the CF::Resource interface, which provides remote access to the Component’s Properties through the query() and configure() methods. The query() method provides a means for reading a Component’s current Property settings and the configure() method provides a means for setting a Component’s Property values.

Properties can be readonly, writeonly, or readwrite. Properties with read privileges can only be accessed using the query() method and Properties with write privileges can only be set using the configure() method.

The REDHAWK library base classes provide a complete implementation of configure(), with the creation of specific Properties handled per Component by the generated base classes. Beyond the basic updating of local values, the standard configure() implementation provides:

Because of these enhancements, developers are strongly discouraged from overloading either the query() or configure() methods.

4.6.1 Property Change Listeners

Often, it is useful to trigger additional actions when the value of a Property changes. Components support a type of notification called Property change listeners that enable the developer to register callback methods that are executed whenever configure() is called with new values for the particular Property.

Property change listeners are executed while holding the lock that protects access to all Properties for the Component. This ensures that no outside changes can occur while responding to Property changes. The callback may alter the value of the Property or call additional functions; however, avoid computationally expensive or blocking operations.

4.6.1.1 C++

C++ Components support notification of Property value changes using member function callbacks.

The following example explains how to add a Property change listener for the freqMHz simple Property of type float, of a Component named MyComponent.

In [component].h, add a private method declaration for your callback. The callback receives two arguments, the old and new values:

Implement the function in [component].cpp.

Then, in the Component constructor(), register the change listener:

addPropertyListener takes three arguments: the Property’s member variable, the target object (typically this) and a pointer to a member function.

When defining a property listener for struct or sequence Property, the new and old values are passed by const reference:

4.6.1.2 Python

Like C++, Python Components allow registering listeners by Property. The callback is typically a member function.

The following example explains how to add a Property change listener for the freqMHz Property.

Define the callback as a member function on your Component. Excluding the implicit self argument, the callback receives three arguments: the Property Identifier (ID) and the old and new values.

In your Component constructor() method, register the change listener:

4.6.1.3 Java

Java Properties support an idiomatic listener interface for responding to changes. As opposed to C++ and Python, listener registration is performed directly on the Property object.

The following example explains how to add a Property change listener for the freqMHz Property of a Component named MyComponent.

Define your callback that will respond to changes for the Property as a member function on your Component class. For simple numeric Properties, the old and new value arguments can be the primitive type (for example, float):

In your Component’s constructor() method, define an anonymous subclass of org.ossie.properties.PropertyListener that connects the Property’s change notification to your callback. For simple numeric Properties, the type parameter of the PropertyListener class must be the boxed type (for example, Float).

4.6.2 Customizing Query and Configure

This feature is C++-only in REDHAWK 2.0.5.

The REDHAWK libraries and generated Component code automatically handle query() and configure() for all defined Properties. However, in some cases, it may be preferable to retrieve the current value of a Property in response to a query(), such as when fetching status from an external library. A developer may also want more control over how the Property value is set. Components support per-property callbacks to customize query and configure behavior.

The query callback is called when the Component receives a query() for that Property, in lieu of consulting the local state. Likewise, the configure callback is called when the Component receives a configure() for that Property, instead of updating the Component local state.

Unlike property listeners, the configure callback is always called regardless of whether the new value is equal to the old value.

Query and configure callbacks are executed while holding the lock that protects access to all Properties for the Component. This ensures that the callback has exclusive access to the Component Properties. If possible, avoid computationally expensive or blocking operations to ensure that the Component remains responsive.

4.6.2.1 C++

In C++, query and configure callbacks are registered on the Components. Registering a new callback replaces the old one.

Query Callbacks To create a query callback, in [component].h, add a private member function declaration. It takes no arguments and returns the value:

Implement the function in [component].cpp.

Then, in the body of constructor(), register the query function:

setPropertyQueryImpl takes three arguments: the Property’s member variable, the target object (typically this) and a pointer to a member function.

Configure Callbacks To create a configure callback, in [component].h, add a private member function declaration. It takes one argument, the new value, and returns void:

Implement the function in [component].cpp.

Then, in the body of constructor(), register the configure function:

setPropertyConfigureImpl takes three arguments: the Property’s member variable, the target object (typically this) and a pointer to a member function.

When a configure callback is set, the member variable is not updated automatically. It is up to the Component developer to update the member variable, if desired.

4.6.2.2 Overriding the configure() Method

For the vast majority of cases, the standard configure() implementation is sufficient. Developers are strongly discouraged from overriding configure(). However, in the event that additional functionality beyond what is provided is required, the overridden method should call the base class configure() method to ensure that the behavior expected by the library and framework is preserved. Whether the base class method is pre- or post-extended is left to the discretion of the Component developer.

4.7 Creating a REDHAWK Shared Library Project

The REDHAWK Shared Library Project Wizard enables users to quickly create a C++ shared library for use in REDHAWK. In the wizard, the user specifies the project name and can then generate a simple set of code files to begin adding in library functions. The following procedure explains how to use the Shared Library Project Wizard.

  1. To open the Shared Library Project Wizard, select File > New > Other.

    The Select a wizard dialog is displayed (Figure 4.1).


    PIC
    Figure 4.1: Select a Wizard Dialog


  2. Select REDHAWK Shared Library Project and click Next.

    The Create a REDHAWK Shared Library Project dialog is displayed (Figure 4.2).


    PIC
    Figure 4.2: Create a REDHAWK Shared Library Project Dialog


  3. In the Project name field, enter a project name.
  4. Click Finish.

    The Shared Libary Project is created and the Overview tab is displayed. (Figure 4.3).


    PIC
    Figure 4.3: The Shared Library Project Overview tab


  5. Click the Generate All Implementations icon to generate the source code.

4.8 Including External Libraries

This section details how to link a C++ Component with a library. Two examples are given: using a pkg-config (.pc) file to find and link against a library, and directly linking against a library. Using the pkg-config file will allow your project to check for the presence of the library and issue an error while running configure if it is not found. It also will let you avoid hard-coded options. If a pkg-config file is not available, or if you wish to directly supply the compiler/linker flags, you can use the second method.

4.8.1 Adding a Library by Referencing a pkg-config File

To add a library by referencing a pkg-config (.pc) file, edit the configure.ac file in your Component’s implementation directory. Find the line referencing PROJECTDEPS in the code that looks like this:

and add your library to the list of requirements. For example, if you need version 1.2.3 or greater of the foo library:

If your pkg-config file is not on the pkg-config path, you can augment the pkg-config search path by adding a line just before the call to PKG_CHECK_MODULES:

4.8.2 Adding a Library Directly

To add a library, edit the Makefile.am file in your component’s implementation directory. Append compiler and linker flags to the end of the CXXFLAGS and LDADD lines, respectively. For example:


Creative Commons License
REDHAWK Documentation is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.