# HG changeset patch # User Michiel Broek # Date 1408720362 -7200 # Node ID b7c9673597713d4c0e8dcc09f53194c523df8a60 # Parent e02393b29733e697b61560b4727a6b3f0d4f2d7e Added framework for a simulation of a fridge with heater to use as controlled fermentor diff -r e02393b29733 -r b7c967359771 config.h.in --- a/config.h.in Wed Aug 20 22:15:12 2014 +0200 +++ b/config.h.in Fri Aug 22 17:12:42 2014 +0200 @@ -6,6 +6,9 @@ /* Compile experimental code (may not be present) */ #undef USE_EXPERIMENT +/* Compile simulator code */ +#undef USE_SIMULATOR + /* According to Sun we MUST define this in the source */ #define _REENTRANT 1 diff -r e02393b29733 -r b7c967359771 configure --- a/configure Wed Aug 20 22:15:12 2014 +0200 +++ b/configure Fri Aug 22 17:12:42 2014 +0200 @@ -683,6 +683,7 @@ ac_user_opts=' enable_option_checking enable_experiment +enable_simulator enable_debugging ' ac_precious_vars='build_alias @@ -1302,6 +1303,7 @@ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-experiment Compile experimental code + --enable-simulator Compile simulator code --enable-debugging Compile for debugging Some influential environment variables: @@ -2032,7 +2034,7 @@ PACKAGE="mbsePi-apps" -VERSION="0.2.0" +VERSION="0.2.1" COPYRIGHT="Copyright (C) 2014 Michiel Broek, All Rights Reserved" CYEARS="2014" @@ -3521,6 +3523,18 @@ fi +# Check whether --enable-simulator was given. +if test "${enable_simulator+set}" = set; then : + enableval=$enable_simulator; simulator=$enableval +else + simulator=no +fi + +if test "$simulator" = "yes"; then + $as_echo "#define USE_SIMULATOR 1" >>confdefs.h + +fi + # Check whether --enable-debugging was given. if test "${enable_debugging+set}" = set; then : enableval=$enable_debugging; debugging=$enableval diff -r e02393b29733 -r b7c967359771 configure.ac --- a/configure.ac Wed Aug 20 22:15:12 2014 +0200 +++ b/configure.ac Fri Aug 22 17:12:42 2014 +0200 @@ -8,7 +8,7 @@ dnl General settings dnl After changeing the version number, run autoconf! PACKAGE="mbsePi-apps" -VERSION="0.2.0" +VERSION="0.2.1" COPYRIGHT="Copyright (C) 2014 Michiel Broek, All Rights Reserved" CYEARS="2014" AC_SUBST(PACKAGE) @@ -47,6 +47,11 @@ AC_DEFINE(USE_EXPERIMENT) fi +AC_ARG_ENABLE(simulator, [ --enable-simulator Compile simulator code], [ simulator=$enableval ], [ simulator=no ]) +if test "$simulator" = "yes"; then + AC_DEFINE(USE_SIMULATOR) +fi + AC_ARG_ENABLE(debugging, [ --enable-debugging Compile for debugging], [ debugging=$enableval ], [ debugging=no ]) if test "$debugging" = "yes"; then CFLAGS="-O -g -Wall -Wshadow -Wwrite-strings -Wstrict-prototypes -Winline" diff -r e02393b29733 -r b7c967359771 thermferm/Makefile --- a/thermferm/Makefile Wed Aug 20 22:15:12 2014 +0200 +++ b/thermferm/Makefile Fri Aug 22 17:12:42 2014 +0200 @@ -62,8 +62,9 @@ lock.o: lock.h thermferm.h logger.o: logger.h thermferm.h futil.h xutil.h lcd-pcf8574.o: thermferm.h lcd-pcf8574.h -thermferm.o: lock.h logger.h rdconfig.h devices.h server.h thermferm.h lcd-pcf8574.h lcd-buffer.h panel.h futil.h xutil.h +thermferm.o: lock.h logger.h rdconfig.h devices.h server.h thermferm.h simulator.h lcd-pcf8574.h lcd-buffer.h panel.h futil.h xutil.h xutil.o: thermferm.h xutil.h server.o: rdconfig.h thermferm.h logger.h devices.h server.h lcd-buffer.h xutil.h +simulator.o: thermferm.h simulator.h rdconfig.o: rdconfig.h thermferm.h futil.h xutil.h # End of generated dependencies diff -r e02393b29733 -r b7c967359771 thermferm/devices.c --- a/thermferm/devices.c Wed Aug 20 22:15:12 2014 +0200 +++ b/thermferm/devices.c Fri Aug 22 17:12:42 2014 +0200 @@ -213,6 +213,11 @@ if ((device->type == DEVTYPE_W1) && (device->direction == DEVDIR_OUT_BIN) && (device->present == DEVPRESENT_YES)) { } +#ifdef USE_SIMULATOR + if ((device->type == DEVTYPE_SIM) && (device->direction == DEVDIR_OUT_BIN) && (device->present == DEVPRESENT_YES)) { + + } +#endif } else { return 0; } @@ -393,6 +398,70 @@ } #endif +#ifdef USE_SIMULATOR + found = FALSE; + for (device = Config.devices; device; device = device->next) { + if (device->type == DEVTYPE_SIM) { + found = TRUE; + break; + } + } + + if (found == FALSE) { + subdevices = 5; + for (i = 0; i < subdevices; i++) { + ndev = (devices_list *)malloc(sizeof(devices_list)); + ndev->next = NULL; + ndev->version = 1; + ndev->uuid = malloc(37); + uuid_generate(uu); + uuid_unparse(uu, ndev->uuid); + ndev->type = DEVTYPE_SIM; + ndev->value = ndev->offset = 0; + ndev->present = DEVPRESENT_YES; + ndev->subdevice = i; + ndev->gpiopin = -1; + ndev->comment = xstrcpy((char *)"Auto detected device"); + ndev->timestamp = time(NULL); + ndev->inuse = 0; + switch (i) { + case 0: ndev->direction = DEVDIR_IN_ANALOG; + ndev->address = xstrcpy((char *)"SimRoomTemp"); + ndev->description = xstrcpy((char *)"Simulated room temperature"); + break; + case 1: ndev->direction = DEVDIR_IN_ANALOG; + ndev->address = xstrcpy((char *)"SimAirTemp"); + ndev->description = xstrcpy((char *)"Simulated air temperature"); + break; + case 2: ndev->direction = DEVDIR_IN_ANALOG; + ndev->address = xstrcpy((char *)"SimBeerTemp"); + ndev->description = xstrcpy((char *)"Simulated beer temperature"); + break; + case 3: ndev->direction = DEVDIR_OUT_ANALOG; + ndev->address = xstrcpy((char *)"SimHeater"); + ndev->description = xstrcpy((char *)"Simulated heater"); + break; + case 4: ndev->direction = DEVDIR_OUT_ANALOG; + ndev->address = xstrcpy((char *)"SimCooler"); + ndev->description = xstrcpy((char *)"Simulated cooler"); + break; + } + + if (Config.devices == NULL) { + Config.devices = ndev; + } else { + for (device = Config.devices; device; device = device->next) { + if (device->next == NULL) { + device->next = ndev; + break; + } + } + } + rc++; + } + } +#endif + return rc; } @@ -536,7 +605,26 @@ } break; #endif - +#ifdef USE_SIMULATOR + case DEVTYPE_SIM: +#ifdef HAVE_WIRINGPI_H + piLock(LOCK_DEVICES); +#endif + if (device->subdevice == 0) { + device->value = 20000; + device->timestamp = time(NULL); + } else if (device->subdevice == 1) { + device->value = 20125; + device->timestamp = time(NULL); + } else if (device->subdevice == 2) { + device->value = 20250; + device->timestamp = time(NULL); + } +#ifdef HAVE_WIRINGPI_H + piUnlock(LOCK_DEVICES); +#endif + break; +#endif default: break; } diff -r e02393b29733 -r b7c967359771 thermferm/rdconfig.c --- a/thermferm/rdconfig.c Wed Aug 20 22:15:12 2014 +0200 +++ b/thermferm/rdconfig.c Fri Aug 22 17:12:42 2014 +0200 @@ -34,7 +34,7 @@ const char TEMPSTATE[3][8] = { "OK", "MISSING", "ERROR" }; const char UNITMODE[5][8] = { "OFF", "NONE", "FRIDGE", "BEER", "PROFILE" }; const char PROFSTATE[5][6] = { "OFF", "PAUSE", "RUN", "DONE", "ABORT" }; -const char DEVTYPE[7][6] = { "NA", "W1", "GPIO", "RC433", "DHT", "I2C", "SPI" }; +const char DEVTYPE[8][6] = { "NA", "W1", "GPIO", "RC433", "DHT", "I2C", "SPI", "SIM" }; const char DEVPRESENT[4][6] = { "UNDEF", "NO", "YES", "ERROR" }; const char DEVDIR[7][11] = { "UNDEF", "IN_BIN", "OUT_BIN", "IN_ANALOG", "OUT_ANALOG", "OUT_PWM", "INTERN" }; @@ -46,6 +46,9 @@ profiles_list *tmp3; prof_step *tmp4; devices_list *device; +#ifdef USE_SIMULATOR + simulator_list *simulator; +#endif if (Config.name) free(Config.name); @@ -112,6 +115,17 @@ } Config.devices = NULL; +#ifdef USE_SIMULATOR + for (simulator = Config.simulators; simulator; simulator = simulator->next) { + if (simulator->uuid) + free(simulator->uuid); + if (simulator->name) + free(simulator->name); + free(simulator); + } + Config.simulators = NULL; +#endif + #ifdef HAVE_WIRINGPI_H Config.lcd_cols = 16; Config.lcd_rows = 2; @@ -131,6 +145,9 @@ profiles_list *tmp4; prof_step *tmp5; devices_list *device; +#ifdef USE_SIMULATOR + simulator_list *simulator; +#endif /* * Create a new XML buffer, to which the XML document will be written @@ -585,6 +602,121 @@ } } +#ifdef USE_SIMULATOR + if (Config.simulators) { + if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "SIMULATORS")) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterStartElement"); + return 1; + } + for (simulator = Config.simulators; simulator; simulator = simulator->next) { + if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "SIMULATOR")) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterStartElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "VERSION", "%d", simulator->version)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "UUID", "%s", simulator->uuid)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteFormatElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "NAME", "%s", simulator->name)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteFormatElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "VOLUME_AIR", "%d", simulator->volume_air)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "VOLUME_BEER", "%d", simulator->volume_beer)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "ROOM_TEMPERATURE", "%.1f", simulator->room_temperature)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "AIR_TEMPERATURE", "%.1f", simulator->air_temperature)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_TEMPERATURE", "%.1f", simulator->beer_temperature)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_TEMP", "%.1f", simulator->cooler_temp)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_TIME", "%d", simulator->cooler_time)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_SIZE", "%.3f", simulator->cooler_size)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_TEMP", "%.1f", simulator->heater_temp)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_TIME", "%d", simulator->heater_time)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_SIZE", "%.3f", simulator->heater_size)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_STATE", "%d", simulator->heater_state)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_STATE", "%d", simulator->cooler_state)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "FRIGO_ISOLATION", "%.3f", simulator->frigo_isolation)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_YEAST_HEAT", "%.1f", simulator->s_yeast_heat)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_YEAST_STARTED", "%d", (int)simulator->s_yeast_started)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_COOL_TEMP", "%.1f", simulator->s_cool_temp)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_HEAT_TEMP", "%.1f", simulator->s_heat_temp)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_COOL_CHANGED", "%d", (int)simulator->s_cool_changed)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_HEAT_CHANGED", "%d", (int)simulator->s_heat_changed)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement"); + return 1; + } + if ((rc = xmlTextWriterEndElement(writer)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterEndElement"); + return 1; + } + } + if ((rc = xmlTextWriterEndElement(writer)) < 0) { + syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterEndElement"); + return 1; + } + } +#endif + /* * All done, close any open elements */ @@ -1065,7 +1197,7 @@ } if ((!xmlStrcmp(cur->name, (const xmlChar *)"TYPE"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); - for (i = 0; i < 7; i++) { + for (i = 0; i < 8; i++) { if (! xmlStrcmp(key, (const xmlChar *)DEVTYPE[i])) { device->type = i; break; @@ -1172,6 +1304,198 @@ +#ifdef USE_SIMULATOR +int parseSimulator(xmlDocPtr doc, xmlNodePtr cur) +{ + xmlChar *key; + simulator_list *simulator, *tmp; + int ival; + float fval; + + simulator = (simulator_list *)malloc(sizeof(simulator_list)); + simulator->next = NULL; + simulator->version = 1; + simulator->uuid = simulator->name = NULL; + simulator->volume_air = simulator->volume_beer = 0; + simulator->room_temperature = simulator->air_temperature = simulator->beer_temperature = simulator->s_cool_temp = simulator->s_heat_temp = 20.0; + simulator->cooler_temp = simulator->cooler_size = simulator->heater_temp = simulator->heater_size = simulator->frigo_isolation = 0.0; + simulator->cooler_time = simulator->heater_time = simulator->cooler_state = simulator->heater_state = 0; + simulator->s_yeast_started = simulator->s_cool_changed = simulator->s_heat_changed = (time_t)0; + simulator->s_yeast_heat = simulator->s_cool_temp = simulator->s_heat_temp = 0.0; + + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (xmlStrcmp(key, (const xmlChar *)"1")) { + xmlFree(key); + return 1; + } + simulator->version = 1; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"UUID"))) { + simulator->uuid = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) { + simulator->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"VOLUME_AIR"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->volume_air = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"VOLUME_BEER"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->volume_beer = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"ROOM_TEMPERATURE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &fval) == 1) + simulator->room_temperature = fval; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"AIR_TEMPERATURE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &fval) == 1) + simulator->air_temperature = fval; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEER_TEMPERATURE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &fval) == 1) + simulator->beer_temperature = fval; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_TEMP"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &fval) == 1) + simulator->cooler_temp = fval; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_TIME"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->cooler_time = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_SIZE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &fval) == 1) + simulator->cooler_size = fval; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_TEMP"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &fval) == 1) + simulator->heater_temp = fval; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_TIME"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->heater_time = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_SIZE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &fval) == 1) + simulator->heater_size = fval; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_STATE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->heater_state = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_STATE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->cooler_state = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"FRIGO_ISOLATION"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &fval) == 1) + simulator->frigo_isolation = fval; + xmlFree(key); + } + + if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_YEAST_HEAT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &fval) == 1) + simulator->s_yeast_heat = fval; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_YEAST_STARTED"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->s_yeast_started = (time_t)ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_COOL_TEMP"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &fval) == 1) + simulator->s_cool_temp = fval; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_HEAT_TEMP"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &fval) == 1) + simulator->s_heat_temp = fval; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_COOL_CHANGED"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->s_cool_changed = (time_t)ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_HEAT_CHANGED"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->s_heat_changed = (time_t)ival; + xmlFree(key); + } + + cur = cur->next; + } + + if (Config.simulators == NULL) { + Config.simulators = simulator; + } else { + for (tmp = Config.simulators; tmp; tmp = tmp->next) { + if (tmp->next == NULL) { + tmp->next = simulator; + break; + } + } + } + + return 0; +} + + + +int parseSimulators(xmlDocPtr doc, xmlNodePtr cur) +{ + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"SIMULATOR"))) { + parseSimulator(doc, cur); + } + cur = cur->next; + } + return 0; +} +#endif + + + int rdconfig(void) { int rc = 0, ival; @@ -1269,6 +1593,11 @@ if ((!xmlStrcmp(cur->name, (const xmlChar *)"DEVICES"))) { parseDevices(doc, cur); } +#ifdef USE_SIMULATOR + if ((!xmlStrcmp(cur->name, (const xmlChar *)"SIMULATORS"))) { + parseSimulators(doc, cur); + } +#endif cur = cur->next; } xmlFreeDoc(doc); diff -r e02393b29733 -r b7c967359771 thermferm/server.c --- a/thermferm/server.c Wed Aug 20 22:15:12 2014 +0200 +++ b/thermferm/server.c Fri Aug 22 17:12:42 2014 +0200 @@ -36,7 +36,7 @@ extern sys_config Config; extern const char UNITMODE[5][8]; extern const char TEMPSTATE[3][8]; -extern const char DEVTYPE[7][6]; +extern const char DEVTYPE[8][6]; extern const char DEVPRESENT[4][6]; extern const char DEVDIR[7][11]; extern const char PROFSTATE[5][6]; @@ -241,7 +241,7 @@ srv_send((char *)"%s,%s,%d,%d,%s,%s", device->uuid, device->address, device->subdevice, device->inuse, device->comment, DEVDIR[device->direction]); } srv_send((char *)"."); - return 0; + return 1; } if (param == NULL) { @@ -259,7 +259,7 @@ device->uuid = malloc(37); uuid_generate(uu); uuid_unparse(uu, device->uuid); - for (i = 0; i < 7; i++) { + for (i = 0; i < 8; i++) { if (strcmp(param, DEVTYPE[i]) == 0) { device->type = i; break; @@ -379,7 +379,7 @@ val = strtok(NULL, "\0"); if (kwd && val) { if (strcmp(kwd, (char *)"TYPE") == 0) { - for (i = 0; i < 7; i++) { + for (i = 0; i < 8; i++) { if (strcmp(val, DEVTYPE[i]) == 0) { device->type = i; break; @@ -601,7 +601,7 @@ srv_send((char *)"%s,%s,%s", unit->uuid, unit->name, UNITMODE[unit->mode]); } srv_send((char *)"."); - return 0; + return 1; } else if (strcmp(opt, (char *)"LOG") == 0) { @@ -642,7 +642,7 @@ free(filename); filename = NULL; srv_send((char *)"."); - return 0; + return 1; } srv_send((char *)"504 Subcommand error"); @@ -905,6 +905,282 @@ +#ifdef USE_SIMULATOR +int delete_Simulator(char *uuid) +{ + simulator_list *current = Config.simulators; + simulator_list *previous = NULL; + + while (current) { + if (strcmp(current->uuid, uuid) == 0) { + if (previous == NULL) { + Config.simulators = current->next; + free(current->uuid); + current->uuid = NULL; + free(current->name); + current->name = NULL; + free(current); + return 1; + } else { + free(current->uuid); + current->uuid = NULL; + free(current->name); + current->name = NULL; + previous->next = current->next; + free(current); + current = previous->next; + return 1; + } + } else { + previous = current; + current = current->next; + } + } + return 0; +} + + + +/* + * SIMULATOR ADD name + * SIMULATOR DEL uuid + * SIMULATOR LIST + * SIMULATOR GET uuid + * SIMULATOR PUT uuid + */ +int cmd_simulator(char *buf) +{ + char *opt, *param, *kwd, *val, ibuf[SS_BUFSIZE]; + simulator_list *simulator, *tmps; + socklen_t fromlen; + int i, rc, rlen, ival; + float fval; + uuid_t uu; + + opt = strtok(buf, " \0"); + opt = strtok(NULL, " \0"); + + if (opt == NULL) { + srv_send((char *)"501 Subcommand missing"); + return 1; + } + param = strtok(NULL, "\0"); + + if (strcmp(opt, (char *)"LIST") == 0) { + srv_send((char *)"212 Simulators list follows:"); + for (simulator = Config.simulators; simulator; simulator = simulator->next) { + srv_send((char *)"%s,%s", simulator->uuid, simulator->name); + } + srv_send((char *)"."); + return 1; + } + + if (param == NULL) { + srv_send((char *)"502 Parameter missing"); + return 1; + } + + if (strcmp(opt, (char *)"ADD") == 0) { + + /* + * For now, only one simulator is allowed. + */ + if (Config.simulators) { + srv_send((char *)"441 Maximum simulators reached"); + return 1; + } + + simulator = (simulator_list *)malloc(sizeof(simulator_list)); + simulator->next = NULL; + simulator->version = 1; + simulator->uuid = malloc(37); + uuid_generate(uu); + uuid_unparse(uu, simulator->uuid); + simulator->name = xstrcpy(param); + simulator->volume_air = 150; + simulator->volume_beer = 50; + simulator->room_temperature = simulator->air_temperature = simulator->beer_temperature = simulator->s_cool_temp = simulator->s_heat_temp = 20.0; + simulator->cooler_temp = -3.0; /* Cooling temperature */ + simulator->cooler_time = 720; /* About 12 minutes for the cooler plate */ + simulator->cooler_size = 0.8; /* 0.8 square meter cooler plate */ + simulator->heater_temp = 150.0; /* Heating temperature */ + simulator->heater_time = 3; /* 3 seconds to heat-up */ + simulator->heater_size = 0.01; /* 0.01 square meter heater plate */ + simulator->heater_state = simulator->cooler_state = 0; + simulator->frigo_isolation = 0.002; + simulator->s_yeast_heat = 0.0; + simulator->s_yeast_started = simulator->s_cool_changed = simulator->s_heat_changed = (int)0; + + if (Config.simulators == NULL) { + Config.simulators = simulator; + } else { + for (tmps = Config.simulators; tmps; tmps = tmps->next) { + if (tmps->next == NULL) { + tmps->next = simulator; + break; + } + } + } + + syslog(LOG_NOTICE, "Simulator %s added", simulator->uuid); + srv_send((char *)"211 Simulator %s added", simulator->uuid); + return 0; + } + + if (strcmp(opt, (char *)"DEL") == 0) { + rc = delete_Simulator(param); + if (rc) { + syslog(LOG_NOTICE, "Simulator %s deleted", param); + srv_send((char *)"211 Simulator %s deleted", param); + return 0; + } else { + srv_send((char *)"440 No such simulator"); + return 1; + } + } + + if (strcmp(opt, (char *)"GET") == 0) { + for (simulator = Config.simulators; simulator; simulator = simulator->next) { + if (strcmp(simulator->uuid, param) == 0) { + srv_send((char *)"213 Simulator record follows:"); + srv_send((char *)"NAME,%s", simulator->name); + srv_send((char *)"VOLUME_AIR,%d", simulator->volume_air); + srv_send((char *)"VOLUME_BEER,%d", simulator->volume_beer); + srv_send((char *)"ROOM_TEMPERATURE,%.1f", simulator->room_temperature); + srv_send((char *)"AIR_TEMPERATURE,%.3f", simulator->air_temperature); + srv_send((char *)"BEER_TEMPERATURE,%.3f", simulator->beer_temperature); + srv_send((char *)"COOLER_TEMP,%.1f", simulator->cooler_temp); + srv_send((char *)"COOLER_TIME,%d", simulator->cooler_time); + srv_send((char *)"COOLER_SIZE,%.3d", simulator->cooler_size); + srv_send((char *)"HEATER_TEMP,%.1f", simulator->heater_temp); + srv_send((char *)"HEATER_TIME,%d", simulator->heater_time); + srv_send((char *)"HEATER_SIZE,%.3f", simulator->heater_size); + srv_send((char *)"HEATER_STATE,%d", simulator->heater_state); + srv_send((char *)"COOLER_STATE,%d", simulator->cooler_state); + srv_send((char *)"FRIGO_ISOLATION,%.6f", simulator->frigo_isolation); + srv_send((char *)"."); + return 1; + } + } + srv_send((char *)"440 No such simulator"); + return 1; + } + + if (strcmp(opt, (char *)"PUT") == 0) { + for (simulator = Config.simulators; simulator; simulator = simulator->next) { + if (strcmp(simulator->uuid, param) == 0) { + while (1) { + memset((char *)&ibuf, 0, SS_BUFSIZE); + fromlen = sizeof(peeraddr_in); + rlen = recvfrom(s, ibuf, sizeof(ibuf) -1, 0, (struct sockaddr *)&peeraddr_in, &fromlen); + if (rlen == -1) { + syslog(LOG_WARNING, "recvfrom(): %s", strerror(errno)); + srv_send((char *)"518 recfrom(): %s", strerror(errno)); + return 1; + } + for (i = 0; i < strlen(ibuf); i++) { + if (ibuf[i] == '\n') + ibuf[i] = '\0'; + if (ibuf[i] == '\r') + ibuf[i] = '\0'; + } + for (i = strlen(ibuf) -1; i > 0; i--) { + if (ibuf[i] == ' ') + ibuf[i] = '\0'; + else + break; + } + if (strlen(ibuf)) { + if (debug) { + syslog(LOG_NOTICE, "recv: \"%s\"", ibuf); + fprintf(stdout, "recv: \"%s\"\n", ibuf); + } + if (strcmp(ibuf, (char *)".") == 0) { + srv_send((char *)"219 Accepted Simulator record"); + return 0; + } + kwd = strtok(ibuf, ",\0"); + val = strtok(NULL, "\0"); + if (kwd && val) { + + if (strcmp(kwd, (char *)"NAME") == 0) { + if (simulator->name) + free(simulator->name); + simulator->name = xstrcpy(val); + + } else if (strcmp(kwd, (char *)"VOLUME_AIR") == 0) { + if (sscanf(val, "%d", &ival) == 1) + simulator->volume_air = ival; + + } else if (strcmp(kwd, (char *)"VOLUME_BEER") == 0) { + if (sscanf(val, "%d", &ival) == 1) + simulator->volume_beer = ival; + + } else if (strcmp(kwd, (char *)"ROOM_TEMPERATURE") == 0) { + if (sscanf(val, "%f", &fval) == 1) + simulator->room_temperature = fval; + + } else if (strcmp(kwd, (char *)"AIR_TEMPERATURE") == 0) { + if (sscanf(val, "%f", &fval) == 1) + simulator->air_temperature = fval; + + } else if (strcmp(kwd, (char *)"BEER_TEMPERATURE") == 0) { + if (sscanf(val, "%f", &fval) == 1) + simulator->beer_temperature = fval; + + } else if (strcmp(kwd, (char *)"COOLER_TEMP") == 0) { + if (sscanf(val, "%f", &fval) == 1) + simulator->cooler_temp = fval; + + } else if (strcmp(kwd, (char *)"COOLER_TIME") == 0) { + if (sscanf(val, "%d", &ival) == 1) + simulator->cooler_time = ival; + + } else if (strcmp(kwd, (char *)"COOLER_SIZE") == 0) { + if (sscanf(val, "%f", &fval) == 1) + simulator->cooler_size = fval; + + } else if (strcmp(kwd, (char *)"HEATER_TEMP") == 0) { + if (sscanf(val, "%f", &fval) == 1) + simulator->heater_temp = fval; + + } else if (strcmp(kwd, (char *)"HEATER_TIME") == 0) { + if (sscanf(val, "%d", &ival) == 1) + simulator->heater_time = ival; + + } else if (strcmp(kwd, (char *)"HEATER_SIZE") == 0) { + if (sscanf(val, "%f", &fval) == 1) + simulator->heater_size = fval; + + } else if (strcmp(kwd, (char *)"HEATER_STATE") == 0) { + if (sscanf(val, "%d", &ival) == 1) + simulator->heater_state = ival; + + } else if (strcmp(kwd, (char *)"COOLER_STATE") == 0) { + if (sscanf(val, "%d", &ival) == 1) + simulator->cooler_state = ival; + + } else if (strcmp(kwd, (char *)"FRIGO_ISOLATION") == 0) { + if (sscanf(val, "%f", &fval) == 1) + simulator->frigo_isolation = fval; + + } + } + } + } + } + } + srv_send((char *)"440 No such simulator"); + return 1; + } + + srv_send((char *)"504 Subcommand error"); + return 1; +} +#endif + + + int delete_Unit(char *uuid) { units_list *current = Config.units; @@ -1381,7 +1657,6 @@ return 1; } - srv_send((char *)"504 Subcommand error"); return 1; } @@ -1442,14 +1717,21 @@ srv_send((char *)"LIST List all fermenter units"); srv_send((char *)"LIST LOG uuid List logfile data in 1 hour lines"); srv_send((char *)"PROFILE uuid,name Profile rename"); - srv_send((char *)"PROFILE ADD name Add new profile with name"); - srv_send((char *)"PROFILE DEL uuid Delete profile by uuid"); + srv_send((char *)"PROFILE ADD name Add new Profile with name"); + srv_send((char *)"PROFILE DEL uuid Delete Profile by uuid"); srv_send((char *)"PROFILE LIST List available profiles"); srv_send((char *)"PROFILE GET uuid Get Profile record by uuid"); srv_send((char *)"PROFILE PUT uuid Put Profile record by uuid"); srv_send((char *)"PROFILE GETS uuid Profile get steps list"); srv_send((char *)"PROFILE PUTS uuid Profile put steps list"); - srv_send((char *)"UNIT ADD name Add a new unit with name"); +#ifdef USE_SIMULATOR + srv_send((char *)"SIMULATOR ADD name Add a new Simulator with name"); + srv_send((char *)"SIMULATOR DEL uuid Delete Simulator by uuid"); + srv_send((char *)"SIMULATOR LIST List all Simulators"); + srv_send((char *)"SIMULATOR GET uuid Get Simulator record by uuid"); + srv_send((char *)"SIMULATOR PUT uuid Put Simulator record by uuid"); +#endif + srv_send((char *)"UNIT ADD name Add a new Unit with name"); srv_send((char *)"UNIT DEL uuid Delete Unit by uuid"); srv_send((char *)"UNIT LIST List all Units"); srv_send((char *)"UNIT GET uuid Get Unit record by uuid"); @@ -1460,6 +1742,11 @@ } else if (strncmp(buf, "PROFILE", 7) == 0) { if (cmd_profile(buf) == 0) wrconfig(); +#ifdef USE_SIMULATOR + } else if (strncmp(buf, "SIMULATOR", 9) == 0) { + if (cmd_simulator(buf) == 0) + wrconfig(); +#endif } else if (strncmp(buf, "UNIT", 4) == 0) { if (cmd_unit(buf) == 0) wrconfig(); diff -r e02393b29733 -r b7c967359771 thermferm/simulator.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thermferm/simulator.c Fri Aug 22 17:12:42 2014 +0200 @@ -0,0 +1,59 @@ +/***************************************************************************** + * Copyright (C) 2008-2014 + * + * Michiel Broek + * + * This file is part of the mbsePi-apps + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * mbsePi-apps is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ThermFerm; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + *****************************************************************************/ + +#include "thermferm.h" +#include "simulator.h" + +#ifdef USE_SIMULATOR + +extern int my_shutdown; +extern int debug; + + + +#ifdef HAVE_WIRINGPI_H +PI_THREAD (my_simulator_loop) +#else +void *my_simulator_loop(void *threadid) +#endif +{ + + syslog(LOG_NOTICE, "Thread my_simulator_loop started"); + if (debug) + fprintf(stdout, "Thread my_simulator_loop started\n"); + + for (;;) { + + if (my_shutdown) + break; + + usleep(100000); + } + + syslog(LOG_NOTICE, "Thread my_simulator_loop stopped"); + if (debug) + fprintf(stdout, "Thread my_simulator_loop stopped\n"); + return 0; +} + + +#endif diff -r e02393b29733 -r b7c967359771 thermferm/simulator.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thermferm/simulator.h Fri Aug 22 17:12:42 2014 +0200 @@ -0,0 +1,15 @@ +#ifndef SIMULATOR_H +#define SIMULATOR_H + +#ifdef USE_SIMULATOR + + +#ifdef HAVE_WIRINGPI_H +PI_THREAD (my_simulator_loop); +#else +void *my_simulator_loop(void *); +#endif + +#endif + +#endif diff -r e02393b29733 -r b7c967359771 thermferm/thermferm.c --- a/thermferm/thermferm.c Wed Aug 20 22:15:12 2014 +0200 +++ b/thermferm/thermferm.c Fri Aug 22 17:12:42 2014 +0200 @@ -26,6 +26,7 @@ #include "devices.h" #include "server.h" #include "thermferm.h" +#include "simulator.h" #include "lcd-pcf8574.h" #include "lcd-buffer.h" #include "panel.h" @@ -49,7 +50,7 @@ #endif #ifndef HAVE_WIRINGPI_H -pthread_t threads[4]; +pthread_t threads[5]; #endif extern const char UNITMODE[5][8]; extern const char PROFSTATE[4][6]; @@ -889,6 +890,23 @@ } #endif + +#ifdef USE_SIMULATOR +#ifdef HAVE_WIRINGPI_H + rc = piThreadCreate(my_simulator_loop); +#else + rc = pthread_create(&threads[t], NULL, my_simulator_loop, (void *)t ); +#endif + if (rc) { + fprintf(stderr, "my_simulator_loop thread didn't start rc=%d\n", rc); + syslog(LOG_WARNING, "my_simulator_loop thread didn't start rc=%d", rc); +#ifndef HAVE_WIRINGPI_H + } else { + t++; +#endif + } +#endif + /* * Initialize units for processing */ diff -r e02393b29733 -r b7c967359771 thermferm/thermferm.h --- a/thermferm/thermferm.h Wed Aug 20 22:15:12 2014 +0200 +++ b/thermferm/thermferm.h Fri Aug 22 17:12:42 2014 +0200 @@ -201,6 +201,9 @@ #define DEVTYPE_DHT 4 /* DHT type device on GPIO */ #define DEVTYPE_I2C 5 /* I2C bus device */ #define DEVTYPE_SPI 6 /* SPI bus device */ +#ifdef USE_SIMULATOR +#define DEVTYPE_SIM 7 /* Simulated device */ +#endif #define DEVPRESENT_UNDEF 0 /* Precence not testable */ #define DEVPRESENT_NO 1 /* Device is missing */ @@ -215,6 +218,46 @@ #define DEVDIR_OUT_PWM 5 /* PWM outout */ #define DEVDIR_INTERN 6 /* Internal function */ +#ifdef USE_SIMULATOR + +/* + * The frigo is a simulation of a fridge with a heating device. + * It has a volume air, a volume of your beer. There is a simulated + * thermal sensor that measures the air and one that measures the beer. + * It looks like a normal live setup. + */ +typedef struct _simulator { + struct _simulator *next; + int version; /* Version of this record */ + char *uuid; /* Simulator uuid */ + char *name; /* Simulator name */ + int volume_air; /* Volume air of the frigo */ + int volume_beer; /* Volume beer inside frigo */ + float room_temperature; /* Temp outside frigo */ + float air_temperature; /* Simulated air temperature */ + float beer_temperature; /* Simulated beer temperature */ + float cooler_temp; /* Lowest cooler temperature */ + int cooler_time; /* Time to reach temperature */ + float cooler_size; /* Size of cooler in square mtr */ + float heater_temp; /* Highest heater temperature */ + int heater_time; /* Time to reach temperature */ + float heater_size; /* Size of heater in square mtr */ + int heater_state; /* Heater status */ + int cooler_state; /* Cooler status */ + float frigo_isolation; /* Frigo isolation value */ + /* + * Status values, maintained by the simulator but stored + * here so they don't get lost over program restarts. + */ + float s_yeast_heat; /* Heat generated by yeast */ + time_t s_yeast_started; /* Start date/time fermentation */ + float s_cool_temp; /* Temp cooler */ + float s_heat_temp; /* Temp heater */ + time_t s_cool_changed; /* Start date/time cooler */ + time_t s_heat_changed; /* Start date/time heater */ +} simulator_list; + +#endif typedef struct _sys_config { char *name; /* Configuration name */ @@ -234,6 +277,9 @@ units_list *units; /* Fermenter units */ profiles_list *profiles; /* Ferment profiles */ devices_list *devices; /* Sensors and switches */ +#ifdef USE_SIMULATOR + simulator_list *simulators; /* Simulators */ +#endif } sys_config;