# HG changeset patch # User Michiel Broek # Date 1589459900 -7200 # Node ID 14322825cb3d335eed3fa662f41878286153ec88 # Parent 6e82fece1f8fda848fa78dbf18165aff368203a8 The co2meters monitor screen rebuild to use websockets instead of data polling. diff -r 6e82fece1f8f -r 14322825cb3d bmsd/Makefile --- a/bmsd/Makefile Tue May 12 16:08:30 2020 +0200 +++ b/bmsd/Makefile Thu May 14 14:38:20 2020 +0200 @@ -66,4 +66,5 @@ 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 +websocket.o: bms.h xutil.h websocket.h co2meters.h # End of generated dependencies diff -r 6e82fece1f8f -r 14322825cb3d bmsd/co2meters.c --- a/bmsd/co2meters.c Tue May 12 16:08:30 2020 +0200 +++ b/bmsd/co2meters.c Thu May 14 14:38:20 2020 +0200 @@ -40,12 +40,147 @@ +void co2meter_ws_send(sys_co2meter_list *co2meter) +{ + char *msg = NULL, buf[65]; + + msg = xstrcpy((char *)"{\"device\":\"co2meters\",\"node\":\""); + msg = xstrcat(msg, co2meter->node); + msg = xstrcat(msg, (char *)"\",\"unit\":\""); + msg = xstrcat(msg, co2meter->alias); + msg = xstrcat(msg, (char *)"\",\"online\":"); + msg = xstrcat(msg, co2meter->online ? (char *)"1":(char *)"0"); + msg = xstrcat(msg, (char *)",\"mode\":\""); + msg = xstrcat(msg, co2meter->mode); + msg = xstrcat(msg, (char *)"\",\"beercode\":\""); + msg = xstrcat(msg, co2meter->beercode); + msg = xstrcat(msg, (char *)"\",\"beername\":\""); + msg = xstrcat(msg, co2meter->beername); + msg = xstrcat(msg, (char *)"\",\"temperature\":"); + snprintf(buf, 64, "%.3f", co2meter->temperature); + msg = xstrcat(msg, buf); + msg = xstrcat(msg, (char *)",\"pressure_channel\":"); + snprintf(buf, 64, "%d", co2meter->pressure_channel); + msg = xstrcat(msg, buf); + msg = xstrcat(msg, (char *)",\"pressure_voltage\":"); + snprintf(buf, 64, "%.3f", co2meter->pressure_voltage); + msg = xstrcat(msg, buf); + msg = xstrcat(msg, (char *)",\"pressure_zero\":"); + snprintf(buf, 64, "%.3f", co2meter->pressure_zero); + msg = xstrcat(msg, buf); + msg = xstrcat(msg, (char *)",\"pressure_bar\":"); + snprintf(buf, 64, "%.3f", co2meter->pressure_bar); + msg = xstrcat(msg, buf); + msg = xstrcat(msg, (char *)",\"alarm\":"); + snprintf(buf, 64, "%d", co2meter->alarm); + msg = xstrcat(msg, buf); + msg = xstrcat(msg, (char *)"}"); + ws_broadcast(msg); + free(msg); + msg = NULL; + +} + + + +void co2meter_ws_receive(char *payload) +{ + struct json_object *jobj, *val; + sys_co2meter_list *tmpp; + char *node = NULL, *alias = NULL, *beeruuid = NULL, *beercode = NULL, *beername = NULL; + char query[512], *end; + MYSQL *con2 = NULL; + + syslog(LOG_NOTICE, "co2meter_ws_receive(%s)", payload); + + /* + * Process the JSON formatted payload. + */ + jobj = json_tokener_parse(payload); + if (json_object_object_get_ex(jobj, "node", &val)) + node = xstrcpy((char *)json_object_get_string(val)); + if (json_object_object_get_ex(jobj, "unit", &val)) + alias = xstrcpy((char *)json_object_get_string(val)); + if (json_object_object_get_ex(jobj, "beeruuid", &val)) + beeruuid = xstrcpy((char *)json_object_get_string(val)); + if (json_object_object_get_ex(jobj, "beercode", &val)) + beercode = xstrcpy((char *)json_object_get_string(val)); + if (json_object_object_get_ex(jobj, "beername", &val)) + beername = xstrcpy((char *)json_object_get_string(val)); + json_object_put(jobj); + + /* + * Search co2meter record in the memory array and use it if found. + */ + if (co2meters) { + for (tmpp = co2meters; tmpp; tmpp = tmpp->next) { + if ((strcmp(tmpp->alias, alias) == 0) && (strcmp(tmpp->node, node) == 0)) { + if (beeruuid && beercode && beername) { + con2 = mysql_init(NULL); + if (con2 == NULL) { + syslog(LOG_NOTICE, "MySQL: mysql_init() failed"); + } else { + if (mysql_real_connect(con2, Config.mysql_host, Config.mysql_user, Config.mysql_pass, Config.mysql_database, Config.mysql_port, NULL, 0) == NULL) { + syslog(LOG_NOTICE, "MySQL: mysql_real_connect() %s", mysql_error(con2)); + } else { + end = stpcpy(query, "UPDATE mon_co2meters SET beeruuid='"); + end += mysql_real_escape_string(con2, end, beeruuid, strlen(beeruuid)); + end = stpcpy(end, "', beercode='"); + end += mysql_real_escape_string(con2, end, beercode, strlen(beercode)); + end = stpcpy(end, "', beername='"); + end += mysql_real_escape_string(con2, end, beername, strlen(beername)); + end = stpcpy(end, "' WHERE node='"); + end += mysql_real_escape_string(con2, end, node, strlen(node)); + end = stpcpy(end, "' AND alias='"); + end += mysql_real_escape_string(con2, end, alias, strlen(alias)); + end = stpcpy(end, "'"); + + if (mysql_real_query(con2, query, (unsigned int) (end - query))) { + syslog(LOG_NOTICE, "MySQL: `%s' error %u (%s))", query, mysql_errno(con2), mysql_error(con2)); + } else { + /* Database updated, now update internal memory */ + //syslog(LOG_NOTICE, "MySQL: `%s' Ok", query); + if (tmpp->beercode) + free(tmpp->beercode); + tmpp->beercode = xstrcpy(beercode); + if (tmpp->beername) + free(tmpp->beername); + tmpp->beername = xstrcpy(beername); + if (tmpp->beeruuid) + free(tmpp->beeruuid); + tmpp->beeruuid = xstrcpy(beeruuid); + /* Report new state to the client */ + co2meter_ws_send(tmpp); + syslog(LOG_NOTICE, "Set co2meter %s/%s new beer %s %s", node, alias, beercode, beername); + } + mysql_close(con2); + } + } + } + break; + } + } + } + + if (node) + free(node); + if (alias) + free(alias); + if (beeruuid) + free(beeruuid); + if (beercode) + free(beercode); + if (beername) + free(beername); +} + + + void co2meter_set(char *edge_node, char *alias, char *payload) { struct json_object *jobj, *val, *sensor; sys_co2meter_list *co2meter, *tmpp; bool new_co2meter = true; - char *msg = NULL, buf[65]; // fprintf(stdout, "co2meter_set: %s/%s %s\n", edge_node, alias, payload); @@ -139,36 +274,7 @@ } json_object_put(jobj); - msg = xstrcpy((char *)"{\"device\":\"co2meters\",\"node\":\""); - msg = xstrcat(msg, edge_node); - msg = xstrcat(msg, (char *)"\",\"unit\":\""); - msg = xstrcat(msg, alias); - msg = xstrcat(msg, (char *)"\",\"online\":"); - msg = xstrcat(msg, co2meter->online ? (char *)"1":(char *)"0"); - msg = xstrcat(msg, (char *)",\"mode\":\""); - msg = xstrcat(msg, co2meter->mode); - msg = xstrcat(msg, (char *)"\",\"temperature\":"); - snprintf(buf, 64, "%.3f", co2meter->temperature); - msg = xstrcat(msg, buf); - msg = xstrcat(msg, (char *)",\"pressure_channel\":"); - snprintf(buf, 64, "%d", co2meter->pressure_channel); - msg = xstrcat(msg, buf); - msg = xstrcat(msg, (char *)",\"pressure_voltage\":"); - snprintf(buf, 64, "%.3f", co2meter->pressure_voltage); - msg = xstrcat(msg, buf); - msg = xstrcat(msg, (char *)",\"pressure_zero\":"); - snprintf(buf, 64, "%.3f", co2meter->pressure_zero); - msg = xstrcat(msg, buf); - msg = xstrcat(msg, (char *)",\"pressure_bar\":"); - snprintf(buf, 64, "%.3f", co2meter->pressure_bar); - msg = xstrcat(msg, buf); - msg = xstrcat(msg, (char *)",\"alarm\":"); - snprintf(buf, 64, "%d", co2meter->alarm); - msg = xstrcat(msg, buf); - msg = xstrcat(msg, (char *)"}"); - ws_broadcast(msg); - free(msg); - msg = NULL; + co2meter_ws_send(co2meter); // co2meter_dump(co2meter); diff -r 6e82fece1f8f -r 14322825cb3d bmsd/co2meters.h --- a/bmsd/co2meters.h Tue May 12 16:08:30 2020 +0200 +++ b/bmsd/co2meters.h Thu May 14 14:38:20 2020 +0200 @@ -8,6 +8,12 @@ void co2meter_dump(sys_co2meter_list *co2meter); /** + * @brief Process received command from a websocket. + * @param payload The received data in JSON format. + */ +void co2meter_ws_receive(char *payload); + +/** * @brief Birth of a co2meter or data update. Create it in the database if * never seen before, else just update the database entry. * @param topic The MQTT topic string, contains the co2meter type and name. diff -r 6e82fece1f8f -r 14322825cb3d bmsd/websocket.c --- a/bmsd/websocket.c Tue May 12 16:08:30 2020 +0200 +++ b/bmsd/websocket.c Thu May 14 14:38:20 2020 +0200 @@ -27,6 +27,7 @@ #include "bms.h" #include "xutil.h" #include "websocket.h" +#include "co2meters.h" #include @@ -64,7 +65,8 @@ 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; + int n, m; + char buf[513]; switch (reason) { @@ -110,31 +112,14 @@ 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++; + memcpy(buf, in, len); + buf[len] = '\0'; + syslog(LOG_NOTICE, "ws: reveived %ld bytes %s", len, buf); + if (strncmp(buf, (char *)"{\"device\":\"co2meters\",", 22) == 0) { + co2meter_ws_receive(buf); + } - 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: @@ -152,7 +137,7 @@ static struct lws_protocols protocols[] = { - { "bmsd-protocol", callback_ws, sizeof(struct per_session_data__lws_mirror), 128 }, + { "bmsd-protocol", callback_ws, sizeof(struct per_session_data__lws_mirror), 512 }, { NULL, NULL, 0, 0 } /* terminator */ }; diff -r 6e82fece1f8f -r 14322825cb3d www/Makefile --- a/www/Makefile Tue May 12 16:08:30 2020 +0200 +++ b/www/Makefile Thu May 14 14:38:20 2020 +0200 @@ -3,7 +3,7 @@ include ../Makefile.global -SRC = cmd_fermenter.php cmd_co2meter.php cmd_ispindel.php config.php.dist crontasks.php \ +SRC = cmd_fermenter.php cmd_ispindel.php config.php.dist crontasks.php \ export_equipments.php export_fermentables.php export_hops.php export_mashs.php \ export_miscs.php export_styles.php export_suppliers.php export_waters.php \ export_yeasts.php favicon.ico gen_about.php \ diff -r 6e82fece1f8f -r 14322825cb3d www/cmd_co2meter.php --- a/www/cmd_co2meter.php Tue May 12 16:08:30 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ - diff -r 6e82fece1f8f -r 14322825cb3d www/js/global.js --- a/www/js/global.js Tue May 12 16:08:30 2020 +0200 +++ b/www/js/global.js Thu May 14 14:38:20 2020 +0200 @@ -750,6 +750,28 @@ }, mashlist = new $.jqx.dataAdapter(mashProfileSource); +/* Websocket interface. Place "websocket.onmessage = function(evt) {}" in the user script. */ +var websocket = new WebSocket('ws://'+location.hostname+'/ws'); + +websocket.onopen = function(evt) { + console.log('WebSocket connection opened'); + $('#wsstatus').html('WebSocket open'); +} + +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); +} + +/* Handle global menu manipulation called by the user script. */ +function ws_global(msg) { + +} $(document).ready(function() { @@ -762,38 +784,6 @@ theme: theme }); $('#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); - } }); diff -r 6e82fece1f8f -r 14322825cb3d www/js/mon_co2meter.js --- a/www/js/mon_co2meter.js Tue May 12 16:08:30 2020 +0200 +++ b/www/js/mon_co2meter.js Thu May 14 14:38:20 2020 +0200 @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (C) 2019 + * Copyright (C) 2019-2020 * * Michiel Broek * @@ -18,15 +18,13 @@ * 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. - *****************************************************************************/ + */ $(document).ready(function() { var record = {}, blank = {}, - newProduct = false, - schedule = 0, productSource = { datatype: 'json', @@ -168,40 +166,8 @@ $('#gaugeContainer_pressure').jqxGauge(gaugeoptionsp); $('#gaugeContainer_pressure').jqxGauge({ caption: { value: 'Bar: 00.000' }}); - function sendProduct(code, name, uuid) { - - console.log('sendProduct(' + code + ', ' + name + ', ' + uuid + ')'); - var data = 'uuid=' + record.uuid + '&beeruuid=' + uuid + '&beercode=' + code + '&beername=' + name; - $.ajax({ - url: 'cmd_co2meter.php', - data: data, - type: 'POST', - success: function(data) {}, - error: function(jqXHR, textStatus, errorThrown) { console.log('sendProduct() error'); } - }); - } - - // Get the data immediatly and then at regular intervals to refresh. + // Get the data dataAdapter.dataBind(); - setInterval(function() { - var skip = false; - if (newProduct) { - sendProduct(record.beercode, record.beername, record.beeruuid); - newProduct = false; - skip = true; - } - if (skip) { - schedule = 4; // 2 seconds wait to get the results - } else { - if (schedule > 0) - schedule--; - } - - if (schedule <= 0) { - dataAdapter.dataBind(); - schedule = 20; - } - }, 500); $('#select_beer').on('select', function(event) { if (event.args) { @@ -210,7 +176,10 @@ record.beercode = datarecord.code; record.beername = datarecord.name; record.beeruuid = datarecord.uuid; - newProduct = true; + console.log('Select beer ' + record.beercode + ', ' + record.beername); + var msg = '{"device":"co2meters","node":"' + record.node + '","unit":"' + record.alias + + '","beeruuid":"' + record.beeruuid + '","beercode":"' + record.beercode + '","beername":"' + record.beername + '"}'; + websocket.send(msg); } }); @@ -219,4 +188,41 @@ $('#FLog').click(function() { window.open('log_co2pressure.php?code=' + record.beercode + '&name=' + record.beername); }); + + websocket.onmessage = function(evt) { + var msg = evt.data; + var obj = JSON.parse(msg); + if (obj.device == "co2meters" && obj.node == record.node && obj.unit == record.alias) { + console.log('ws got this device ' + msg); + if (obj.online) { + $('#info_online').html('On-line'); + } else { + $('#info_online').html('Off-line'); + } + $('#info_beer').html(obj.beercode + ' - ' + obj.beername); + $('#info_mode').html(obj.mode); + if (obj.online && obj.mode != 'OFF') { + $('#co2meter_powerled').html('
Power'); + $('#select_beer').jqxDropDownList({ disabled: true }); + $('#select_beer').jqxDropDownList('clearSelection'); + $('#select_beer').hide(); + } else { + $('#co2meter_powerled').html('
Power'); + $('#select_beer').show(); + $('#select_beer').jqxDropDownList({ disabled: false }); + } + if (obj.online && (obj.alarm != '0')) { + $('#co2meter_alarmled').html('
Alarm'); + } else { + $('#co2meter_alarmled').html('
Alarm'); + } + + $('#gaugeContainer_temperature').jqxGauge({ caption: { value: 'Temp: ' + obj.temperature.toFixed(3) }}); + $('#gaugeContainer_temperature').jqxGauge({ value: obj.temperature }); + $('#gaugeContainer_pressure').jqxGauge({ caption: { value: 'Bar: ' + obj.pressure_bar.toFixed(2) }}); + $('#gaugeContainer_pressure').jqxGauge({ value: obj.pressure_bar }); + + } + ws_global(msg); + } });