How to pass global user defined structures to each callbacks libwebsockets C - c

im not sure if this related to lws but i just can't find way to pass global structure which holds its values between the callbacks .
the simple story is that i have simple hashtable in C
https://github.com/cgjones/android-system-core/blob/master/libcutils/hashmap.c
i try to explain in the example :
i have the main :
//HERE I DEFINE IT AS GLOBAL
Hashmap *users_map;
static struct lws_protocols protocols[] = {
{
"wsapi",
callback_wsapi,
sizeof(struct per_session_data__apigataway),
128,
} ,
{ NULL, NULL, 0, 0 } /* terminator */
};
int main(int argc, char **argv)
{
struct lws_context_creation_info info;
//HERE i init the hash map
users_map = hashmapCreate(10, str_hash_fn, str_eq);
memset(&info, 0, sizeof info);
info.port = server_port;
info.protocols = protocols;
...
info.options = opts | LWS_SERVER_OPTION_LIBUV;
context = lws_create_context(&info);
if (lws_uv_initloop(context, NULL, 0)) {
lwsl_err("lws_uv_initloop failed\n");
goto bail;
}
uv_timer_init(lws_uv_getloop(context, 0), &timeout_watcher);
uv_timer_start(&timeout_watcher, main_loop_count_callback, 1000, 1000);
lws_libuv_run(context, 0);
return 0;
}
and this is the callback_wsapi C file i removed allot of code just to show the important stuff
//HERE I SET IT AS EXTERN SO IT WILL BE VISIBLE TO ALL
extern Hashmap *users_map;
int
callback_iogame(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
unsigned char out[LWS_PRE + 512];
struct per_session_data__apigataway *pss =
(struct per_session_data__apigataway *)user;
switch (reason) {
case LWS_CALLBACK_ESTABLISHED:
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
{
//HERE IT LOSSING SCOPE AND THE HASHMAP NOT INITIALIZED
int bfor2 = hashmapSize(users_map);
break;
}
case LWS_CALLBACK_RECEIVE:
{
char* client_req_str;
client_req_str = (char*)in;
if (strncmp((const char *)client_req_str, "player\n",6) == 0)
{
//ON THE FIRST REQUEST FROM THE CLINET IT WORKS
int bfor = hashmapSize(users_map);
hashmapPut(users_map, pss->id, pss);
int after = hashmapSize(users_map);
}
//Only invoke callback back to client when baby client is ready to eat
lws_callback_on_writable(wsi);
break;
}
case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
break;
case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE:
break;
default:
break;
}
So i can get the hashmap only in the first request when it gets to : LWS_CALLBACK_RECEIVE
then it just losing scope .
Questions :
1. How can i make this hashmap global to the callbacks ? it supposed to hold the server total users .

Related

C: How to access value returned by Net-SNMP GET

I apologize for the naive question, Iam new to Net-SNMP. I have tried using this simple SNMP demo app given in Net-SNMP website.
This code performs a SNMP-GET and manipulates the response to check if the value returned is a ASN_OCTET_STRING, and if yes, access the string using vars->val.string and assigned to a character pointer sp.
But Iam unable to figure out how to access this value if the type is anything other than ASN_OCTET_STRING. For example how do I take this value and, say, assign it to a variable if it is of type 'ASN_INTEGER' or 'ASN_OBJECT_ID'.
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <string.h>
#define DEMO_USE_SNMP_VERSION_3
#ifdef DEMO_USE_SNMP_VERSION_3
const char *our_v3_passphrase = "MD5Password";
#endif
int main(int argc, char ** argv)
{
netsnmp_session session, *ss;
netsnmp_pdu *pdu;
netsnmp_pdu *response;
oid anOID[MAX_OID_LEN];
size_t anOID_len;
netsnmp_variable_list *vars;
int status;
int count=1;
init_snmp("snmpdemoapp");
snmp_sess_init( &session );
session.peername = strdup("localhost:161");
#ifdef DEMO_USE_SNMP_VERSION_3
session.version=SNMP_VERSION_3;
session.securityName = strdup("user2");
session.securityNameLen = strlen(session.securityName);
session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
session.securityAuthProto = usmHMACMD5AuthProtocol;
session.securityAuthProtoLen = sizeof(usmHMACMD5AuthProtocol)/sizeof(oid);
session.securityAuthKeyLen = USM_AUTH_KU_LEN;
if (generate_Ku(session.securityAuthProto,
session.securityAuthProtoLen,
(u_char *) our_v3_passphrase, strlen(our_v3_passphrase),
session.securityAuthKey,
&session.securityAuthKeyLen) != SNMPERR_SUCCESS) {
snmp_perror(argv[0]);
snmp_log(LOG_ERR,
"Error generating Ku from authentication pass phrase. \n");
exit(1);
}
#else /* we'll use the insecure (but simplier) SNMPv1 */
session.version = SNMP_VERSION_1;
session.community = "demopublic";
session.community_len = strlen(session.community);
#endif /* SNMPv1 */
SOCK_STARTUP;
ss = snmp_open(&session);
if (!ss) {
snmp_sess_perror("ack", &session);
SOCK_CLEANUP;
exit(1);
}
pdu = snmp_pdu_create(SNMP_MSG_GET);
anOID_len = MAX_OID_LEN;
if (!snmp_parse_oid("ip.21.1.8.xx.xx.xx.xx", anOID, &anOID_len)) {
snmp_perror("ip.21.1.8.xx.xx.xx.xx");
SOCK_CLEANUP;
exit(1);
}
snmp_add_null_var(pdu, anOID, anOID_len);
status = snmp_synch_response(ss, pdu, &response);
if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) {
for(vars = response->variables; vars; vars = vars->next_variable)
print_variable(vars->name, vars->name_length, vars);
/* manipuate the information ourselves */
for(vars = response->variables; vars; vars = vars->next_variable) {
if (vars->type == ASN_OCTET_STR) {
char *sp = (char *)malloc(1 + vars->val_len);
memcpy(sp, vars->val.string, vars->val_len);
sp[vars->val_len] = '\0';
printf("value #%d is a string: %s\n", count++, sp); //Here sp now has the string - But this doesnt work when the string is for eg."HOST-RESOURCES-MIB::hrSWInstalledDate.1953 = STRING: 0-1-1,0:0:0.0"
free(sp);
}
else if(vars->type == ASN_INTEGER) {
printf("value is an Integer\n");
int ObjVal;
// How do I get the Integer value and assign it to 'ObjVal'
}
else if(vars->type == ASN_OBJECT_ID) {
printf("value is an OID\n");
// How do I get the OID and assign it to some variable
}
else if(vars->type == ASN_TIMETICKS) {
printf("value is in Timeticks\n");
// How do I get the Timeticks and assign it to some variable for further processing
}
}
} else {
if (status == STAT_SUCCESS)
fprintf(stderr, "Error in packet\nReason: %s\n",
snmp_errstring(response->errstat));
else if (status == STAT_TIMEOUT)
fprintf(stderr, "Timeout: No response from %s.\n",
session.peername);
else
snmp_sess_perror("snmpdemoapp", ss);
}
if (response)
snmp_free_pdu(response);
snmp_close(ss);
SOCK_CLEANUP;
return (0);
}
Tried vars->val.integer or vars->val.object_id, but that doesnot contain the value. What am I missing here?
My another question is, even when it is of type ASN_OCTET_STRING, when the GET reply is something like this,
HOST-RESOURCES-MIB::hrSWInstalledDate.1953 = STRING: 0-1-1,0:0:0.0
then vars->val.string doesnt have "0-1-1,0:0:0.0" as string.
Basically my question is How does the value get stored in the response structure from which I can retrieve the values?
Thanks in Advance!!
P.S: Makefile link from Net-SNMP website.
Edit1:
For Integers, i can read using *vars->val->string as pointed out by immibis. Any Ideas about how to access other datatypes?
As you can see in /usr/include/net-snmp/types.h file or similar on your system, net-snmp vars->val has the following union type:
typedef union {
long *integer;
u_char *string;
oid *objid;
u_char *bitstring;
struct counter64 *counter64;
#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
float *floatVal;
double *doubleVal;
/*
* t_union *unionVal;
*/
#endif /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
} netsnmp_vardata;
also *vars has val_len field, where the length of data stored.
So you can access integer as *vars->val.integer, string as pointer to u_char vars->val.string with vars->val_len chars, oid as pointer to oid vars->val.objid with vars->val_len/sizeof(oid) oid elements and so on.

