diff -r 000000000000 -r 033898178630 bmsd/fermenters.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bmsd/fermenters.c Sat Aug 04 21:19:15 2018 +0200 @@ -0,0 +1,635 @@ +/** + * @file fermenters.c + * @brief Handle fermenters status + * @author Michiel Broek + * + * Copyright (C) 2018 + * + * 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 "fermenters.h" +#include "mysql.h" + + +sys_fermenter_list *fermenters = NULL; + +extern int debug; + + + + +void fermenter_set(char *edge_node, char *alias, bool birth, char *payload) +{ + struct json_object *jobj, *val, *sensor, *temp; + sys_fermenter_list *fermenter, *tmpp; + bool new_fermenter = true; + +// fprintf(stdout, "fermenter_set: %s/%s %s %s\n", edge_node, alias, birth ? "BIRTH":"DATA", payload); + + /* + * Search fermenter record in the memory array and use it if found. + */ + if (fermenters) { + for (tmpp = fermenters; tmpp; tmpp = tmpp->next) { + if ((strcmp(tmpp->alias, alias) == 0) && (strcmp(tmpp->node, edge_node) == 0)) { + new_fermenter = false; + fermenter = tmpp; + break; + } + } + } + + if (! birth && new_fermenter) { + printf("ERROR got DDATA and fermenter %s/%s doesn't exist\n", edge_node, alias); + return; + } +//printf("new_fermenter %s\n", new_fermenter ? "true":"false"); + + /* + * Allocate new fermenter if not yet known. + */ + if (new_fermenter) { + fermenter = (sys_fermenter_list *)malloc(sizeof(sys_fermenter_list)); + memset(fermenter, 0, sizeof(sys_fermenter_list)); + fermenter->alias = xstrcpy(alias); + fermenter->node = xstrcpy(edge_node); + fermenter->mode = xstrcpy((char *)"OFF"); + fermenter->stage = xstrcpy((char *)"PRIMARY"); + } + + fermenter->online = true; + if (birth) { + syslog(LOG_NOTICE, "Online fermenter %s/%s mode %s", edge_node, alias, fermenter->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, "uuid", &val)) { + if (fermenter->uuid) + free(fermenter->uuid); + fermenter->uuid = xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(jobj, "mode", &val)) { + if (fermenter->mode) { + if (strcmp(fermenter->mode, (char *)json_object_get_string(val))) { + syslog(LOG_NOTICE, "Change mode fermenter %s/%s: %s to %s", edge_node, alias, fermenter->mode, (char *)json_object_get_string(val)); + } + free(fermenter->mode); + } + fermenter->mode = xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(jobj, "stage", &val)) { + if (fermenter->stage) { + if (strcmp(fermenter->stage, (char *)json_object_get_string(val))) { + syslog(LOG_NOTICE, "Change stage fermenter %s/%s: %s to %s", edge_node, alias, fermenter->stage, (char *)json_object_get_string(val)); + } + free(fermenter->stage); + } + fermenter->stage = xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(jobj, "alarm", &val)) { + if (fermenter->alarm != json_object_get_int(val)) { + syslog(LOG_NOTICE, "Change alarm fermenter %s/%s: %d to %d", edge_node, alias, fermenter->alarm, json_object_get_int(val)); + } + fermenter->alarm = json_object_get_int(val); + } + if (json_object_object_get_ex(jobj, "product", &sensor)) { + if (json_object_object_get_ex(sensor, "code", &val)) { + if (fermenter->beercode) + free(fermenter->beercode); + fermenter->beercode = xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(sensor, "name", &val)) { + if (fermenter->beername) + free(fermenter->beername); + fermenter->beername = xstrcpy((char *)json_object_get_string(val)); + } + } + if (json_object_object_get_ex(jobj, "air", &sensor)) { + if (json_object_object_get_ex(sensor, "address", &val)) { + if (fermenter->air_address) + free(fermenter->air_address); + fermenter->air_address= xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(sensor, "state", &val)) { + if (fermenter->air_state) + free(fermenter->air_state); + fermenter->air_state= xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(sensor, "temperature", &val)) { + fermenter->air_temperature = json_object_get_double(val); + } + } + if (json_object_object_get_ex(jobj, "beer", &sensor)) { + if (json_object_object_get_ex(sensor, "address", &val)) { + if (fermenter->beer_address) + free(fermenter->beer_address); + fermenter->beer_address= xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(sensor, "state", &val)) { + if (fermenter->beer_state) + free(fermenter->beer_state); + fermenter->beer_state= xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(sensor, "temperature", &val)) { + fermenter->beer_temperature = json_object_get_double(val); + } + } + if (json_object_object_get_ex(jobj, "chiller", &sensor)) { + if (json_object_object_get_ex(sensor, "address", &val)) { + if (fermenter->chiller_address) + free(fermenter->chiller_address); + fermenter->chiller_address= xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(sensor, "state", &val)) { + if (fermenter->chiller_state) + free(fermenter->chiller_state); + fermenter->chiller_state= xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(sensor, "temperature", &val)) { + fermenter->chiller_temperature = json_object_get_double(val); + } + } + if (json_object_object_get_ex(jobj, "heater", &sensor)) { + if (json_object_object_get_ex(sensor, "address", &val)) { + if (fermenter->heater_address) + free(fermenter->heater_address); + fermenter->heater_address= xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(sensor, "state", &val)) { + fermenter->heater_state = json_object_get_int(val); + } + if (json_object_object_get_ex(sensor, "usage", &val)) { + fermenter->heater_usage = json_object_get_int(val); + } + } + if (json_object_object_get_ex(jobj, "cooler", &sensor)) { + if (json_object_object_get_ex(sensor, "address", &val)) { + if (fermenter->cooler_address) + free(fermenter->cooler_address); + fermenter->cooler_address= xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(sensor, "state", &val)) { + fermenter->cooler_state = json_object_get_int(val); + } + if (json_object_object_get_ex(sensor, "usage", &val)) { + fermenter->cooler_usage = json_object_get_int(val); + } + } + if (json_object_object_get_ex(jobj, "fan", &sensor)) { + if (json_object_object_get_ex(sensor, "address", &val)) { + if (fermenter->fan_address) + free(fermenter->fan_address); + fermenter->fan_address= xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(sensor, "state", &val)) { + fermenter->fan_state = json_object_get_int(val); + } + if (json_object_object_get_ex(sensor, "usage", &val)) { + fermenter->fan_usage = json_object_get_int(val); + } + } + if (json_object_object_get_ex(jobj, "light", &sensor)) { + if (json_object_object_get_ex(sensor, "address", &val)) { + if (fermenter->light_address) + free(fermenter->light_address); + fermenter->light_address= xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(sensor, "state", &val)) { + fermenter->light_state = json_object_get_int(val); + } + if (json_object_object_get_ex(sensor, "usage", &val)) { + fermenter->light_usage = json_object_get_int(val); + } + } + if (json_object_object_get_ex(jobj, "door", &sensor)) { + if (json_object_object_get_ex(sensor, "address", &val)) { + if (fermenter->door_address) + free(fermenter->door_address); + fermenter->door_address= xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(sensor, "state", &val)) { + fermenter->door_state = json_object_get_int(val); + } + } + if (json_object_object_get_ex(jobj, "psu", &sensor)) { + if (json_object_object_get_ex(sensor, "address", &val)) { + if (fermenter->psu_address) + free(fermenter->psu_address); + fermenter->psu_address= xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(sensor, "state", &val)) { + fermenter->psu_state = json_object_get_int(val); + } + } + if (json_object_object_get_ex(jobj, "setpoint", &sensor)) { + if (json_object_object_get_ex(sensor, "low", &val)) { + fermenter->setpoint_low = json_object_get_double(val); + } + if (json_object_object_get_ex(sensor, "high", &val)) { + fermenter->setpoint_high = json_object_get_double(val); + } + } + if (json_object_object_get_ex(jobj, "profile", &sensor)) { + if (strcmp(json_object_to_json_string_ext(sensor, 0), "null")) { +// printf("profile: %s\n", json_object_to_json_string_ext(sensor, 0)); + + if (json_object_object_get_ex(sensor, "uuid", &val)) { + if (fermenter->profile_uuid) + free(fermenter->profile_uuid); + fermenter->profile_uuid = xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(sensor, "name", &val)) { + if (fermenter->profile_name) + free(fermenter->profile_name); + fermenter->profile_name = xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(sensor, "state", &val)) { + if (fermenter->profile_state) + free(fermenter->profile_state); + fermenter->profile_state = xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(sensor, "inittemp", &temp)) { + if (json_object_object_get_ex(temp, "low", &val)) { + fermenter->profile_inittemp_low = json_object_get_double(val); + } + if (json_object_object_get_ex(temp, "high", &val)) { + fermenter->profile_inittemp_high = json_object_get_double(val); + } + } + if (json_object_object_get_ex(sensor, "steps", &val)) { + if (fermenter->profile_steps) + free(fermenter->profile_steps); + fermenter->profile_steps = xstrcpy((char *)json_object_to_json_string_ext(val, 0)); + } + + + } else { + if (fermenter->profile_uuid) + free(fermenter->profile_uuid); + if (fermenter->profile_name) + free(fermenter->profile_name); + if (fermenter->profile_state) + free(fermenter->profile_state); + if (fermenter->profile_steps) + free(fermenter->profile_steps); + fermenter->profile_uuid = fermenter->profile_name = fermenter->profile_state = fermenter->profile_steps = NULL; + fermenter->profile_percent = 0; + fermenter->profile_inittemp_high = fermenter->profile_inittemp_low = 0.0; + } + } + json_object_put(jobj); + +// fermenter_dump(fermenter); + + if (new_fermenter) { + if (fermenters == NULL) { + fermenters = fermenter; + } else { + for (tmpp = fermenters; tmpp; tmpp = tmpp->next) { + if (tmpp->next == NULL) { + tmpp->next = fermenter; + break; + } + } + } + fermenter_mysql_insert(fermenter); + } else { + fermenter_mysql_update(fermenter); + } + +} + + + +/* + * With DBIRTH all active fermenters are publishd in an array. + * With DDATA only one fermenter is published in the payload. + */ +void fermenter_birth_data(char *topic, char *payload) +{ + char *message_type, *edge_node, *alias; + struct json_object *jobj, *val, *metric, *units, *unit; + int arraylen; + + strtok(topic, "/"); // ignore namespace + strtok(NULL, "/"); + message_type = strtok(NULL, "/"); + edge_node = strtok(NULL, "/\0"); + alias = strtok(NULL, "/\0"); + + if ((alias == NULL) && (strcmp("DBIRTH", message_type) == 0)) { + /* + * Global initial DBIRTH message with array of fermenters. + */ + jobj = json_tokener_parse(payload); + + if (json_object_object_get_ex(jobj, "metric", &metric)) { + if (json_object_object_get_ex(metric, "units", &units)) { + arraylen = json_object_array_length(units); + for (int i = 0; i < arraylen; i++) { + /* + * Parse the array of units + */ + unit = json_object_array_get_idx(units, i); + + if (json_object_object_get_ex(unit, "alias", &val)) { + if (alias) + free(alias); + alias = xstrcpy((char *)json_object_get_string(val)); + fermenter_set(edge_node, alias, true, (char *)json_object_to_json_string_ext(unit, 0)); + free(alias); + alias = NULL; + } + } + } + } + json_object_put(jobj); + return; + } + + if (strcmp("DBIRTH", message_type) == 0) { + /* + * DBIRTH for just one fermenter. + */ + jobj = json_tokener_parse(payload); + + if (json_object_object_get_ex(jobj, "metric", &metric)) { + fermenter_set(edge_node, alias, true, (char *)json_object_to_json_string_ext(metric, 0)); + } + json_object_put(jobj); + return; + } + + if (strcmp("DDATA", message_type) == 0) { + /* + * DDATA update messages for each fermenter. + */ + jobj = json_tokener_parse(payload); + + if (json_object_object_get_ex(jobj, "metric", &metric)) { + fermenter_set(edge_node, alias, false, (char *)json_object_to_json_string_ext(metric, 0)); + } + json_object_put(jobj); + return; + } + + /* + * The rest are errors. + */ + printf("ERROR fermenter_birth_data: %s %s %s\n", message_type, edge_node, alias); +} + + + +void fermenter_log(char *topic, char *payload) +{ + char *edge_node, *alias; + struct json_object *jobj, *val, *metric, *metric2; + fermentation_log *log; + struct tm *mytime; + time_t timestamp; + + strtok(topic, "/"); // ignore namespace + strtok(NULL, "/"); // group_id + strtok(NULL, "/"); // message_type + edge_node = strtok(NULL, "/\0"); + alias = strtok(NULL, "/\0"); + + log = (fermentation_log *)malloc(sizeof(fermentation_log)); + memset(log, 0, sizeof(fermentation_log)); + + log->fermenter_node = xstrcpy(edge_node); + log->fermenter_alias = xstrcpy(alias); + jobj = json_tokener_parse(payload); + + if (json_object_object_get_ex(jobj, "timestamp", &val)) { + timestamp = json_object_get_int(val); + log->datetime = malloc(21); + mytime = localtime(×tamp); + snprintf(log->datetime, 20, "%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); + } + + if (json_object_object_get_ex(jobj, "metric", &metric)) { + + if (json_object_object_get_ex(metric, "product", &metric2)) { + if (json_object_object_get_ex(metric2, "uuid", &val)) { + if (strcmp((char *)"(null)", json_object_get_string(val))) + log->product_uuid = xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(metric2, "code", &val)) { + if (strcmp((char *)"(null)", json_object_get_string(val))) + log->product_code = xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(metric2, "name", &val)) { + if (strcmp((char *)"(null)", json_object_get_string(val))) + log->product_name = xstrcpy((char *)json_object_get_string(val)); + } + } + if (json_object_object_get_ex(metric, "stage", &val)) { + log->stage = xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(metric, "mode", &val)) { + log->mode = xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(metric, "event", &val)) { + if (strcmp((char *)"(null)", json_object_get_string(val))) + log->event = xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(metric, "fermenter_uuid", &val)) { + if (strcmp((char *)"(null)", json_object_get_string(val))) + log->fermenter_uuid = xstrcpy((char *)json_object_get_string(val)); + } + if (json_object_object_get_ex(metric, "temperature", &metric2)) { + if (json_object_object_get_ex(metric2, "air", &val)) { + log->temperature_air = json_object_get_double(val); + } + if (json_object_object_get_ex(metric2, "beer", &val)) { + log->temperature_beer = json_object_get_double(val); + } + if (json_object_object_get_ex(metric2, "chiller", &val)) { + log->temperature_chiller = json_object_get_double(val); + } + if (json_object_object_get_ex(metric2, "room", &val)) { + log->temperature_room = json_object_get_double(val); + } + } + if (json_object_object_get_ex(metric, "setpoint", &metric2)) { + if (json_object_object_get_ex(metric2, "low", &val)) { + log->setpoint_low = json_object_get_double(val); + } + if (json_object_object_get_ex(metric2, "high", &val)) { + log->setpoint_high = json_object_get_double(val); + } + } + if (json_object_object_get_ex(metric, "heater", &metric2)) { + if (json_object_object_get_ex(metric2, "power", &val)) { + log->heater_power = json_object_get_int(val); + } + if (json_object_object_get_ex(metric2, "usage", &val)) { + log->heater_usage = json_object_get_int(val); + } + } + if (json_object_object_get_ex(metric, "cooler", &metric2)) { + if (json_object_object_get_ex(metric2, "power", &val)) { + log->cooler_power = json_object_get_int(val); + } + if (json_object_object_get_ex(metric2, "usage", &val)) { + log->cooler_usage = json_object_get_int(val); + } + } + if (json_object_object_get_ex(metric, "fan", &metric2)) { + if (json_object_object_get_ex(metric2, "power", &val)) { + log->fan_power = json_object_get_int(val); + } + if (json_object_object_get_ex(metric2, "usage", &val)) { + log->fan_usage = json_object_get_int(val); + } + } +// printf("%s\n", (char *)json_object_to_json_string_ext(metric, 0)); + } + json_object_put(jobj); + + fermentation_mysql_log(log); +/* + printf("datetime %s\n", log->datetime); + printf("product %s %s\n", log->product_code, log->product_name); + printf("stage/mode %s %s\n", log->stage, log->mode); + printf("temp air %.3f\n", log->temperature_air); + printf("temp beer %.3f\n", log->temperature_beer); + printf("temp chiller %.3f\n", log->temperature_chiller); + printf("temp room %.3f\n", log->temperature_room); + printf("setpoint %.1f %.1f\n", log->setpoint_low, log->setpoint_high); + printf("heater %3d %ld\n", log->heater_power, log->heater_usage); + printf("cooler %3d %ld\n", log->cooler_power, log->cooler_usage); + printf("fan %3d %ld\n", log->fan_power, log->fan_usage); + printf("event %s\n", log->event); + printf("fermenter %s\n", log->fermenter_uuid); +*/ + if (log->datetime) + free(log->datetime); + if (log->product_uuid ) + free(log->product_uuid ); + if (log->product_code ) + free(log->product_code ); + if (log->product_name ) + free(log->product_name ); + if (log->stage) + free(log->stage); + if (log->mode) + free(log->mode); + if (log->event) + free(log->event); + if (log->fermenter_uuid) + free(log->fermenter_uuid); + if (log->fermenter_node) + free(log->fermenter_node); + if (log->fermenter_alias) + free(log->fermenter_alias); + free(log); +} + + + +void fermenter_dump(sys_fermenter_list *fermenter) +{ + if (debug) { + printf("uuid %s\n", fermenter->uuid); + printf("alias %s\n", fermenter->alias); + printf("node %s\n", fermenter->node); + printf("online %s\n", fermenter->online ? "yes":"no"); + printf("product %s / %s\n", fermenter->beercode, fermenter->beername); + if (fermenter->air_address) + printf("Air %-36s %10s %8.3f\n", fermenter->air_address, fermenter->air_state, fermenter->air_temperature); + if (fermenter->beer_address) + printf("Beer %-36s %10s %8.3f\n", fermenter->beer_address, fermenter->beer_state, fermenter->beer_temperature); + if (fermenter->chiller_address) + printf("Chiller %-36s %10s %8.3f\n", fermenter->chiller_address, fermenter->chiller_state, fermenter->chiller_temperature); + if (fermenter->heater_address) + printf("Heater %-36s %9d%% %8lu\n", fermenter->heater_address, fermenter->heater_state, fermenter->heater_usage); + if (fermenter->cooler_address) + printf("Cooler %-36s %9d%% %8lu\n", fermenter->cooler_address, fermenter->cooler_state, fermenter->cooler_usage); + if (fermenter->fan_address) + printf("Fan %-36s %9d%% %8lu\n", fermenter->fan_address, fermenter->fan_state, fermenter->fan_usage); + if (fermenter->light_address) + printf("Light %-36s %9d%% %8lu\n", fermenter->light_address, fermenter->light_state, fermenter->light_usage); + if (fermenter->door_address) + printf("Door %-36s %10d\n", fermenter->door_address, fermenter->door_state); + if (fermenter->psu_address) + printf("PSU %-36s %10d\n", fermenter->psu_address, fermenter->psu_state); + printf("mode %s\n", fermenter->mode); + printf("alarm %04x\n", fermenter->alarm); + printf("sp high %8.3f\n", fermenter->setpoint_high); + printf("sp low %8.3f\n", fermenter->setpoint_low); + if (fermenter->profile_uuid) { + printf("profile uuid %s\n", fermenter->profile_uuid); + printf("profile name %s\n", fermenter->profile_name); + printf("profile state %s\n", fermenter->profile_state); + printf("profile percent %d\n", fermenter->profile_percent); + printf("profile sp high %.3f\n", fermenter->profile_inittemp_high); + printf("profile sp low %.3f\n", fermenter->profile_inittemp_low); + printf("profile steps %s\n", fermenter->profile_steps); + } + } +} + + + +void fermenter_death(char *topic) +{ + char *edge_node, *alias; + sys_fermenter_list *tmpp; + + printf("fermenter_death: %s\n", topic); + strtok(topic, "/"); // ignore namespace + strtok(NULL, "/"); // ignore group_id + strtok(NULL, "/"); // ignore message_type + edge_node = strtok(NULL, "/\0"); + alias = strtok(NULL, "/\0"); + + fermenter_mysql_death(edge_node, alias); + + if (alias) { + for (tmpp = fermenters; tmpp; tmpp = tmpp->next) { + if ((strcmp(tmpp->node, edge_node) == 0) && (strcmp(tmpp->alias, alias) == 0)) { + if (tmpp->online) + syslog(LOG_NOTICE, "Offline fermenter %s/%s", tmpp->node, tmpp->alias); + tmpp->online = false; + break; + } + } + } else { + for (tmpp = fermenters; tmpp; tmpp = tmpp->next) { + if (strcmp(tmpp->node, edge_node) == 0) { + if (tmpp->online) + syslog(LOG_NOTICE, "Offline fermenter %s/%s", tmpp->node, tmpp->alias); + tmpp->online = false; + } + } + } +} + +