MAK Data Logger API Documentation for DIS
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
3 - Embedding the Logger in an Application

Table of Contents

You can use the Logger API to embed logging capability into your own applications.

The core functionality of the Logger is provided in a class called DtLogger. A DtLogger object has a system driver and a protocol registration object. The system driver provides all low level functionality, such as command passing, driver registration, managing of the pipeline, and the base simulation manager. A protocol registration object virtual constructor must be set before creating a DtLogger object. Utility classes DtDisLogger and DtHlaLogger provide automatic registration of their respective protocols and are designed for use when embedding the Logger in other applications.

If you are using Logger functionality in your own application, you must instantiate a DtLogger class. If you are using it in conjunction with one (or more) Logger interface components, then you must also instantiate these components. If you are creating a Logger plug-in, then the standard Logger application creates the instance of the DtLogger class for you, and allows you to access it from your plug-in code.

The Logger includes the following examples of using DtLogger in a custom application: playFile.cxx, readFile.cxx, recordFile.cxx, and writeFile.cxx. They are in ./examples/simpleFileActions.

3.1 Embedding Plug-ins in an Application

Important parts of Logger functionality, such as annotations, are implemented as plug-ins, rather than as core libraries. If you are embedding the Logger in an application, you can also embed plug-ins. To embed plug-in functionality, link to the plug-in libraries and use the plug-in’s classes in your application as if they were part of the Logger API.

3.2 The playFile Example

This section describes the playFile example.

First construct a Logger object. We use the helper classes to facilitate protocol registration.

#if DtDIS
DtDisLogger* aLogger = DtDisLogger::create(true);
#endif
#if DtHLA
DtHlaLogger* aLogger = DtHlaLogger::create(true);
#endif
Next, connect the Logger object to an exercise. (This does not necessarily need to come first; however, it makes for clearer logic.)
Note
The Logger is not actually connected after the connect call since only a connect command is submitted. Call tick() to execute the command.
DtExerciseConnInitializer exConnInit;
aLogger->connect(exConnInit);

While the Logger object provides some basic functions, to control and receive responses from the Logger, you must construct an interface to the Logger. This portion of the example constructs a simulation interface and attaches it to the Logger object.

DtLgrSimInterface* simInterface = DtLgrSimInterface::create(*aLogger);

The Logger is ticked to process the submitted commands, which up to this point will be setConnectionParameters and connect. The results of these commands will be sent to the interface. The interface, like the Logger, must be updated to process the responses, which is done via the update() member function.

aLogger->tick();
simInterface->update();

With the interface up-to-date, you can check to see if the Logger was able to connect.

if(!simInterface->isConnected())
{
return 1;
}

When an interface is no longer needed, delete it:

delete simInterface;

It is better to maintain a single interface rather than repeatedly creating and destroying them. A single interface can be shared across all objects in the same execution thread. When you write multi-threaded code, you must have one interface per thread. Interfaces are designed with thread safety in mind, which makes them an optimal solution for multi-threaded programs.

To control Logger playback, you need a playback interface. Any number of interfaces may be constructed and attached to the Logger. The performance overhead for each interface is not large; however, it is often best to minimize the number of interfaces used.

DtLgrPlaybackInterface* pbInterface = DtLgrPlaybackInterface::create(*aLogger);

Each interface has two types of functions – those that control the Logger by sending commands, and those that read the state of the interface. For example, to open a file and determine if the operation was successful requires the command to be submitted, the Logger to be ticked, the interface to be updated, and the state to be read back.

pbInterface->openFile(fileName);
aLogger->tick();
pbInterface->update();
if(!pbInterface->isFileLoaded())
{
return 1;
}

This pattern is repeated by all operations, for example:

pbInterface->play();
aLogger->tick();
pbInterface->update();
if(!pbInterface->isPlaying())
{
return 1;
}

When performing a continuous action, for example playing, you must construct an execution loop. (This is not necessary if you use the DtThreadedLogger, which runs in a separate thread.)

double loopDelay = .1;
while(pbInterface->isPlaying())
{
aLogger->tick();
pbInterface->update();
DtSleep(loopDelay);
if(keyCheck("q\r")) break;
}

It is not strictly necessary to stop the Logger before deleting it, however it is included here for completeness.

if(pbInterface->isPlaying())
{
pbInterface->stop();
aLogger->tick();
}

When you are finished with the Logger, delete all interfaces, then delete the Logger object.

delete pbInterface;
delete aLogger;
return 0;

[<< The Logger Architecture] [Home] [Top of Page] [Creating a Stand-Alone Logger Application >>]


Document ID: Generated on Tue Aug 1 09:00:26 EDT 2017 from SVN revision 179128
Copyright © 2017 VT MÄK. All Rights Reserved (www.mak.com)