Refactoring: Very similar switch cases

I have several struct declared which contain different data. I also have an enum that corresponds to those structures. There are several places in my code where I need to access information about the structures and I'm doing it via the enum. This results in few switch statements that return this information.
I've enclosed those switch statements in their own functions in order to re-use wherever possible. This resulted in three functions that look very similar.
Example psuedo-code:
#include <stdio.h>
typedef struct
{
int varA;
char varB;
} A;
typedef struct
{
int varA;
int varB;
int varC;
} B;
typedef struct
{
int varA;
short varB;
} C;
typedef enum { structA, structB, structC } STRUCT_ENUM;
int returnSize(STRUCT_ENUM structType)
{
int retVal = 0;
switch(structType)
{
case structA:
retVal = sizeof(A);
break;
case structB:
retVal = sizeof(B);
break;
case structC:
retVal = sizeof(C);
break;
default:
break;
}
return retVal;
}
void printStructName(STRUCT_ENUM structType)
{
switch(structType)
{
case structA:
printf("Struct: A\r\n");
break;
case structB:
printf("Struct: B\r\n");
break;
case structC:
printf("Struct: C\r\n");
break;
default:
break;
}
}
void createDataString(STRUCT_ENUM structType, char* output, unsigned char* input)
{
switch(structType)
{
case structA:
{
A a = *(A*)input;
sprintf(output, "data: %d, %d", a.varA, a.varB);
break;
}
case structB:
{
B b = *(B*)input;
sprintf(output, "data: %d, %d, %d", b.varA, b.varB, b.varC);
break;
}
case structC:
{
C c = *(C*)input;
sprintf(output, "data: %d, %d", c.varA, c.varB);
break;
}
default:
break;
}
}
int main(void) {
char foobar[50];
printf("Return size: %d\r\n", returnSize(structA));
printStructName(structB);
C c = { 10, 20 };
createDataString(structC, foobar, (unsigned char*) &c);
printf("Data string: %s\r\n", foobar);
return 0;
}
Those free functions basically contain the same switch with different code placed in the cases. With this setup, adding new struct and enum value results in three places in the code that needs changing.
The question is: is there a way to refactor this into something more maintainable? Additional constraint is that the code is written in C.
EDIT: online example: http://ideone.com/xhXmXu
You can always use static arrays and use STRUCT_ENUM as the index. Given the nature of your functions, I don't really know if you would consider it more maintainable, but it's an alternative I usually prefer, examples for names and sizes:
typedef enum { structA, structB, structC, STRUCT_ENUM_MAX } STRUCT_ENUM;
char *struct_name[STRUCT_ENUM_MAX] = {[structA] = "Struct A", [structB] = "Struct B", [structC] = "Struct C"};
size_t struct_size[STRUCT_ENUM_MAX] = {[structA] = sizeof(A), [structB] = sizeof(B), [structC] = sizeof(C)};
for printing content you can keep a similar array of functions receiving a void * that will print the value of this argument.
Edit:
Added designated initializers as per Jen Gustedt's comment.
You can make it into a single function and a single switch, with an additional parameter. Like so
int enumInfo(STRUCT_ENUM structType, int type) // 1 = returnSize 2 = printStructName
{
int retVal = 0;
switch(structType)
{
case structA:
If ( type == 1 ) { retVal = sizeof(A); }
else { printf("Struct: A"); }
break;
case structB:
If ( type == 1 ) { retVal = sizeof(B); }
else { printf("Struct: B"); }
break;
case structC:
If ( type == 1 ) { retVal = sizeof(C); }
else { printf("Struct: C"); }
break;
default:
break;
}
return retVal;
}

