# HG changeset patch # User Michiel Broek # Date 1714490801 -7200 # Node ID 24749c296a501698e9071874525c8a1f2883e5b8 # Parent ea24b4ce02b1242964e8f3e33b9c347302557d1c Version 0.9.19b2. Simulator redesign and it is now possible to run more then one simulator. All simulated devices have address names that include the simulator number. Added the setup screen for the most part. Not compatible with previous versions if a simulator was used, delete all simulators and simulated devices during stop and start. diff -r ea24b4ce02b1 -r 24749c296a50 configure --- a/configure Sun Apr 28 15:50:42 2024 +0200 +++ b/configure Tue Apr 30 17:26:41 2024 +0200 @@ -2037,7 +2037,7 @@ PACKAGE="mbsePi-apps" -VERSION="0.9.19b1" +VERSION="0.9.19b2" COPYRIGHT="Copyright (C) 2014-2024 Michiel Broek, All Rights Reserved" CYEARS="2014-2024" diff -r ea24b4ce02b1 -r 24749c296a50 configure.ac --- a/configure.ac Sun Apr 28 15:50:42 2024 +0200 +++ b/configure.ac Tue Apr 30 17:26:41 2024 +0200 @@ -8,7 +8,7 @@ dnl General settings dnl After changeing the version number, run autoconf! PACKAGE="mbsePi-apps" -VERSION="0.9.19b1" +VERSION="0.9.19b2" COPYRIGHT="Copyright (C) 2014-2024 Michiel Broek, All Rights Reserved" CYEARS="2014-2024" AC_SUBST(PACKAGE) diff -r ea24b4ce02b1 -r 24749c296a50 thermferm/Makefile --- a/thermferm/Makefile Sun Apr 28 15:50:42 2024 +0200 +++ b/thermferm/Makefile Tue Apr 30 17:26:41 2024 +0200 @@ -67,8 +67,8 @@ pid.o: thermferm.h pid.h rc-switch.o: thermferm.h xutil.h delay.h rc-switch.h rdconfig.o: rdconfig.h thermferm.h pid.h futil.h xutil.h -server.o: rdconfig.h thermferm.h delay.h devices.h server.h lcd-buffer.h xutil.h mqtt.h -simulator.o: thermferm.h delay.h simulator.h +server.o: rdconfig.h thermferm.h delay.h one-wire.h devices.h server.h simulator.h lcd-buffer.h xutil.h mqtt.h +simulator.o: thermferm.h delay.h xutil.h websocket.h simulator.h slcd.o: thermferm.h slcd.h futil.h xutil.h thermferm.o: lock.h rdconfig.h server.h thermferm.h devices.h delay.h simulator.h lcd-pcf8574.h lcd-buffer.h slcd.h panel.h one-wire.h futil.h xutil.h pid.h mqtt.h statetbl.h websocket.h websocket.o: thermferm.h xutil.h devices.h websocket.h diff -r ea24b4ce02b1 -r 24749c296a50 thermferm/devices.c --- a/thermferm/devices.c Sun Apr 28 15:50:42 2024 +0200 +++ b/thermferm/devices.c Tue Apr 30 17:26:41 2024 +0200 @@ -250,6 +250,9 @@ int rc, i; char buf[40]; #endif +#ifdef USE_SIMULATOR + simulator_list *simulator; +#endif if (uuid == NULL) return 0; @@ -314,22 +317,33 @@ } #ifdef USE_SIMULATOR - if ((device->type == DEVTYPE_SIM) && (device->direction == DEVDIR_OUT_BIN) && (device->present == DEVPRESENT_YES)) { - if ((strcmp((char *)"SimCooler", device->address) == 0) || (strcmp((char *)"SimHeater", device->address) == 0) || - (strcmp((char *)"SimFan" , device->address) == 0) || (strcmp((char *)"SimLight" , device->address) == 0)) { - if (value != device->value) { - syslog(LOG_NOTICE, "SIM %s value=%d", device->address, value); - device->value = value; - device->timestamp = time(NULL); - if (strcmp((char *)"SimCooler", device->address) == 0) - SIMcooling = value; - if (strcmp((char *)"SimHeater", device->address) == 0) - SIMheating = value; - if (strcmp((char *)"SimFan", device->address) == 0) - SIMfan = value; - if (strcmp((char *)"SimLight", device->address) == 0) - SIMlight = value; - devices_ws(); + if ((device->type == DEVTYPE_SIM) && (device->direction == DEVDIR_OUT_BIN)) { + for (simulator = Config.simulators; simulator; simulator = simulator->next) { + if ((strcmp(device->address, simulator->cooler_address) == 0) || + (strcmp(device->address, simulator->heater_address) == 0) || + (strcmp(device->address, simulator->fan_address) == 0) || + (strcmp(device->address, simulator->light_address) == 0)) { + if (value != device->value) { + int rc = DEVPRESENT_UNDEF; + device->value = value; + device->timestamp = time(NULL); + if (strcmp(device->address, simulator->cooler_address) == 0) { + simulator->cooler_power = value; + rc = simulator->cooler_present; + } else if (strcmp(device->address, simulator->heater_address) == 0) { + simulator->heater_power = value; + rc = simulator->heater_present; + } else if (strcmp(device->address, simulator->fan_address) == 0) { + simulator->fan_power = value; + rc = simulator->fan_present; + } else if (strcmp(device->address, simulator->light_address) == 0) { + simulator->light_power = value; + rc = simulator->light_present; + } + syslog(LOG_NOTICE, "SIM %s value=%d, present=%s", device->address, value, DEVPRESENT[rc]); + device->present = rc; + devices_ws(); + } } } } @@ -462,6 +476,9 @@ int pin; char buf[40]; #endif +#ifdef USE_SIMULATOR + simulator_list *simulator; +#endif /* * Scan for 1-wire devices. These are already detected by the @@ -686,87 +703,110 @@ #endif #ifdef USE_SIMULATOR - found = 0; - for (device = Config.devices; device; device = device->next) { - if (device->type == DEVTYPE_SIM) { - found++; - } - } + + for (simulator = Config.simulators; simulator; simulator = simulator->next) { + for (i = 0; i < 10; i++) { + + found = FALSE; + for (device = Config.devices; device; device = device->next) { + if ((i == 0 && strcmp(device->address, simulator->room_tempaddress) == 0) || + (i == 1 && strcmp(device->address, simulator->air_address) == 0) || + (i == 2 && strcmp(device->address, simulator->beer_address) == 0) || + (i == 3 && strcmp(device->address, simulator->heater_address) == 0) || + (i == 4 && strcmp(device->address, simulator->cooler_address) == 0) || + (i == 5 && strcmp(device->address, simulator->room_humaddress) == 0) || + (i == 6 && strcmp(device->address, simulator->chiller_address) == 0) || + (i == 7 && strcmp(device->address, simulator->fan_address) == 0) || + (i == 8 && strcmp(device->address, simulator->light_address) == 0) || + (i == 9 && strcmp(device->address, simulator->beer_address2) == 0)) { + found = TRUE; + break; + } + } + + if (found == FALSE) { + ndev = (devices_list *)malloc(sizeof(devices_list)); + ndev->next = NULL; + ndev->uuid = malloc(37); + uuid_generate(uu); + uuid_unparse(uu, ndev->uuid); + ndev->type = DEVTYPE_SIM; + ndev->value = ndev->offset = 0; + ndev->present = DEVPRESENT_NO; + ndev->gpiopin = -1; + ndev->subdevice = i; + ndev->comment = xstrcpy((char *)"Auto detected device"); + ndev->timestamp = time(NULL); + ndev->inuse = 0; - /* - * Create simulated devices, or upgrade with new devices. - */ - subdevices = 10; - for (i = found; i < subdevices; i++) { - ndev = (devices_list *)malloc(sizeof(devices_list)); - ndev->next = NULL; - 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_BIN; - ndev->address = xstrcpy((char *)"SimHeater"); - ndev->description = xstrcpy((char *)"Simulated heater"); - break; - case 4: ndev->direction = DEVDIR_OUT_BIN; - ndev->address = xstrcpy((char *)"SimCooler"); - ndev->description = xstrcpy((char *)"Simulated cooler"); - break; - case 5: ndev->direction = DEVDIR_IN_ANALOG; - ndev->address = xstrcpy((char *)"SimRoomHum"); - ndev->description = xstrcpy((char *)"Simulated room humidity"); - break; - case 6: ndev->direction = DEVDIR_IN_ANALOG; - ndev->address = xstrcpy((char *)"SimChillerTemp"); - ndev->description = xstrcpy((char *)"Simulated Chiller temperature"); - break; - case 7: ndev->direction = DEVDIR_OUT_BIN; - ndev->address = xstrcpy((char *)"SimFan"); - ndev->description = xstrcpy((char *)"Simulated fan"); - break; - case 8: ndev->direction = DEVDIR_OUT_BIN; - ndev->address = xstrcpy((char *)"SimLight"); - ndev->description = xstrcpy((char *)"Simulated light"); - break; - case 9: ndev->direction = DEVDIR_IN_ANALOG; - ndev->address = xstrcpy((char *)"SimBeerTemp2"); - ndev->description = xstrcpy((char *)"Simulated beer temperature (alt)"); - break; - } + switch (i) { + case 0: ndev->direction = DEVDIR_IN_ANALOG; + ndev->address = xstrcpy(simulator->room_tempaddress); + ndev->description = xstrcpy((char *)"Simulated room temperature"); + ndev->present = DEVPRESENT_YES; + break; + case 1: ndev->direction = DEVDIR_IN_ANALOG; + ndev->address = xstrcpy(simulator->air_address); + ndev->description = xstrcpy((char *)"Simulated air temperature"); + ndev->present = simulator->air_present; + break; + case 2: ndev->direction = DEVDIR_IN_ANALOG; + ndev->address = xstrcpy(simulator->beer_address); + ndev->description = xstrcpy((char *)"Simulated beer temperature"); + ndev->present = simulator->beer_present; + break; + case 3: ndev->direction = DEVDIR_OUT_BIN; + ndev->address = xstrcpy(simulator->heater_address); + ndev->description = xstrcpy((char *)"Simulated heater"); + ndev->present = simulator->heater_present; + break; + case 4: ndev->direction = DEVDIR_OUT_BIN; + ndev->address = xstrcpy(simulator->cooler_address); + ndev->description = xstrcpy((char *)"Simulated cooler"); + ndev->present = simulator->cooler_present; + break; + case 5: ndev->direction = DEVDIR_IN_ANALOG; + ndev->address = xstrcpy(simulator->room_humaddress); + ndev->description = xstrcpy((char *)"Simulated room humidity"); + ndev->present = DEVPRESENT_YES; + break; + case 6: ndev->direction = DEVDIR_IN_ANALOG; + ndev->address = xstrcpy(simulator->chiller_address); + ndev->description = xstrcpy((char *)"Simulated Chiller temperature"); + ndev->present = simulator->chiller_present; + break; + case 7: ndev->direction = DEVDIR_OUT_BIN; + ndev->address = xstrcpy(simulator->fan_address); + ndev->description = xstrcpy((char *)"Simulated fan"); + ndev->present = simulator->fan_present; + break; + case 8: ndev->direction = DEVDIR_OUT_BIN; + ndev->address = xstrcpy(simulator->light_address); + ndev->description = xstrcpy((char *)"Simulated light"); + ndev->present = simulator->light_present; + break; + case 9: ndev->direction = DEVDIR_IN_ANALOG; + ndev->address = xstrcpy(simulator->beer_address2); + ndev->description = xstrcpy((char *)"Simulated beer temperature (alt)"); + ndev->present = simulator->beer_present2; + break; + } - syslog(LOG_NOTICE, "New Simulator device %s, subdevice %d, %s", ndev->address, ndev->subdevice, ndev->description); + syslog(LOG_NOTICE, "New Simulator device %s, subdevice %d", ndev->address, ndev->subdevice); - if (Config.devices == NULL) { - Config.devices = ndev; - } else { - for (device = Config.devices; device; device = device->next) { - if (device->next == NULL) { - device->next = ndev; - 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++; } } - rc++; } #endif diff -r ea24b4ce02b1 -r 24749c296a50 thermferm/rdconfig.c --- a/thermferm/rdconfig.c Sun Apr 28 15:50:42 2024 +0200 +++ b/thermferm/rdconfig.c Tue Apr 30 17:26:41 2024 +0200 @@ -154,6 +154,26 @@ free(simulator->uuid); if (simulator->name) free(simulator->name); + if (simulator->room_tempaddress) + free(simulator->room_tempaddress); + if (simulator->room_humaddress) + free(simulator->room_humaddress); + if (simulator->air_address) + free(simulator->air_address); + if (simulator->beer_address) + free(simulator->beer_address); + if (simulator->beer_address2) + free(simulator->beer_address2); + if (simulator->chiller_address) + free(simulator->chiller_address); + if (simulator->cooler_address) + free(simulator->cooler_address); + if (simulator->heater_address) + free(simulator->heater_address); + if (simulator->fan_address) + free(simulator->fan_address); + if (simulator->light_address) + free(simulator->light_address); free(simulator); } Config.simulators = NULL; @@ -430,22 +450,45 @@ xmlTextWriterStartElement(writer, BAD_CAST "SIMULATOR"); xmlTextWriterWriteFormatElement(writer, BAD_CAST "UUID", "%s", simulator->uuid); xmlTextWriterWriteFormatElement(writer, BAD_CAST "NAME", "%s", simulator->name); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "SIMNO", "%d", simulator->simno); xmlTextWriterWriteFormatElement(writer, BAD_CAST "VOLUME_AIR", "%d", simulator->volume_air); xmlTextWriterWriteFormatElement(writer, BAD_CAST "VOLUME_BEER", "%d", simulator->volume_beer); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "ROOM_TEMPADDRESS", "%s", simulator->room_tempaddress); xmlTextWriterWriteFormatElement(writer, BAD_CAST "ROOM_TEMPERATURE", "%.1f", simulator->room_temperature); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "ROOM_HUMADDRESS", "%s", simulator->room_humaddress); xmlTextWriterWriteFormatElement(writer, BAD_CAST "ROOM_HUMIDITY", "%.1f", simulator->room_humidity); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "AIR_ADDRESS", "%s", simulator->air_address); xmlTextWriterWriteFormatElement(writer, BAD_CAST "AIR_TEMPERATURE", "%f", simulator->air_temperature); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "AIR_PRESENT", "%s", DEVPRESENT[simulator->air_present]); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_ADDRESS", "%s", simulator->beer_address); xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_TEMPERATURE", "%f", simulator->beer_temperature); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_PRESENT", "%s", DEVPRESENT[simulator->beer_present]); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_ADDRESS2", "%s", simulator->beer_address2); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_TEMPERATURE2", "%f", simulator->beer_temperature2); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_PRESENT2", "%s", DEVPRESENT[simulator->beer_present2]); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "CHILLER_ADDRESS", "%s", simulator->chiller_address); xmlTextWriterWriteFormatElement(writer, BAD_CAST "CHILLER_TEMPERATURE", "%f", simulator->chiller_temperature); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "CHILLER_PRESENT", "%s", DEVPRESENT[simulator->chiller_present]); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_ADDRESS", "%s", simulator->cooler_address); xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_TEMP", "%f", simulator->cooler_temp); xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_TIME", "%d", simulator->cooler_time); xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_SIZE", "%.3f", simulator->cooler_size); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_PRESENT", "%s", DEVPRESENT[simulator->cooler_present]); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_POWER", "%d", simulator->cooler_power); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_ADDRESS", "%s", simulator->heater_address); xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_TEMP", "%f", simulator->heater_temp); xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_TIME", "%d", simulator->heater_time); xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_SIZE", "%.3f", simulator->heater_size); - xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_STATE", "%d", simulator->heater_state); - xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_STATE", "%d", simulator->cooler_state); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_PRESENT", "%s", DEVPRESENT[simulator->heater_present]); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_POWER", "%d", simulator->heater_power); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "FAN_ADDRESS", "%s", simulator->fan_address); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "FAN_PRESENT", "%s", DEVPRESENT[simulator->fan_present]); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "FAN_POWER", "%d", simulator->fan_power); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "LIGHT_ADDRESS", "%s", simulator->light_address); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "LIGHT_PRESENT", "%s", DEVPRESENT[simulator->light_present]); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "LIGHT_POWER", "%d", simulator->light_power); xmlTextWriterWriteFormatElement(writer, BAD_CAST "FRIGO_ISOLATION", "%.3f", simulator->frigo_isolation); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "TIMESTAMP", "%ld", (long)simulator->timestamp); xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_YEAST_HEAT", "%f", simulator->s_yeast_heat); xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_YEAST_STARTED", "%d", (int)simulator->s_yeast_started); xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_COOL_TEMP", "%f", simulator->s_cool_temp); @@ -1328,18 +1371,22 @@ { xmlChar *key; simulator_list *simulator, *tmp; - int ival; + int ival, i; float fval; simulator = (simulator_list *)malloc(sizeof(simulator_list)); simulator->next = NULL; simulator->uuid = simulator->name = NULL; - simulator->volume_air = simulator->volume_beer = 0; + simulator->room_tempaddress = simulator->room_humaddress = simulator->air_address = simulator->beer_address = simulator->beer_address2 = NULL; + simulator->chiller_address = simulator->cooler_address = simulator->heater_address = simulator->fan_address = simulator->light_address = NULL; + simulator->simno = 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->chiller_temperature = 1.5; simulator->room_humidity = 49.2; 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->cooler_time = simulator->heater_time = 0; + simulator->air_present = simulator->beer_present = DEVPRESENT_YES; + simulator->beer_present2 = simulator->chiller_present = simulator->cooler_present = simulator->heater_present = DEVPRESENT_UNDEF; 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; @@ -1351,6 +1398,12 @@ if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) { simulator->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"SIMNO"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->simno = ival; + xmlFree(key); + } if ((!xmlStrcmp(cur->name, (const xmlChar *)"VOLUME_AIR"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%d", &ival) == 1) @@ -1363,36 +1416,103 @@ simulator->volume_beer = ival; xmlFree(key); } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"ROOM_TEMPADDRESS"))) { + simulator->room_tempaddress = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } 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 *)"ROOM_GUMIDITY"))) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"ROOM_HUMADDRESS"))) { + simulator->room_humaddress = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"ROOM_HUMIDITY"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%f", &fval) == 1) simulator->room_humidity= fval; xmlFree(key); } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"AIR_ADDRESS"))) { + simulator->air_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } 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 *)"AIR_PRESENT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + for (i = 0; i < 4; i++) { + if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) { + simulator->air_present = i; + break; + } + } + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEER_ADDRESS"))) { + simulator->beer_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } 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 *)"BEER_PRESENT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + for (i = 0; i < 4; i++) { + if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) { + simulator->beer_present = i; + break; + } + } + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEER_ADDRESS2"))) { + simulator->beer_address2 = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEER_TEMPERATURE2"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &fval) == 1) + simulator->beer_temperature2 = fval; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEER_PRESENT2"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + for (i = 0; i < 4; i++) { + if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) { + simulator->beer_present2 = i; + break; + } + } + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"CHILLER_ADDRESS"))) { + simulator->chiller_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } if ((!xmlStrcmp(cur->name, (const xmlChar *)"CHILLER_TEMPERATURE"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%f", &fval) == 1) simulator->chiller_temperature = fval; xmlFree(key); } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"CHILLER_PRESENT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + for (i = 0; i < 4; i++) { + if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) { + simulator->chiller_present = i; + break; + } + } + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_ADDRESS"))) { + simulator->cooler_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_TEMP"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%f", &fval) == 1) @@ -1411,6 +1531,25 @@ simulator->cooler_size = fval; xmlFree(key); } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_PRESENT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + for (i = 0; i < 4; i++) { + if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) { + simulator->cooler_present = i; + break; + } + } + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_POWER"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->cooler_power = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_ADDRESS"))) { + simulator->heater_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_TEMP"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%f", &fval) == 1) @@ -1429,24 +1568,72 @@ 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 *)"HEATER_PRESENT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + for (i = 0; i < 4; i++) { + if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) { + simulator->heater_present = i; + break; + } + } + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_POWER"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->heater_power = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"FAN_ADDRESS"))) { + simulator->fan_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"FAN_PRESENT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + for (i = 0; i < 4; i++) { + if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) { + simulator->fan_present = i; + break; + } + } + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"FAN_POWER"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->fan_power = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"LIGHT_ADDRESS"))) { + simulator->light_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"LIGHT_PRESENT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + for (i = 0; i < 4; i++) { + if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) { + simulator->light_present = i; + break; + } + } + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"LIGHT_POWER"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->light_power = 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 *)"TIMESTAMP"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->timestamp = ival; + xmlFree(key); + } if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_YEAST_HEAT"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); diff -r ea24b4ce02b1 -r 24749c296a50 thermferm/server.c --- a/thermferm/server.c Sun Apr 28 15:50:42 2024 +0200 +++ b/thermferm/server.c Tue Apr 30 17:26:41 2024 +0200 @@ -26,6 +26,7 @@ #include "one-wire.h" #include "devices.h" #include "server.h" +#include "simulator.h" #include "lcd-buffer.h" #include "xutil.h" #include "mqtt.h" @@ -1003,7 +1004,7 @@ { char *opt, *param, *kwd, *val, ibuf[SS_BUFSIZE]; simulator_list *simulator, *tmps; - int rc, rlen, ival; + int rc, rlen, ival, i; float fval; uuid_t uu; @@ -1024,6 +1025,7 @@ srv_send(s, (char *)"SIMULATOR LIST List all Simulators"); srv_send(s, (char *)"SIMULATOR GET uuid Get Simulator record by uuid"); srv_send(s, (char *)"SIMULATOR PUT uuid Put Simulator record by uuid"); + srv_send(s, (char *)"SIMULATOR JSON Get json records"); srv_send(s, (char *)"."); return 0; } @@ -1031,23 +1033,57 @@ if (strcmp(opt, (char *)"LIST") == 0) { srv_send(s, (char *)"212 Simulators list follows:"); for (simulator = Config.simulators; simulator; simulator = simulator->next) { - srv_send(s, (char *)"%s,%s", simulator->uuid, simulator->name); + srv_send(s, (char *)"%s,%d,%s", simulator->uuid, simulator->simno, simulator->name); } srv_send(s, (char *)"."); return 0; } + if (strcmp(opt, (char *)"JSON") == 0) { + char *payload = NULL, *payloadu = NULL; + bool comma = false; + + if (param == NULL) { + srv_send(s, (char *)"212 Simulators json list follows:"); + payload = xstrcpy((char *)"["); + for (simulator = Config.simulators; simulator; simulator = simulator->next) { + if (comma) + payload = xstrcat(payload, (char *)","); + payloadu = simulator_json(simulator); + payload = xstrcat(payload, payloadu); + comma = true; + free(payloadu); + payloadu = NULL; + } + payload = xstrcat(payload, (char *)"]"); + large_send(s, payload); + srv_send(s, (char *)"."); + free(payload); + payload = NULL; + return 0; + } + } + if (param == NULL) { srv_send(s, (char *)"502 Parameter missing"); return 0; } if (strcmp(opt, (char *)"ADD") == 0) { + int highno = 0, count = 0; + char abuf[64]; /* * For now, only one simulator is allowed. */ if (Config.simulators) { + for (tmps = Config.simulators; tmps; tmps = tmps->next) { + if (tmps->simno > highno) + highno = tmps->simno; + count++; + } + } + if (count >= 5) { srv_send(s, (char *)"441 Maximum simulators reached"); return 0; } @@ -1058,10 +1094,33 @@ uuid_generate(uu); uuid_unparse(uu, simulator->uuid); simulator->name = xstrcpy(param); + simulator->simno = highno + 1; + sprintf(abuf, "%d-", simulator->simno); 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->room_tempaddress = xstrcpy(abuf); + simulator->room_tempaddress = xstrcat(simulator->room_tempaddress, (char *)"SimRoomTemp"); + simulator->room_temperature = simulator->air_temperature = simulator->beer_temperature = simulator->beer_temperature2 = 20.0; + simulator->s_cool_temp = simulator->s_heat_temp = 20.0; + simulator->room_humaddress = xstrcpy(abuf); + simulator->room_humaddress = xstrcat(simulator->room_humaddress, (char *)"SimRoomHum"); simulator->room_humidity = 48.6; + simulator->air_address = xstrcpy(abuf); + simulator->air_address = xstrcat(simulator->air_address, (char *)"SimAirTemp"); + simulator->beer_address = xstrcpy(abuf); + simulator->beer_address = xstrcat(simulator->beer_address, (char *)"SimBeerTemp"); + simulator->beer_address2 = xstrcpy(abuf); + simulator->beer_address2 = xstrcat(simulator->beer_address2, (char *)"SimBeerTemp2"); + simulator->chiller_address = xstrcpy(abuf); + simulator->chiller_address = xstrcat(simulator->chiller_address, (char *)"SimChillerTemp"); + simulator->heater_address = xstrcpy(abuf); + simulator->heater_address = xstrcat(simulator->heater_address, (char *)"SimHeater"); + simulator->cooler_address = xstrcpy(abuf); + simulator->cooler_address = xstrcat(simulator->cooler_address, (char *)"SimCooler"); + simulator->fan_address = xstrcpy(abuf); + simulator->fan_address = xstrcat(simulator->fan_address, (char *)"SimFan"); + simulator->light_address = xstrcpy(abuf); + simulator->light_address = xstrcat(simulator->light_address, (char *)"SimLight"); simulator->chiller_temperature = 1.5; /* Chiller temperature */ simulator->cooler_temp = 1.5; /* Cooling temperature */ simulator->cooler_time = 720; /* About 12 minutes for the cooler plate */ @@ -1069,8 +1128,10 @@ 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->air_present = simulator->beer_present = DEVPRESENT_YES; + simulator->beer_present2 = simulator->chiller_present = simulator->cooler_present = simulator->heater_present = DEVPRESENT_UNDEF; simulator->frigo_isolation = 0.002; + simulator->timestamp = time(NULL); simulator->s_yeast_heat = 0.0; simulator->s_yeast_started = simulator->s_cool_changed = simulator->s_heat_changed = (int)0; @@ -1085,12 +1146,14 @@ } } - syslog(LOG_NOTICE, "Simulator %s added", simulator->uuid); + syslog(LOG_NOTICE, "Simulator %s no %d added", simulator->uuid, simulator->simno); srv_send(s, (char *)"211 Simulator %s added", simulator->uuid); return 1; } if (strcmp(opt, (char *)"DEL") == 0) { + // TODO: check devices in use. + // TODO: delete simulated devices. rc = delete_Simulator(param); if (rc) { syslog(LOG_NOTICE, "Simulator %s deleted", param); @@ -1107,22 +1170,45 @@ if (strcmp(simulator->uuid, param) == 0) { srv_send(s, (char *)"213 Simulator record follows:"); srv_send(s, (char *)"NAME,%s", simulator->name); + srv_send(s, (char *)"SIMNO,%d", simulator->simno); srv_send(s, (char *)"VOLUME_AIR,%d", simulator->volume_air); srv_send(s, (char *)"VOLUME_BEER,%d", simulator->volume_beer); + srv_send(s, (char *)"ROOM_TEMPADDRESS,%s", simulator->room_tempaddress); srv_send(s, (char *)"ROOM_TEMPERATURE,%.1f", simulator->room_temperature); + srv_send(s, (char *)"ROOM_HUMADDRESS,%s", simulator->room_humaddress); srv_send(s, (char *)"ROOM_HUMIDITY,%.1f", simulator->room_humidity); + srv_send(s, (char *)"AIR_ADDRESS,%s", simulator->air_address); srv_send(s, (char *)"AIR_TEMPERATURE,%.3f", simulator->air_temperature); + srv_send(s, (char *)"AIR_PRESENT,%s", DEVPRESENT[simulator->air_present]); + srv_send(s, (char *)"BEER_ADDRESS,%s", simulator->beer_address); srv_send(s, (char *)"BEER_TEMPERATURE,%.3f", simulator->beer_temperature); + srv_send(s, (char *)"BEER_PRESENT,%s", DEVPRESENT[simulator->beer_present]); + srv_send(s, (char *)"BEER_ADDRESS2,%s", simulator->beer_address2); + srv_send(s, (char *)"BEER_TEMPERATURE2,%.3f", simulator->beer_temperature2); + srv_send(s, (char *)"BEER_PRESENT2,%s", DEVPRESENT[simulator->beer_present2]); + srv_send(s, (char *)"CHILLER_ADDRESS,%s", simulator->chiller_address); srv_send(s, (char *)"CHILLER_TEMPERATURE,%.3f", simulator->chiller_temperature); + srv_send(s, (char *)"CHILLER_PRESENT,%s", DEVPRESENT[simulator->chiller_present]); + srv_send(s, (char *)"COOLER_ADDRESS,%s", simulator->cooler_address); srv_send(s, (char *)"COOLER_TEMP,%.1f", simulator->cooler_temp); srv_send(s, (char *)"COOLER_TIME,%d", simulator->cooler_time); srv_send(s, (char *)"COOLER_SIZE,%.3f", simulator->cooler_size); + srv_send(s, (char *)"COOLER_PRESENT,%s", DEVPRESENT[simulator->cooler_present]); + srv_send(s, (char *)"COOLER_POWER,%d", simulator->cooler_power); + srv_send(s, (char *)"HEATER_ADDRESS,%s", simulator->heater_address); srv_send(s, (char *)"HEATER_TEMP,%.1f", simulator->heater_temp); srv_send(s, (char *)"HEATER_TIME,%d", simulator->heater_time); srv_send(s, (char *)"HEATER_SIZE,%.3f", simulator->heater_size); - srv_send(s, (char *)"HEATER_STATE,%d", simulator->heater_state); - srv_send(s, (char *)"COOLER_STATE,%d", simulator->cooler_state); + srv_send(s, (char *)"HEATER_PRESENT,%s", DEVPRESENT[simulator->heater_present]); + srv_send(s, (char *)"HEATER_POWER,%d", simulator->heater_power); + srv_send(s, (char *)"FAN_ADDRESS,%s", simulator->fan_address); + srv_send(s, (char *)"FAN_PRESENT,%s", DEVPRESENT[simulator->fan_present]); + srv_send(s, (char *)"FAN_POWER,%d", simulator->fan_power); + srv_send(s, (char *)"LIGHT_ADDRESS,%s", simulator->light_address); + srv_send(s, (char *)"LIGHT_PRESENT,%s", DEVPRESENT[simulator->light_present]); + srv_send(s, (char *)"LIGHT_POWER,%d", simulator->light_power); srv_send(s, (char *)"FRIGO_ISOLATION,%.3f", simulator->frigo_isolation); + srv_send(s, (char *)"TIMESTAMP,%ld", (long)simulator->timestamp); srv_send(s, (char *)"."); return 0; } @@ -1142,6 +1228,7 @@ if (strlen(ibuf)) { if (strcmp(ibuf, (char *)".") == 0) { srv_send(s, (char *)"219 Accepted Simulator record"); + simulator->timestamp = time(NULL); return 1; } kwd = strtok(ibuf, ",\0"); @@ -1191,6 +1278,16 @@ simulator->air_temperature = fval; } + } else if (strcmp(kwd, (char *)"AIR_PRESENT") == 0) { + for (i = 0; i < 4; i++) { + if (strcmp(val, DEVPRESENT[i]) == 0) { + if (simulator->air_present != i) + syslog(LOG_NOTICE, "Simulator %s air_present %s to %s", simulator->uuid, DEVPRESENT[simulator->air_present], DEVPRESENT[i]); + simulator->air_present = i; + break; + } + } + } else if (strcmp(kwd, (char *)"BEER_TEMPERATURE") == 0) { if (sscanf(val, "%f", &fval) == 1) { if (simulator->beer_temperature != fval) @@ -1198,6 +1295,33 @@ simulator->beer_temperature = fval; } + } else if (strcmp(kwd, (char *)"BEER_PRESENT") == 0) { + for (i = 0; i < 4; i++) { + if (strcmp(val, DEVPRESENT[i]) == 0) { + if (simulator->beer_present != i) + syslog(LOG_NOTICE, "Simulator %s beer_present %s to %s", simulator->uuid, DEVPRESENT[simulator->beer_present], DEVPRESENT[i]); + simulator->beer_present = i; + break; + } + } + + } else if (strcmp(kwd, (char *)"BEER_TEMPERATURE2") == 0) { + if (sscanf(val, "%f", &fval) == 1) { + if (simulator->beer_temperature2 != fval) + syslog(LOG_NOTICE, "Simulator %s beer temperature2 %.1f to %.1f", simulator->uuid, simulator->beer_temperature2, fval); + simulator->beer_temperature2 = fval; + } + + } else if (strcmp(kwd, (char *)"BEER_PRESENT2") == 0) { + for (i = 0; i < 4; i++) { + if (strcmp(val, DEVPRESENT[i]) == 0) { + if (simulator->beer_present2 != i) + syslog(LOG_NOTICE, "Simulator %s beer_present2 %s to %s", simulator->uuid, DEVPRESENT[simulator->beer_present2], DEVPRESENT[i]); + simulator->beer_present2 = i; + break; + } + } + } else if (strcmp(kwd, (char *)"CHILLER_TEMPERATURE") == 0) { if (sscanf(val, "%f", &fval) == 1) { if (simulator->chiller_temperature != fval) @@ -1205,6 +1329,16 @@ simulator->chiller_temperature = fval; } + } else if (strcmp(kwd, (char *)"CHILLER_PRESENT") == 0) { + for (i = 0; i < 4; i++) { + if (strcmp(val, DEVPRESENT[i]) == 0) { + if (simulator->chiller_present != i) + syslog(LOG_NOTICE, "Simulator %s chiller_present %s to %s", simulator->uuid, DEVPRESENT[simulator->chiller_present], DEVPRESENT[i]); + simulator->chiller_present = i; + break; + } + } + } else if (strcmp(kwd, (char *)"COOLER_TEMP") == 0) { if (sscanf(val, "%f", &fval) == 1) { if (simulator->cooler_temp != fval) @@ -1226,6 +1360,23 @@ simulator->cooler_size = fval; } + } else if (strcmp(kwd, (char *)"COOLER_PRESENT") == 0) { + for (i = 0; i < 4; i++) { + if (strcmp(val, DEVPRESENT[i]) == 0) { + if (simulator->cooler_present != i) + syslog(LOG_NOTICE, "Simulator %s cooler_present %s to %s", simulator->uuid, DEVPRESENT[simulator->cooler_present], DEVPRESENT[i]); + simulator->cooler_present = i; + break; + } + } + + } else if (strcmp(kwd, (char *)"COOLER_POWER") == 0) { + if (sscanf(val, "%d", &ival) == 1) { + if (simulator->cooler_power != ival) + syslog(LOG_NOTICE, "Simulator %s cooler power %d to %d", simulator->uuid, simulator->cooler_power, ival); + simulator->cooler_power = ival; + } + } else if (strcmp(kwd, (char *)"HEATER_TEMP") == 0) { if (sscanf(val, "%f", &fval) == 1) { if (simulator->heater_temp != fval) @@ -1247,19 +1398,56 @@ simulator->heater_size = fval; } - } else if (strcmp(kwd, (char *)"HEATER_STATE") == 0) { - if (sscanf(val, "%d", &ival) == 1) { - if (simulator->heater_state != ival) - syslog(LOG_NOTICE, "Simulator %s heater state %d to %d", simulator->uuid, simulator->heater_state, ival); - simulator->heater_state = ival; - } + } else if (strcmp(kwd, (char *)"HEATER_PRESENT") == 0) { + for (i = 0; i < 4; i++) { + if (strcmp(val, DEVPRESENT[i]) == 0) { + if (simulator->heater_present != i) + syslog(LOG_NOTICE, "Simulator %s heater_present %s to %s", simulator->uuid, DEVPRESENT[simulator->heater_present], DEVPRESENT[i]); + simulator->heater_present = i; + break; + } + } + + } else if (strcmp(kwd, (char *)"HEATER_POWER") == 0) { + if (sscanf(val, "%d", &ival) == 1) { + if (simulator->heater_power != ival) + syslog(LOG_NOTICE, "Simulator %s heater power %d to %d", simulator->uuid, simulator->heater_power, ival); + simulator->heater_power = ival; + } - } else if (strcmp(kwd, (char *)"COOLER_STATE") == 0) { - if (sscanf(val, "%d", &ival) == 1) { - if (simulator->cooler_state != ival) - syslog(LOG_NOTICE, "Simulator %s cooler state %d to %d", simulator->uuid, simulator->cooler_state, ival); - simulator->cooler_state = ival; - } + } else if (strcmp(kwd, (char *)"FAN_PRESENT") == 0) { + for (i = 0; i < 4; i++) { + if (strcmp(val, DEVPRESENT[i]) == 0) { + if (simulator->fan_present != i) + syslog(LOG_NOTICE, "Simulator %s fan_present %s to %s", simulator->uuid, DEVPRESENT[simulator->fan_present], DEVPRESENT[i]); + simulator->fan_present = i; + break; + } + } + + } else if (strcmp(kwd, (char *)"FAN_POWER") == 0) { + if (sscanf(val, "%d", &ival) == 1) { + if (simulator->fan_power != ival) + syslog(LOG_NOTICE, "Simulator %s fan power %d to %d", simulator->uuid, simulator->fan_power, ival); + simulator->fan_power = ival; + } + + } else if (strcmp(kwd, (char *)"LIGHT_PRESENT") == 0) { + for (i = 0; i < 4; i++) { + if (strcmp(val, DEVPRESENT[i]) == 0) { + if (simulator->light_present != i) + syslog(LOG_NOTICE, "Simulator %s light_present %s to %s", simulator->uuid, DEVPRESENT[simulator->light_present], DEVPRESENT[i]); + simulator->light_present = i; + break; + } + } + + } else if (strcmp(kwd, (char *)"LIGHT_POWER") == 0) { + if (sscanf(val, "%d", &ival) == 1) { + if (simulator->fan_power != ival) + syslog(LOG_NOTICE, "Simulator %s light power %d to %d", simulator->uuid, simulator->light_power, ival); + simulator->light_power = ival; + } } else if (strcmp(kwd, (char *)"FRIGO_ISOLATION") == 0) { if (sscanf(val, "%f", &fval) == 1) { diff -r ea24b4ce02b1 -r 24749c296a50 thermferm/simulator.c --- a/thermferm/simulator.c Sun Apr 28 15:50:42 2024 +0200 +++ b/thermferm/simulator.c Tue Apr 30 17:26:41 2024 +0200 @@ -22,6 +22,8 @@ #include "thermferm.h" #include "delay.h" +#include "xutil.h" +#include "websocket.h" #include "simulator.h" int my_simulator_state = 0; @@ -29,6 +31,7 @@ #ifdef USE_SIMULATOR extern sys_config Config; +extern const char DEVPRESENT[4][6]; int my_simulator_shutdown = 0; int SIMcooling = 0; @@ -37,6 +40,157 @@ int SIMlight = 0; +/* + * Return json data for one simulator + */ +char *simulator_json(simulator_list *simulator) +{ + char *payload, buf[64]; + + payload = xstrcpy((char *)"{\"uuid\":\""); + payload = xstrcat(payload, simulator->uuid); + payload = xstrcat(payload, (char *)"\",\"name\":\""); + payload = xstrcat(payload, simulator->name); + payload = xstrcat(payload, (char *)"\",\"simno\":"); + sprintf(buf, "%d", simulator->simno); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"volume_air\":"); + sprintf(buf, "%d", simulator->volume_air); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"volume_beer\":"); + sprintf(buf, "%d", simulator->volume_beer); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"room\":{\"tempaddress\":\""); + payload = xstrcat(payload, simulator->room_tempaddress); + payload = xstrcat(payload, (char *)"\",\"temperature\":"); + sprintf(buf, "%.1f", simulator->room_temperature); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"humaddress\":\""); + payload = xstrcat(payload, simulator->room_humaddress); + payload = xstrcat(payload, (char *)"\",\"humidity\":"); + sprintf(buf, "%.1f", simulator->room_humidity); + payload = xstrcat(payload, buf); + + payload = xstrcat(payload, (char *)"},\"air\":{\"address\":\""); + payload = xstrcat(payload, simulator->air_address); + payload = xstrcat(payload, (char *)"\",\"temperature\":"); + sprintf(buf, "%.4f", simulator->air_temperature); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"present\":\""); + payload = xstrcat(payload, (char *)DEVPRESENT[simulator->air_present]); + payload = xstrcat(payload, (char *)"\"},\"beer\":{\"address\":\""); + + payload = xstrcat(payload, simulator->beer_address); + payload = xstrcat(payload, (char *)"\",\"temperature\":"); + sprintf(buf, "%.4f", simulator->beer_temperature); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"present\":\""); + payload = xstrcat(payload, (char *)DEVPRESENT[simulator->beer_present]); + payload = xstrcat(payload, (char *)"\"},\"beer2\":{\"address\":\""); + + payload = xstrcat(payload, simulator->beer_address2); + payload = xstrcat(payload, (char *)"\",\"temperature\":"); + sprintf(buf, "%.4f", simulator->beer_temperature2); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"present\":\""); + payload = xstrcat(payload, (char *)DEVPRESENT[simulator->beer_present2]); + payload = xstrcat(payload, (char *)"\"},\"chiller\":{\"address\":\""); + + payload = xstrcat(payload, simulator->chiller_address); + payload = xstrcat(payload, (char *)"\",\"temperature\":"); + sprintf(buf, "%.4f", simulator->chiller_temperature); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"present\":\""); + payload = xstrcat(payload, (char *)DEVPRESENT[simulator->chiller_present]); + payload = xstrcat(payload, (char *)"\"},\"cooler\":{\"address\":\""); + + payload = xstrcat(payload, simulator->cooler_address); + payload = xstrcat(payload, (char *)"\",\"temperature\":"); + sprintf(buf, "%.4f", simulator->cooler_temp); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"time\":"); + sprintf(buf, "%d", simulator->cooler_time); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"size\":"); + sprintf(buf, "%.4f", simulator->cooler_size); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"present\":\""); + payload = xstrcat(payload, (char *)DEVPRESENT[simulator->cooler_present]); + payload = xstrcat(payload, (char *)"\",\"power\":"); + sprintf(buf, "%d", simulator->cooler_power); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)"},\"heater\":{\"address\":\""); + + payload = xstrcat(payload, simulator->heater_address); + payload = xstrcat(payload, (char *)"\",\"temperature\":"); + sprintf(buf, "%.4f", simulator->heater_temp); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"time\":"); + sprintf(buf, "%d", simulator->heater_time); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"size\":"); + sprintf(buf, "%.4f", simulator->heater_size); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"present\":\""); + payload = xstrcat(payload, (char *)DEVPRESENT[simulator->heater_present]); + payload = xstrcat(payload, (char *)"\",\"power\":"); + sprintf(buf, "%d", simulator->heater_power); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)"},\"fan\":{\"address\":\""); + + payload = xstrcat(payload, simulator->fan_address); + payload = xstrcat(payload, (char *)"\",\"present\":\""); + payload = xstrcat(payload, (char *)DEVPRESENT[simulator->fan_present]); + payload = xstrcat(payload, (char *)"\",\"power\":"); + sprintf(buf, "%d", simulator->fan_power); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)"},\"light\":{\"address\":\""); + + payload = xstrcat(payload, simulator->light_address); + payload = xstrcat(payload, (char *)"\",\"present\":\""); + payload = xstrcat(payload, (char *)DEVPRESENT[simulator->light_present]); + payload = xstrcat(payload, (char *)"\",\"power\":"); + sprintf(buf, "%d", simulator->light_power); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)"},\"frigo_isolation\":"); + + sprintf(buf, "%.4f", simulator->frigo_isolation); + payload = xstrcat(payload, buf); + + payload = xstrcat(payload, (char *)",\"timestamp\":"); + sprintf(buf, "%ld", (long)simulator->timestamp); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)"}"); + + return payload; +} + + + +void simulator_ws(void) +{ + bool comma = false; + char *payload = NULL, *payloadu = NULL; + simulator_list *simulator; + + payload = xstrcpy((char *)"{\"type\":\"simulator\",\"metric\":["); + for (simulator = Config.simulators; simulator; simulator = simulator->next) { + if (comma) + payload = xstrcat(payload, (char *)","); + payloadu = simulator_json(simulator); + payload = xstrcat(payload, payloadu); + comma = true; + free(payloadu); + payloadu = NULL; + } + payload = xstrcat(payload, (char *)"]}"); + ws_broadcast(payload); + free(payload); + payload = NULL; +} + + + void *my_simulator_loop(void *threadid) { simulator_list *simulator; @@ -59,17 +213,17 @@ if (my_simulator_shutdown) break; - for (simulator = Config.simulators; simulator; simulator = simulator->next) { - if (my_simulator_shutdown) - break; + now = time(NULL); + if (now != last) { + last = now; + /* + * Each second + */ + seconds++; - now = time(NULL); - if (now != last) { - last = now; - /* - * Each second - */ - seconds++; + for (simulator = Config.simulators; simulator; simulator = simulator->next) { + if (my_simulator_shutdown) + break; /* * First, calculate temperature difference between the room and the air in the @@ -79,7 +233,7 @@ */ sqm_room_air = (cbrtl(simulator->volume_air) * cbrtl(simulator->volume_air) * 6) / 100; /* square meters all fridge sides */ thick_room_air = 0.04; /* 4 cm walls */ - k_room_air = 0.03; /* Polystrene */ + k_room_air = 0.03; /* Polystrene */ air_heat_transfer=(k_room_air * sqm_room_air * (simulator->room_temperature - simulator->air_temperature)) / thick_room_air; air_change = (air_heat_transfer / (vhc_air * ((simulator->volume_air - simulator->volume_beer) * 1000))) / 60.0; simulator->air_temperature += air_change; @@ -117,7 +271,7 @@ simulator->s_cool_temp -= (simulator->s_cool_temp - simulator->air_temperature) / 25.0; } - /* + /* * Calculate final temperature of the beer and the air. */ // Cheap trick, just follow slowly the air temp. diff -r ea24b4ce02b1 -r 24749c296a50 thermferm/simulator.h --- a/thermferm/simulator.h Sun Apr 28 15:50:42 2024 +0200 +++ b/thermferm/simulator.h Tue Apr 30 17:26:41 2024 +0200 @@ -3,12 +3,10 @@ #ifdef USE_SIMULATOR +char *simulator_json(simulator_list *simulator); +void simulator_ws(void); -#ifdef HAVE_WIRINGPI_H -PI_THREAD (my_simulator_loop); -#else void *my_simulator_loop(void *); -#endif #endif diff -r ea24b4ce02b1 -r 24749c296a50 thermferm/thermferm.c --- a/thermferm/thermferm.c Sun Apr 28 15:50:42 2024 +0200 +++ b/thermferm/thermferm.c Tue Apr 30 17:26:41 2024 +0200 @@ -1412,7 +1412,7 @@ if ((unit->mode == UNITMODE_BEER) && ((unit->air_temperature / 1000.0) > (unit->PID_heat->Input + 8.0))) { unit->PID_heat->OutP = 0.0; } - if (seconds == 60) { + if ((seconds == 60) && (unit->mode > UNITMODE_NONE)) { syslog(LOG_NOTICE, "Heat: sp=%.3f Input=%.3f iState=%.3f Err=%.3f Out=%.1f", unit->PID_heat->SetP, unit->PID_heat->Input, unit->PID_heat->iState, unit->PID_heat->Err, unit->PID_heat->OutP); } @@ -1438,7 +1438,7 @@ } } } - if (seconds == 60) { + if ((seconds == 60) && (unit->mode > UNITMODE_NONE)) { syslog(LOG_NOTICE, "Cool: sp=%.3f Input=%.3f iState=%.3f Err=%.3f Out=%.1f", unit->PID_cool->SetP, unit->PID_cool->Input, unit->PID_cool->iState, unit->PID_cool->Err, unit->PID_cool->OutP); } diff -r ea24b4ce02b1 -r 24749c296a50 thermferm/thermferm.h --- a/thermferm/thermferm.h Sun Apr 28 15:50:42 2024 +0200 +++ b/thermferm/thermferm.h Tue Apr 30 17:26:41 2024 +0200 @@ -330,22 +330,46 @@ struct _simulator *next; char *uuid; /* Simulator uuid */ char *name; /* Simulator name */ + int simno; /* Simulator number */ int volume_air; /* Volume air of the frigo */ int volume_beer; /* Volume beer inside frigo */ + char *room_tempaddress; /* Address */ double room_temperature; /* Temp outside frigo */ + char *room_humaddress; /* Address */ double room_humidity; /* Humidity outside frigo */ + char *air_address; /* Simulated air address */ double air_temperature; /* Simulated air temperature */ + int air_present; /* Simulated air present */ + char *beer_address; double beer_temperature; /* Simulated beer temperature */ + int beer_present; /* Simulated beer present */ + char *beer_address2; + double beer_temperature2; /* Simulated beer temperature2 */ + int beer_present2; /* Simulated beer present2 */ + char *chiller_address; double chiller_temperature; /* Simulated chiller temp. */ + int chiller_present; /* Simulated chiller present */ + char *cooler_address; double cooler_temp; /* Lowest cooler temperature */ int cooler_time; /* Time to reach temperature */ float cooler_size; /* Size of cooler in square mtr */ + int cooler_present; /* Simulated cooler present */ + int cooler_power; /* Simulated cooler 0..100% */ + char *heater_address; double 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 */ + int heater_present; /* Simulated heater present */ + int heater_power; /* Simulated heater 0..100% */ + char *fan_address; /* Simulated fan */ + int fan_present; + int fan_power; + char *light_address; /* Simulated interior light */ + int light_present; + int light_power; float frigo_isolation; /* Frigo isolation value */ + time_t timestamp; + /* * Status values, maintained by the simulator but stored * here so they don't get lost over program restarts. diff -r ea24b4ce02b1 -r 24749c296a50 www/dbdevices.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/www/dbdevices.php Tue Apr 30 17:26:41 2024 +0200 @@ -0,0 +1,112 @@ + 15, 'usec' => 0)); + } else { + socket_close($sock); + } + } + return $sock; +} + + +/** + * @param string $command to send to the server. + * @return string with the complete reply from the + * server. This can be a multiline reply. + */ +function send_cmd($command) +{ + $sock = open_socket(); + if ($sock == false) { + return ""; + } + socket_write($sock, $command . "\r\n", 4096); + + $answer = ""; + while (1) { + $line = socket_read($sock, 4096); + if ($line === '') + break; + $answer .= $line; + } + socket_close($sock); + + return $answer; +} + + +function startsWith($haystack, $needle) +{ + return !strncmp($haystack, $needle, strlen($needle)); +} + + +$response = array( + 'error' => false, + 'msg' => 'Ok', +); + + +if (isset($_POST['update'])) { + + $cmd = "DEVICE PUT " . $_POST['uuid'] . "\r\n"; + $cmd .= "TYPE," . $_POST['type'] . "\r\n"; + $cmd .= "DIRECTION," . $_POST['direction'] . "\r\n"; + $cmd .= "VALUE," . $_POST['value'] . "\r\n"; // Only for outputs + $cmd .= "OFFSET," . $_POST['offset'] . "\r\n"; // Only analog + $cmd .= "PRESENT," . $_POST['present'] . "\r\n"; + $cmd .= "ADDRESS," . $_POST['address'] . "\r\n"; // Not for auto detected + $cmd .= "SUBDEVICE," . $_POST['subdevice'] . "\r\n"; // Not for auto detected + $cmd .= "GPIOPIN," . $_POST['gpiopin'] . "\r\n"; + $cmd .= "DESCRIPTION," . $_POST['description'] . "\r\n"; + $cmd .= "COMMENT," . $_POST['comment'] . "\r\n"; + $cmd .= "."; + $answer = send_cmd($cmd); + $arr = explode("\r\n", $answer); + if (! startsWith($arr[0], "219")) { + $response['error'] = true; + $response['msg'] = $arr[0]; + } + exit(json_encode($response)); + +} else if (isset($_POST['add'])) { + + $answer = send_cmd("DEVICE ADD " . $_POST['type']); + $arr = explode("\r\n", $answer); + if (! startsWith($arr[0], "211")) { + $response['error'] = true; + $response['msg'] = $arr[0]; + } + exit(json_encode($response)); + +} else if (isset($_POST['del'])) { + + $answer = send_cmd("DEVICE DEL " . $_POST['uuid']); + $arr = explode("\r\n", $answer); + if (! startsWith($arr[0], "211")) { + $response['error'] = true; + $response['msg'] = $arr[0]; + } + exit(json_encode($response)); + +} else { + + $answer = send_cmd("DEVICE JSON"); + header("Content-type: application/json"); + + $arr = explode("\r\n", $answer); + if (startsWith($arr[0], "212")) { + echo $arr[1]; + } else { + echo '{}'; + } +} + +?> diff -r ea24b4ce02b1 -r 24749c296a50 www/dbsimulators.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/www/dbsimulators.php Tue Apr 30 17:26:41 2024 +0200 @@ -0,0 +1,126 @@ + 15, 'usec' => 0)); + } else { + socket_close($sock); + } + } + return $sock; +} + + +/** + * @param string $command to send to the server. + * @return string with the complete reply from the + * server. This can be a multiline reply. + */ +function send_cmd($command) +{ + $sock = open_socket(); + if ($sock == false) { + return ""; + } + socket_write($sock, $command . "\r\n", 4096); + + $answer = ""; + while (1) { + $line = socket_read($sock, 4096); + if ($line === '') + break; + $answer .= $line; + } + socket_close($sock); + + return $answer; +} + + +function startsWith($haystack, $needle) +{ + return !strncmp($haystack, $needle, strlen($needle)); +} + + +$response = array( + 'error' => false, + 'msg' => 'Ok', +); + + +if (isset($_POST['update'])) { + + $cmd = "SIMULATOR PUT " . $_POST['uuid'] . "\r\n"; + $cmd .= "NAME," . $_POST['name'] . "\r\n"; + $cmd .= "VOLUME_AIR," . $_POST['volume_air'] . "\r\n"; + $cmd .= "VOLUME_BEER," . $_POST['volume_beer'] . "\r\n"; + $cmd .= "ROOM_TEMPERATURE," . $_POST['room_temperature'] . "\r\n"; + $cmd .= "ROOM_HUMIDITY," . $_POST['room_humidity'] . "\r\n"; + $cmd .= "AIR_PRESENT," . $_POST['air_present'] . "\r\n"; + $cmd .= "BEER_PRESENT," . $_POST['beer_present'] . "\r\n"; + $cmd .= "BEER_PRESENT2," . $_POST['beer_present2'] . "\r\n"; + $cmd .= "CHILLER_PRESENT," . $_POST['chiller_present'] . "\r\n"; + $cmd .= "COOLER_TEMP," . $_POST['cooler_temp'] . "\r\n"; + $cmd .= "COOLER_TIME," . $_POST['cooler_time'] . "\r\n"; + $cmd .= "COOLER_SIZE," . $_POST['cooler_size'] . "\r\n"; + $cmd .= "COOLER_PRESENT," . $_POST['cooler_present'] . "\r\n"; + $cmd .= "COOLER_POWER," . $_POST['cooler_power'] . "\r\n"; + $cmd .= "HEATER_TEMP," . $_POST['heater_temp'] . "\r\n"; + $cmd .= "HEATER_TIME," . $_POST['heater_time'] . "\r\n"; + $cmd .= "HEATER_SIZE," . $_POST['heater_size'] . "\r\n"; + $cmd .= "HEATER_PRESENT," . $_POST['heater_present'] . "\r\n"; + $cmd .= "HEATER_POWER," . $_POST['heater_power'] . "\r\n"; + $cmd .= "FAN_PRESENT," . $_POST['fan_present'] . "\r\n"; + $cmd .= "FAN_POWER," . $_POST['fan_power'] . "\r\n"; + $cmd .= "LIGHT_PRESENT," . $_POST['light_present'] . "\r\n"; + $cmd .= "LIGHT_POWER," . $_POST['light_power'] . "\r\n"; + $cmd .= "FRIGO_ISOLATION," . $_POST['frigo_isolation'] . "\r\n"; + $cmd .= "."; + $answer = send_cmd($cmd); + $arr = explode("\r\n", $answer); + if (! startsWith($arr[0], "219")) { + $response['error'] = true; + $response['msg'] = $arr[0]; + } + exit(json_encode($response)); + +} else if (isset($_POST['add'])) { + + $answer = send_cmd("SIMULATOR ADD " . $_POST['name']); + $arr = explode("\r\n", $answer); + if (! startsWith($arr[0], "211")) { + $response['error'] = true; + $response['msg'] = $arr[0]; + } + exit(json_encode($response)); + +} else if (isset($_POST['del'])) { + + $answer = send_cmd("SIMULATOR DEL " . $_POST['uuid']); + $arr = explode("\r\n", $answer); + if (! startsWith($arr[0], "211")) { + $response['error'] = true; + $response['msg'] = $arr[0]; + } + exit(json_encode($response)); + +} else { + + $answer = send_cmd("SIMULATOR JSON"); + header("Content-type: application/json"); + + $arr = explode("\r\n", $answer); + if (startsWith($arr[0], "212")) { + echo $arr[1]; + } else { + echo '{}'; + } +} + +?> diff -r ea24b4ce02b1 -r 24749c296a50 www/getdevices.php --- a/www/getdevices.php Sun Apr 28 15:50:42 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,112 +0,0 @@ - 15, 'usec' => 0)); - } else { - socket_close($sock); - } - } - return $sock; -} - - -/** - * @param string $command to send to the server. - * @return string with the complete reply from the - * server. This can be a multiline reply. - */ -function send_cmd($command) -{ - $sock = open_socket(); - if ($sock == false) { - return ""; - } - socket_write($sock, $command . "\r\n", 4096); - - $answer = ""; - while (1) { - $line = socket_read($sock, 4096); - if ($line === '') - break; - $answer .= $line; - } - socket_close($sock); - - return $answer; -} - - -function startsWith($haystack, $needle) -{ - return !strncmp($haystack, $needle, strlen($needle)); -} - - -$response = array( - 'error' => false, - 'msg' => 'Ok', -); - - -if (isset($_POST['update'])) { - - $cmd = "DEVICE PUT " . $_POST['uuid'] . "\r\n"; - $cmd .= "TYPE," . $_POST['type'] . "\r\n"; - $cmd .= "DIRECTION," . $_POST['direction'] . "\r\n"; - $cmd .= "VALUE," . $_POST['value'] . "\r\n"; // Only for outputs - $cmd .= "OFFSET," . $_POST['offset'] . "\r\n"; // Only analog - $cmd .= "PRESENT," . $_POST['present'] . "\r\n"; - $cmd .= "ADDRESS," . $_POST['address'] . "\r\n"; // Not for auto detected - $cmd .= "SUBDEVICE," . $_POST['subdevice'] . "\r\n"; // Not for auto detected - $cmd .= "GPIOPIN," . $_POST['gpiopin'] . "\r\n"; - $cmd .= "DESCRIPTION," . $_POST['description'] . "\r\n"; - $cmd .= "COMMENT," . $_POST['comment'] . "\r\n"; - $cmd .= "."; - $answer = send_cmd($cmd); - $arr = explode("\r\n", $answer); - if (! startsWith($arr[0], "219")) { - $response['error'] = true; - $response['msg'] = $arr[0]; - } - exit(json_encode($response)); - -} else if (isset($_POST['add'])) { - - $answer = send_cmd("DEVICE ADD " . $_POST['type']); - $arr = explode("\r\n", $answer); - if (! startsWith($arr[0], "211")) { - $response['error'] = true; - $response['msg'] = $arr[0]; - } - exit(json_encode($response)); - -} else if (isset($_POST['del'])) { - - $answer = send_cmd("DEVICE DEL " . $_POST['uuid']); - $arr = explode("\r\n", $answer); - if (! startsWith($arr[0], "211")) { - $response['error'] = true; - $response['msg'] = $arr[0]; - } - exit(json_encode($response)); - -} else { - - $answer = send_cmd("DEVICE JSON"); - header("Content-type: application/json"); - - $arr = explode("\r\n", $answer); - if (startsWith($arr[0], "212")) { - echo $arr[1]; - } else { - echo '{}'; - } -} - -?> diff -r ea24b4ce02b1 -r 24749c296a50 www/js/global.js --- a/www/js/global.js Sun Apr 28 15:50:42 2024 +0200 +++ b/www/js/global.js Tue Apr 30 17:26:41 2024 +0200 @@ -83,10 +83,14 @@ Show0dec = { inputMode: 'simple', theme: theme, width: 90, height: 23, readOnly: true, decimalDigits: 0 }, Show1dec = { inputMode: 'simple', theme: theme, width: 90, height: 23, readOnly: true, decimalDigits: 1 }, +Show2dec = { inputMode: 'simple', theme: theme, width: 90, height: 23, readOnly: true, decimalDigits: 2 }, +Show3dec = { inputMode: 'simple', theme: theme, width: 90, height: 23, readOnly: true, decimalDigits: 3 }, +Show4dec = { inputMode: 'simple', theme: theme, width: 90, height: 23, readOnly: true, decimalDigits: 4 }, Spin0dec = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, decimalDigits: 0, spinButtons: true }, Spin1dec = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, decimalDigits: 1, spinButtons: true }, Spin2dec = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, decimalDigits: 2, spinButtons: true }, Spin3dec = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, decimalDigits: 3, spinButtons: true }, +Spin4dec = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, decimalDigits: 4, spinButtons: true }, PosInt = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, decimalDigits: 0, spinButtons: true }, SubInt = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, max: 63, decimalDigits: 0, spinButtons: true }, GPIOInt = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: -1, max:31, decimalDigits: 0, spinButtons: true }, diff -r ea24b4ce02b1 -r 24749c296a50 www/js/set_devices.js --- a/www/js/set_devices.js Sun Apr 28 15:50:42 2024 +0200 +++ b/www/js/set_devices.js Tue Apr 30 17:26:41 2024 +0200 @@ -78,7 +78,7 @@ $(document).ready(function() { var dataRecord = {}, - url = 'getdevices.php', + url = 'dbdevices.php', source = { datatype: 'json', cache: false, diff -r ea24b4ce02b1 -r 24749c296a50 www/js/set_simulators.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/www/js/set_simulators.js Tue Apr 30 17:26:41 2024 +0200 @@ -0,0 +1,358 @@ +/***************************************************************************** + * Copyright (C) 2024 + * + * Michiel Broek + * + * This file is part of 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. + * + * BrewCloud 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. + *****************************************************************************/ + + +function createDelElements() { + $('#eventWindow').jqxWindow({ + theme: theme, + position: { x: 430, y: 210 }, + width: 420, + height: 175, + resizable: false, + isModal: true, + modalOpacity: 0.4, + okButton: $('#delOk'), + cancelButton: $('#delCancel'), + initContent: function() { + $('#delOk').jqxButton({ template: 'danger', width: '65px', theme: theme }); + $('#delCancel').jqxButton({ template: 'success', width: '65px', theme: theme }); + $('#delCancel').focus(); + } + }); + $('#eventWindow').jqxWindow('hide'); +} + + +function createAddElements() { + $('#addWindow').jqxWindow({ + theme: theme, + position: { x: 400, y: 210 }, + width: 480, + height: 180, + resizable: false, + isModal: true, + modalOpacity: 0.4, + okButton: $('#addOk'), + cancelButton: $('#addCancel'), + initContent: function() { + $('#addOk').jqxButton({ template: 'success', width: '65px', theme: theme }); + $('#addCancel').jqxButton({ template: 'primary', width: '65px', theme: theme }); + $('#addCancel').focus(); + } + }); + $('#addWindow').jqxWindow('hide'); +} + + + +$(document).ready(function() { + var dataRecord = {}, + url = 'dbsimulators.php', + source = { + datatype: 'json', + cache: false, + datafields: [ + { name: 'uuid', type: 'string' }, + { name: 'name', type: 'string' }, + { name: 'simno', type: 'int' }, + { name: 'volume_air', type: 'int' }, + { name: 'volume_beer', type: 'int' }, + { name: 'room_tempaddress', map: 'room>tempaddress' }, + { name: 'room_temperature', map: 'room>temperature', type: 'float' }, + { name: 'room_humaddress', map: 'room>humaddress' }, + { name: 'room_humidity', map: 'room>humidity', type: 'float' }, + { name: 'air_address', map: 'air>address' }, + { name: 'air_temperature', map: 'air>temperature', type: 'float' }, + { name: 'air_present', map: 'air>present' }, + { name: 'beer_address', map: 'beer>address' }, + { name: 'beer_temperature', map: 'beer>temperature', type: 'float' }, + { name: 'beer_present', map: 'beer>present' }, + { name: 'beer_address2', map: 'beer2>address' }, + { name: 'beer_temperature2', map: 'beer2>temperature', type: 'float' }, + { name: 'beer_present2', map: 'beer2>present' }, + { name: 'chiller_address', map: 'chiller>address' }, + { name: 'chiller_temperature', map: 'chiller>temperature', type: 'float' }, + { name: 'chiller_present', map: 'chiller>present' }, + { name: 'cooler_address', map: 'cooler>address' }, + { name: 'cooler_temp', map: 'cooler>temperature', type: 'float' }, + { name: 'cooler_time', map: 'cooler>time', type: 'int' }, + { name: 'cooler_size', map: 'cooler>size', type: 'float' }, + { name: 'cooler_present', map: 'cooler>present' }, + { name: 'cooler_power', map: 'cooler>power', type: 'int' }, + { name: 'heater_address', map: 'heater>address' }, + { name: 'heater_temp', map: 'heater>temperature', type: 'float' }, + { name: 'heater_time', map: 'heater>time', type: 'int' }, + { name: 'heater_size', map: 'heater>size', type: 'float' }, + { name: 'heater_present', map: 'heater>present' }, + { name: 'heater_power', map: 'heater>power', type: 'int' }, + { name: 'fan_address', map: 'fan>address' }, + { name: 'fan_present', map: 'fan>present' }, + { name: 'fan_power', map: 'fan>power', type: 'int' }, + { name: 'light_address', map: 'light>address' }, + { name: 'light_present', map: 'light>present' }, + { name: 'light_power', map: 'light>power', type: 'int' }, + { name: 'frigo_isolation', type: 'float' }, + { name: 'timestamp', type: 'int' } + ], + id: 'uuid', + url: url + }, + dataAdapter = new $.jqx.dataAdapter(source), + editrow = -1, + tzoffset = (new Date()).getTimezoneOffset() * 60000; //offset in milliseconds + + // initialize the input fields. + $('#name').jqxInput({ theme: theme, width: 180, height: 23 }); + $('#simno').jqxNumberInput(Show0dec); + $('#uuid').jqxInput({ theme: theme, width: 360, height: 23 }); + $('#room_temperature,#room_humidity').jqxNumberInput(Perc1dec); + $('#volume_air,#volume_beer').jqxNumberInput(PosInt); + $('#frigo_isolation').jqxNumberInput(Spin3dec); + $('#timestamp').jqxInput({ theme: theme, width: 180, height: 23 }); + + $('#air_address,#beer_address,#beer_address2,#chiller_address,#cooler_address,#heater_address,#fan_address,#light_address').jqxInput({ theme: theme, width: 180, height: 23 }); + $('#air_temperature,#beer_temperature,#beer_temperature2,#chiller_temperature').jqxNumberInput(Show4dec); + $('#air_present,#beer_present,#beer_present2,#chiller_present,#cooler_present,#heater_present,#fan_present,#light_present').jqxDropDownList({ + theme: theme, + source: DevicePresentAdapter, + valueMember: 'mno', + displayMember: 'en', + width: 180, + height: 23, + autoDropDownHeight: true + }); + $('#cooler_power,#heater_power,#fan_power,#light_power').jqxNumberInput(Perc0); + $('#cooler_temp,#heater_temp').jqxNumberInput(Spin1dec); + $('#cooler_time,#heater_time').jqxNumberInput(PosInt); + $('#cooler_size,#heater_size').jqxNumberInput(Spin4dec); + + // initialize jqxGrid + $('#jqxgrid').jqxGrid({ + width: 1280, + height: 630, + source: dataAdapter, + theme: theme, + showstatusbar: true, + renderstatusbar: function(statusbar) { + var rowCount = $("#jqxgrid").jqxGrid('getrows').length; + statusbar.append('
Total items: ' + rowCount + '
'); + var container, addButton; + container = $('
'); + addButton = $('
New
'); + container.append(addButton); + statusbar.append(container); + addButton.jqxButton({ theme: theme, width: 90, height: 17 }); + // add new row. + addButton.click(function(event) { + $('#addWindow').jqxWindow('open'); + $('#addName').jqxInput({ theme: theme, width: 180, height: 23 }); + $('#addOk').click(function() { + var data, + data = 'add=true&name=' + $('#addName').val(); + console.log(data); + $.ajax({ + dataType: 'json', + url: url, + cache: false, + data: data, + type: 'POST', + success: function(data) { + if (data.error) { + console.log('add: ' + data.msg); + alert('Error: ' + data.msg); + } else { + console.log('add: success'); + } + }, + error: function(jqXHR, textStatus, errorThrown) {} + }); + $('#jqxgrid').jqxGrid('updatebounddata'); + }); + }); + }, + columns: [ + { text: 'Name', datafield: 'name' }, + { text: 'No.', datafield: 'simno', width: 80 }, + { text: 'Air temp', datafield: 'air_temperature', width: 100 }, + { text: 'Beer temp', datafield: 'beer_temperature', width: 100 }, + { text: 'Heater', datafield: 'heater_power', width: 80 }, + { text: 'Cooler', datafield: 'cooler_power', width: 80 }, + { text: 'Fan', datafield: 'fan_power', width: 80 }, + { text: 'Last change', datafield: 'timestamp', width: 190, + cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { + var date = new Date((value * 1000) - tzoffset).toISOString().slice(0, 19).replace("T", " "); + return '' + date + ''; + } + }, + { text: '', datafield: 'Edit', width: 100, align: 'center', columntype: 'button', cellsrenderer: function() { + return 'Edit'; + }, buttonclick: function(row) { + // open the popup window when the user clicks a button. + editrow = row; + $('#popupWindow').jqxWindow({ position: { x: 40, y: 15 } }); + dataRecord = $('#jqxgrid').jqxGrid('getrowdata', editrow); + $('#name').val(dataRecord.name); + $('#uuid').val(dataRecord.uuid); + $('#simno').val(dataRecord.simno); + $('#room_temperature').val(dataRecord.room_temperature); + $('#room_humidity').val(dataRecord.room_humidity); + $('#volume_air').val(dataRecord.volume_air); + $('#volume_beer').val(dataRecord.volume_beer); + $('#frigo_isolation').val(dataRecord.frigo_isolation); + + $('#air_address').val(dataRecord.air_address); + $('#air_temperature').val(dataRecord.air_temperature); + $('#air_present').val(dataRecord.air_present); + $('#beer_address').val(dataRecord.beer_address); + $('#beer_temperature').val(dataRecord.beer_temperature); + $('#beer_present').val(dataRecord.beer_present); + $('#beer_address2').val(dataRecord.beer_address2); + $('#beer_temperature2').val(dataRecord.beer_temperature2); + $('#beer_present2').val(dataRecord.beer_present2); + $('#chiller_address').val(dataRecord.chiller_address); + $('#chiller_temperature').val(dataRecord.chiller_temperature); + $('#chiller_present').val(dataRecord.chiller_present); + $('#cooler_address').val(dataRecord.cooler_address); + $('#cooler_power').val(dataRecord.cooler_power); + $('#cooler_present').val(dataRecord.cooler_present); + $('#cooler_temp').val(dataRecord.cooler_temp); + $('#cooler_time').val(dataRecord.cooler_time); + $('#cooler_size').val(dataRecord.cooler_size); + $('#heater_address').val(dataRecord.heater_address); + $('#heater_power').val(dataRecord.heater_power); + $('#heater_present').val(dataRecord.heater_present); + $('#heater_temp').val(dataRecord.heater_temp); + $('#heater_time').val(dataRecord.heater_time); + $('#heater_size').val(dataRecord.heater_size); + $('#fan_address').val(dataRecord.fan_address); + $('#fan_power').val(dataRecord.fan_power); + $('#fan_present').val(dataRecord.fan_present); + $('#light_address').val(dataRecord.light_address); + $('#light_power').val(dataRecord.light_power); + $('#light_present').val(dataRecord.light_present); + var date = new Date((dataRecord.timestamp * 1000) - tzoffset).toISOString().slice(0, 19).replace("T", " "); + $('#timestamp').val(date); + // show the popup window. + $('#popupWindow').jqxWindow('open'); + } + } + ], + }); + + // initialize the popup window and buttons. + $('#popupWindow').jqxWindow({ + width: 1280, + height: 625, + resizable: false, + theme: theme, + isModal: true, + autoOpen: false, + cancelButton: $('#Cancel'), + modalOpacity: 0.40 + }); + $('#popupWindow').on('open', function() { + $('#dev_description').jqxInput('selectAll'); + }); + $('#Delete').jqxButton({ template: 'danger', width: '90px', theme: theme }); + $('#Delete').click(function() { + if (editrow >= 0) { + // Open a popup to confirm this action. + $('#eventWindow').jqxWindow('open'); + $('#delOk').click(function() { + var data, + data = 'del=true&uuid=' + $('#dev_uuid').val(); + $.ajax({ + dataType: 'json', + url: url, + cache: false, + data: data, + type: 'POST', + success: function(data) { + if (data.error) { + console.log('del: ' + data.msg); + alert('Error: ' + data.msg); + } else { + console.log('del: success'); + } + }, + error: function(jqXHR, textStatus, errorThrown) {} + }); + $('#jqxgrid').jqxGrid('updatebounddata'); + }); + } + $('#popupWindow').jqxWindow('hide'); + }); + $('#Cancel').jqxButton({ template: 'primary', width: '90px', theme: theme }); + $('#Save').jqxButton({ template: 'success', width: '90px', theme: theme }); + $('#Save').click(function() { + var data, + row = { + uuid: dataRecord.uuid, + type: $('#dev_type').val(), + direction: $('#dev_direction').val(), + value: parseInt($('#dev_value').jqxNumberInput('decimal')), + offset: parseInt($('#dev_offset').jqxNumberInput('decimal')), + present: $('#dev_present').val(), + address: $('#dev_address').val(), + subdevice: parseInt($('#dev_subdevice').jqxNumberInput('decimal')), + gpiopin: parseInt($('#dev_gpiopin').jqxNumberInput('val')), + description: $('#dev_description').val(), + comment: $('#dev_comment').val() + }; + data = 'update=true&' + $.param(row); + console.log(data); + $.ajax({ + dataType: 'json', + url: url, + cache: false, + data: data, + type: 'POST', + success: function(data) { + if (data.error) { + console.log('update: ' + data.msg); + alert('Error: ' + data.msg); + } else { + console.log('update: success'); + } + }, + error: function(jqXHR, textStatus, errorThrown) {} + }); + $('#popupWindow').jqxWindow('hide'); + }); + createDelElements(); + createAddElements(); + + websocket.onmessage = function(evt) { + var msg = evt.data; + var obj = JSON.parse(msg); + + if (obj.ping) { + websocket.send('{"pong":' + obj.ping + '}'); + } + + if (obj.type == 'simulator') { + // Use the message to trigger update. + $('#jqxgrid').jqxGrid('updatebounddata'); + } + } +}); diff -r ea24b4ce02b1 -r 24749c296a50 www/set_simulators.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/www/set_simulators.php Tue Apr 30 17:26:41 2024 +0200 @@ -0,0 +1,175 @@ + + +
+
+
+
+
+ + + + + +
+
+ Add new device +
+
+
+ Press "OK" to create a new simulator record.
+ Press "Cancel" to close without adding a new simulator. +
+
+
Name:
+
+
+
+ + +
+
+
+
+ +