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
Client
:The component that needs to create or retrieve an event report. EventRepository
:The component that defines and implements the basic operations that can be performed on generic event repositories. SpecificEventRepository
:Event repository that stores events of type SpecificEvent
. It modifies the operations defined by its base class to suit the needs ofSpecificEvent
.Event
:Component that defines the basic attributes of events that are applicable to all types of events (e.g. event type identifier, time stamp identifying the time when the event was created, etc) SpecificEvent
:Component that adds application-specific attributes to the base event components
Collaborations
The typical operational scenarios for this design pattern are:
- Components that may need to create event reports, hold a reference to the relevant event repository and create an event report by calling the
create
method on it. - Components that may need to retrieve the event reports, hold a reference to the relevant event repository and use its iterator methods to retrieve all event reports in sequence.
- Components that may need to enable or disable the storage of event reports call the enable/disable methods on the corresponding event repository.
Consequences
- Clients are decoupled from the logic for the management of the event reports that is confined to the event repository.
-
Handling of requests to disable/enable storage of event reports is centralized in the event repository. Clients always call the
create
operation when the conditions for the creation of an event report have occurred but the event report is actually created only if the event repository is enabled. - Applications that need to handle event reports, can reuse the base event repository and event classes. Application-specific features are added by subclassing these classes and adding the new features to them.
- If an application needs to handle several categories of events, it will need to create several event repositories.
Applicability
This design pattern is useful when:
- components in an application need to be able to create and process reports describing event-like occurrences
- it is necessary to be able to vary the information attached to each event report
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:
- Eventcomponent -->
DC_Event
- EventRepositorycomponent -->
DC_EventRepository
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