Read POST parameters from apache module

I'm trying to read POST parameters from an apache c module.
Here's the code I'm using :
/* Include the required headers from httpd */
#include "httpd.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_request.h"
#include "http_config.h"
#include "apr_strings.h"
#include "apr_network_io.h"
#include "apr_dbd.h"
#include <apr_file_info.h>
#include <apr_file_io.h>
#include <apr_tables.h>
#include "util_script.h"
/* Define prototypes of our functions in this module */
typedef struct {
const char *key;
const char *value;
} keyValuePair;
static void register_hooks(apr_pool_t *pool);
static int example_handler(request_rec *r);
keyValuePair *readPost(request_rec *r);
/* Define our module as an entity and assign a function for registering hooks */
module AP_MODULE_DECLARE_DATA example_module =
{
STANDARD20_MODULE_STUFF,
NULL, // Per-directory configuration handler
NULL, // Merge handler for per-directory configurations
NULL, // Per-server configuration handler
NULL, // Merge handler for per-server configurations
NULL, // Any directives we may have for httpd
register_hooks // Our hook registering function
};
/* register_hooks: Adds a hook to the httpd process */
static void register_hooks(apr_pool_t *pool)
{
/* Hook the request handler */
ap_hook_handler(example_handler, NULL, NULL, APR_HOOK_LAST);
}
/* The handler function for our module.
* This is where all the fun happens!
*/
static int example_handler(request_rec *r)
{
/* First off, we need to check if this is a call for the "example" handler.
* If it is, we accept it and do our things, it not, we simply return DECLINED,
* and Apache will try somewhere else.
*/
if (!r->handler || strcmp(r->handler, "example-handler")) return (DECLINED);
// The first thing we will do is write a simple "Hello, world!" back to the client.
ap_rputs("Hello, world!<br/>", r);
return OK;
}
keyValuePair *readPost(request_rec *r) {
apr_array_header_t *pairs = NULL;
apr_off_t len;
apr_size_t size;
int res;
int i = 0;
char *buffer;
keyValuePair *kvp;
res = ap_parse_form_data(r, NULL, &pairs, -1, HUGE_STRING_LEN);
if (res != OK || !pairs) return NULL; /* Return NULL if we failed or if there are is no POST data */
kvp = apr_pcalloc(r->pool, sizeof(keyValuePair) * (pairs->nelts + 1));
while (pairs && !apr_is_empty_array(pairs)) {
ap_form_pair_t *pair = (ap_form_pair_t *) apr_array_pop(pairs);
apr_brigade_length(pair->value, 1, &len);
size = (apr_size_t) len;
buffer = apr_palloc(r->pool, size + 1);
apr_brigade_flatten(pair->value, buffer, &size);
buffer[len] = 0;
kvp[i].key = apr_pstrdup(r->pool, pair->name);
kvp[i].value = buffer;
ap_rputs(kvp[i].key,r);
ap_rputs(kvp[i].value,r);
i++;
}
return kvp;
}
I have copied the read post function from the apache website:
https://httpd.apache.org/docs/2.4/developer/modguide.html#snippets
I get the following error while trying to compile the module:
mod_example.c:82:9: error: use of undeclared identifier
'ap_form_pair_t'
ap_form_pair_t *pair = (ap_form_pair_t *) apr_array_pop(pairs);
apxs does not recognize ap_form_pair_t. Am I missing any header file ?
Can you please help me resolve this ?
ap_form_pair_t comes with apache version 2.4, so I think you use a lower version.
This function writes all post data in a buffer, it may help you:
int util_read(request_rec *r, char **rbuf, size_t &length){
int rc;
length = 0;
if((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)) != OK){
return rc;
}
if(ap_should_client_block(r)){
char argsbuffer[HUGE_STRING_LEN];
int rsize, len_read, rpos=0;
length = r->remaining;
*rbuf = (char*)apr_pcalloc(r->pool, length + 1);
while((len_read = ap_get_client_block(r, argsbuffer, sizeof(argsbuffer))) > 0){
if((rpos + len_read) > length){
rsize = length - rpos;
} else {
rsize = len_read;
}
memcpy((char*)*rbuf + rpos, argsbuffer, rsize);
rpos += rsize;
}
}
return rc;
}

