All services in the CANopen stack are asynchronous and therefore implemented as event-driven finite-state machines (FSMs). Each implementation follows the pattern shown here. To ensure the maintainability and extensibility of these state machines, the pattern was designed to have the following properties:
Additionally, the implementation should support parametrized events and transient states (states which transition to another state without an external event). The latter greatly simplifies error handling.
The implementation shown here is an extension of the function-pointer-based state machine and is similar to the object-oriented state design pattern. The state machine is an object — see the documentation of the Lely utilities library (liblely-util) for the general object interface — containing the context and a pointer to the current state. Each state is a (constant) array of pointers to transitions functions, one for each event. The transition functions return a pointer to the next state, or NULL if no transition occurs. There are two special (optional) transition functions, on_enter() and on_leave(), called when a state is entered or left, respectively. A transient state is simply a state whose on_enter() function exists and returns a pointer to another state.
struct __fsm_state;
typedef const struct __fsm_state fsm_state_t;
struct __fsm {
...
fsm_state_t *state;
...
};
static void fsm_enter(fsm_t *fsm, fsm_state_t *next);
static inline void fsm_emit_event(fsm_t *fsm, Args... args);
static inline void fsm_emit_time(fsm_t *fsm, const struct timespec *tp);
struct __fsm_state {
fsm_state_t *(*on_enter)(fsm_t *fsm);
fsm_state_t *(*on_event)(fsm_t *fsm, Args... args);
fsm_state_t *(*on_time)(fsm_t *fsm, const struct timespec *tp);
void (*on_leave)(fsm_t *fsm);
};
#define LELY_LIB_DEFINE_STATE(name, ...) \
static fsm_state_t *const name = &(fsm_state_t){ __VA_ARGS__ };
LELY_LIB_DEFINE_STATE(fsm_idle_state, NULL)
static fsm_state_t *fsm_init_on_enter(fsm_t *fsm);
LELY_LIB_DEFINE_STATE(fsm_init_state,
.on_enter = &fsm_init_on_enter
)
static fsm_state_t *fsm_wait_on_event(fsm_t *fsm, Args... args);
static fsm_state_t *fsm_wait_on_time(fsm_t *fsm, const struct timespec *tp);
LELY_LIB_DEFINE_STATE(fsm_wait_state,
.on_event = &fsm_wait_on_event,
.on_time = &fsm_wait_on_time
)
static fsm_state_t *fsm_fini_on_enter(fsm_t *fsm);
LELY_LIB_DEFINE_STATE(fsm_fini_state,
.on_enter = &fsm_fini_on_enter
)
#undef LELY_LIB_DEFINE_STATE
struct __fsm *
__fsm_init(struct __fsm *fsm, Args... args)
{
...
fsm->state = NULL;
fsm_enter(fsm, fsm_idle_state);
...
}
int
fsm_is_idle(const fsm_t *fsm)
{
assert(fsm);
return fsm->state == fsm_idle_state;
}
int
fsm_req(fsm_t *fsm, Args... args)
{
assert(fsm);
if (!fsm_is_idle(fsm)) {
return -1;
}
...
fsm_enter(fsm, fsm_init_state);
return 0;
}
static void
fsm_enter(fsm_t *fsm, fsm_state_t *next)
{
assert(fsm);
while (next) {
fsm_state_t *prev = fsm->state;
sdo->state = next;
if (prev->on_leave)
prev->on_leave(fsm);
next = next->on_enter ? next->on_enter(fsm) : NULL;
}
}
static inline void
fsm_emit_event(fsm_t *fsm, Args... args)
{
assert(fsm);
assert(fsm->state);
assert(fsm->state->on_event);
fsm_enter(fsm, fsm->state->on_event(fsm, args...));
}
static inline void
fsm_emit_time(fsm_t *fsm, const struct timespec *tp)
{
assert(fsm);
assert(fsm->state);
assert(fsm->state->on_time);
fsm_enter(fsm, fsm->state->on_time(fsm, tp));
}
static fsm_state_t *
fsm_init_on_enter(fsm_t *fsm)
{
assert(fsm);
...
return fsm_wait_state;
}
static fsm_state_t *
fsm_wait_on_event(fsm_t *fsm, Args... args)
{
assert(fsm);
...
return fsm_fini_state;
}
static fsm_state_t *
fsm_wait_on_time(fsm_t *fsm, const struct timespec *tp)
{
assert(fsm);
...
return fsm_idle_state;
}
static fsm_state_t *
fsm_fini_on_enter(fsm_t *fsm)
{
assert(fsm);
...
return fsm_idle_state;
}