# 8. Modifications to CPF State Processing Code for Handling Events

Date: 2023-02-13

## Status

Accepted 2023-03-11

## Context

State processing code must change to work with the concept of events. Additionally, this code has not yet been reviewed by Phillip, and while working with it a number of improvements were noted.

## Decision 

Changes have been made to the existing StateBase design. Partially, this work was done to support events. But also, changes have been made to streamline the design and provide better enforcement of operations.

General changes:

- StateBase itself is not valid to execute, so functions which need to be defined by inheritors have been marked pure virtual (`void function() = 0`). A plain state base cannot be created.
- There are new top-level state interfaces that are defined in the `StateBase` class: `enter()` and `exit(timeout)`
	* These functions perform the actions that were previously defined in `StdStateEntry()` and `StdStateExit()`. 
	* These functions invoked work defined in derived classes via new pure virtual functions: `doStateEntry` and `doStateExit`
	* This has the same effect as the previous implementation, but with an important improvement: the base class enforces the standard behaviors are called; you don't have to manually invoke the functions in the derived classes
- defineActionLabels is removed The behavior could be part of the derived class constructor, if desired, or names can be kept inside of action classes.

General changes related to supporting events:
 
- `doStateActions` has been removed. Instead, state base has a `process(event)` function that returns the next state.
	+ Proper state machine structure is that: `next_state = process(event)`, and we are mimicking that
- `checkEvents` has been removed and merged into `process`. This is to support the proper state machine structure. State changes and event handling happen in a single place, rather than in two separate places.
		
Changes related to the `etl::fsm` proposal:

- StateBase is now a proper state machine, implemented with etl::fsm. Actions are now properly modeled as states
- There is no more manual action switching. This happens through the ETL FSM infrastructure. 
	* **If you wanted to keep your state logging, which is now gone, I would put a message  into the entry and exit functions on the Action implementations**. There are `LOG_ACTION_ENTRY()` and `LOG_ACTION_EXIT()` macros in `ActionBase.hpp` that can be used for this.
	* Entry/exit actions will automatically be invoked during a transition. This will result in a slightly different model from "just one action per iteration through the loop". We don't think this is a problem for the functioning of the system.


## Consequences

Many of the consequences are outlined above. But general consequences of this change include:

- Some of these changes result in simpler implementations
- Some of these changes result in reduced memory usage
- Some of these changes result in better enforcement of expected usage patterns.
	+ For example, now the StateBase enforces standard actions and defers specific steps to sub-classes. This is better than expecting each sub-class to call a base class function at the appropriate point. Manual enforcement is error-prone: it is easy to forget to do a step, or to put a step at the wrong spot.
	
Notes regarding the elimination of `checkEvents()`:

- The existence of  `checkEvents()` in the previous implementation assumed a polling paradigm. The basic ideas
- For one example, “every pass through a state cycle” is not the same conceptually as in the previous implementation.
	+ Previously, the system went through the full loop at a fixed frequency, whether there was work to do or not. 
	+ With events, the state loop is run when events are in the queue - you could have no events for a period, or suddenly need to process 4 events, and that will be 4 back-to-back runs of the state loop before the system sleeps. Does it really make sense to check depth 4 times in 500 microseconds when that happens?  
	+ What if checking the depth is an asynchronous operation that generates an event when the read completes? Now it is possible to request a new depth reading before the other one finished, which might result in a “busy” return code from the driver. 
	+ Some of the logic previously implemented in `checkEvents()` would also require that function to handle events - better to do that all in one place.

More refined sub-states are required to operate in a pure event-driven context when compared to the polling implementation that relied on `checkEvents()`. 

- For example, checking depth previously happened in `checkEvents()`, which executed on every iteration through the state loop. 
- With the event-driven paradigm, there needs to be a new action that is responsible for checking depth.
	+ You can create a timer that requests a new depth reading at a specific frequency. (you set the period and in the callback, call something like `requestDepth()`)
	+ The action has an event handler for new depth readings. When there's a new depth reading available, an event will be fired, and this handler will be invoked.
	+ When the depth has passed the target threshold, you move to the new action/state.
	+ You can start a periodic timer on action entry, stop on exit. 
		* Alternatively, you can start a one-shot timer on action entry and restart the timer every time it expires. This model supports finer-grained error handling, as you can take the step of only reloading the timer when an event has been successfully processed (taking a separate path for errors).