Asterisk create_stasis_message Invalid magic number

I stuck to send a stasis_message for a self made module to the ARI.
I try to use the code example from the documentation :
https://wiki.asterisk.org/wiki/display/AST/Stasis+Message+Bus
I use asterisk 13 instead example (who use the 12), and some signature are changed.
Here is the initialisation :
struct stasis_topic *foo_topic;
static int load_module(void)
{
// Register to stasis.
stasis_app_register(app, callback_stasis, 0);
// Create a bridge on witch ARI can conenct.
stasis_app_bridge_create("mixing", app, "11000");
// Create the topic
foo_topic = stasis_topic_create(app);
return ast_register_application_xml(app, exec);
}
And the code method who is calling when phone arrive :
static int exec()
{
publish_foo();
}
static void publish_foo()
{
printf("Trace 1\n");
char* test = "dataToSend";
RAII_VAR(struct stasis_message_type*, foo_type, NULL, ao2_cleanup);
stasis_message_type_create(app, NULL, &foo_type);
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
printf("Trace 3\n");
msg = stasis_message_create(type, test);
if (!msg)
return;
stasis_publish(foo_topic, msg);
printf("PASSING MESSAGE 4\n");
}
I always get message like :
bad magic number 0x332065 for object 0x7f2ea5ab8ec5
And this error appends in the method stasis_create_message().
[Edit]
I do not understand the error and the cause any help is appreciated.
As suggest by arheops, there is the function who are created problem. Apparently my object cannot be convert to an Asterisk object. Probably the structure I need to send to the create_message_function must be on a astobj2 type.
static struct astobj2 *INTERNAL_OBJ(void *user_data)
{
struct astobj2 *p;
if (!user_data) {
ast_log(LOG_ERROR, "user_data is NULL\n");
return NULL;
}
p = (struct astobj2 *) ((char *) user_data - sizeof(*p));
if (AO2_MAGIC != p->priv_data.magic) {
if (p->priv_data.magic) {
ast_log(LOG_ERROR, "bad magic number 0x%x for object %p\n",
p->priv_data.magic, user_data);
} else {
ast_log(LOG_ERROR,
"bad magic number for object %p. Object is likely destroyed.\n",
user_data);
}
ast_assert(0);
return NULL;
}
return p;
}
And the struct of astobj2 definition :
struct astobj2
{
struct __priv_data priv_data;
void *user_data[0];
};
I tried to create a a2object like describe here, and I get an error :
*** Error in `asterisk': free(): invalid pointer:
Thanks
To send a stasis message, you need to create an a2object, normally you can perform this part with the macro :
RAII_VAR
But I cannot get a working example with this, so I create my-self the object with the following methods :
typedef struct ast_foo
{
int n;
} ast_foo;
// Destructor is automatically called when the message is not referenced anymore.
static void foo_dtor(void *obj)
{
struct foo *obj_foo = obj;
// Free all resources you have reserve here.
}
/**
* #return a ast_foo struct, with destructor setted.
*/
static struct ast_foo* make_me_a_foo(void)
{
struct ast_foo *obj_foo;
obj_foo = ao2_alloc(sizeof(ast_foo), foo_dtor);
// if char* do malloc for them.
if (!obj_foo) {
ast_log(LOG_NOTICE, "make foo failed... 2\n");
return NULL;
}
return obj_foo;
}
There is a full example for send and subscribe a stasis message :
static const char app[] = "StasisTest";
struct stasis_topic *foo_topic;
typedef struct ast_foo
{
int n;
} ast_foo;
// Destructor automatically call when message is not referenced anymore.
static void foo_dtor(void *obj)
{
struct foo *obj_foo = obj;
// Free all resources you have reserve here.
}
/**
* #return a ast_foo struct, with destructor setted.
*/
static struct ast_foo* make_me_a_foo(void)
{
struct ast_foo *obj_foo;
obj_foo = ao2_alloc(sizeof(ast_foo), foo_dtor);
// if char* do malloc for them.
if (!obj_foo) {
ast_log(LOG_NOTICE, "make foo failed... 2\n");
return NULL;
}
return obj_foo;
}
/**
* Send a stasis message, with the long way...
*/
static void publish_foo()
{
ast_log(LOG_NOTICE, "Enter publish message\n");
RAII_VAR(struct stasis_message_type*, foo_type, NULL, ao2_cleanup);
ast_log(LOG_NOTICE, "Create data to send\n");
ast_foo* foo_data = make_me_a_foo();
foo_data->n = 12;
ast_log(LOG_NOTICE, "Create the message to send.\n");
stasis_message_type_create(app, NULL, &foo_type);
if (!foo_type)
{
ast_log(LOG_NOTICE, "Oh no my type is NULL \n");
}
else
{
ast_log(LOG_NOTICE, "Ok foo type \n");
}
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
msg = stasis_message_create(foo_type, foo_data);
if (!msg)
{
ast_log(LOG_NOTICE, "Fail to send message\n");
sleep(1);
return;
}
stasis_publish(foo_topic, msg);
}
static int exec()
{
// First method.
publish_foo();
return 0;
}
static int unload_module(void) {
stasis_app_unregister(app);
ao2_cleanup(foo_topic);
foo_topic = NULL;
return ast_unregister_application(app);
}
void bar_callback(void *data, struct stasis_subscription *sub, struct stasis_message *message)
{
ast_log(LOG_NOTICE, "Test stasis received a message from topic\n");
}
static int load_module(void) {
stasis_init();
// Register.
ast_foo* foo_data2 = make_me_a_foo();
foo_topic = stasis_topic_create("StasisTest");
stasis_subscribe(foo_topic, bar_callback, foo_data2);
return ast_register_application_xml(app, exec);
}
But there is a really more simpler way with the send of json object tought
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/astobj2.h"
#include "asterisk/module.h"
#include "asterisk/stasis.h"
#include "asterisk/json.h"
#include "asterisk/stasis_app.h"
#include "StasisTest.h"
#define AST_MODULE "stasis_test"
static const char app[] = "StasisTest";
static int exec()
{
// Second simpler method.
struct ast_json* inte = ast_json_integer_create(51);
int result = stasis_app_send("StasisTest", inte);
ast_log(LOG_NOTICE, "Stasis send %d\n", result);
return 0;
}
static int unload_module(void)
{
stasis_app_unregister(app);
return ast_unregister_application(app);
}
//Test stasis
void callback_stasis(void* data, const char* app_name, struct ast_json* message)
{
ast_log(LOG_NOTICE, "Receive a stasis message from json\n");
int json_res = ast_json_integer_get(message);
ast_log(LOG_NOTICE, "Integer get : %d\n", json_res);
}
static int load_module(void) {
stasis_init();
// Register for the short way.
stasis_app_register(app, callback_stasis, 0);
return ast_register_application_xml(app, exec);
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, 0, "The wonders of foo", .load = load_module, .unload = unload_module);

Function pointers in FSM

HI.. I want an example of how to implement FSM using function pointers in C.
See this simple example on how to implement a finite state machine in C.
An example is too big to write as an answer here.
Here's an existing example, which I found by Googling for state machine c "function pointer": Implementing Efficient State Machines
Here is a little demo of using function pointers in ARDUINO. This example does not allow for concurrency. It perfectly transferable to normal C if make write the setup and loop inside main()
Each state is a void() function. Each state function is responsible for reading input and setting output. When this is done the function should return immediately. It will be called again directly. The function is also responsible for state-transition by calling the leave function immediately before returning. Each state function should have a static long variable for timekeeping.
A global variable state is set to point to the initial state in the setup routine.
I wanted timekeeping in the different states so i implemented the state transitions by 2 functions:
void enter(long *stateTime), this should be called the very first thing when entering the state functions. It activates the state if inactive end keeps time.
void leave(void (*next)(), long *statetime), this changes the global state pointer and deactivates the current state.
void (*state)();//function pointer for state machine
long prevMillis = 0;//timekeeper
const int LEDPIN = 13;
int counter1 = 0;
void enter(long *statetime){
if(*statetime==-1){//check for passive state
prevMillis = millis();//set timemark when entering state
}//if(statetime==0)
*statetime = millis()-prevMillis;//keep time
}//enter()
void leave(void (*next)(), long *statetime){
*statetime=-1;//set state to passive
state=next;//point to next state
}//leave()
void off500ms(){
static long stateMillis;//timer for this state
enter(&stateMillis);//update timer
digitalWrite(LEDPIN, LOW);
if(stateMillis>499){//check if time is up
leave(on500ms, &stateMillis);
}//if(stateMillis>499)
}//off500ms()
void off2s(){
static long stateMillis;//timer for this state
enter(&stateMillis);//update timer
digitalWrite(LEDPIN, LOW);
if(stateMillis>1999){//check if time is up
leave(on500ms, &stateMillis);
}//if(stateMillis>499)
}//off2s()
void on500ms(){
static long stateMillis;//timer for this state
enter(&stateMillis);//update timer
digitalWrite(LEDPIN, HIGH);
if(stateMillis >499){//time is up
if(++counter1==6){//number of blinks
leave(off2s, &stateMillis);
counter1=0;//reset counter
}else{//if(++counter1==6)
leave(off500ms, &stateMillis);
}//if(++counter1==6)
}//if(stateMills>499)
}//on500ms
void setup(){
pinMode(LEDPIN, OUTPUT);
state = on500ms;//set initial state
}/setup()
void loop(){
state();//start FSM
}//loop
I would say initialize a array of pointers to event handlers. So each element of a array is a function pointer to a particular event which is part of an enum.
if foo is your array of function pointers which is initialized to event then call foo[event]() when any event occurs.
Try coding for calling a function pointer first, next you can move to array and come back to SO if there are more doubts.
For a start you can read about function pointers here.
State transtion code can be utilize either by array or switch case. Written under if else directive.
#include <stdio.h>
#include <stdlib.h>
int entry_state(void);
int foo_state(void);
int bar_state(void);
int exit_state(void);
enum state_codes lookup_transitions(enum state_codes, enum ret_codes);
/* array and enum below must be in sync! */
int (* state[])(void) = { entry_state, foo_state, bar_state, exit_state};
enum state_codes { entry, foo, bar, end};
enum ret_codes { ok, fail, repeat};
struct transition {
enum state_codes src_state;
enum ret_codes ret_code;
enum state_codes dst_state;
};
/* transitions from end state aren't needed */
struct transition state_transitions[] = {
{entry, ok, foo},
{entry, fail, end},
{foo, ok, bar},
{foo, fail, end},
{foo, repeat, foo},
{bar, ok, end},
{bar, fail, end},
{bar, repeat, foo}};
int main(int argc, char *argv[]) {
enum state_codes cur_state = entry;
enum ret_codes rc;
int (* state_fun)(void);
for (;;) {
state_fun = state[cur_state];
rc = state_fun();
if (end == cur_state)
break;
cur_state = lookup_transitions(cur_state, rc);
}
return EXIT_SUCCESS;
}
/*
* lookup_transition() function has time complexity of class O(n).
* We can optimize it.
* */
enum state_codes
lookup_transitions(enum state_codes cur_state, enum ret_codes rc)
{
#if 0
switch (cur_state) {
case entry:
cur_state = ((rc == ok) ? (foo) : (end));
break;
case foo:
cur_state = ((rc == ok) ? (bar) : ((rc == fail) ? (end) : (foo)));
break;
default:
cur_state = ((rc == ok) ? (end) : ((rc == fail) ? (end) : (foo)));
break;
}
return cur_state;
#else
char arr_size = (sizeof(state_transitions) / sizeof(state_transitions[0])); /* This can be shifted to main function to avoid redundant job. */
char count;
for (count = 0; count < arr_size; count++) {
if ((state_transitions[count].src_state == cur_state) && (state_transitions[count].ret_code == rc)) {
return (state_transitions[count].dst_state);
}
}
#endif
}
int entry_state(void)
{
int st;
enum ret_codes rc;
printf("YOU ARE IN ENTRY STATE.\nEnter 0/1: ");
scanf("%d", &st);
rc = ((st == 1) ? (fail) : (ok));
return rc;
}
int foo_state(void)
{
int st;
enum ret_codes rc;
printf("YOU ARE IN FOO STATE.\nEnter 0/1/2: ");
scanf("%d", &st);
rc = ((st == 0) ? (ok) : ((st == 2) ? (repeat) : (fail)));
return rc;
}
int bar_state(void)
{
int st;
enum ret_codes rc;
printf("YOU ARE IN BAR STATE.\nEnter 0/1/2: ");
scanf("%d", &st);
rc = ((st == 0) ? (ok) : ((st == 2) ? (repeat) : (fail)));
return rc;
}
int exit_state(void)
{
printf("YOU ARE IN EXIT STATE.\n");
exit(EXIT_SUCCESS);
}

Resources