Tue, 14 Feb 2023 14:01:02 +0100
Version 0.3.43 bmsd if there is no ssid received from a node message then send '' to the mon_nodes table in the database instead of (null). (one last file)
/** * @file ispindels.c * @brief Handle ispindels data * @author Michiel Broek <mbroek at mbse dot eu> * * Copyright (C) 2019-2023 * * 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 "ispindels.h" #include "mysql.h" #include "nodes.h" #include "websocket.h" sys_ispindel_list *ispindels = NULL; extern int debug; extern sys_config Config; extern MYSQL *con; extern MYSQL_RES *res_set; extern MYSQL_ROW row; void ispindel_ws_send(sys_ispindel_list *ispindel) { char *msg = NULL, buf[65]; msg = xstrcpy((char *)"{\"device\":\"ispindels\",\"node\":\""); msg = xstrcat(msg, ispindel->node); msg = xstrcat(msg, (char *)"\",\"unit\":\""); msg = xstrcat(msg, ispindel->alias); msg = xstrcat(msg, (char *)"\",\"online\":"); msg = xstrcat(msg, ispindel->online ? (char *)"1":(char *)"0"); msg = xstrcat(msg, (char *)",\"mode\":\""); msg = xstrcat(msg, ispindel->mode); msg = xstrcat(msg, (char *)"\",\"beeruuid\":\""); msg = xstrcat(msg, ispindel->beeruuid); msg = xstrcat(msg, (char *)"\",\"beercode\":\""); msg = xstrcat(msg, ispindel->beercode); msg = xstrcat(msg, (char *)"\",\"beername\":\""); msg = xstrcat(msg, ispindel->beername); msg = xstrcat(msg, (char *)"\",\"yeast_lo\":"); snprintf(buf, 64, "%.3f", ispindel->yeast_lo); msg = xstrcat(msg, buf); msg = xstrcat(msg, (char *)",\"yeast_hi\":"); snprintf(buf, 64, "%.3f", ispindel->yeast_hi); msg = xstrcat(msg, buf); msg = xstrcat(msg, (char *)",\"temperature\":"); snprintf(buf, 64, "%.4f", ispindel->temperature); msg = xstrcat(msg, buf); msg = xstrcat(msg, (char *)",\"angle\":"); snprintf(buf, 64, "%.6f", ispindel->angle); msg = xstrcat(msg, buf); msg = xstrcat(msg, (char *)",\"battery\":"); snprintf(buf, 64, "%.6f", ispindel->battery); msg = xstrcat(msg, buf); msg = xstrcat(msg, (char *)",\"gravity\":"); snprintf(buf, 64, "%.6f", ispindel->gravity); msg = xstrcat(msg, buf); msg = xstrcat(msg, (char *)",\"og_gravity\":"); snprintf(buf, 64, "%.6f", ispindel->og_gravity); msg = xstrcat(msg, buf); msg = xstrcat(msg, (char *)",\"alarm\":"); snprintf(buf, 64, "%d", ispindel->alarm); msg = xstrcat(msg, buf); msg = xstrcat(msg, (char *)"}"); ws_broadcast(msg); free(msg); msg = NULL; } void ispindel_ws_receive(char *payload) { struct json_object *jobj, *val; sys_ispindel_list *tmpp; char *node = NULL, *alias = NULL, *mode = NULL, *beeruuid = NULL, *beercode = NULL, *beername = NULL; float yeast_lo = 20, yeast_hi = 25; char query[512], buf[65], *end; MYSQL *con2 = NULL; /* * 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)); if (json_object_object_get_ex(jobj, "yeast_lo", &val)) yeast_lo = json_object_get_double(val); if (json_object_object_get_ex(jobj, "yeast_hi", &val)) yeast_hi = json_object_get_double(val); if (json_object_object_get_ex(jobj, "mode", &val)) mode = xstrcpy((char *)json_object_get_string(val)); json_object_put(jobj); /* * Search ispindel record in the memory array and use it if found. */ if (ispindels) { for (tmpp = ispindels; tmpp; tmpp = tmpp->next) { if (strcmp(tmpp->node, node) == 0) { 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 { if (beeruuid && beercode && beername) { end = stpcpy(query, "UPDATE mon_ispindels 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, "', og_gravity='0.0', yeast_lo='"); snprintf(buf, 64, "%.3f", yeast_lo); end = stpcpy(end, buf); end = stpcpy(end, "', yeast_hi='"); snprintf(buf, 64, "%.3f", yeast_hi); end = stpcpy(end, buf); end = stpcpy(end, "' WHERE node='"); end += mysql_real_escape_string(con2, end, node, strlen(node)); 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 */ 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); tmpp->og_gravity = 0.0; tmpp->yeast_lo = yeast_lo; tmpp->yeast_hi = yeast_hi; /* Report new state to the client */ ispindel_ws_send(tmpp); syslog(LOG_NOTICE, "Set ispindel %s/%s new beer %s %s", node, alias, beercode, beername); } } if (mode) { end = stpcpy(query, "UPDATE mon_ispindels SET mode='"); end += mysql_real_escape_string(con2, end, mode, strlen(mode)); end = stpcpy(end, "' WHERE node='"); end += mysql_real_escape_string(con2, end, node, strlen(node)); 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 */ if (tmpp->mode) free(tmpp->mode); tmpp->mode = xstrcpy(mode); /* Report new state to the client */ ispindel_ws_send(tmpp); syslog(LOG_NOTICE, "Set ispindel %s/%s new mode %s", node, alias, mode); } } mysql_close(con2); } } break; } } } if (node) free(node); if (alias) free(alias); if (mode) free(mode); if (beeruuid) free(beeruuid); if (beercode) free(beercode); if (beername) free(beername); } void ispindel_set(char *node, char *payload) { sys_ispindel_list *ispindel, *tmpp; struct json_object *jobj, *metric, *val; bool new_ispindel = true; char *datetime, buf[65], *line, *logfile, *query = malloc(512); struct tm *mytime; time_t timestamp; FILE *fp; // syslog(LOG_NOTICE, "ispindel_set: %s %s", node, payload); /* * Search ispindel record in the memory array and use it if found. */ if (ispindels) { for (tmpp = ispindels; tmpp; tmpp = tmpp->next) { if (strcmp(tmpp->node, node) == 0) { new_ispindel = false; ispindel = tmpp; break; } } } // if (debug) // printf("new_ispindel %s\n", new_ispindel ? "true":"false"); /* * Allocate new ispindel if not yet known. */ if (new_ispindel) { ispindel = (sys_ispindel_list *)malloc(sizeof(sys_ispindel_list)); memset(ispindel, 0, sizeof(sys_ispindel_list)); ispindel->node = xstrcpy(node); ispindel->mode = xstrcpy((char *)"OFF"); } if (! ispindel->online) { ispindel->online = true; syslog(LOG_NOTICE, "Online ispindel %s mode %s", node, ispindel->mode); } /* * Process the JSON formatted payload. * Update only the fields that are found in the payload. */ jobj = json_tokener_parse(payload); if (json_object_object_get_ex(jobj, "unit", &metric)) { if (json_object_object_get_ex(metric, "uuid", &val)) { if (ispindel->uuid) free(ispindel->uuid); ispindel->uuid = xstrcpy((char *)json_object_get_string(val)); } if (json_object_object_get_ex(metric, "alias", &val)) { if (ispindel->alias) free(ispindel->alias); ispindel->alias = xstrcpy((char *)json_object_get_string(val)); } if (json_object_object_get_ex(metric, "alarm", &val)) ispindel->alarm = json_object_get_int(val); if (json_object_object_get_ex(metric, "interval", &val)) ispindel->interval = json_object_get_int(val); if (json_object_object_get_ex(metric, "angle", &val)) ispindel->angle = json_object_get_double(val); if (json_object_object_get_ex(metric, "temperature", &val)) ispindel->temperature = json_object_get_double(val); if (json_object_object_get_ex(metric, "battery", &val)) ispindel->battery = json_object_get_double(val); if (json_object_object_get_ex(metric, "gravity", &val)) { ispindel->gravity = json_object_get_double(val); if (ispindel->gravity > ispindel->og_gravity) ispindel->og_gravity = ispindel->gravity; } } // ispindel_dump(ispindel); if (new_ispindel) { if (ispindels == NULL) { ispindels = ispindel; } else { for (tmpp = ispindels; tmpp; tmpp = tmpp->next) { if (tmpp->next == NULL) { tmpp->next = ispindel; break; } } } ispindel_mysql_insert(ispindel); } else { ispindel_mysql_update(ispindel); } ispindel_ws_send(ispindel); /* * The data is complete, see if we can write a log entry. */ if (ispindel->beercode && strlen(ispindel->beercode) && ispindel->beername && strlen(ispindel->beername) && ispindel->online && (strcmp(ispindel->mode, (char *)"ON") == 0)) { datetime = malloc(72); timestamp = time(NULL); mytime = localtime(×tamp); snprintf(datetime, 72, "%04d-%02d-%02d %02d:%02d:%02d", mytime->tm_year + 1900, mytime->tm_mon + 1, mytime->tm_mday, mytime->tm_hour, mytime->tm_min, mytime->tm_sec); line = xstrcpy(datetime); line = xstrcat(line, (char *)","); snprintf(buf, 64, "%.4f", ispindel->temperature); line = xstrcat(line, buf); line = xstrcat(line, (char *)","); snprintf(buf, 64, "%.5f", ispindel->gravity); line = xstrcat(line, buf); line = xstrcat(line, (char *)","); snprintf(buf, 64, "%.5f", 1.00001 + (0.0038661 * ispindel->gravity) + (1.3488e-5 * ispindel->gravity * ispindel->gravity) + (4.3074e-8 * ispindel->gravity * ispindel->gravity * ispindel->gravity)); line = xstrcat(line, buf); line = xstrcat(line, (char *)","); snprintf(buf, 64, "%.6f", ispindel->battery); line = xstrcat(line, buf); line = xstrcat(line, (char *)","); snprintf(buf, 64, "%.5f", ispindel->angle); line = xstrcat(line, buf); line = xstrcat(line, (char *)","); snprintf(buf, 64, "%d", ispindel->interval); line = xstrcat(line, buf); line = xstrcat(line, (char *)","); line = xstrcat(line, ispindel->uuid); snprintf(query, 511, "INSERT IGNORE INTO log_ispindel SET code='%s', datetime='%s', " \ "temperature='%.4f', plato='%.5f', sg='%.5f', battery='%.6f', " \ "angle='%.5f', refresh='%d', uuid='%s'", ispindel->beercode, datetime, ispindel->temperature, ispindel->gravity, 1.00001 + (0.0038661 * ispindel->gravity) + (1.3488e-5 * ispindel->gravity * ispindel->gravity) + (4.3074e-8 * ispindel->gravity * ispindel->gravity * ispindel->gravity), ispindel->battery, ispindel->angle, ispindel->interval, ispindel->uuid); //syslog(LOG_NOTICE, "%s", query); bms_mysql_query(query); /* * Build logfile name */ logfile = xstrcpy(Config.web_root); logfile = xstrcat(logfile, (char *)"/log/ispindel/"); logfile = xstrcat(logfile, ispindel->beercode); logfile = xstrcat(logfile, (char *)" "); logfile = xstrcat(logfile, ispindel->beername); logfile = xstrcat(logfile, (char *)".log"); fp = fopen(logfile, "a"); if (fp) { fprintf(fp, "%s\n", line); fclose(fp); } else { syslog(LOG_NOTICE, "cannot append to `%s'", logfile); } free(logfile); logfile = NULL; free(line); line = NULL; free(datetime); datetime = NULL; } } /* * Process iSpindel MQTT message. */ void ispindel_birth_data(char *topic, char *payload) { char *message_type, *edge_node; strtok(topic, "/"); // ignore namespace strtok(NULL, "/"); message_type = strtok(NULL, "/"); edge_node = strtok(NULL, "/\0"); if (strcmp("DBIRTH", message_type) == 0) { ispindel_set(edge_node, payload); } } void ispindel_dump(sys_ispindel_list *ispindel) { if (debug) { printf("node/alias %s / %s\n", ispindel->node, ispindel->alias); printf("uuid %s\n", ispindel->uuid); printf("online %s\n", ispindel->online ? "yes":"no"); printf("mode %s\n", ispindel->mode); printf("product %s / %s\n", ispindel->beercode, ispindel->beername); printf("tilt %.5f\n", ispindel->angle); printf("temperature %.4f\n", ispindel->temperature); printf("battery %.6f\n", ispindel->battery); printf("gravity %.5f\n", ispindel->gravity); printf("interval %d\n", ispindel->interval); printf("og_gravity %.5f\n", ispindel->og_gravity); } }