Version 0.9.19b3. The simulator thread can be paused to be able to add and delete simulators. Added simulated door and PSU status. Devices can now fully use multiple simulators. Better rounding of simulated temperature values. The server SIMULATOR DEL and ADD commands pause the simulator when the linked list is manipulated. Fixed SIGSEGV when a simulator is added. Added socket SO_REUSEADDR again to the server socket.

Thu, 02 May 2024 15:49:16 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Thu, 02 May 2024 15:49:16 +0200
changeset 716
5c30c8ef83a8
parent 715
f5d85af156ab
child 717
22dd7ab614e5

Version 0.9.19b3. The simulator thread can be paused to be able to add and delete simulators. Added simulated door and PSU status. Devices can now fully use multiple simulators. Better rounding of simulated temperature values. The server SIMULATOR DEL and ADD commands pause the simulator when the linked list is manipulated. Fixed SIGSEGV when a simulator is added. Added socket SO_REUSEADDR again to the server socket.

configure file | annotate | diff | comparison | revisions
configure.ac file | annotate | diff | comparison | revisions
thermferm/devices.c file | annotate | diff | comparison | revisions
thermferm/rdconfig.c file | annotate | diff | comparison | revisions
thermferm/server.c file | annotate | diff | comparison | revisions
thermferm/simulator.c file | annotate | diff | comparison | revisions
thermferm/thermferm.c file | annotate | diff | comparison | revisions
thermferm/thermferm.h file | annotate | diff | comparison | revisions
www/dbsimulators.php file | annotate | diff | comparison | revisions
www/js/set_simulators.js file | annotate | diff | comparison | revisions
www/set_simulators.php file | annotate | diff | comparison | revisions
--- a/configure	Wed May 01 14:38:37 2024 +0200
+++ b/configure	Thu May 02 15:49:16 2024 +0200
@@ -2037,7 +2037,7 @@
 
 
 PACKAGE="mbsePi-apps"
-VERSION="0.9.19b2"
+VERSION="0.9.19b3"
 COPYRIGHT="Copyright (C) 2014-2024 Michiel Broek, All Rights Reserved"
 CYEARS="2014-2024"
 
--- a/configure.ac	Wed May 01 14:38:37 2024 +0200
+++ b/configure.ac	Thu May 02 15:49:16 2024 +0200
@@ -8,7 +8,7 @@
 dnl General settings
 dnl After changeing the version number, run autoconf!
 PACKAGE="mbsePi-apps"
-VERSION="0.9.19b2"
+VERSION="0.9.19b3"
 COPYRIGHT="Copyright (C) 2014-2024 Michiel Broek, All Rights Reserved"
 CYEARS="2014-2024"
 AC_SUBST(PACKAGE)
--- a/thermferm/devices.c	Wed May 01 14:38:37 2024 +0200
+++ b/thermferm/devices.c	Thu May 02 15:49:16 2024 +0200
@@ -40,6 +40,9 @@
 extern const char	DEVPRESENT[4][6];
 extern const char	DEVDIR[7][11];
 
