# Event System README

The event system is a framework for handling events in your system. It is based on the [Embedded Template Library](https://www.etlcpp.com/) (ETL), and uses the ETL's message system as the basis for its event system. 

This document provides a high-level overview of working with the components of this event subsystem. You will find detailed documentation for all of the types and functions in each [header file](#files). You will also find a number of architectural decisions documented in [`docs/decisions`](decisions/). In particular, a detailed overview can be found in [0004. Use ETL as the Basis for the Event System](decisions/0004-use-etl-as-the-basis-for-the-event-system.md).

**Table of Contents**

1. [Event-Driven Operations]
1. [The Short and Sweet Version](#the-short-and-sweet-version)
1. [Files](#files)
1. [Working with the Event System](#working-with-the-event-system)
	1. [Event-Driven Paradigm](#event-driven-paradigm)
	1. [Defining New Events](#defining-new-events)
	2. [Generating Events](#generating-events)
	3. [Enqueuing Callbacks](#enqueuing-callbacks)
	4. [Handling Events](#handling-events)

## The Short and Sweet Version

- **The system processing loop is event-driven.** Without events firing, there will be no work done.
- Define new events by modifying `CPF/interfaces/events/CPFEventTypes.hpp`
	+ For more information, see [Defining New Events](#defining-new-events)
- Work with the `CentralEventQueue` by including `CPF/interfaces/events/event_queue_interface.hpp`
	- [Add events to the queue](#generating-events)
	```cpp
	CentralEventQueue::enqueue(GPSPositionReadyEvent());
	CentralEventQueue::enqueue(GPSCommunicationErrorEvent(GPSCommunicationErrorEvent::Error::UnexpectedMessage));
	```
	- [Add callback functions to the queue](#enqueuing-callbacks)
	```cpp
	CentralEventQueue::callback(time_received_callback);
	```
- Events and callbacks are processed in the order that they are added to the queue.
- The core state processing code processes events and callbacks, so no work is needed by you on that front.
- Action handlers in each CPF state are where the [processing of events actually occurs](#handling-events)

## Files

The following source files are related to the event-driven subsystem:

- `CPF/Interfaces/events`: This folder contains the primary event infrastructure
	+ The main files of interest for the application developer are:
		* `CPFEventTypes.hpp` - this is where application-specific event types are defined
		* `event_queue_interface.hpp` - this contains the primary `CentralEventQueue` interface that you will work with from the application code. 
			- For the most part, you will need to use the interfaces for:
				+ Adding events to the queue
				```cpp
				CentralEventQueue::enqueue(GPSPositionReadyEvent());
				CentralEventQueue::enqueue(GPSCommunicationErrorEvent(GPSCommunicationErrorEvent::Error::UnexpectedMessage));
				```
				+ Adding callback functions to the queue
				```cpp
				CentralEventQueue::callback(time_received_callback);
				```
			- Pulling from the queue is implemented in `CPF/CPFStateProcessing.cpp`, and you will not need to worry about that
		* `CallbackDispatcher.hpp` - this provides a simplified interface that can be
	+ Supporting infrastructure is contained in the following files. For the most part, as the application developer, you will not need to modify these files unless some foundational work is needed. Noted below are modifications might be made in these files based on your need.
		* `event_base_types.hpp` - contains the base types for the event system, such as `EventBase_t` and the `CallableEvent`
			- If you want to add some commonality to _all_ events, you can modify `EventBase_t` to support the desired functions or member variables.
		* `event_handler.hpp` - defines an event processor interface
			- This type is not used in the ETL at this moment, as `etl::fsm` provides the processing basis. However, if you wish to define alternative message handling structures, you can use the types defined in this header.
		* `event_queue.hpp` provides an event queue implementation
			- This wraps either an `etl::queue` or `std::queue` depending on the template parameter settings
			- If you wish to support additional queue APIs, or modify the implementation to support a priority queue, this is where you will perform the modifications. Note, however, that changes to the interface also need to be made in `event_queue_interface.hpp`.
- `CPF/State/`: This contains
	+ `StateBase.hpp` contains the `StateBase` class definition
		* StateBase has a `process()` API that is invoked by `CPFStateProcessing.cpp` to handle incoming events. These events are forwarded to the current Action state for handling.
	+ `CPFEventProcessing.cpp`
		* Defines the application-specific message packet type, which wraps all of the application's event types
		* Contains the central event queue that stores events in the system
		* Implements the interfaces defined in `event_queue_interface.hpp` and `CallbackDispatcher.hpp`.
- `CPF/CPFStateProcessing.cpp` interacts with the `CentralEventQueue` to process callbacks and events.
	
## Working with the Event System

1. [Event-Driven Paradigm](#event-driven-paradigm)
1. [Defining New Events](#defining-new-events)
2. [Generating Events](#generating-events)	

### Event-Driven Paradigm

**The system processing loop is event-driven.** This means that the system is primarily reactive: Without events firing, there will be no work done and the system will essentially sleep forever. 

You must keep this in mind.

Action handlers must always take some step that generates a future event for the system to process, even if that is just an entry event from transitioning from one action to the next.

For periodic events, use a software timer to generate a repeated input source. For example, if you're checking depth every Xms in order to determine whether you've hit your target, set up a timer to call `requestDepth()` (or an equivalent API) with the target period.

You must remember the sacred rule: the state processing loop only runs if there are events fired. You must fire off events, and you must ensure your actions keep those events firing.

### Defining New Events

You can define new event types by modifying `CPF/Interfaces/events/CPFEventTypes.hpp`. First, you will need to modify `CPFEventIDs` to add a new event ID value.

```cpp
// These event IDs are unique to the solution
enum CPFEventIDs : event_id_t {
	ID_EntryAction,
	ID_GPSNewTimestampReady,
	ID_GPSNewPositionReady,
};
```

You can use any event ID that is not in the `ReservedEventIDs` enumeration, found in `CPF/Interfaces/events/event_base_types.hpp`. These event IDs count backward from `0xFF`. As of the initial release, this only contains a `Callable` event type that is used to store callbacks.

```cpp
enum ReservedEventIDs : event_id_t {
	Callable = 0xFF
};
```

Next, you will need to define an event type. To do this, create a struct for the event. The struct should inherit from `EventBase<NewEventID>`. If you simply want an event with no corresponding data, you can leave the `struct` implementation empty. 

```cpp
/// Specific types of events
struct GPSPositionReadyEvent : public EventBase<GPS_NewPosition_Ready>
{
	// impl...;
}
```

If you want to store data with the event, you can add members.

```cpp
struct GPSCommunicationErrorEvent : public EventBase<ID_GPSCommunicationError>
{
	enum class Error : uint8_t
	{
		ErrorUnknown,
		UnexpectedMessage,
		Timeout,
		BusError,
	};

	GPSCommunicationErrorEvent() : error(Error::ErrorUnknown) {}
	GPSCommunicationErrorEvent(Error type) : error(type) {}

	Error error;
};
```

Finally, you need to update the definition of `CPFEventStorage_t` to include the event `struct` you just created. This definition is found in `CPF/State/CPFEventProcessing.cpp`. If you forget this step, you will encounter a runtime assertion whenever you try to enqueue the new event type.

```cpp
using CPFEventStorage_t = MakeEventStorageType<
	GPSPositionReadyEvent,
	GPSTimestampReadyEvent,
	GPSCommunicationErrorEvent
>;
```

### Generating Events

Events are represented as instances of the appropriate event structure. You create an event object and add it to the `CentralEventQueue` for future processing.

```cpp
// Event with no data
CentralEventQueue::enqueue(GPSPositionReadyEvent());

// Event with data
CentralEventQueue::enqueue(GPSCommunicationErrorEvent(GPSCommunicationErrorEvent::Error::Timeout));

// Event with data, initialized before enqueuing
GPSCommunicationErrorEvent event;
event.error = GPSCommunicationErrorEvent::Error::Timeout;
CentralEventQueue::enqueue(event);
```

### Enqueuing Callbacks

When working in an asynchronous system, many APIs will involve callbacks. However, callbacks may be invoked during interrupt context, where some operations are not permitted. Rather than directly invoking callback functions, you can add them to the event queue.

The easiest way is to pass a functor to the `CentralEventQueue` using the `callback` API. Internally, this creates a `Callable` event type and adds it to the queue.

```cpp
CentralEventQueue::callback(position_received_callback);
```

You could generate a `Callable` event manually, initializing it with your functor, and enqueue it as with other events.

```cpp
CallableEvent my_callback(position_received_callback);
CentralEventQueue::enqueue(my_callback);
```

If you don't want to involve other event resources, but only need to dispatch callbacks, you can include `CallbackDispatcher.hpp` and use the supplied function:

```cpp
Callback::dispatch(position_received_callback);
```

### Handling Events

Events are popped off of the queue and fed into the current CPF system state as part of the CPF State Processing loop. This means that events are ONLY handled by the CPF state machine and action handlers.

For more information on defining these objects, see [AddingANewState.md](AddingANewState.md).
