From: John Hodge Date: Sun, 20 Feb 2011 07:12:38 +0000 (+0800) Subject: Added periodic thread (reloads item config, checks coke slots) X-Git-Url: https://git.ucc.asn.au/?p=tpg%2Fopendispense2.git;a=commitdiff_plain;h=ec400f11ebc2e81079e464145915fb2d25602fbc Added periodic thread (reloads item config, checks coke slots) --- diff --git a/items.cfg b/items.cfg index df3305a..eb3e8e6 100644 --- a/items.cfg +++ b/items.cfg @@ -19,7 +19,7 @@ pseudo 1 30 manual phone # Ring Ring! pseudo 2 128 clue # clue.flac - Don't Ask pseudo 3 3500 polo postorder # Polo Shirt! (With UCC Sun Logo) pseudo 4 2500 membership # here comes the money! - +pseudo 5 1 testy mc test -door 0 0 Open Door # Open Sesame # Snack machine diff --git a/src/server/common.h b/src/server/common.h index 3c117d6..34361b9 100644 --- a/src/server/common.h +++ b/src/server/common.h @@ -73,6 +73,7 @@ extern int giDebugLevel; // === FUNCTIONS === // --- Helpers -- +extern void AddPeriodicFunction(void (*Fcn)(void)); extern void CompileRegex(regex_t *Regex, const char *Pattern, int Flags); extern int RunRegex(regex_t *regex, const char *string, int nMatches, regmatch_t *matches, const char *errorMessage); extern int InitSerial(const char *Path, int BaudRate); diff --git a/src/server/handler_coke.c b/src/server/handler_coke.c index 188beb4..09bcc8e 100644 --- a/src/server/handler_coke.c +++ b/src/server/handler_coke.c @@ -18,6 +18,7 @@ #include #include #include +#include #define READ_TIMEOUT 2 // 2 seconds for ReadChar #define TRACE_COKE 1 @@ -26,6 +27,8 @@ // === PROTOTYPES === int Coke_InitHandler(); + int Coke_int_GetSlotStatus(char *Buffer, int Slot); +void Coke_int_UpdateSlotStatuses(void); int Coke_CanDispense(int User, int Item); int Coke_DoDispense(int User, int Item); int Writef(const char *Format, ...); @@ -42,6 +45,8 @@ tHandler gCoke_Handler = { char *gsCoke_SerialPort = "/dev/ttyS0"; int giCoke_SerialFD; regex_t gCoke_StatusRegex; + int gaCoke_CachedStatus[7]; +pthread_mutex_t gCoke_Mutex = PTHREAD_MUTEX_INITIALIZER; // == CODE === int Coke_InitHandler() @@ -70,15 +75,74 @@ int Coke_InitHandler() Writef("n5 Slot5\n"); WaitForColon(); Writef("n6 Coke\n"); + + Coke_int_UpdateSlotStatuses(); } } + AddPeriodicFunction(Coke_int_UpdateSlotStatuses); + CompileRegex(&gCoke_StatusRegex, "^slot\\s+([0-9]+)\\s+([^:]+):([a-zA-Z]+)\\s*", REG_EXTENDED); return 0; } +int Coke_int_GetSlotStatus(char *Buffer, int Slot) +{ + regmatch_t matches[4]; + int ret; + char *status; + + // Parse status response + ret = RunRegex(&gCoke_StatusRegex, Buffer, sizeof(matches)/sizeof(matches[0]), matches, "Bad Response"); + if( ret ) { + return -1; + } + + // Get slot status + Buffer[ matches[3].rm_eo ] = '\0'; + status = &Buffer[ matches[3].rm_so ]; + + #if TRACE_COKE + printf("Coke_CanDispense: Machine responded slot status '%s'\n", status); + #endif + + if( strcmp(status, "full") == 0 ) { + gaCoke_CachedStatus[Slot] = 0; // 0: Avaliiable + return 0; + } + else { + gaCoke_CachedStatus[Slot] = 1; // 1: Empty + return 1; + } +} + +void Coke_int_UpdateSlotStatuses(void) +{ + int i; + int len; + char tmp[40]; + + pthread_mutex_lock(&gCoke_Mutex); + + if( WaitForColon() ) return ; + Writef("d7\r\n"); // Update slot statuses + WaitForColon(); + Writef("s\n"); + ReadLine(sizeof tmp, tmp); // Read back what we just said + + for( i = 0; i <= 6; i ++ ) + { + len = ReadLine(sizeof tmp, tmp); + if( len == -1 ) return ; // I give up :( + Coke_int_GetSlotStatus(tmp, i); + } + pthread_mutex_unlock(&gCoke_Mutex); +} + int Coke_CanDispense(int UNUSED(User), int Item) { + // Disabled in favor of caching + #if 0 char tmp[40], *status; regmatch_t matches[4]; int ret; @@ -144,6 +208,7 @@ int Coke_CanDispense(int UNUSED(User), int Item) while( tmp[0] == ':' || tmp[1] != 'l' ) { ret = ReadLine(sizeof(tmp)-1, tmp); + if( ret == -1 ) return -1; printf("ret = %i, tmp = '%s'\n", ret, tmp); } @@ -163,24 +228,17 @@ int Coke_CanDispense(int UNUSED(User), int Item) // Eat rest of response WaitForColon(); - // Parse status response - ret = RunRegex(&gCoke_StatusRegex, tmp, sizeof(matches)/sizeof(matches[0]), matches, "Bad Response"); - if( ret ) { - return -1; - } - - // Get slot status - tmp[ matches[3].rm_eo ] = '\0'; - status = &tmp[ matches[3].rm_so ]; + return Coke_GetSlotStatus(tmp, Item); + #else + // Sanity please + if( Item < 0 || Item > 6 ) return -1; // -EYOURBAD - #if TRACE_COKE - printf("Coke_CanDispense: Machine responded slot status '%s'\n", status); + // Can't dispense if the machine is not connected + if( giCoke_SerialFD == -1 ) + return -2; + + return gaCoke_CachedStatus[Item]; #endif - - if( strcmp(status, "full") == 0 ) - return 0; - - return 1; } /** @@ -189,7 +247,7 @@ int Coke_CanDispense(int UNUSED(User), int Item) int Coke_DoDispense(int UNUSED(User), int Item) { char tmp[32]; - int ret; + int ret, len; // Sanity please if( Item < 0 || Item > 6 ) return -1; @@ -198,6 +256,9 @@ int Coke_DoDispense(int UNUSED(User), int Item) if( giCoke_SerialFD == -1 ) return -2; + // LOCK + pthread_mutex_lock(&gCoke_Mutex); + #if TRACE_COKE printf("Coke_DoDispense: flushing input\n"); #endif @@ -224,7 +285,10 @@ int Coke_DoDispense(int UNUSED(User), int Item) // Read empty lines and echo-backs do { ret = ReadLine(sizeof(tmp)-1, tmp); - if( ret == -1 ) return -1; + if( ret == -1 ) { + pthread_mutex_unlock(&gCoke_Mutex); + return -1; + } #if TRACE_COKE printf("Coke_DoDispense: read %i '%s'\n", ret, tmp); #endif @@ -241,12 +305,23 @@ int Coke_DoDispense(int UNUSED(User), int Item) // We think dispense worked // - The machine returns 'ok' if an empty slot is dispensed, even if // it doesn't actually try to dispense (no sound) - return 0; + ret = 0; } - - printf("Machine returned unknown value '%s'\n", tmp); - - return -1; + else { + printf("Machine returned unknown value '%s'\n", tmp); + ret = -1; + } + + // Update status + Writef("s%i\r\n", Item); + len = ReadLine(sizeof tmp, tmp); + if(len == -1) gaCoke_CachedStatus[Item] = -1; + Coke_int_GetSlotStatus(tmp, Item); + + + pthread_mutex_unlock(&gCoke_Mutex); + + return ret; } char ReadChar() diff --git a/src/server/itemdb.c b/src/server/itemdb.c index e63bb65..e48ab86 100644 --- a/src/server/itemdb.c +++ b/src/server/itemdb.c @@ -13,10 +13,8 @@ #include #include "common.h" #include -#include -#include -#include -#include +#include +#include // === IMPORTS === extern tHandler gCoke_Handler; @@ -25,13 +23,14 @@ extern tHandler gDoor_Handler; // === PROTOTYPES === void Init_Handlers(void); -void ItemList_Changed(int signum); void Load_Itemlist(void); +void Items_ReadFromFile(void); char *trim(char *__str); // === GLOBALS === int giNumItems = 0; tItem *gaItems = NULL; +time_t gItems_LastUpdated; tHandler gPseudo_Handler = {Name:"pseudo"}; tHandler *gaHandlers[] = {&gPseudo_Handler, &gCoke_Handler, &gSnack_Handler, &gDoor_Handler}; int giNumHandlers = sizeof(gaHandlers)/sizeof(gaHandlers[0]); @@ -39,6 +38,7 @@ char *gsItemListFile = DEFAULT_ITEM_FILE; #if USE_INOTIFY int giItem_INotifyFD; #endif +regex_t gItemFile_Regex; // === CODE === void Init_Handlers() @@ -81,30 +81,55 @@ void ItemList_Changed(int signum) #endif /** - * \brief Read the item list from disk + * \brief Read the initiali item list */ void Load_Itemlist(void) { - FILE *fp = fopen(gsItemListFile, "r"); + int rv; + rv = regcomp(&gItemFile_Regex, "^-?([a-zA-Z][a-zA-Z]*)\\s+([0-9]+)\\s+([0-9]+)\\s+(.*)", REG_EXTENDED); + if( rv ) + { + size_t len = regerror(rv, &gItemFile_Regex, NULL, 0); + char errorStr[len]; + regerror(rv, &gItemFile_Regex, errorStr, len); + fprintf(stderr, "Rexex compilation failed - %s\n", errorStr); + exit(-1); + } + + Items_ReadFromFile(); + + // Re-read the item file periodically + // TODO: Be less lazy here and check the timestamp + AddPeriodicFunction( Items_ReadFromFile ); +} +/** + * \brief Read the item list from disk + */ +void Items_ReadFromFile(void) +{ + FILE *fp; char buffer[BUFSIZ]; char *line; int lineNum = 0; - int i; - regex_t regex; + int i, numItems = 0; + tItem *items = NULL; regmatch_t matches[5]; - - i = regcomp(®ex, "^-?([a-zA-Z][a-zA-Z]*)\\s+([0-9]+)\\s+([0-9]+)\\s+(.*)", REG_EXTENDED); - if( i ) + + if( gItems_LastUpdated ) { - size_t len = regerror(i, ®ex, NULL, 0); - char *errorStr = malloc(len); - regerror(i, ®ex, errorStr, len); - fprintf(stderr, "Rexex compilation failed - %s\n", errorStr); - free(errorStr); - exit(-1); + struct stat buf; + if( stat(gsItemListFile, &buf) ) { + fprintf(stderr, "Unable to stat() item file '%s'\n", gsItemListFile); + return ; + } + + // Check if the update is needed + if( gItems_LastUpdated > buf.st_mtime ) + return ; } // Error check + fp = fopen(gsItemListFile, "r"); if(!fp) { fprintf(stderr, "Unable to open item file '%s'\n", gsItemListFile); perror("Unable to open item file"); @@ -131,13 +156,9 @@ void Load_Itemlist(void) if(strlen(line) == 0) continue; // Pass regex over line - if( (i = regexec(®ex, line, 5, matches, 0)) ) { - size_t len = regerror(i, ®ex, NULL, 0); - char *errorStr = malloc(len); - regerror(i, ®ex, errorStr, len); - fprintf(stderr, "Syntax error on line %i of item file '%s'\n%s", lineNum, gsItemListFile, errorStr); - free(errorStr); - exit(-1); + if( RunRegex( &gItemFile_Regex, line, 5, matches, NULL) ) { + fprintf(stderr, "Syntax error on line %i of item file '%s'\n", lineNum, gsItemListFile); + return ; } // Read line data @@ -162,27 +183,41 @@ void Load_Itemlist(void) continue ; } - for( i = 0; i < giNumItems; i ++ ) + for( i = 0; i < numItems; i ++ ) { - if( gaItems[i].Handler != handler ) continue; - if( gaItems[i].ID != num ) continue; + if( items[i].Handler != handler ) continue; + if( items[i].ID != num ) continue; printf("Redefinition of %s:%i, updated\n", handler->Name, num); - gaItems[i].Price = price; - free(gaItems[i].Name); - gaItems[i].Name = strdup(desc); + items[i].Price = price; + free(items[i].Name); + items[i].Name = strdup(desc); break; } - if( i < giNumItems ) continue; - - gaItems = realloc( gaItems, (giNumItems + 1)*sizeof(gaItems[0]) ); - gaItems[giNumItems].Handler = handler; - gaItems[giNumItems].ID = num; - gaItems[giNumItems].Price = price; - gaItems[giNumItems].Name = strdup(desc); - gaItems[giNumItems].bHidden = (line[0] == '-'); - giNumItems ++; - } + if( i < numItems ) continue; + + items = realloc( items, (numItems + 1)*sizeof(items[0]) ); + items[numItems].Handler = handler; + items[numItems].ID = num; + items[numItems].Price = price; + items[numItems].Name = strdup(desc); + items[numItems].bHidden = (line[0] == '-'); + numItems ++; + } + + // Clean up old + if( giNumItems ) + { + giNumItems = 0; + free(gaItems); + gaItems = NULL; + } + + // Replace with new + giNumItems = numItems; + gaItems = items; + + gItems_LastUpdated = time(NULL); } char *trim(char *__str) diff --git a/src/server/main.c b/src/server/main.c index 6105936..5ca4723 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "../cokebank.h" // === IMPORTS === @@ -30,9 +31,17 @@ extern char *gsCoke_SerialPort; extern char *gsSnack_SerialPort; extern char *gsDoor_Password; +// === PROTOTYPES === +void *Periodic_Thread(void *Unused); + // === GLOBALS === int giDebugLevel = 0; char *gsCokebankPath = "cokebank.db"; +// - Functions called every 20s (or so) +#define ciMaxPeriodics 10 +struct sPeriodicCall { + void (*Function)(void); +} gaPeriodicCalls[ciMaxPeriodics]; // === CODE === void sigint_handler() @@ -43,6 +52,7 @@ void sigint_handler() int main(int argc, char *argv[]) { int i; + pthread_t timer_thread; // Parse Arguments for( i = 1; i < argc; i++ ) @@ -102,12 +112,46 @@ int main(int argc, char *argv[]) Load_Itemlist(); + pthread_create( &timer_thread, NULL, Periodic_Thread, NULL ); + Server_Start(); + pthread_kill(timer_thread, SIGKILL); return 0; } +void *Periodic_Thread(void *Unused) +{ + int i; + Unused = NULL; // quiet, gcc + + for( ;; ) + { + sleep(10); // Sleep for a while + printf("Periodic firing\n"); + for( i = 0; i < ciMaxPeriodics; i ++ ) + { + if( gaPeriodicCalls[i].Function ) + gaPeriodicCalls[i].Function(); + } + } + return NULL; +} + +void AddPeriodicFunction(void (*Fcn)(void)) +{ + int i; + for( i = 0; i < ciMaxPeriodics; i ++ ) + { + if( gaPeriodicCalls[i].Function ) continue; + gaPeriodicCalls[i].Function = Fcn; + return ; + } + + fprintf(stderr, "Error: No space for %p in periodic list\n", Fcn); +} + int RunRegex(regex_t *regex, const char *string, int nMatches, regmatch_t *matches, const char *errorMessage) { int ret; diff --git a/src/server/server.c b/src/server/server.c index a536121..f0d7d46 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -483,7 +483,7 @@ void Server_int_SendItem(tClient *Client, tItem *Item) if( Item->Handler->CanDispense ) { - switch(Item->Handler->CanDispense(Item->ID, Client->UID)) + switch(Item->Handler->CanDispense(Client->UID, Item->ID)) { case 0: status = "avail"; break; case 1: status = "sold"; break;