Added commandline switch --foreground, the --debug now only activates extra debug logging. No more console output. The thermferm process uses a state table, this makes the code more readable and have less large functions.

Sun, 07 Apr 2024 20:26:37 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sun, 07 Apr 2024 20:26:37 +0200
changeset 666
48cc8868f9f4
parent 665
66fae54fa7ba
child 667
bba6ca418e09

Added commandline switch --foreground, the --debug now only activates extra debug logging. No more console output. The thermferm process uses a state table, this makes the code more readable and have less large functions.

thermferm/mqtt.c file | annotate | diff | comparison | revisions
thermferm/rdconfig.c file | annotate | diff | comparison | revisions
thermferm/server.c file | annotate | diff | comparison | revisions
thermferm/thermferm.c file | annotate | diff | comparison | revisions
--- a/thermferm/mqtt.c	Sun Apr 07 16:05:06 2024 +0200
+++ b/thermferm/mqtt.c	Sun Apr 07 20:26:37 2024 +0200
@@ -171,7 +171,7 @@
     	*c = 0 ;
   
     if (debug)
-	fprintf(stdout, "piBoardId: Revision string: %s\n", line) ;
+	syslog(LOG_NOTICE, "piBoardId: Revision string: %s", line) ;
 
 // Need to work out if it's using the new or old encoding scheme:
 
