Вопрос только о С, без плюсов.
В очередной раз по новому реализуя конечный автомат, задумался - а кто вообще как делает обычно? Также по ситуации или обычно одно и тоже?
Когда простая прошивка - делаю на свитчах. Когда сложнее, раньше обычно использовал массив структур с переходами, вроде
Код: Выделить всё
{STATE1, EVENT1, handler_1},
{STATE1, EVENT1, handler_2},
{STATE2, EVENT2, handler_3}
Но тратится время на поиск перебором. И т.к. эвенты у меня генерились прерываниями, бывали косяки. Потом уже придумал, что эвенты можно из прерываний записывать в некий стек ну или просто временные переменные и уже только лишь в нужно время выдергивать их по одному.
Сейчас вот решил сделать на двумерном массиве, что вроде как шаг назад и менее эффективно. Таблица занимает состояния * эвенты * sizeof(указатель на функцию). Сначала примеры в сети увидел когда на тему таблицы - там зачем-то заполняли все элементы. Не красиво. Подумал - ведь в современном C можно только нужные переходы указать в таблице и все будет также лаконично, как и с массивом стуктур.
Но мне нравится простота реализации. Как-то так в общем виде:
Спойлер
Код: Выделить всё
eState fsNormalBypass() {
...
}
...
// Transition table type
typedef eState (*trans_t[ST_LAST][EV_LAST])(void);
// Transition table
trans_t trans = {
[ST_NORMAL][EV_BTN_SHORT] = fsNormalBypass,
[ST_NORMAL][EV_BTN_LONG] = fsNormalClosed,
[ST_BYPASS][EV_BTN_SHORT] = fsBypassNormal
...
};
// Get event in order of priority
eEvent fsGetEvent() {
eEvent ret = EV_NONE;
...
return ret;
}
// Run the transition based on event
eRetCode fsTransition() {
#ifdef VERBOSE_LOGS
LOG("Transition: st %d, ev %d\r\n", state.cur_state, state.event);
#endif
if ((state.event < EV_LAST) && trans[state.cur_state][state.event] != NULL) {
state.prev_state = state.cur_state; // save previous state
state.cur_state = trans[state.cur_state][state.event](); // run the transition
return RET_OK;
}
return RET_ERROR;
}
int main(void)
{
...
while(1)
{
state.event = fsGetEvent();
if (state.event != EV_NONE)
fsTransition();
}
}
fsGetEvent() пробегает по "стеку" эвентов и возвращает эвент с наивысшим приоритетом. fsTransition() делает простейшую проверочку и вызывает обработчик просто обратившись к его указателю в адресе 2D массива.
А вы как делаете?