+#ifdef USE_SIMULATOR
+extern int		my_simulator_state;
+#endif
 
 
 /*
@@ -306,6 +309,7 @@
 
 #ifdef USE_SIMULATOR
 		if ((device->type == DEVTYPE_SIM) && (device->direction == DEVDIR_OUT_BIN)) {
+		    while (my_simulator_state == THREAD_PAUSE) { mDelay(20); };
 		    for (simulator = Config.simulators; simulator; simulator = simulator->next) {
 			if ((strcmp(device->address, simulator->cooler_address) == 0) ||
 			    (strcmp(device->address, simulator->heater_address) == 0) ||
@@ -715,20 +719,22 @@
 #ifdef USE_SIMULATOR
 
     for (simulator = Config.simulators; simulator; simulator = simulator->next) {
-	for (i = 0; i < 10; i++) {
+	for (i = 0; i < 12; 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)) {
+            	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) ||
+		    (i == 10 && strcmp(device->address, simulator->door_address) == 0) ||
+		    (i == 11 && strcmp(device->address, simulator->psu_address) == 0)) {
                     found = TRUE;
                     break;
             	}
@@ -800,6 +806,16 @@
 				ndev->description = xstrcpy((char *)"Simulated beer temperature (alt)");
 				ndev->present = simulator->beer_present2;
 				break;
+		    case 10:	ndev->direction = DEVDIR_IN_BIN;
+				ndev->address = xstrcpy(simulator->door_address);
+				ndev->description = xstrcpy((char *)"Simulated fridge door");
+				ndev->present = simulator->door_present;
+				break;
+		    case 11:	ndev->direction = DEVDIR_IN_BIN;
+				ndev->address = xstrcpy(simulator->psu_address);
+				ndev->description = xstrcpy((char *)"Simulated PSU status");
+				ndev->present = simulator->psu_present;
+				break;
 		}
 
 		syslog(LOG_NOTICE, "New Simulator device %s, subdevice %d", ndev->address, ndev->subdevice);
@@ -960,46 +976,60 @@
 #endif
 #ifdef USE_SIMULATOR
 		case DEVTYPE_SIM:
+			while (my_simulator_state == THREAD_PAUSE) { mDelay(20); };
 //			pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
-			if (Config.simulators) {
+			for (simulator = Config.simulators; simulator; simulator = simulator->next) {
 			    int		val;
 
-			    simulator = Config.simulators;
-			    if (device->subdevice == 0) {
+			    if (strcmp(simulator->room_tempaddress, device->address) == 0) {
 				val = (int)((int)(simulator->room_temperature * 1000) / 500) * 500;
 				if (device->value != val) {
 			    	    device->value = val;
 			    	    device->timestamp = time(NULL);
 				    changed = true;
 				}
-			    } else if (device->subdevice == 1) {
-				val = (int)((int)(simulator->air_temperature * 1000) / 62.5) * 62.5;
+			    } else if (strcmp(simulator->air_address, device->address) == 0) {
+				val = (int)((int)((simulator->air_temperature * 1000) + 31.25) / 62.5) * 62.5;
 				if (device->value != val) {
 			    	    device->value = val;
 			    	    device->timestamp = time(NULL);
 				    changed = true;
 				}
-			    } else if (device->subdevice == 2) {
-				val = (int)((int)(simulator->beer_temperature * 1000) / 62.5) * 62.5;
+			    } else if (strcmp(simulator->beer_address, device->address) == 0) {
+				val = (int)((int)((simulator->beer_temperature * 1000) + 31.25) / 62.5) * 62.5;
 				if (device->value != val) {
 			    	    device->value = val;
 			    	    device->timestamp = time(NULL);
 				    changed = true;
 				}
-			    } else if (device->subdevice == 5) {
+			    } else if (strcmp(simulator->room_humaddress, device->address) == 0) {
 				val = (int)((int)(simulator->room_humidity * 1000) / 500) * 500;
 				if (device->value != val) {
 				    device->value = val;
 				    device->timestamp = time(NULL);
 				    changed = true;
 				}
-			    } else if (device->subdevice == 6) {
+			    } else if (strcmp(simulator->chiller_address, device->address) == 0) {
 				val = (int)((int)(simulator->chiller_temperature * 1000) / 62.5) * 62.5;
 				if (device->value != val) {
 				    device->value = val;
 				    device->timestamp = time(NULL);
 				    changed = true;
 				}
+			    } else if (strcmp(simulator->door_address, device->address) == 0) {
+				val = simulator->door_value;
+				if (device->value != val) {
+				    device->value = val;
+				    device->timestamp = time(NULL);
+				    changed = true;
+				}
+			    } else if (strcmp(simulator->psu_address, device->address) == 0) {
+				val = simulator->psu_value;
+				if (device->value != val) {
+                                    device->value = val;
+                                    device->timestamp = time(NULL);
+                                    changed = true;
+                                }
 			    }
 			}
 //			pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
--- a/thermferm/rdconfig.c	Wed May 01 14:38:37 2024 +0200
+++ b/thermferm/rdconfig.c	Thu May 02 15:49:16 2024 +0200
@@ -174,6 +174,10 @@
 	    free(simulator->fan_address);
 	if (simulator->light_address)
 	    free(simulator->light_address);
+	if (simulator->door_address)
+	    free(simulator->door_address);
+	if (simulator->psu_address)
+	    free(simulator->psu_address);
 	free(simulator);
     }
     Config.simulators = NULL;
@@ -487,6 +491,12 @@
 	    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 "DOOR_ADDRESS", "%s", simulator->door_address);
+            xmlTextWriterWriteFormatElement(writer, BAD_CAST "DOOR_PRESENT", "%s", DEVPRESENT[simulator->door_present]);
+            xmlTextWriterWriteFormatElement(writer, BAD_CAST "DOOR_VALUE", "%d", simulator->door_value);
+	    xmlTextWriterWriteFormatElement(writer, BAD_CAST "PSU_ADDRESS", "%s", simulator->psu_address);
+            xmlTextWriterWriteFormatElement(writer, BAD_CAST "PSU_PRESENT", "%s", DEVPRESENT[simulator->psu_present]);
+            xmlTextWriterWriteFormatElement(writer, BAD_CAST "PSU_VALUE", "%d", simulator->psu_value);
 	    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);
@@ -1379,6 +1389,7 @@
     simulator->uuid = simulator->name = NULL;
     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->door_address = simulator->psu_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;
@@ -1622,6 +1633,44 @@
                 simulator->light_power = ival;
             xmlFree(key);
         }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"DOOR_ADDRESS"))) {
+            simulator->door_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"DOOR_PRESENT"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            for (i = 0; i < 4; i++) {
+                if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) {
+                    simulator->door_present = i;
+                    break;
+                }
+            }
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"DOOR_VALUE"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%d", &ival) == 1)
+                simulator->door_value = ival;
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PSU_ADDRESS"))) {
+            simulator->psu_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PSU_PRESENT"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            for (i = 0; i < 4; i++) {
+                if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) {
+                    simulator->psu_present = i;
+                    break;
+                }
+            }
+            xmlFree(key);
+        }
+        if ((!xmlStrcmp(cur->name, (const xmlChar *)"PSU_VALUE"))) {
+            key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+            if (sscanf((const char *)key, "%d", &ival) == 1)
+                simulator->psu_value = 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)
--- a/thermferm/server.c	Wed May 01 14:38:37 2024 +0200
+++ b/thermferm/server.c	Thu May 02 15:49:16 2024 +0200
@@ -958,6 +958,9 @@
 
 
 #ifdef USE_SIMULATOR
+
+extern int	my_simulator_command, my_simulator_state;
+
 int delete_Simulator(char *uuid)
 {
     simulator_list	*current = Config.simulators;
@@ -968,16 +971,36 @@
 	    if (previous == NULL) {
 		Config.simulators = current->next;
 	    	free(current->uuid);
-	    	current->uuid = NULL;
+//	    	current->uuid = NULL;
 	    	free(current->name);
-	    	current->name = NULL;
+//	    	current->name = NULL;
+		free(current->air_address);
+		free(current->beer_address);
+		free(current->beer_address2);
+		free(current->chiller_address);
+		free(current->cooler_address);
+		free(current->heater_address);
+		free(current->fan_address);
+		free(current->light_address);
+		free(current->door_address);
+		free(current->psu_address);
 	    	free(current);
 	    	return 1;
 	    } else {
 		free(current->uuid);
-		current->uuid = NULL;
+//		current->uuid = NULL;
 		free(current->name);
-		current->name = NULL;
+//		current->name = NULL;
+		free(current->air_address);
+                free(current->beer_address);
+                free(current->beer_address2);
+                free(current->chiller_address);
+                free(current->cooler_address);
+                free(current->heater_address);
+                free(current->fan_address);
+                free(current->light_address);
+                free(current->door_address);
+                free(current->psu_address);
 		previous->next = current->next;
 		free(current);
 		current = previous->next;
@@ -1003,7 +1026,7 @@
 int cmd_simulator(int s, char *buf)
 {
     char		*opt, *param, *kwd, *val, ibuf[SS_BUFSIZE];
-    simulator_list	*simulator, *tmps;
+    simulator_list	*simulator, *nsim;
     int			rc, rlen, ival, i;
     float		fval;
     uuid_t		uu;
@@ -1077,9 +1100,9 @@
 	 * 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;
+	    for (simulator = Config.simulators; simulator; simulator = simulator->next) {
+		if (simulator->simno > highno)
+		    highno = simulator->simno;
 		count++;
 	    }
 	}
@@ -1088,73 +1111,99 @@
 	    return 0;
 	}
 
-	simulator = (simulator_list *)malloc(sizeof(simulator_list));
-	simulator->next = NULL;
-	simulator->uuid = malloc(37);
+	my_simulator_command = THREAD_PAUSE;
+        while (my_simulator_state != THREAD_PAUSE) { mDelay(50); };
+	syslog(LOG_NOTICE, "SIMULATOR ADD thread paused");
+
+	nsim = (simulator_list *)malloc(sizeof(simulator_list));
+	memset(nsim, 0, sizeof(simulator_list));
+	nsim->next = NULL;
+	nsim->uuid = malloc(37);
 	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_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 */
