Event Design Pattern

Intent

Allow application components to create and share reports describing synchronous or asynchronous occurrences together with any data associated to them.

Based On

This pattern is derived from the shared event design pattern of the AOCS Framework (see also: A. Pasetti, Embedded Control Systems and Software Frameworks, Springer-Verlag, 2002).

Motivation

Most on-board applications have requirements that the occurrence of special "events" like the reception of a telecommand, the detection of a failure, the execution of a reconfiguration, a change in operational mode, etc, should be recorded in a dedicated memory area - variously called event buffer or history area - to be available for downloading to the ground. The recording of events can usually be dynamically enabled and disabled either in toto or only with respect to selected categories of events.

Implementation of these requirements is usually through a dedicated C module or Ada package that represents the history area or event buffer and that offers operations through which other modules can either create new events or can retrieve them for processing. This solution is not reusable because of the diversity in the format of the information that is associated to each event and because of the variation in the type of operations that must be performed upon them and upon the event history area.

The event pattern proposes a reusable solution to the problem of creating and sharing access to events in an on-board system.

The pattern can additionally be more generally applied wherever there is a need to record and buffer on-board data. In some applications, for instance, there is a need to periodically store the values of certain variables that are operated upon by the OBS. Usually, only the most recent updates of the variables are required. Such a service is conceptually similar to the history service and, like it, can be subsumed under the event storage service covered by the event design pattern with the only (non-important) difference that the generation events are periodic rather than sporadic (as in the case of the history service) and the events carry more data (in the case of the history service they only have a type identifier).

Dictionary Entries

The following abstractions or domain-wide concepts are defined to support the implementation of this design pattern:

Structure

The event design pattern represents the event abstraction as a class Event that defines the invariant properties of events. Adaptation of the format and content of an event report is achieved by subclassing the Event class. Event reports are encapsulated in instances of classes indirectly derived from class Event.

The event repository abstraction is instead represented as a class EventRepository that defines the basic operations that can be performed on generic events.

Components that need to create, inspect or process events do so exclusively through an event repository. To each concrete event class, a corresponding event repository class must be associated that is obtained by inheritance from EventRepository. Note that the client using the services offered by the event repository sees the specific event repository subclass rather than the base class EventRepository. In other design patterns presented in the OBS catalogue, inheritance is used to decouple a user of a service from its implementation. In such cases, the base class is typically abstract (sometimes, it is an abstract interface). In the present case instead, inheritance is used to provide a base implementation of some services that can be reused by more specific subclasses.

Participants

Collaborations

The typical operational scenarios for this design pattern are:

Consequences

Applicability

This design pattern is useful when:

Implementation Issues

In an on-board setting where dynamic memory allocation is not possible, the events objects inside the event repository would normally be stored in a FIFO queue of fixed size that is pre-defined when the event repository is created. When the capacity of the queue is exhausted the oldest event is overwritten.

What do clients that wish to retrieve an event report from an event repository get? Giving them a reference to an event is not safe because events are stored in a FIFO queue and can potentially be overwritten. It is safer to return copies of event objects or alternatively to return the values of the individual attributes associated to the event.

The signature of the create method in the event repositories depends on the attributes that are associated to each event. This is the primary reason why the creation of a new event subclass (which is defined by an expanded set of attributes) normally requires the creation of a corresponding event repository subclass with a create method that matches the attributes of the new event subclass.

Concrete event repositories will often differ only by the type of the concrete events that they store. Concrete event repositories are therefore often implemented using a mechanism like C++ templates.

In many cases, the basic attributes defined by the base class Event will be sufficient and an application will not need to develop new event and event repository components.

OBS Framework Mapping

The implementation of this design pattern in the OBS Framework is supported by the following classes:

Sample Code

Consider an application that has a requirement that event reports be generated when certain events occur. Usually, the only information that events must carry is an identifier that specifies the type of event that has occurred. This application can directly use the EventRepository and Event components.

The event repository interface could be defined as follows:

	
    class EventRepository {

        // add an event of type evtType to the repository
        void create(int evtType);

        // enable and disable storage of events
        void enable();
        void disable();

        // reset event repository
        void reset();

        // iterate through events and return event types of stored events
        int getOldestEvt();
        int getNextEvt();
        bool isLastEvt();
    }
Note that the create method does not take the time stamp as an argument because this is directly added to the event report by the creation mechanism and therefore does not need to be supplied by the client.

With the above event repository interface, the implementation of a hypothetical requirement that states that the rejection of an invalid telecommand must be recorded in the history area becomes:

    EventRepository* rep;
        . . .
        if (TC is invalid)
            rep.create(INVALID_TC);
        . . .     

The module performing the check on the telecommand validity holds a reference to the event repository. If the telecommand check fails, the module records the failure by calling the create method on the event repository. It passes to it a constant that identifies the type of the event. Note that whether or not a new event is actually created inside the repository depends on whether the event creation is disabled or enabled in the event repository. The telecommand checking code however needs not be concerned about the enable status which is handled internally to the event repository.

As a second example, consider requirements that control the collection of history area data for inclusion in telemetry. These requirements could be implemented as follows:

    EventRepository* rep;
    . . .
    // Retrieve all events from the repository
    for (evtType=rep.getOldestEvt(); !isLastEvt();evtType=rep.getNextEvt())
    . . .    // format and store event type in TM buffer; 

As defined here, the iterator methods return the type of the event. In a different implementation, the iterators could return objects representing copies of the events stored in the repository. Collection of telemetry data from the event repository could also be done using the telemeterable design pattern in which case component EventRepository would have to implement interface Telemeterable.

As a third example, consider a requirement that states that a telecommand may lead to logging of history data being stopped. Execution of this telecommand could be implemented as follows:

    EventRepository* rep;
        . . .
    // Execute telecommand to stop logging of history data
    rep.disable();   

Note that in all the above examples, the user code is written uniquely in terms of a reference to the event repository. Changes to the implementation of the event repository have no impact on the user code. It would also be possible for different parts of an application to use different event repositories without repercussions at user code level.

As a fourth and final example consider an controller application where there a requirement that the controller data generated in the last N cycles be stored for possible inspection by the ground. Usage of the event pattern in this case would require the definition of a new event class that can hold the controller data:

	
    class ControllerEvent : Event {
        float[N] controllerData;

        ControllerEvent(int evtType, float[] rd) : Event (evtType) {
            controllerData = rd;
        }
    }

This class adds to the basic event class a field to hold the controller data. Its associated event repository class would look like this:

    class ControllerEventRepository : EventRepository {

        // add an event of type ControllerEvent to the repository
        void create(int evtType, float[] rd);

    }

Thus, the new event repository class modifies its base class by adding a create method that takes as its parameters the parameters required to define a controller event. Its other methods are the same as those of the base class. They can be inherited without changes and need not be re-implemented.

Remarks

None

Author

A. Pasetti (P&P Software)

Last Modified

2003-12-01

Contact Us | The OBS Framework Project