Reflex - the Customizable Event Correlation System
Home
Feedback

The Basics
Quickstart
Download
License
Installation

User's Manual
Overview
Policies
I/O Retargeting
Deployment
Formal Stuff

Language Reference

I/O Retargeting

Reflex has been designed so that the engine itself is abstracted away from the I/O mechanism. By replacing the I/O file, Reflex can communicate using a bus, files, or even just function calls within another program. (Each different Policy replaces the other file, the engine, forming the complete Correlator). To make replacing the I/O file easier, we provide a template that you can use to quickly do the job.

Reflex engines communicate with the I/O file using a two-function interface: the Handle function, to give Events to the engine, and the Alarm function, to receive output from the engine. The engine code implements the Handle function, which has no return value and accepts one Event. The I/O code implements the Alarm function, which also has no return value and also accepts one event. In addition, main() is left in the I/O side, allowing the I/O mechanism to set up a connection and start receiving Events.

So, to alter Reflex to communicate using a new mechanism, simply modify the I/O file (correlator_io.cpp). In this file, implement the Alarm function so that it sends the passed event as output on the appropriate bus, and implement main() to connect to the bus, continually reading events and calling the Handle function for each one.

Of course, external mechanisms will not usually use the same format that Reflex does internally. To convert data into an Event, declare an event and add the data as attributes:

    [data_structure] d;
    Event e;
    e["name"] = d.name;
    e["size"] = d.size;
    ...

All event attributes are named using strings (STL Strings, to be precise) and can accept four types of values: STL Strings, integers, doubles, and bools.

Converting data in an event to an external type is a little more complicated. If you know the names of the attributes that will be in the output Event, you can access them using the same subscript operator. However, they are still general Attributes (called Elements), and not yet basic types. To convert them, you call a getXXXX function:

    d.name = e["name"].getString();
    ...

Also, the name of the pattern that has been matched will be output in a string attribute called "PatternName" (Names are case-sensitive). If you do not want to specifically name the attributes (a good idea), you can read all of them by using an iterator. This iterator acts just like a C++ map iterator:

    Event e;
    Event::iterator i;

    for( i = e.begin(); i != e.end(); i++ )
        cout << i->first << i->second << endl;

Note that Attribute values (called Elements) can be printed, because the stream output operator has been overloaded for them. Because you will not know the specific type of these attributes, a getType function exists that you can use to check the types:

    Event::iterator i;
    for( i = e.begin(); i != e.end(); ++i )
    {
        switch( (i->second).getType() )
        {
        case eSTRING:
            [string] = (i->second).getString();
            break;
        case eINT:
            [int] = (i->second).getInt();
            break;
        case eDOUBLE:
            [double] = (i->second).getDouble();
            break;
        case eBOOL:
            [bool] = (i->second).getBool();
            break;
        default:
            assert( false );
            break;
        }
    }

You may make more or less information available to the engine than exists outside. For example, if outside events do not include timestamps, but you want to add one, simply add it to each new Event before passing to the Handle function:

    Event["time"] = some_time();

If you want to require certain events come one right after the other in a pattern, add a sequence number to each event:

    Event["index"] = index++;

Note that the I/O function Alarm() contains a call back to Handle() by default, allowing patterns to be nested. If you do not have any nested patterns in your Policy, you can remove this call to improve performance. Also, because the I/O mechanism is where main() is defined, you can design it to take any arguments you wish. These arguments are usually used to give information about the bus or file to connect to, but can also be used for self-identification or for control purposes.

As a final suggestion, examine the various I/O files available as a reference while you create yours. You will find a file to communicate on the Siena bus, one for file I/O, and the stress tester, which internally generates random events.

Back to top

Copyright © 2001