-	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->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;
+	uuid_unparse(uu, nsim->uuid);
+	nsim->name = xstrcpy(param);
+	nsim->simno = highno + 1;
+	nsim->volume_air = 150;
+	nsim->volume_beer = 50;
+	snprintf(abuf, 63, "%d-%s", nsim->simno, (char *)"SimRoomTemp");
+	nsim->room_tempaddress = xstrcpy(abuf);
+	nsim->room_temperature = nsim->air_temperature = nsim->beer_temperature = nsim->beer_temperature2 = 20.0;
+	nsim->s_cool_temp = nsim->s_heat_temp = 20.0;
+	snprintf(abuf, 63, "%d-%s", nsim->simno, (char *)"SimRoomHum");
+	nsim->room_humaddress = xstrcpy(abuf);
+	nsim->room_humidity = 48.6;
+	snprintf(abuf, 63, "%d-%s", nsim->simno, (char *)"SimAirTemp");
+	nsim->air_address = xstrcpy(abuf);
+	snprintf(abuf, 63, "%d-%s", nsim->simno, (char *)"SimBeerTemp");
+	nsim->beer_address = xstrcpy(abuf);
+	snprintf(abuf, 63, "%d-%s", nsim->simno, (char *)"SimBeerTemp2");
+	nsim->beer_address2 = xstrcpy(abuf);
+	snprintf(abuf, 63, "%d-%s", nsim->simno, (char *)"SimChillerTemp");
+	nsim->chiller_address = xstrcpy(abuf);
+	snprintf(abuf, 63, "%d-%s", nsim->simno, (char *)"SimHeater");
+	nsim->heater_address = xstrcpy(abuf);
+	snprintf(abuf, 63, "%d-%s", nsim->simno, (char *)"SimCooler");
+	nsim->cooler_address = xstrcpy(abuf);
+	snprintf(abuf, 63, "%d-%s", nsim->simno, (char *)"SimFan");
+	nsim->fan_address = xstrcpy(abuf);
+	snprintf(abuf, 63, "%d-%s", nsim->simno, (char *)"SimLight");
+	nsim->light_address = xstrcpy(abuf);
+	snprintf(abuf, 63, "%d-%s", nsim->simno, (char *)"SimDoor");
+	nsim->door_address = xstrcpy(abuf);
+	snprintf(abuf, 63, "%d-%s", nsim->simno, (char *)"SimPSU");
+	nsim->psu_address = xstrcpy(abuf);
+	nsim->chiller_temperature = 1.5;	/* Chiller temperature */
+	nsim->cooler_temp =  1.5;		/* Cooling temperature */
+	nsim->cooler_time = 720;		/* About 12 minutes for the cooler plate */
+	nsim->cooler_size = 0.8;		/* 0.8 square meter cooler plate */
+	nsim->heater_temp = 150.0;		/* Heating temperature */
+	nsim->heater_time = 3;			/* 3 seconds to heat-up	*/
+	nsim->heater_size = 0.01;		/* 0.01 square meter heater plate */
+	nsim->door_value = nsim->psu_value = 0;
+	nsim->air_present = nsim->beer_present = DEVPRESENT_YES;
+	nsim->beer_present2 = nsim->chiller_present = nsim->cooler_present = nsim->heater_present = DEVPRESENT_UNDEF;
+	nsim->door_present = nsim->psu_present = DEVPRESENT_UNDEF;
+	nsim->frigo_isolation = 0.002;
+	nsim->timestamp = time(NULL);
+	nsim->s_yeast_heat = 0.0;
+	nsim->s_yeast_started = nsim->s_cool_changed = nsim->s_heat_changed = (int)0;
 
 	if (Config.simulators == NULL) {
-	    Config.simulators = simulator;
+		syslog(LOG_NOTICE, "SIMULATOR ADD root");
+	    Config.simulators = nsim;
 	} else {
-	    for (tmps = Config.simulators; tmps; tmps = tmps->next) {
-		if (tmps->next == NULL) {
-		    tmps->next = simulator;
+	    for (simulator = Config.simulators; simulator; simulator = simulator->next) {
+		    syslog(LOG_NOTICE, "SIMULATOR ADD no %d %s", simulator->simno, simulator->name);
+		if (simulator->next == NULL) {
+		    simulator->next = nsim;
+		    syslog(LOG_NOTICE, "SIMULATOR ADD here");
 		    break;
 		}
 	    }
 	}
 
-	syslog(LOG_NOTICE, "Simulator %s no %d added", simulator->uuid, simulator->simno);
-	srv_send(s, (char *)"211 Simulator %s added", simulator->uuid);
+	my_simulator_command = THREAD_RUN;
+        while (my_simulator_state != THREAD_RUN) { mDelay(50); };
+        syslog(LOG_NOTICE, "SIMULATOR ADD thread runs");
+
+	syslog(LOG_NOTICE, "Simulator %s no %d added", param, highno + 1);
+	srv_send(s, (char *)"211 Simulator %s added", param);
 	return 1;
     }
 
     if (strcmp(opt, (char *)"DEL") == 0) {
 	    // TODO: check devices in use.
 	    // TODO: delete simulated devices.
+	syslog(LOG_NOTICE, "Simulator DEL %s", param);
+
+	my_simulator_command = THREAD_PAUSE;
+        while (my_simulator_state != THREAD_PAUSE) { mDelay(50); };
+        syslog(LOG_NOTICE, "SIMULATOR DEL thread paused");
 	rc = delete_Simulator(param);
+	my_simulator_command = THREAD_RUN;
+        while (my_simulator_state != THREAD_RUN) { mDelay(50); };
+        syslog(LOG_NOTICE, "SIMULATOR DEL thread runs");
+
 	if (rc) {
 	    syslog(LOG_NOTICE, "Simulator %s deleted", param);
 	    srv_send(s, (char *)"211 Simulator %s deleted", param);
@@ -1207,6 +1256,12 @@
 		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 *)"DOOR_ADDRESS,%s", simulator->door_address);
+                srv_send(s, (char *)"DOOR_PRESENT,%s", DEVPRESENT[simulator->door_present]);
+                srv_send(s, (char *)"DOOR_VALUE,%d", simulator->door_value);
+		srv_send(s, (char *)"PSU_ADDRESS,%s", simulator->psu_address);
+                srv_send(s, (char *)"PSU_PRESENT,%s", DEVPRESENT[simulator->psu_present]);
+                srv_send(s, (char *)"PSU_VALUE,%d", simulator->psu_value);
 		srv_send(s, (char *)"FRIGO_ISOLATION,%.3f", simulator->frigo_isolation);
 		srv_send(s, (char *)"TIMESTAMP,%ld", (long)simulator->timestamp);
 		srv_send(s, (char *)".");
@@ -1457,6 +1512,40 @@
                                     simulator->light_power = ival;
                                 }
 
