Version 0.3.33 Added websockets framework. Added fermenter status messages to the websockets broadcast.

Mon, 11 May 2020 17:32:08 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Mon, 11 May 2020 17:32:08 +0200
changeset 671
4b54d6f79d25
parent 670
638e7dd1d560
child 672
23f959713fcb

Version 0.3.33 Added websockets framework. Added fermenter status messages to the websockets broadcast.

bmsd/Makefile file | annotate | diff | comparison | revisions
bmsd/bms.c file | annotate | diff | comparison | revisions
bmsd/bms.h file | annotate | diff | comparison | revisions
bmsd/fermenters.c file | annotate | diff | comparison | revisions
bmsd/websocket.c file | annotate | diff | comparison | revisions
bmsd/websocket.h file | annotate | diff | comparison | revisions
config.status file | annotate | diff | comparison | revisions
configure file | annotate | diff | comparison | revisions
configure.ac file | annotate | diff | comparison | revisions
www/includes/global.inc.php file | annotate | diff | comparison | revisions
www/js/global.js file | annotate | diff | comparison | revisions
--- a/bmsd/Makefile	Thu May 07 14:59:50 2020 +0200
+++ b/bmsd/Makefile	Mon May 11 17:32:08 2020 +0200
@@ -59,10 +59,10 @@
 lock.o: lock.h bms.h futil.h
 nodes.o: bms.h xutil.h nodes.h mysql.h
 futil.o: bms.h futil.h
-fermenters.o: bms.h xutil.h fermenters.h mysql.h
+fermenters.o: bms.h xutil.h fermenters.h mysql.h websocket.h
 co2meters.o: bms.h xutil.h co2meters.h mysql.h
 ispindels.o: bms.h xutil.h ispindels.h mysql.h nodes.h
-bms.o: bms.h xutil.h futil.h rdconfig.h lock.h mqtt.h mysql.h nodes.h
+bms.o: bms.h xutil.h futil.h rdconfig.h lock.h mqtt.h mysql.h nodes.h websocket.h
 xutil.o: bms.h xutil.h
 rdconfig.o: bms.h xutil.h futil.h rdconfig.h
 mysql.o: bms.h xutil.h mysql.h nodes.h
--- a/bmsd/bms.c	Thu May 07 14:59:50 2020 +0200
+++ b/bmsd/bms.c	Mon May 11 17:32:08 2020 +0200
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * Copyright (C) 2017-2019
+ * Copyright (C) 2017-2020
  *   
  * Michiel Broek <mbroek at mbse dot eu>
  *
@@ -28,16 +28,19 @@
 #include "mqtt.h"
 #include "mysql.h"
 #include "nodes.h"
+#include "websocket.h"
 
 
 int			my_shutdown = FALSE;
 int			debug = FALSE;
-static pid_t		/*pgrp, */mypid;
+static pid_t		mypid;
 char			*Private_Path = NULL;	/* Users data path		*/
+pthread_t		ws_thread;
 
 extern sys_config       Config;
 
 
+
 void help(void)
 {
     fprintf(stdout, "bmsd v%s starting\n\n", VERSION);
@@ -128,19 +131,30 @@
 	goto endit2;
     }
 
+    rc = pthread_create(&ws_thread, NULL, ws_loop, NULL);
+    if (rc) {
+        fprintf(stderr, "ws_loop thread didn't start rc=%d\n", rc);
+        syslog(LOG_NOTICE, "ws_loop thread didn't start rc=%d", rc);
+	rc = 5;
+	goto endit3;
+    }
+
     if (debug)
 	fprintf(stdout, "[main] Entering main loop\n");
 
     while (my_shutdown == FALSE) {
 
-	usleep(4000000);
+	usleep(3000000);
 	nodes_check_online();
 	usleep(1000000);
 	ispindel_mysql_check();
+	usleep(1000000);
+	ws_check();
     }
     if (debug)
 	fprintf(stdout, "[main] Exit from main loop\n");
 