@@ -201,7 +201,7 @@
 
     if ((revision &  (1 << 23)) != 0) {    // New way
 	if (debug)
-	    fprintf(stdout, "piBoardId: New Way: revision is: %08X\n", revision) ;
+	    syslog(LOG_NOTICE, "piBoardId: New Way: revision is: %08X", revision) ;
 
 	bRev      = (revision & (0x0F <<  0)) >>  0 ;
 	bType     = (revision & (0xFF <<  4)) >>  4 ;
@@ -217,10 +217,10 @@
 	*warranty = bWarranty ;
 
 	if (debug)
-	    fprintf(stdout, "piBoardId: rev: %d, type: %d, proc: %d, mfg: %d, mem: %d, warranty: %d\n", bRev, bType, bProc, bMfg, bMem, bWarranty) ;
+	    syslog(LOG_NOTICE, "piBoardId: rev: %d, type: %d, proc: %d, mfg: %d, mem: %d, warranty: %d", bRev, bType, bProc, bMfg, bMem, bWarranty) ;
     } else {                                 // Old way
 	if (debug)
-	    fprintf(stdout, "piBoardId: Old Way: revision is: %s\n", c) ;
+	    syslog(LOG_NOTICE, "piBoardId: Old Way: revision is: %s", c) ;
 
 	if (!isdigit (*c)) {
 	    syslog(LOG_NOTICE, "Bogus \"Revision\" line (no digit at start of revision)") ;
@@ -374,7 +374,7 @@
 void my_log_callback(struct mosquitto *my_mosq, void *obj, int level, const char *str)
 {
 //    if (debug)
-//    	fprintf(stdout, "MQTT: %s\n", str);
+//    	syslog(LOG_NOTICE, "MQTT: %s", str);
 }
 
 
--- a/thermferm/rdconfig.c	Sun Apr 07 16:05:06 2024 +0200
+++ b/thermferm/rdconfig.c	Sun Apr 07 20:26:37 2024 +0200
@@ -27,6 +27,7 @@
 #include "xutil.h"
 
 int			debug = FALSE;
+int			foreground = FALSE;
 sys_config		Config;			/* System configuration		*/
 extern pthread_mutex_t  mutexes[5];
 
@@ -481,7 +482,7 @@
     mypath = xstrcat(mypath, (char *)"thermferm.xml");
 
     if (debug)
-	fprintf(stdout, "Writing %s\n", mypath);
+	syslog(LOG_NOTICE, "Writing %s", mypath);
 
     if ((fp = fopen(mypath, "w")) == NULL) {
 	syslog(LOG_NOTICE, "could not rewrite %s", mypath);
--- a/thermferm/server.c	Sun Apr 07 16:05:06 2024 +0200
+++ b/thermferm/server.c	Sun Apr 07 20:26:37 2024 +0200
@@ -97,7 +97,6 @@
 
 //    if (debug) {
 //	syslog(LOG_NOTICE, "send: \"%s\"", out);
-//	fprintf(stdout, "send: \"%s\"\n", out);
 //    }
 
     if (send(s, out, strlen(out), 0) != strlen(out)) {
@@ -153,7 +152,6 @@
 
 //    if (debug) {
 //	syslog(LOG_NOTICE, "recv: %d `%s'", bytesloaded, buffer);
-//	fprintf(stdout, "recv: %d `%s'\n", bytesloaded, buffer);
 //    }
     return bytesloaded;
 }
@@ -2159,8 +2157,6 @@
 
     my_server_state = 1;
     syslog(LOG_NOTICE, "Thread my_server_loop started");
-    if (debug)
-	fprintf(stdout, "Thread my_server_loop started\n");
 
     /*
      * Prepare thread to stop in blocking accept() call.
--- a/thermferm/thermferm.c	Sun Apr 07 16:05:06 2024 +0200
+++ b/thermferm/thermferm.c	Sun Apr 07 20:26:37 2024 +0200
@@ -47,6 +47,7 @@
 int			row;
 
 extern int		debug;
+extern int		foreground;
 extern sys_config	Config;
 extern int		lcdHandle;
 extern int		slcdHandle;
@@ -118,7 +119,8 @@
 {
     fprintf(stdout, "mbsePi-apps thermferm v%s starting\n\n", VERSION);
     fprintf(stdout, "Usage: thermferm [-d] [-h]\n");
-    fprintf(stdout, "  -d --debug              Debug and run in foreground\n");
+    fprintf(stdout, "  -d --debug              Extra debug logging\n");
+    fprintf(stdout, "  -f --foreground         Run in foreground\n");
     fprintf(stdout, "  -h --help               Display this help\n");
 }
 
@@ -891,6 +893,7 @@
 	int option_index = 0;
 	static struct option long_options[] = {
 	    {"debug", 0, 0, 'c'},
+	    {"foreground", 0, 0, 'f'},
 	    {"help", 0, 0, 'h'},
 	    {0, 0, 0, 0}
 	};
@@ -902,6 +905,8 @@
 	switch (c) {
 	    case 'd':	debug = TRUE;
 			break;
+	    case 'f':	foreground = TRUE;
+			break;
 	    case 'h':	help();
 			return 1;
 	}
@@ -909,8 +914,6 @@
 
     openlog("thermferm", LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_USER);
     syslog(LOG_NOTICE, "mbsePi-apps thermferm v%s starting", VERSION);
-    if (debug)
-	fprintf(stdout, "mbsePi-apps thermferm v%s starting\n", VERSION);
 
     if (rdconfig()) {
 	fprintf(stderr, "Error reading configuration\n");
@@ -960,9 +963,9 @@
     slcdCharDef(slcdHandle, 5, HeatONOFF);
     slcdCharDef(slcdHandle, 6, RevHeatONOFF);
 
-    if (debug) {
+    if (foreground) {
 	/*
-	 * For debugging run in foreground.
+	 * Run in foreground.
 	 */
 	do {
 	    rc = server();
@@ -1023,13 +1026,11 @@
 
     killconfig();
     syslog(LOG_NOTICE, "Finished, rc=%d", rc);
-    if (debug)
-        fprintf(stdout, "Finished, rc=%d\n", rc);
     return rc;
 }
 
 
-void do_unit(units_list *unit, int LCDunit, int *seconds, int *minutes)
+void do_unit(units_list *unit, int LCDunit, int seconds, int minutes)
 {
     time_t		now;
     prof_step		*step;
@@ -1199,7 +1200,7 @@
 				run_minutes = run_seconds / 60;
 				run_hours = run_minutes / 60;
 				if (debug)
-				    fprintf(stdout, "run_HMS=%d,%d,%d ", run_hours, run_minutes, run_seconds);
+				    syslog(LOG_NOTICE, "run_HMS=%d,%d,%d", run_hours, run_minutes, run_seconds);
 
 				/*
 				 * Primary fermentation tests
@@ -1271,7 +1272,7 @@
 						unit->prof_fridge_mode = step->fridge_mode;
 					    }
 					    if (debug)
-						fprintf(stdout, "prof_fridge_mode=%d run_minutes=%d steptime=%d time_until_now=%d\n",
+						syslog(LOG_NOTICE, "prof_fridge_mode=%d run_minutes=%d steptime=%d time_until_now=%d",
 								unit->prof_fridge_mode, run_minutes, step->steptime, time_until_now);
 					} else {
 					    unit->prof_target_lo = step->target_lo;
@@ -1288,7 +1289,7 @@
 
 				if (valid_step == TRUE) {
 				    unit->prof_percent = (100 * run_minutes) / tot_minutes;
-				    if (((*minutes == 10) || (*minutes == 40)) && (*seconds == 1)) {
+				    if (((minutes == 10) || (minutes == 40)) && (seconds == 1)) {
 					syslog(LOG_NOTICE, "Profile `%s' running %dd %02d:%02d in step %d, %d%% done, fridge/beer %d%% %.3f..%.3f degrees", 
 								unit->profile_name, run_hours / 24, run_hours % 24, run_minutes % 60, current_step, 
 								unit->prof_percent, unit->prof_fridge_mode, unit->prof_target_lo, unit->prof_target_hi);
@@ -1431,7 +1432,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) {
 	    	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);
 	    }
@@ -1452,12 +1453,12 @@
 	   	if ((unit->chiller_temperature / 1000.0) > ((unit->air_temperature / 1000.0) - 1)) {
 		    unit->PID_cool->OutP = 0.0;
 		    unit->alarm_flag |= ALARM_FLAG_CHILLER;
-		    if (*seconds == 60) {
+		    if (seconds == 60) {
 		    	syslog(LOG_NOTICE, "Cool: Air=%.2f Chiller=%.2f alarm", unit->air_temperature / 1000.0, unit->chiller_temperature / 1000.0);
 		    }
 	    	}
 	    }
-	    if (*seconds == 60) {
+	    if (seconds == 60) {
 	    	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);
 	    }
@@ -1612,7 +1613,7 @@
 	    LCDspH = unit->prof_target_hi;
 	}
     }
-    if (*seconds == 60) {
+    if (seconds == 60) {
 	unit->mqtt_flag |= MQTT_FLAG_DATA;
     }
     pthread_mutex_lock(&mutexes[LOCK_LCD]);
@@ -1652,16 +1653,229 @@
 }
 
 
+SM_DECL(thermferm,(char *)"thermferm")
+SM_STATES
+    CheckRun,
+    WaitMinute,
+    DateTime,
+    RoomTHB,
+    Units,
+    ShowLCD,
+    Minute,
+    Keys,
+    Delay
+SM_NAMES
+    (char *)"CheckRun",
+    (char *)"WaitMinute",
+    (char *)"DateTime",
+    (char *)"RoomTHB",
+    (char *)"Units",
+    (char *)"ShowLCD",
+    (char *)"Minute",
+    (char *)"Keys",
+    (char *)"Delay"
+SM_EDECL
+
+    time_t	now, last = (time_t)0, ndata = (time_t)0;
+    int		key, LCDunit, rc, temp, seconds = 0, minutes = 0;
+    struct tm	*tm;
+    units_list	*unit;
+
+SM_START(CheckRun)
+
+SM_STATE(CheckRun)
+
+    if (my_shutdown) {
+	SM_SUCCESS;
+    }
+
+    /*
+     * Use to stop processing units. Should be used when a unit is
+     * added or removed.
+     */
+    if (run_pause) {
+	run_hold = TRUE;
+	syslog(LOG_NOTICE, "run_pause: entering hold state");
+	for (;;) {
+	    mDelay(100);
+	    if (! run_pause)
+		break;
+	}
+	syslog(LOG_NOTICE, "run_pause: leaving hold state");
+	run_hold = FALSE;
+        /*
+         * In case the LCD buffers were cleared, setup the first page.
+         */
+        pthread_mutex_lock(&mutexes[LOCK_LCD]);
+        lcd_buf_write(1, (char *)"   ThermFerm    ");
+        lcd_buf_write(2, (char *)"Version %s      ", VERSION);
+        pthread_mutex_unlock(&mutexes[LOCK_LCD]);
+    }
+    SM_PROCEED(WaitMinute);
+
+SM_STATE(WaitMinute)
+
+    if (my_shutdown) {
+        SM_SUCCESS;
+    }
+
+    now = time(NULL);
+    if (now != last) {
+	/*
+	 * Each second
+	 */
+	last = now;
+	seconds++;
+	SM_PROCEED(DateTime);
+    } else {
+	SM_PROCEED(Keys);
+    }
+
+SM_STATE(DateTime)
+
+    row = 3;
+    tm = localtime(&now);
+    pthread_mutex_lock(&mutexes[LOCK_LCD]);
+    lcd_buf_write(row++, "   %02d-%02d-%04d   ", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900);
+    lcd_buf_write(row++, "    %02d:%02d:%02d    ", tm->tm_hour, tm->tm_min, tm->tm_sec);
+    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
+    SM_PROCEED(RoomTHB);
+
+SM_STATE(RoomTHB)
+
+    pthread_mutex_lock(&mutexes[LOCK_LCD]);
+    lcd_buf_write(row, "Room temp N/A       ");
+    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
+    int updateHT = 0;
+    if (Config.temp_address) {
+	rc = device_in(Config.temp_address, &temp);
+	if (rc == DEVPRESENT_YES) {
+	    if (Config.temp_value != temp)
+		updateHT = 1;
+	    Config.temp_value = temp;
+	    Config.temp_state = 0;
+	    pthread_mutex_lock(&mutexes[LOCK_LCD]);
+	    lcd_buf_write(row, "Room temp %.1f%c    ", Config.temp_value / 1000.0, 0x01);
+	    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
+	} else if (rc == DEVPRESENT_ERROR) {
+	    Config.temp_state = 1;
+	} else {
+	    Config.temp_state = 2;
+	}
+	mDelay(10);
+    }
+    row++;
+
+    pthread_mutex_lock(&mutexes[LOCK_LCD]);
+    lcd_buf_write(row, " Humidity N/A       ");
+    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
+    if (Config.hum_address) {
+	rc = device_in(Config.hum_address, &temp);
+	if (rc == DEVPRESENT_YES) {
+	    if (Config.hum_value != temp)
+		updateHT = 1;
+	    Config.hum_value = temp;
+	    Config.hum_state = 0;
+	    pthread_mutex_lock(&mutexes[LOCK_LCD]);
+	    lcd_buf_write(row, " Humidity %.1f%%     ", Config.hum_value / 1000.0);
+	    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
+	} else if (rc == DEVPRESENT_ERROR) {
+	    Config.hum_state = 1;
+	} else {
+	    Config.hum_state = 2;
+	}
+	mDelay(10);
+    }
+    row++;
+
+    /*
+     * If TH(B) changed. or if 5 minutes without
+     * update, send the NDATA message.
+     */
+    if (updateHT || (now > (ndata + 300))) {
+	publishNData(false, 0);
+	ndata = now;
+    }
+    SM_PROCEED(Units);
+
+SM_STATE(Units)
+
+    LCDunit = 0;
+    for (unit = Config.units; unit; unit = unit->next) {
+	LCDunit++;
+	do_unit(unit, LCDunit, seconds, minutes);
+    }
+    SM_PROCEED(ShowLCD);
+
+SM_STATE(ShowLCD)
+
+    pthread_mutex_lock(&mutexes[LOCK_MENU]);
+    if (setupmenu == MENU_NONE) {
+	pthread_mutex_lock(&mutexes[LOCK_LCD]);
+	lcd_buf_show();
+	pthread_mutex_unlock(&mutexes[LOCK_LCD]);
+    }
+    pthread_mutex_unlock(&mutexes[LOCK_MENU]);
+    SM_PROCEED(Minute);
+
+SM_STATE(Minute)
+
+    if (seconds == 60) {
+	seconds = 0;
+
+	/*
+	 * Publish data every minute if unit is active.
+	 */
+	for (unit = Config.units; unit; unit = unit->next) {
+	    if (unit->mode != UNITMODE_OFF) {
+		publishDLog(unit);
+		if (unit->event_msg)
+		    free(unit->event_msg);
+		unit->event_msg = NULL;
+	    }
+	}
+
+	minutes++;
+	if (minutes == 60) {
+	    minutes = 0;
+	    /*
+	     * Log usage counters every hour
+	     */
+	    for (unit = Config.units; unit; unit = unit->next) {
+		syslog(LOG_NOTICE, "Unit `%s' usage heater=%d cooler=%d fan=%d", unit->alias, unit->heater_usage, unit->cooler_usage, unit->fan_usage);
+	    }
+	}
+
+	/*
+	 * Save the configuration each half hour.
+	 */
+	if ((minutes == 15) || (minutes == 45))
+	    wrconfig();
+    }
+    SM_PROCEED(Keys);
+
+SM_STATE(Keys)
+
+    slcdDummy(slcdHandle);
+    key = keycheck();
+    if (key != KEY_NONE)
+	panel_key_events(key);
+    SM_PROCEED(Delay);
+
+SM_STATE(Delay)
+
+    mDelay(100);
+    SM_PROCEED(CheckRun);
+
+SM_END
+SM_RETURN
+
 
 int server(void)
 {
-    time_t		now, last = (time_t)0, ndata = (time_t)0;;
-    units_list		*unit;
-    int			rc, run = 1, seconds = 0, minutes = 0, temp;
-    int			key;
-    struct tm		*tm;
-    long		t = 0;
-    int			LCDunit;
+    units_list	*unit;
+    int		rc;
+    long	t = 0;
 
     syslog(LOG_NOTICE, "Server process started");
     my_shutdown = my_reboot = FALSE;
@@ -1766,159 +1980,10 @@
     //                                0.9.17a2
     pthread_mutex_unlock(&mutexes[LOCK_LCD]);
 
-    do {
-	if (my_shutdown)
-	    run = 0;
-
-	/*
-	 * Use to stop processing units. Should be used when a unit is
-	 * added or removed.
-	 */
-	if (run_pause) {
-	    run_hold = TRUE;
-	    syslog(LOG_NOTICE, "run_pause: entering hold state");
-	    for (;;) {
-		mDelay(100);
-		if (! run_pause)
-		    break;
-	    }
-	    syslog(LOG_NOTICE, "run_pause: leaving hold state");
-	    run_hold = FALSE;
-	    /*
-	     * In case the LCD buffers were cleared, setup the first page.
-	     */
-	    pthread_mutex_lock(&mutexes[LOCK_LCD]);
-	    lcd_buf_write(1, (char *)"   ThermFerm    ");
-	    lcd_buf_write(2, (char *)"Version %s      ", VERSION);
-	    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
-	}
-
-	now = time(NULL);
-	if (now != last) {
-	    /*
-	     * Each second
-	     */
-	    last = now;
-	    seconds++;
-
-	    row = 3;
-	    tm = localtime(&now);
-	    pthread_mutex_lock(&mutexes[LOCK_LCD]);
-	    lcd_buf_write(row++, "   %02d-%02d-%04d   ", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900);
-	    lcd_buf_write(row++, "    %02d:%02d:%02d    ", tm->tm_hour, tm->tm_min, tm->tm_sec);
-	    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
-
-	    pthread_mutex_lock(&mutexes[LOCK_LCD]);
-	    lcd_buf_write(row, "Room temp N/A       ");
-	    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
-	    int updateHT = 0;
-            if (Config.temp_address) {
-		rc = device_in(Config.temp_address, &temp);
-		if (rc == DEVPRESENT_YES) {
-		    if (Config.temp_value != temp)
-			updateHT = 1;
-		    Config.temp_value = temp;
-		    Config.temp_state = 0;
-		    pthread_mutex_lock(&mutexes[LOCK_LCD]);
-		    lcd_buf_write(row, "Room temp %.1f%c    ", Config.temp_value / 1000.0, 0x01);
-		    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
-		} else if (rc == DEVPRESENT_ERROR) {
-		    Config.temp_state = 1;
-		} else {
-		    Config.temp_state = 2;
-		}
-		mDelay(10);
-	    }
-	    row++;
-
-	    pthread_mutex_lock(&mutexes[LOCK_LCD]);
-	    lcd_buf_write(row, " Humidity N/A       ");
-	    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
-
-	    if (Config.hum_address) {
-		rc = device_in(Config.hum_address, &temp);
-		if (rc == DEVPRESENT_YES) {
-		    if (Config.hum_value != temp)
-			updateHT = 1;
-		    Config.hum_value = temp;
-		    Config.hum_state = 0;
-		    pthread_mutex_lock(&mutexes[LOCK_LCD]);
-		    lcd_buf_write(row, " Humidity %.1f%%     ", Config.hum_value / 1000.0);
-		    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
-		} else if (rc == DEVPRESENT_ERROR) {
-		    Config.hum_state = 1;
-		} else {
-		    Config.hum_state = 2;
-		}
-		mDelay(10);
-	    }
-	    row++;
-	    /*
-	     * If TH(B) changed. or if 5 minutes without
-	     * update, send the NDATA message.
-	     */
-	    if (updateHT || (now > (ndata + 300))) {
-		publishNData(false, 0);
-		ndata = now;
-	    }
-
-	    LCDunit = 0;
-	    for (unit = Config.units; unit; unit = unit->next) {
-		LCDunit++;
-		do_unit(unit, LCDunit, &seconds, &minutes);
-
-	    } /* for units */
-
-	    pthread_mutex_lock(&mutexes[LOCK_MENU]);
-	    if (setupmenu == MENU_NONE) {
-		pthread_mutex_lock(&mutexes[LOCK_LCD]);
-	    	lcd_buf_show();
-		pthread_mutex_unlock(&mutexes[LOCK_LCD]);
-	    }
-	    pthread_mutex_unlock(&mutexes[LOCK_MENU]);
-
-	    if (seconds == 60) {
-		seconds = 0;
-
-		/*
-		 * Publish data every minute if unit is active.
-		 */
-		for (unit = Config.units; unit; unit = unit->next) {
-		    if (unit->mode != UNITMODE_OFF) {
-			publishDLog(unit);
-			if (unit->event_msg)
-			    free(unit->event_msg);
-			unit->event_msg = NULL;
-		    }
-		}
-
-		minutes++;
-		if (minutes == 60) {
-		    minutes = 0;
-		    /*
-		     * Log usage counters every hour
-		     */
-		    for (unit = Config.units; unit; unit = unit->next) {
-		    	syslog(LOG_NOTICE, "Unit `%s' usage heater=%d cooler=%d fan=%d", unit->alias, unit->heater_usage, unit->cooler_usage, unit->fan_usage);
-		    }
-		}
-
-		/*
-		 * Save the configuration each half hour.
-		 */
-		if ((minutes == 15) || (minutes == 45))
-		    wrconfig();
-	    }
-	}
-
-	slcdDummy(slcdHandle);
-	key = keycheck();
-	if (key != KEY_NONE)
-	    panel_key_events(key);
-
-	mDelay(100);
-
-    } while (run);
+    /*
+     * Run state table
+     */
+    thermferm();
 
     /*
      * Stop units processing in a neat way

mercurial