+			    } else if (strcmp(kwd, (char *)"DOOR_PRESENT") == 0) {
+                                for (i = 0; i < 4; i++) {
+                                    if (strcmp(val, DEVPRESENT[i]) == 0) {
+                                        if (simulator->door_present != i)
+                                            syslog(LOG_NOTICE, "Simulator %s door_present %s to %s", simulator->uuid, DEVPRESENT[simulator->door_present], DEVPRESENT[i]);
+                                        simulator->door_present = i;
+                                        device_present(simulator->door_address, i);
+                                        break;
+                                    }
+                                }
+
+			    } else if (strcmp(kwd, (char *)"DOOR_VALUE") == 0) {
+				ival = (strcmp(val, (char *)"true")) ? 0:1;
+                                if (simulator->door_value != ival)
+                            	    syslog(LOG_NOTICE, "Simulator %s door value %d to %d", simulator->uuid, simulator->door_value, ival);
+                                simulator->door_value = ival;
+
+			    } else if (strcmp(kwd, (char *)"PSU_PRESENT") == 0) {
+                                for (i = 0; i < 4; i++) {
+                                    if (strcmp(val, DEVPRESENT[i]) == 0) {
+                                        if (simulator->psu_present != i)
+                                            syslog(LOG_NOTICE, "Simulator %s psu_present %s to %s", simulator->uuid, DEVPRESENT[simulator->psu_present], DEVPRESENT[i]);
+                                        simulator->psu_present = i;
+                                        device_present(simulator->psu_address, i);
+                                        break;
+                                    }
+                                }
+
+			    } else if (strcmp(kwd, (char *)"PSU_VALUE") == 0) {
+				ival = (strcmp(val, (char *)"true")) ? 0:1;
+                                if (simulator->psu_value != ival)
+                                    syslog(LOG_NOTICE, "Simulator %s psu value %d to %d", simulator->uuid, simulator->psu_value, ival);
+                                simulator->psu_value = ival;
+
 			    } else if (strcmp(kwd, (char *)"FRIGO_ISOLATION") == 0) {
 				if (sscanf(val, "%f", &fval) == 1) {
 				    if (simulator->frigo_isolation != fval)
@@ -2264,7 +2353,8 @@
 					if ((unit->mode == UNITMODE_OFF) && (i != UNITMODE_OFF)) {
 					    unit->mqtt_flag |= MQTT_FLAG_BIRTH;
 					}
-					syslog(LOG_NOTICE, "Fermenter unit %s mode %s to %s", unit->uuid, UNITMODE[unit->mode], UNITMODE[i]);
+					if (unit->mode != i)
+					    syslog(LOG_NOTICE, "Fermenter unit %s mode %s to %s", unit->uuid, UNITMODE[unit->mode], UNITMODE[i]);
 					unit->mode = i;
 					/* Allways turn everything off after a mode change */
 					unit->PID_cool->OutP = unit->PID_heat->OutP = 0.0;
@@ -2586,12 +2676,12 @@
 	return 0;
     }
 
-//    if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) {
-//	syslog(LOG_NOTICE, "Can't setsockopt SO_REUSEADDR socket: %s", strerror(errno));
-//	close(ls);
-//	my_server_state = 0;
-//	return 0;
-//    }
+    if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) {
+	syslog(LOG_NOTICE, "Can't setsockopt SO_REUSEADDR socket: %s", strerror(errno));
+	close(ls);
+	my_server_state = 0;
+	return 0;
+    }
 
     if (bind(ls, (struct sockaddr *)&myaddr_in, sizeof(struct sockaddr_in)) == -1) {
 	syslog(LOG_NOTICE, "Can't bind to listen socket: %s", strerror(errno));
--- a/thermferm/simulator.c	Wed May 01 14:38:37 2024 +0200
+++ b/thermferm/simulator.c	Thu May 02 15:49:16 2024 +0200
@@ -29,14 +29,15 @@
 #include "websocket.h"
 #include "simulator.h"
 
-int			my_simulator_state = 0;
+int			my_simulator_state = THREAD_OFF;
+int			my_simulator_command = THREAD_OFF;
+
 
 #ifdef USE_SIMULATOR
 
 extern sys_config	Config;
 extern const char	DEVPRESENT[4][6];
 
-int			my_simulator_shutdown = 0;
 
 
 static int simulate(void);
@@ -154,6 +155,22 @@
     payload = xstrcat(payload, (char *)"\",\"power\":");
     sprintf(buf, "%d", simulator->light_power);
     payload = xstrcat(payload, buf);
+    payload = xstrcat(payload, (char *)"},\"door\":{\"address\":\"");
+
+    payload = xstrcat(payload, simulator->door_address);
+    payload = xstrcat(payload, (char *)"\",\"present\":\"");
+    payload = xstrcat(payload, (char *)DEVPRESENT[simulator->door_present]);
+    payload = xstrcat(payload, (char *)"\",\"value\":");
+    sprintf(buf, "%d", simulator->door_value);
+    payload = xstrcat(payload, buf);
+    payload = xstrcat(payload, (char *)"},\"psu\":{\"address\":\"");
+
+    payload = xstrcat(payload, simulator->psu_address);
+    payload = xstrcat(payload, (char *)"\",\"present\":\"");
+    payload = xstrcat(payload, (char *)DEVPRESENT[simulator->psu_present]);
+    payload = xstrcat(payload, (char *)"\",\"value\":");
+    sprintf(buf, "%d", simulator->psu_value);
+    payload = xstrcat(payload, buf);
     payload = xstrcat(payload, (char *)"},\"frigo_isolation\":");
 
     sprintf(buf, "%.4f", simulator->frigo_isolation);
@@ -195,7 +212,7 @@
 
 void *my_simulator_loop(void *threadid)
 {
-    my_simulator_state = 1;
+    my_simulator_command = THREAD_RUN;
     syslog(LOG_NOTICE, "Thread my_simulator_loop started");
 
     /*
@@ -204,7 +221,6 @@
     simulate();
 
     syslog(LOG_NOTICE, "Thread my_simulator_loop stopped");
-    my_simulator_state = 0;
     return 0;
 }
 
@@ -212,11 +228,13 @@
 SM_DECL(simulate, (char *)"simulator")
 SM_STATES
     Init,
+    Pause,
     Waiting,
     Run,
     Websocket
 SM_NAMES
     (char *)"Init",
+    (char *)"Pause",
     (char *)"Waiting",
     (char *)"run",
     (char *)"Websocket"
@@ -239,13 +257,28 @@
 	 */
 	simulator->s_heat_temp = simulator->s_cool_temp = simulator->room_temperature;
     }
+    my_simulator_state = THREAD_RUN;
     SM_PROCEED(Waiting);
 
+SM_STATE(Pause)
+
+    my_simulator_state = THREAD_PAUSE;
+    if (my_simulator_command == THREAD_OFF) {
+        SM_SUCCESS;
+    } else if (my_simulator_command == THREAD_RUN) {
+	my_simulator_state = THREAD_RUN;
+        SM_PROCEED(Waiting);
+    }
+    mDelay(50);
+
 SM_STATE(Waiting)
 
-    if (my_simulator_shutdown) {
+    if (my_simulator_command == THREAD_OFF) {
 	SM_SUCCESS;
+    } else if (my_simulator_command == THREAD_PAUSE) {
+	SM_PROCEED(Pause);
     }
+
     now = time(NULL);
     if (now != last) {
 	last = now;
@@ -258,8 +291,10 @@
 
     changed = false;
     for (simulator = Config.simulators; simulator; simulator = simulator->next) {
-	if (my_simulator_shutdown) {
+	if (my_simulator_command == THREAD_OFF) {
 	    SM_SUCCESS;
+	} else if (my_simulator_command == THREAD_PAUSE) {
+	    SM_PROCEED(Pause);
 	}
 
 	/*
@@ -347,8 +382,10 @@
 
 SM_STATE(Websocket)
 
-    if (my_simulator_shutdown) {
+    if (my_simulator_command == THREAD_OFF) {
 	SM_SUCCESS;
+    } else if (my_simulator_command == THREAD_PAUSE) {
+        SM_PROCEED(Pause);
     }
     if (changed) {
 	simulator_ws();
@@ -357,6 +394,9 @@
     SM_PROCEED(Waiting);
 
 SM_END
+
+    my_simulator_state = THREAD_OFF;
+
 SM_RETURN
 
 
--- a/thermferm/thermferm.c	Wed May 01 14:38:37 2024 +0200
+++ b/thermferm/thermferm.c	Thu May 02 15:49:16 2024 +0200
@@ -62,7 +62,7 @@
 extern int		my_ws_shutdown;
 extern int		my_simulator_state;
 #ifdef USE_SIMULATOR
-extern int		my_simulator_shutdown;
+extern int		my_simulator_command;
 #endif
 extern int		my_one_wire_state;
 extern int		my_one_wire_shutdown;
@@ -1869,9 +1869,9 @@
     my_shutdown = my_reboot = FALSE;
     my_devices_shutdown = my_panel_shutdown = my_server_shutdown = my_ws_shutdown = my_one_wire_shutdown = 0;
     my_devices_state = my_panel_state = my_server_state = my_ws_state = my_one_wire_state = 0;
-    my_simulator_state = 0;
+    my_simulator_state = THREAD_OFF;
 #ifdef USE_SIMULATOR
-    my_simulator_shutdown = 0;
+    my_simulator_command = THREAD_OFF;
 #endif
     if (lockprog((char *)"thermferm")) {
 	syslog(LOG_NOTICE, "Can't lock");
@@ -2026,8 +2026,8 @@
      * Stop threads
      */
 #ifdef USE_SIMULATOR
-    my_simulator_shutdown = 1;
-    while (my_simulator_state) { mDelay(50); };
+    my_simulator_command = THREAD_OFF;
+    while (my_simulator_state != THREAD_OFF) { mDelay(50); };
 #endif
     my_panel_shutdown = 1;
     while (my_panel_state) { mDelay(50); };
--- a/thermferm/thermferm.h	Wed May 01 14:38:37 2024 +0200
+++ b/thermferm/thermferm.h	Thu May 02 15:49:16 2024 +0200
@@ -63,6 +63,14 @@
 
 
 /*
+ * Thread states
+ */
+#define THREAD_OFF		0
+#define	THREAD_RUN		1
+#define	THREAD_PAUSE		2
+
+
+/*
  * Frontpanel menu numbers
  */
 #define MENU_NONE       	0
@@ -367,6 +375,12 @@
     char		*light_address;		/* Simulated interior light	*/
     int			light_present;
     int			light_power;
+    char		*door_address;		/* Simulated fridge door	*/
+    int			door_present;
+    int			door_value;
+    char		*psu_address;		/* Simulated PSU status		*/
+    int			psu_present;
+    int			psu_value;
     float		frigo_isolation;	/* Frigo isolation value	*/
     time_t		timestamp;
 
--- a/www/dbsimulators.php	Wed May 01 14:38:37 2024 +0200
+++ b/www/dbsimulators.php	Thu May 02 15:49:16 2024 +0200
@@ -80,6 +80,10 @@
     $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 .= "DOOR_PRESENT," . $_POST['door_present'] . "\r\n";
+    $cmd .= "DOOR_VALUE," . $_POST['door_value'] . "\r\n";
+    $cmd .= "PSU_PRESENT," . $_POST['psu_present'] . "\r\n";
+    $cmd .= "PSU_VALUE," . $_POST['psu_value'] . "\r\n";
     $cmd .= "FRIGO_ISOLATION," . $_POST['frigo_isolation'] . "\r\n";
     $cmd .= ".";
     $answer = send_cmd($cmd);
--- a/www/js/set_simulators.js	Wed May 01 14:38:37 2024 +0200
+++ b/www/js/set_simulators.js	Thu May 02 15:49:16 2024 +0200
@@ -110,6 +110,12 @@
    { name: 'light_address', map: 'light>address' },
    { name: 'light_present', map: 'light>present' },
    { name: 'light_power', map: 'light>power', type: 'int' },
+   { name: 'door_address', map: 'door>address' },
+   { name: 'door_present', map: 'door>present' },
+   { name: 'door_value', map: 'door>value', type: 'int' },
+   { name: 'psu_address', map: 'psu>address' },
+   { name: 'psu_present', map: 'psu>present' },
+   { name: 'psu_value', map: 'psu>value', type: 'int' },
    { name: 'frigo_isolation', type: 'float' },
    { name: 'timestamp', type: 'int' }
   ],
@@ -121,7 +127,7 @@
  tzoffset = (new Date()).getTimezoneOffset() * 60000; //offset in milliseconds
 
  // initialize the input fields.
- $('#name').jqxInput({ theme: theme, width: 180, height: 23 });
+ $('#name').jqxInput({ theme: theme, width: 240, height: 23 });
  $('#simno').jqxNumberInput(Show0dec);
  $('#uuid').jqxInput({ theme: theme, width: 360, height: 23 });
  $('#room_temperature,#room_humidity').jqxNumberInput(Perc1dec);
@@ -129,9 +135,9 @@
  $('#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_address,#beer_address,#beer_address2,#chiller_address,#cooler_address,#heater_address,#fan_address,#light_address,#door_address,#psu_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({
+ $('#air_present,#beer_present,#beer_present2,#chiller_present,#cooler_present,#heater_present,#fan_present,#light_present,#door_present,#psu_present').jqxDropDownList({
   theme: theme,
   source: DevicePresentAdapter,
   valueMember: 'mno',
@@ -144,6 +150,8 @@
  $('#cooler_temp,#heater_temp').jqxNumberInput(Spin1dec);
  $('#cooler_time,#heater_time').jqxNumberInput(PosInt);
  $('#cooler_size,#heater_size').jqxNumberInput(Spin4dec);
+ $('#door_value').jqxSwitchButton({ height: 23, width: 100, theme: theme });
+ $('#psu_value').jqxSwitchButton({ height: 23, width: 100, theme: theme });
 
  // initialize jqxGrid
  $('#jqxgrid').jqxGrid({
@@ -250,6 +258,12 @@
      $('#light_address').val(dataRecord.light_address);
      $('#light_power').val(dataRecord.light_power);
      $('#light_present').val(dataRecord.light_present);
+     $('#door_address').val(dataRecord.door_address);
+     $('#door_value').val(dataRecord.door_value);
+     $('#door_present').val(dataRecord.door_present);
+     $('#psu_address').val(dataRecord.psu_address);
+     $('#psu_value').val(dataRecord.psu_value);
+     $('#psu_present').val(dataRecord.psu_present);
      var date = new Date((dataRecord.timestamp * 1000) - tzoffset).toISOString().slice(0, 19).replace("T", " ");
      $('#timestamp').val(date);
      // show the popup window.
@@ -275,31 +289,29 @@
  });
  $('#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');
+  // Open a popup to confirm this action.
+  $('#eventWindow').jqxWindow('open');
+  $('#delOk').click(function() {
+   var data,
+   data = 'del=true&uuid=' + $('#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 });
@@ -331,6 +343,10 @@
    fan_power: parseInt($('#fan_power').jqxNumberInput('decimal')),
    light_present: $('#light_present').val(),
    light_power: parseInt($('#light_power').jqxNumberInput('decimal')),
+   door_present: $('#door_present').val(),
+   door_value: $('#door_value').val(),
+   psu_present: $('#psu_present').val(),
+   psu_value: $('#psu_value').val(),
    frigo_isolation: parseFloat($('#frigo_isolation').jqxNumberInput('decimal'))
   };
   data = 'update=true&' + $.param(row);
--- a/www/set_simulators.php	Wed May 01 14:38:37 2024 +0200
+++ b/www/set_simulators.php	Thu May 02 15:49:16 2024 +0200
@@ -131,10 +131,26 @@
        <td style="padding: 3px;"><div style="float: left;" id="light_present"></div></td>
        <td></td><td></td>
       </tr>
-
+      <tr>
+       <td style="vertical-align: top; float: right; padding: 3px;">Door address:</td>
+       <td style="vertical-align: top; padding: 3px;"><input id="door_address" readonly /></td>
+       <td style="vertical-align: top; float: right; padding: 3px;">Value:</td>
+       <td style="padding: 3px;"><div style="float: left;" id="door_value"></div></td>
+       <td style="vertical-align: top; float: right; padding: 3px;">Present:</td>
+       <td style="padding: 3px;"><div style="float: left;" id="door_present"></div></td>
+       <td></td><td></td>
+      </tr>
+      <tr>
+       <td style="vertical-align: top; float: right; padding: 3px;">PSU address:</td>
+       <td style="vertical-align: top; padding: 3px;"><input id="psu_address" readonly /></td>
+       <td style="vertical-align: top; float: right; padding: 3px;">Value:</td>
+       <td style="padding: 3px;"><div style="float: left;" id="psu_value"></div></td>
+       <td style="vertical-align: top; float: right; padding: 3px;">Present:</td>
+       <td style="padding: 3px;"><div style="float: left;" id="psu_present"></div></td>
+       <td></td><td></td>
+      </tr>
 
-
-      <tr><td colspan="8"></td></tr>
+      <tr><td colspan="8">&nbsp;</td></tr>
       <tr>
        <td style="padding-top: 20px;" align="right"><input type="button" id="Delete" value="Delete" /></td>
        <td></td>

mercurial