+endit3:
     /*
      * Remove our topics and close MQTT connection.
      */
@@ -212,17 +226,8 @@
     } else {
 	/*
 	 * Server initialization is complete. Now we can fork the 
-	 * daemon and return to the user. We need to do a setpgrp
-	 * so that the daemon will no longer be assosiated with the
-	 * users control terminal. This is done before the fork, so
-	 * that the child will not be a process group leader. Otherwise,
-	 * if the child were to open a terminal, it would become
-	 * associated with that terminal as its control terminal.
+	 * daemon and return to the user.
 	 */
-//	if ((pgrp = setpgid(0, 0)) == -1) {
-//	    syslog(LOG_NOTICE, "setpgid failed: %s", strerror(errno));
-//	}
-
 	frk = fork();
 	switch (frk) {
 	    case -1:    
--- a/bmsd/bms.h	Thu May 07 14:59:50 2020 +0200
+++ b/bmsd/bms.h	Mon May 11 17:32:08 2020 +0200
@@ -40,6 +40,7 @@
 #include <math.h>
 #include <assert.h>
 #include <libgen.h>
+#include <pthread.h>
 #include <libxml/xmlmemory.h>
 #include <libxml/parser.h>
 #include <libxml/encoding.h>
--- a/bmsd/fermenters.c	Thu May 07 14:59:50 2020 +0200
+++ b/bmsd/fermenters.c	Mon May 11 17:32:08 2020 +0200
@@ -3,7 +3,7 @@
  * @brief Handle fermenters status
  * @author Michiel Broek <mbroek at mbse dot eu>
  *
- * Copyright (C) 2018-2019
+ * Copyright (C) 2018-2020
  *
  * This file is part of the bms (Brewery Management System)
  *
@@ -27,6 +27,7 @@
 #include "xutil.h"
 #include "fermenters.h"
 #include "mysql.h"
+#include "websocket.h"
 
 
 sys_fermenter_list	*fermenters = NULL;
@@ -41,6 +42,7 @@
     struct json_object	*jobj, *val, *sensor, *temp;
     sys_fermenter_list	*fermenter, *tmpp;
     bool		new_fermenter = true;
+    char		*msg = NULL, buf[65];
 
 //    fprintf(stdout, "fermenter_set: %s/%s %s %s\n", edge_node, alias, birth ? "BIRTH":"DATA", payload);
 
@@ -318,6 +320,76 @@
     }
     json_object_put(jobj);
 
+    msg = xstrcpy((char *)"{\"device\":\"fermenter\",\"node\":\"");
+    msg = xstrcat(msg, edge_node);
+    msg = xstrcat(msg, (char *)"\",\"unit\":\"");
+    msg = xstrcat(msg, alias);
+    msg = xstrcat(msg, (char *)"\",\"online\":");
+    msg = xstrcat(msg, fermenter->online ? (char *)"1":(char *)"0");
+    msg = xstrcat(msg, (char *)",\"mode\":\"");
+    msg = xstrcat(msg, fermenter->mode);
+    msg = xstrcat(msg, (char *)"\",\"yeast_lo\":");
+    snprintf(buf, 64, "%.3f", fermenter->yeast_lo);
+    msg = xstrcat(msg, buf);
+    msg = xstrcat(msg, (char *)",\"yeast_hi\":");
+    snprintf(buf, 64, "%.3f", fermenter->yeast_hi);
+    msg = xstrcat(msg, buf);
+    if (fermenter->air_address) {
+    	msg = xstrcat(msg, (char *)",\"air\":");
+	snprintf(buf, 64, "%.3f", fermenter->air_temperature);
+	msg = xstrcat(msg, buf);
+    }
+    if (fermenter->beer_address) {
+        msg = xstrcat(msg, (char *)",\"beer\":");
+        snprintf(buf, 64, "%.3f", fermenter->beer_temperature);
+        msg = xstrcat(msg, buf);
+    }
+    if (fermenter->chiller_address) {
+        msg = xstrcat(msg, (char *)",\"chiller\":");
+        snprintf(buf, 64, "%.3f", fermenter->chiller_temperature);
+        msg = xstrcat(msg, buf);
+    }
+    if (fermenter->heater_address) {
+        msg = xstrcat(msg, (char *)",\"heater\":");
+        snprintf(buf, 64, "%d", fermenter->heater_state);
+        msg = xstrcat(msg, buf);
+    }
+    if (fermenter->cooler_address) {
+        msg = xstrcat(msg, (char *)",\"cooler\":");
+        snprintf(buf, 64, "%d", fermenter->cooler_state);
+        msg = xstrcat(msg, buf);
+    }
+    if (fermenter->fan_address) {
+        msg = xstrcat(msg, (char *)",\"fan\":");
+        snprintf(buf, 64, "%d", fermenter->fan_state);
+        msg = xstrcat(msg, buf);
+    }
+    if (fermenter->light_address) {
+        msg = xstrcat(msg, (char *)",\"light\":");
+        snprintf(buf, 64, "%d", fermenter->light_state);
+        msg = xstrcat(msg, buf);
+    }
+    if (fermenter->door_address) {
+        msg = xstrcat(msg, (char *)",\"door\":");
+        snprintf(buf, 64, "%d", fermenter->door_state);
+        msg = xstrcat(msg, buf);
+    }
+    msg = xstrcat(msg, (char *)",\"sp_lo\":");
+    snprintf(buf, 64, "%.3f", fermenter->setpoint_low);
+    msg = xstrcat(msg, buf);
+    msg = xstrcat(msg, (char *)",\"sp_hi\":");
+    snprintf(buf, 64, "%.3f", fermenter->setpoint_high);
+    msg = xstrcat(msg, buf);
+    msg = xstrcat(msg, (char *)",\"alarm\":");
+    snprintf(buf, 64, "%d", fermenter->alarm);
+    msg = xstrcat(msg, buf);
+    msg = xstrcat(msg, (char *)",\"stage\":\"");
+    msg = xstrcat(msg, fermenter->stage);
+    msg = xstrcat(msg, (char *)"\"}");
+    ws_broadcast(msg);
+    free(msg);
+    msg = NULL;
+
 //    fermenter_dump(fermenter);
 
     if (new_fermenter) {
@@ -671,7 +743,7 @@
 
 void fermenter_death(char *topic)
 {
-    char		*edge_node, *alias;
+    char		*edge_node, *alias, *msg = NULL;
     sys_fermenter_list	*tmpp;
 
     printf("fermenter_death: %s\n", topic);
@@ -689,6 +761,14 @@
 		if (tmpp->online)
 		    syslog(LOG_NOTICE, "Offline fermenter %s/%s", tmpp->node, tmpp->alias);
 	    	tmpp->online = false;
+		msg = xstrcpy((char *)"{\"device\":\"fermenter\",\"node\":\"");
+    		msg = xstrcat(msg, edge_node);
+    		msg = xstrcat(msg, (char *)"\",\"unit\":\"");
+    		msg = xstrcat(msg, alias);
+    		msg = xstrcat(msg, (char *)"\",\"online\":0}");
+		ws_broadcast(msg);
+    		free(msg);
+    		msg = NULL;
 	    	break;
 	    }
     	}
@@ -698,6 +778,14 @@
 		if (tmpp->online)
 		    syslog(LOG_NOTICE, "Offline fermenter %s/%s", tmpp->node, tmpp->alias);
 		tmpp->online = false;
+		msg = xstrcpy((char *)"{\"device\":\"fermenter\",\"node\":\"");
+                msg = xstrcat(msg, edge_node);
+                msg = xstrcat(msg, (char *)"\",\"unit\":\"");
+                msg = xstrcat(msg, tmpp->alias);
+                msg = xstrcat(msg, (char *)"\",\"online\":0}");
+                ws_broadcast(msg);
+                free(msg);
+                msg = NULL;
 	    }
 	}
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bmsd/websocket.c	Mon May 11 17:32:08 2020 +0200
@@ -0,0 +1,246 @@
+/**
+ * @file websocket.c
+ * @brief WebSockets interface
+ * @author Michiel Broek <mbroek at mbse dot eu>
+ *
+ * Copyright (C) 2020
+ *
+ * Michiel Broek <mbroek at mbse dot eu>
+ *
+ * This file is part of the bms (Brewery Management System)
+ *
+ * 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.
+ *
+ * bms is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with ThermFerm; see the file COPYING.  If not, write to the Free
+ * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "bms.h"
+#include "xutil.h"
+#include "websocket.h"
+#include <libwebsockets.h>
+
+
+extern sys_config       Config;
+extern int		debug;
+extern int		my_shutdown;
+
+struct			lws_context *context;
+int			ws_clients = 0;
+time_t			last_msg = 0;
+pthread_mutex_t		ws_mutex;
+
+
+/*
+ * Based on lws-mirror-protocol from libwebsockets v2.0.x
+ * Debian ships v2.0.3, on Slackware we have 2.4.0 and there
+ * are lots of changes in the api.
+ */
+#define MAX_MESSAGE_QUEUE 512
+
+/*
+ * one of these created for each message
+ */
+struct a_message {
+    void		*payload; /* is malloc'd */
+    size_t		len;
+};
+
+
+static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
+static int ringbuffer_head;
+
+
+
+static int callback_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
+{
+    struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user;
+    int n, m;
+
+    switch (reason) {
+
+	case LWS_CALLBACK_ESTABLISHED: {
+		ws_clients++;
+		syslog(LOG_NOTICE, "ws: new connection, total %d", ws_clients);
+		pss->ringbuffer_tail = ringbuffer_head;
+                pss->wsi = wsi;
+		break;
+	}
+
+	case LWS_CALLBACK_PROTOCOL_DESTROY:
+		syslog(LOG_NOTICE, "ws: protocol cleaning up");
+                for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++)
+                    if (ringbuffer[n].payload)
+                	free(ringbuffer[n].payload);
+                break;
+
+        case LWS_CALLBACK_SERVER_WRITEABLE:
+                while (pss->ringbuffer_tail != ringbuffer_head) {
+                    m = ringbuffer[pss->ringbuffer_tail].len;
+                    n = lws_write(wsi, (unsigned char *)ringbuffer[pss->ringbuffer_tail].payload + LWS_PRE, m, LWS_WRITE_TEXT);
+                    if (n < 0) {
+                        syslog(LOG_NOTICE, "ws: ERROR %d writing", n);
+                        return -1;
+                    }
+                    if (n < m)
+                        syslog(LOG_NOTICE, "ws: partial write %d vs %d", n, m);
+
+                    if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
+                        pss->ringbuffer_tail = 0;
+                    else
+                        pss->ringbuffer_tail++;
+
+                    if (((ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15))
+                        lws_rx_flow_allow_all_protocol(lws_get_context(wsi), lws_get_protocol(wsi));
+
+                    if (lws_send_pipe_choked(wsi)) {
+                        lws_callback_on_writable(wsi);
+                        break;
+                    }
+                }
+                break;
+
+	case LWS_CALLBACK_RECEIVE:
+                if (((ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
+                    syslog(LOG_NOTICE, "ws: dropping!");
+                    goto choke;
+                }
+
+                if (ringbuffer[ringbuffer_head].payload)
+                    free(ringbuffer[ringbuffer_head].payload);
+
+                ringbuffer[ringbuffer_head].payload = malloc(LWS_PRE + len);
+                ringbuffer[ringbuffer_head].len = len;
+                memcpy((char *)ringbuffer[ringbuffer_head].payload + LWS_PRE, in, len);
+                if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
+                    ringbuffer_head = 0;
+                else
+                    ringbuffer_head++;
+
+                if (((ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
+                    goto done;
+
+choke:
+                syslog(LOG_NOTICE, "ws: LWS_CALLBACK_RECEIVE: throttling");
+                lws_rx_flow_control(wsi, 0);
+
+done:
+                lws_callback_on_writable_all_protocol(lws_get_context(wsi), lws_get_protocol(wsi));
+                break;
+
+	case LWS_CALLBACK_CLOSED:
+		ws_clients--;
+		syslog(LOG_NOTICE, "ws: connection closed, left %d", ws_clients);
+		break;
+
+	default:
+		break;
+  }
+
+  return 0;
+}
+
+
+
+static struct lws_protocols protocols[] = {
+	{ "bmsd-protocol", callback_ws, sizeof(struct per_session_data__lws_mirror), 128 },
+        { NULL, NULL, 0, 0 } /* terminator */
+};
+
+
+/*
+ *  {"device":"fermenter","node":"seaport","unit":"unit0","online":1,"mode":"FRIDGE","yeast_lo":12.0,"yeast_hi":24.0,"air":19.875,"beer":19.812,"chiller":1.500,"heater":100,"cooler":0,"fan":100,"light":0,"door":0,"sp_lo":17.0,"sp_hi":17.5,"alarm":0,"stage":"PRIMARY"}
+ */
+void ws_broadcast(char *msg)
+{
+    int         len, err;
+
+    err = pthread_mutex_lock(&ws_mutex);
+    if (err) {
+	syslog(LOG_NOTICE, "ws_broadcast pthread_mutex_lock error %d", err);
+    } else {
+
+    	len = strlen(msg);
+    	if (ringbuffer[ringbuffer_head].payload)
+            free(ringbuffer[ringbuffer_head].payload);
+
+    	ringbuffer[ringbuffer_head].payload = malloc(LWS_PRE + len);
+    	ringbuffer[ringbuffer_head].len = len;
+    	memcpy((char *)ringbuffer[ringbuffer_head].payload + LWS_PRE, msg, len);
+    	if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
+            ringbuffer_head = 0;
+    	else
+            ringbuffer_head++;
+
+//    syslog(LOG_NOTICE, "ws: %d %s", ringbuffer_head, msg);
+    	syslog(LOG_NOTICE, "ws: broadcast buffer=%d  len=%d", ringbuffer_head, len);
+
+    	lws_callback_on_writable_all_protocol(context, &protocols[0]);
+    	err = pthread_mutex_unlock(&ws_mutex);
+    	if (err) {
+            syslog(LOG_NOTICE, "ws_broadcast pthread_mutex_unlock error %d", err);
+    	}
+    	last_msg = time(NULL);
+    }
+}
+
+
+
+/*
+ * Called every 5 seconds.
+ */
+void ws_check(void)
+{
+    time_t	now = time(NULL);
+
+    if (((int)now - (int)last_msg) > 45) {
+	ws_broadcast((char *)"{\"ping\":1}");
+    }
+}
+
+
+
+void *ws_loop(void *threadid)
+{
+    struct	lws_context_creation_info info;
+    int		n = 0;
+
+    syslog(LOG_NOTICE, "Thread ws_loop started");
+
+    memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
+    info.port = 8010;
+    info.protocols = protocols;
+    info.gid = -1;
+    info.uid = -1;
+    info.keepalive_timeout = 900;
+    info.options = LWS_SERVER_OPTION_VALIDATE_UTF8;
+
+    context = lws_create_context(&info);
+
+    if (context == NULL) {
+	syslog(LOG_NOTICE, "libwebsocket_create_context() failed");
+    }
+
+    /*
+     * Loop forever until external shutdown variable is set.
+     */
+    while (n >= 0 && ! my_shutdown) {
+
+	n = lws_service(context, 50);
+    }
+    lws_context_destroy(context);
+
+    syslog(LOG_NOTICE, "Thread ws_loop stopped");
+    return 0;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bmsd/websocket.h	Mon May 11 17:32:08 2020 +0200
@@ -0,0 +1,31 @@
+/**
+ * @file websocket.h
+ */
+
+#ifndef _WEBSOCKET_H
+#define	_WEBSOCKET_H
+
+
+struct per_session_data__lws_mirror {
+        struct lws *wsi;
+        int ringbuffer_tail;
+};
+
+/**
+ * @brief Broadcast messages to all connected websocket clients.
+ * @param msg The message to send. Messages are placed in a ringbuffer queue.
+ */
+void ws_broadcast(char *msg);
+
+/**
+ * @brief Check if a connection is idle for 45 seconds and if so send a ping like
+ *        message to keep the connection alive. Call this function at regular intervals.
+ */
+void ws_check(void);
+
+/**
+ * @brief The websockets server thread.
+ */
+void *ws_loop(void *);
+
+#endif
--- a/config.status	Thu May 07 14:59:50 2020 +0200
+++ b/config.status	Mon May 11 17:32:08 2020 +0200
@@ -621,13 +621,13 @@
 S["CC"]="gcc"
 S["CYEARS"]="2016-2020"
 S["COPYRIGHT"]="Copyright (C) 2016-2020 Michiel Broek, All Rights Reserved"
-S["VERSION"]="0.3.32"
+S["VERSION"]="0.3.33"
 S["PACKAGE"]="bms"
 S["SUBDIRS"]="bmsd doc script tools www"
 S["target_alias"]=""
 S["host_alias"]=""
 S["build_alias"]=""
-S["LIBS"]=" -lm -lmosquitto -ljson-c -lmysqlclient -lxml2 -luuid"
+S["LIBS"]=" -lm -lmosquitto -ljson-c -lmysqlclient -lxml2 -luuid -lwebsockets"
 S["ECHO_T"]=""
 S["ECHO_N"]="-n"
 S["ECHO_C"]=""
@@ -710,7 +710,7 @@
 D["PACKAGE_STRING"]=" \"\""
 D["PACKAGE_BUGREPORT"]=" \"\""
 D["PACKAGE_URL"]=" \"\""
-D["VERSION"]=" \"0.3.32\""
+D["VERSION"]=" \"0.3.33\""
 D["COPYRIGHT"]=" \"Copyright (C) 2016-2020 Michiel Broek, All Rights Reserved\""
 D["STDC_HEADERS"]=" 1"
 D["HAVE_SYS_TYPES_H"]=" 1"
@@ -727,6 +727,7 @@
 D["HAVE_MARIADB_MYSQL_H"]=" 1"
 D["HAVE_LIBXML_XMLMEMORY_H"]=" 1"
 D["HAVE_UUID_UUID_H"]=" 1"
+D["HAVE_LIBWEBSOCKETS_H"]=" 1"
 D["STDC_HEADERS"]=" 1"
   for (key in D) D_is_set[key] = 1
   FS = ""
--- a/configure	Thu May 07 14:59:50 2020 +0200
+++ b/configure	Mon May 11 17:32:08 2020 +0200
@@ -2043,7 +2043,7 @@
 
 
 PACKAGE="bms"
-VERSION="0.3.32"
+VERSION="0.3.33"
 COPYRIGHT="Copyright (C) 2016-2020 Michiel Broek, All Rights Reserved"
 CYEARS="2016-2020"
 
@@ -3790,6 +3790,67 @@
   as_fn_error $? "libuuid not found" "$LINENO" 5
 fi
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for lws_client_connect in -lwebsockets" >&5
+$as_echo_n "checking for lws_client_connect in -lwebsockets... " >&6; }
+if ${ac_cv_lib_websockets_lws_client_connect+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lwebsockets  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char lws_client_connect ();
+int
+main ()
+{
+return lws_client_connect ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_websockets_lws_client_connect=yes
+else
+  ac_cv_lib_websockets_lws_client_connect=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_websockets_lws_client_connect" >&5
+$as_echo "$ac_cv_lib_websockets_lws_client_connect" >&6; }
+if test "x$ac_cv_lib_websockets_lws_client_connect" = xyes; then :
+  result=yes
+else
+  result=no
+fi
+
+if test "$result" = "yes"; then
+  LIBS="$LIBS -lwebsockets"
+  for ac_header in libwebsockets.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "libwebsockets.h" "ac_cv_header_libwebsockets_h" "$ac_includes_default"
+if test "x$ac_cv_header_libwebsockets_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBWEBSOCKETS_H 1
+_ACEOF
+
+fi
+
+done
+
+else
+  as_fn_error $? "libwebsockets not found" "$LINENO" 5
+fi
+
+
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
 $as_echo_n "checking for ANSI C header files... " >&6; }
--- a/configure.ac	Thu May 07 14:59:50 2020 +0200
+++ b/configure.ac	Mon May 11 17:32:08 2020 +0200
@@ -8,7 +8,7 @@
 dnl General settings
 dnl After changeing the version number, run autoconf!
 PACKAGE="bms"
-VERSION="0.3.32"
+VERSION="0.3.33"
 COPYRIGHT="Copyright (C) 2016-2020 Michiel Broek, All Rights Reserved"
 CYEARS="2016-2020"
 AC_SUBST(PACKAGE)
@@ -89,6 +89,15 @@
   AC_MSG_ERROR(libuuid not found)
 fi
 
+AC_CHECK_LIB(websockets,lws_client_connect,result=yes,result=no)
+if test "$result" = "yes"; then
+  LIBS="$LIBS -lwebsockets"
+  AC_CHECK_HEADERS(libwebsockets.h)
+else
+  AC_MSG_ERROR(libwebsockets not found)
+fi
+
+
 
 dnl Checks for header files.
 AC_HEADER_STDC
--- a/www/includes/global.inc.php	Thu May 07 14:59:50 2020 +0200
+++ b/www/includes/global.inc.php	Mon May 11 17:32:08 2020 +0200
@@ -322,6 +322,7 @@
        <li><img style='float: left; margin-right: 5px;' src='images/help-about.png' /><a href="gen_about.php">Informatie</a></li>
       </ul>
      </li>
+     <li style='width: 300px;'><div id="wsstatus">ws: nc</div></li>
     </ul>
    </div> <!-- End menu -->
 
--- a/www/js/global.js	Thu May 07 14:59:50 2020 +0200
+++ b/www/js/global.js	Mon May 11 17:32:08 2020 +0200
@@ -763,6 +763,37 @@
  });
  $('#jqxWidget').css('visibility', 'visible');
 
+ var websocket = new WebSocket('ws://'+location.hostname+'/ws');
+
+ websocket.onopen = function(evt) {
+  console.log('WebSocket connection opened');
+  document.getElementById("wsstatus").innerHTML = "";
+ }
+
+ websocket.onmessage = function(evt) {
+    var msg = evt.data;
+    var value;
+
+  console.log('ws got: ' + msg);
+//    switch (msg.charAt(0)) {
+//        case '{':
+//                BrewBoard.p_msg(evt.data);
+//                break;
+//        default:
+//                document.getElementById("output").innerHTML = evt.data;
+//                break;
+//    }
+ }
+
+ websocket.onclose = function(evt) {
+  console.log('Websocket connection closed');
+//  $('#wsstatus').html('WebSocket closed');
+ }
+
+ websocket.onerror = function(event) {
+  console.log('Websocket error: ' + event.data);
+  $('#wsstatus').html('WebSocket error: ' + event.data);
+ }
 });
 
 

mercurial