Last active
April 21, 2020 17:14
-
-
Save iddoeldor/66cfb8778e0df9fed3f8dd5677890bd0 to your computer and use it in GitHub Desktop.
c state machine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CXX = gcc | |
COMPFLAGS = -c -Wall -g | |
state_machine: state_machine.o | |
$(CXX) -lrt state_machine.o -o state_machine | |
state_machine.o: state_machine.c | |
$(CXX) $(COMPFLAGS) state_machine.c | |
clean: | |
rm state_machine state_machine.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdint.h> | |
#include <assert.h> | |
#include <unistd.h> // sysconf() | |
#include <errno.h> // errno | |
#include <string.h> // strerror() | |
#include <sys/time.h> // gettimeofday() | |
#include <fcntl.h> // For O_* constants | |
#include <sys/stat.h> // For mode constants | |
#include <mqueue.h> | |
#include <poll.h> | |
//------------------------------------------------ | |
// States | |
//------------------------------------------------ | |
typedef enum | |
{ | |
ST_UNKNOWN = 0, | |
ST_UNINIT, | |
ST_INIT, | |
ST_CONNECTED, | |
ST_MTU_NEGOTIATED, | |
ST_AUTHENTICATED, | |
ST_ERROR, | |
ST_DONT_CHANGE, | |
ST_TERM, | |
} fsmState_t; | |
//------------------------------------------------ | |
// Events | |
//------------------------------------------------ | |
typedef enum | |
{ | |
EV_UNKNOWN = 0, | |
EV_INIT_SUCCESS, | |
EV_INIT_FAIL, | |
EV_MASTER_CMD_MSG, | |
EV_CONNECT_SUCCESS, | |
EV_CONNECT_FAIL, | |
EV_MTU_SUCCESS, | |
EV_MTU_FAIL, | |
EV_AUTH_SUCCESS, | |
EV_AUTH_FAIL, | |
EV_TX_SUCCESS, | |
EV_TX_FAIL, | |
EV_DISCONNECTED, | |
EV_DISCON_FAILED, | |
EV_LAST_ENTRY, | |
} fsmEvName_t; | |
typedef struct fsmEvent_type | |
{ | |
fsmEvName_t name; | |
struct timeval genTime; // Time the event was generated. | |
// This allows us to see how old the event is. | |
} fsmEvent_t; | |
// Finite State Machine Data Members | |
typedef struct fsmData_type | |
{ | |
int connectTries; | |
int MTUtries; | |
int authTries; | |
int txTries; | |
} fsmData_t; | |
// Each row of the state table | |
typedef struct stateTable_type { | |
fsmState_t st; // Current state | |
fsmEvName_t evName; // Got this event | |
int (*conditionfn)(void *); // If this condition func returns TRUE | |
fsmState_t nextState; // Change to this state and | |
void (*fn)(void *); // Run this function | |
} stateTable_t; | |
// Finite State Machine state structure | |
typedef struct fsm_type | |
{ | |
const stateTable_t *pStateTable; // Pointer to state table | |
int numStates; // Number of entries in the table | |
fsmState_t currentState; // Current state | |
fsmEvent_t currentEvent; // Current event | |
fsmData_t *fsmData; // Pointer to the data attributes | |
mqd_t mqdes; // Message Queue descriptor | |
mqd_t master_cmd_mqdes; // Master command message queue | |
} fsm_t; | |
// Wildcard events and wildcard state | |
#define EV_ANY -1 | |
#define ST_ANY -1 | |
#define TRUE (1) | |
#define FALSE (0) | |
// Maximum priority for message queues (see "man mq_overview") | |
#define FSM_PRIO (sysconf(_SC_MQ_PRIO_MAX) - 1) | |
static void addev (fsm_t *fsm, fsmEvName_t ev); | |
static void doNothing (void *fsm) {addev(fsm, EV_MASTER_CMD_MSG);} | |
static void doInit (void *fsm) {addev(fsm, EV_INIT_SUCCESS);} | |
static void doConnect (void *fsm) {addev(fsm, EV_CONNECT_SUCCESS);} | |
static void doMTU (void *fsm) {addev(fsm, EV_MTU_SUCCESS);} | |
static void reportFailConnect (void *fsm) {addev(fsm, EV_ANY);} | |
static void doAuth (void *fsm) {addev(fsm, EV_AUTH_SUCCESS);} | |
static void reportDisConnect (void *fsm) {addev(fsm, EV_ANY);} | |
static void doDisconnect (void *fsm) {addev(fsm, EV_ANY);} | |
static void doTransaction (void *fsm) {addev(fsm, EV_TX_FAIL);} | |
static void fsmError (void *fsm) {addev(fsm, EV_ANY);} | |
static int currentlyLessThanMaxConnectTries (void *fsm) { | |
fsm_t *l = (fsm_t *)fsm; | |
return (l->fsmData->connectTries < 5 ? TRUE : FALSE); | |
} | |
static int isMoreThanMaxConnectTries (void *fsm) {return TRUE;} | |
static int currentlyLessThanMaxMTUtries (void *fsm) {return TRUE;} | |
static int isMoreThanMaxMTUtries (void *fsm) {return TRUE;} | |
static int currentyLessThanMaxAuthTries (void *fsm) {return TRUE;} | |
static int isMoreThanMaxAuthTries (void *fsm) {return TRUE;} | |
static int currentlyLessThanMaxTXtries (void *fsm) {return FALSE;} | |
static int isMoreThanMaxTXtries (void *fsm) {return TRUE;} | |
static int didNotSelfDisconnect (void *fsm) {return TRUE;} | |
static int waitForEvent (fsm_t *fsm); | |
static void runEvent (fsm_t *fsm); | |
static void runStateMachine(fsm_t *fsm); | |
static int newEventIsValid(fsmEvent_t *event); | |
static void getTime(struct timeval *time); | |
void printState(fsmState_t st); | |
void printEvent(fsmEvName_t ev); | |
// Global State Table | |
const stateTable_t GST[] = { | |
// Current state Got this event If this condition func returns TRUE Change to this state and Run this function | |
{ ST_UNINIT, EV_INIT_SUCCESS, NULL, ST_INIT, &doNothing }, | |
{ ST_UNINIT, EV_INIT_FAIL, NULL, ST_UNINIT, &doInit }, | |
{ ST_INIT, EV_MASTER_CMD_MSG, NULL, ST_INIT, &doConnect }, | |
{ ST_INIT, EV_CONNECT_SUCCESS, NULL, ST_CONNECTED, &doMTU }, | |
{ ST_INIT, EV_CONNECT_FAIL, ¤tlyLessThanMaxConnectTries, ST_INIT, &doConnect }, | |
{ ST_INIT, EV_CONNECT_FAIL, &isMoreThanMaxConnectTries, ST_INIT, &reportFailConnect }, | |
{ ST_CONNECTED, EV_MTU_SUCCESS, NULL, ST_MTU_NEGOTIATED, &doAuth }, | |
{ ST_CONNECTED, EV_MTU_FAIL, ¤tlyLessThanMaxMTUtries, ST_CONNECTED, &doMTU }, | |
{ ST_CONNECTED, EV_MTU_FAIL, &isMoreThanMaxMTUtries, ST_CONNECTED, &doDisconnect }, | |
{ ST_CONNECTED, EV_DISCONNECTED, &didNotSelfDisconnect, ST_INIT, &reportDisConnect }, | |
{ ST_MTU_NEGOTIATED, EV_AUTH_SUCCESS, NULL, ST_AUTHENTICATED, &doTransaction }, | |
{ ST_MTU_NEGOTIATED, EV_AUTH_FAIL, ¤tyLessThanMaxAuthTries, ST_MTU_NEGOTIATED, &doAuth }, | |
{ ST_MTU_NEGOTIATED, EV_AUTH_FAIL, &isMoreThanMaxAuthTries, ST_MTU_NEGOTIATED, &doDisconnect }, | |
{ ST_MTU_NEGOTIATED, EV_DISCONNECTED, &didNotSelfDisconnect, ST_INIT, &reportDisConnect }, | |
{ ST_AUTHENTICATED, EV_TX_SUCCESS, NULL, ST_AUTHENTICATED, &doDisconnect }, | |
{ ST_AUTHENTICATED, EV_TX_FAIL, ¤tlyLessThanMaxTXtries, ST_AUTHENTICATED, &doTransaction }, | |
{ ST_AUTHENTICATED, EV_TX_FAIL, &isMoreThanMaxTXtries, ST_AUTHENTICATED, &doDisconnect }, | |
{ ST_AUTHENTICATED, EV_DISCONNECTED, &didNotSelfDisconnect, ST_INIT, &reportDisConnect }, | |
{ ST_ANY, EV_DISCON_FAILED, NULL, ST_DONT_CHANGE, &doDisconnect }, | |
{ ST_ANY, EV_ANY, NULL, ST_UNINIT, &fsmError } // Wildcard state for errors | |
}; | |
#define GST_COUNT (sizeof(GST)/sizeof(stateTable_t)) | |
int main() | |
{ | |
int ret = 0; | |
fsmData_t dataAttr; | |
dataAttr.connectTries = 0; | |
dataAttr.MTUtries = 0; | |
dataAttr.authTries = 0; | |
dataAttr.txTries = 0; | |
fsm_t lfsm; | |
memset(&lfsm, 0, sizeof(fsm_t)); | |
lfsm.pStateTable = GST; | |
lfsm.numStates = GST_COUNT; | |
lfsm.currentState = ST_UNINIT; | |
lfsm.currentEvent.name = EV_ANY; | |
lfsm.fsmData = &dataAttr; | |
struct mq_attr attr; | |
attr.mq_maxmsg = 30; | |
attr.mq_msgsize = sizeof(fsmEvent_t); | |
// Dev info | |
//printf("Size of fsmEvent_t [%ld]\n", sizeof(fsmEvent_t)); | |
ret = mq_unlink("/abcmq"); | |
if (ret == -1) { | |
fprintf(stderr, "Error on mq_unlink(), errno[%d] strerror[%s]\n", | |
errno, strerror(errno)); | |
} | |
lfsm.mqdes = mq_open("/abcmq", O_CREAT | O_RDWR, S_IWUSR | S_IRUSR, &attr); | |
if (lfsm.mqdes == (mqd_t)-1) { | |
fprintf(stderr, "Error on mq_open(), errno[%d] strerror[%s]\n", | |
errno, strerror(errno)); | |
return -1; | |
} | |
doInit(&lfsm); // This will generate the first event | |
runStateMachine(&lfsm); | |
return 0; | |
} | |
static void runStateMachine(fsm_t *fsm) | |
{ | |
int ret = 0; | |
if (fsm == NULL) { | |
fprintf(stderr, "[%s] NULL argument\n", __func__); | |
return; | |
} | |
// Cycle through the state machine | |
while (fsm->currentState != ST_TERM) { | |
printf("current state ["); | |
printState(fsm->currentState); | |
printf("]\n"); | |
ret = waitForEvent(fsm); | |
if (ret == 0) { | |
printf("got event ["); | |
printEvent(fsm->currentEvent.name); | |
printf("]\n"); | |
runEvent(fsm); | |
} | |
sleep(2); | |
} | |
} | |
static int waitForEvent(fsm_t *fsm) | |
{ | |
//const int numFds = 2; | |
const int numFds = 1; | |
struct pollfd fds[numFds]; | |
int timeout_msecs = -1; // -1 is forever | |
int ret = 0; | |
int i = 0; | |
ssize_t num = 0; | |
fsmEvent_t newEv; | |
if (fsm == NULL) { | |
fprintf(stderr, "[%s] NULL argument\n", __func__); | |
return -1; | |
} | |
fsm->currentEvent.name = EV_ANY; | |
fds[0].fd = fsm->mqdes; | |
fds[0].events = POLLIN; | |
//fds[1].fd = fsm->master_cmd_mqdes; | |
//fds[1].events = POLLIN; | |
ret = poll(fds, numFds, timeout_msecs); | |
if (ret > 0) { | |
// An event on one of the fds has occurred | |
for (i = 0; i < numFds; i++) { | |
if (fds[i].revents & POLLIN) { | |
// Data may be read on device number i | |
num = mq_receive(fds[i].fd, (void *)(&newEv), | |
sizeof(fsmEvent_t), NULL); | |
if (num == -1) { | |
fprintf(stderr, "Error on mq_receive(), errno[%d] " | |
"strerror[%s]\n", errno, strerror(errno)); | |
return -1; | |
} | |
if (newEventIsValid(&newEv)) { | |
fsm->currentEvent = newEv; | |
} else { | |
return -1; | |
} | |
} | |
} | |
} else { | |
fprintf(stderr, "Error on poll(), ret[%d] errno[%d] strerror[%s]\n", | |
ret, errno, strerror(errno)); | |
return -1; | |
} | |
return 0; | |
} | |
static int newEventIsValid(fsmEvent_t *event) | |
{ | |
if (event == NULL) { | |
fprintf(stderr, "[%s] NULL argument\n", __func__); | |
return FALSE; | |
} | |
printf("[%s]\n", __func__); | |
struct timeval now; | |
getTime(&now); | |
if ( (event->name < EV_LAST_ENTRY) && | |
((now.tv_sec - event->genTime.tv_sec) < (60*5)) | |
) | |
{ | |
return TRUE; | |
} else { | |
return FALSE; | |
} | |
} | |
//------------------------------------------------ | |
// Performs event handling on the FSM (finite state machine). | |
// Make sure there is a wildcard state at the end of | |
// your table, otherwise; the event will be ignored. | |
//------------------------------------------------ | |
static void runEvent(fsm_t *fsm) | |
{ | |
int i; | |
int condRet = 0; | |
if (fsm == NULL) { | |
fprintf(stderr, "[%s] NULL argument\n", __func__); | |
return; | |
} | |
printf("[%s]\n", __func__); | |
// Find a relevant entry for this state and event | |
for (i = 0; i < fsm->numStates; i++) { | |
// Look in the table for our current state or ST_ANY | |
if ( (fsm->pStateTable[i].st == fsm->currentState) || | |
(fsm->pStateTable[i].st == ST_ANY) | |
) | |
{ | |
// Is this the event we are looking for? | |
if ( (fsm->pStateTable[i].evName == fsm->currentEvent.name) || | |
(fsm->pStateTable[i].evName == EV_ANY) | |
) | |
{ | |
if (fsm->pStateTable[i].conditionfn != NULL) { | |
condRet = fsm->pStateTable[i].conditionfn(fsm->fsmData); | |
} | |
// See if there is a condition associated | |
// or we are not looking for any condition | |
// | |
if ( (condRet != 0) || (fsm->pStateTable[i].conditionfn == NULL)) | |
{ | |
// Set the next state (if applicable) | |
if (fsm->pStateTable[i].nextState != ST_DONT_CHANGE) { | |
fsm->currentState = fsm->pStateTable[i].nextState; | |
printf("new state ["); | |
printState(fsm->currentState); | |
printf("]\n"); | |
} | |
// Call the state callback function | |
fsm->pStateTable[i].fn(fsm); | |
break; | |
} | |
} | |
} | |
} | |
} | |
//------------------------------------------------ | |
// EVENT HANDLERS | |
//------------------------------------------------ | |
static void getTime(struct timeval *time) | |
{ | |
if (time == NULL) { | |
fprintf(stderr, "[%s] NULL argument\n", __func__); | |
return; | |
} | |
printf("[%s]\n", __func__); | |
int ret = gettimeofday(time, NULL); | |
if (ret != 0) { | |
fprintf(stderr, "gettimeofday() failed: errno [%d], strerror [%s]\n", | |
errno, strerror(errno)); | |
memset(time, 0, sizeof(struct timeval)); | |
} | |
} | |
static void addev (fsm_t *fsm, fsmEvName_t ev) | |
{ | |
int ret = 0; | |
if (fsm == NULL) { | |
fprintf(stderr, "[%s] NULL argument\n", __func__); | |
return; | |
} | |
printf("[%s] ev[%d]\n", __func__, ev); | |
if (ev == EV_ANY) { | |
// Don't generate a new event, just return... | |
return; | |
} | |
fsmEvent_t newev; | |
getTime(&(newev.genTime)); | |
newev.name = ev; | |
ret = mq_send(fsm->mqdes, (void *)(&newev), sizeof(fsmEvent_t), FSM_PRIO); | |
if (ret == -1) { | |
fprintf(stderr, "[%s] mq_send() failed: errno [%d], strerror [%s]\n", | |
__func__, errno, strerror(errno)); | |
} | |
} | |
//------------------------------------------------ | |
// end EVENT HANDLERS | |
//------------------------------------------------ | |
void printState(fsmState_t st) | |
{ | |
switch(st) { | |
case ST_UNKNOWN: | |
printf("ST_UNKNOWN"); | |
break; | |
case ST_UNINIT: | |
printf("ST_UNINIT"); | |
break; | |
case ST_INIT: | |
printf("ST_INIT"); | |
break; | |
case ST_CONNECTED: | |
printf("ST_CONNECTED"); | |
break; | |
case ST_MTU_NEGOTIATED: | |
printf("ST_MTU_NEGOTIATED"); | |
break; | |
case ST_AUTHENTICATED: | |
printf("ST_AUTHENTICATED"); | |
break; | |
case ST_ERROR: | |
printf("ST_ERROR"); | |
break; | |
case ST_TERM: | |
printf("ST_TERM"); | |
break; | |
default: | |
printf("unknown state"); | |
break; | |
} | |
} | |
void printEvent(fsmEvName_t ev) | |
{ | |
switch (ev) { | |
case EV_UNKNOWN: | |
printf("EV_UNKNOWN"); | |
break; | |
case EV_INIT_SUCCESS: | |
printf("EV_INIT_SUCCESS"); | |
break; | |
case EV_INIT_FAIL: | |
printf("EV_INIT_FAIL"); | |
break; | |
case EV_MASTER_CMD_MSG: | |
printf("EV_MASTER_CMD_MSG"); | |
break; | |
case EV_CONNECT_SUCCESS: | |
printf("EV_CONNECT_SUCCESS"); | |
break; | |
case EV_CONNECT_FAIL: | |
printf("EV_CONNECT_FAIL"); | |
break; | |
case EV_MTU_SUCCESS: | |
printf("EV_MTU_SUCCESS"); | |
break; | |
case EV_MTU_FAIL: | |
printf("EV_MTU_FAIL"); | |
break; | |
case EV_AUTH_SUCCESS: | |
printf("EV_AUTH_SUCCESS"); | |
break; | |
case EV_AUTH_FAIL: | |
printf("EV_AUTH_FAIL"); | |
break; | |
case EV_TX_SUCCESS: | |
printf("EV_TX_SUCCESS"); | |
break; | |
case EV_TX_FAIL: | |
printf("EV_TX_FAIL"); | |
break; | |
case EV_DISCONNECTED: | |
printf("EV_DISCONNECTED"); | |
break; | |
case EV_LAST_ENTRY: | |
printf("EV_LAST_ENTRY"); | |
break; | |
default: | |
printf("unknown event"); | |
break; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment