Wed, 02 Jul 2014 17:57:06 +0200
Added unit command and list unit command
/***************************************************************************** * Copyright (C) 2008-2014 * * Michiel Broek <mbroek at mbse dot eu> * * This file is part of the mbsePi-apps * * 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. * * mbsePi-apps 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 EC-65K; see the file COPYING. If not, write to the Free * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *****************************************************************************/ #include "thermferm.h" extern int my_shutdown; extern int debug; extern int current_unit; #ifdef HAVE_WIRINGPI_H extern int lcdHandle; extern unsigned char lcdbuf[MAX_LCDS][20][4]; #endif extern sys_config Config; extern const char UNITMODE[5][8]; int s; /* connected socket */ int ls; /* listen socket */ struct sockaddr_in myaddr_in; /* for local socket address */ struct sockaddr_in peeraddr_in; /* for peer socket address */ struct hostent *hp; #define SS_BUFSIZE 1024 #define SS_TIMEOUT 300 float cv_beerDiff = 0.0; void defaultControlSettings(void) { Config.cs_mode = 'o'; /* o = Off, f = fridge, b = beer, p = profile-run */ Config.cs_beerSet = 20.0; Config.cs_fridgeSet = 20.0; Config.cs_heatEstimator = 0.2; Config.cs_coolEstimator = 5; } void defaultControlConstants(void) { Config.tempFormat = 'C'; Config.cc_tempSetMin = 1.0; Config.cc_tempSetMax = 30.0; Config.cc_idleRangeH = 1.000; Config.cc_idleRangeL = -1.000; } /* * Send message to client */ int srv_send(const char *format, ...) { char out[SS_BUFSIZE]; va_list va_ptr; if (s == -1) return -1; va_start(va_ptr, format); vsnprintf(out, SS_BUFSIZE-1, format, va_ptr); va_end(va_ptr); if (debug) { syslog(LOG_NOTICE, "send: \"%s\"", out); fprintf(stdout, "send: \"%s\"\n", out); } if (send(s, out, strlen(out), 0) != strlen(out)) { syslog(LOG_NOTICE, "srv_send failed"); return -1; } if (send(s, (char *)"\r\n", 2, 0) != 2) { syslog(LOG_NOTICE, "srv_send failed"); return -1; } return 0; } int unit_add(char *buf) { units_list *unit, *tmp; uuid_t uu; char *cmd, *opt; int id = 1; cmd = strtok(buf, " \0"); opt = strtok(NULL, "\0"); if (opt == NULL) { srv_send((char *)"501 Parameter missing"); return 1; } if (debug) fprintf(stdout, "cmd: '%s' opt: '%s'\n", MBSE_SS(cmd), MBSE_SS(opt)); unit = (units_list *)malloc(sizeof(units_list)); unit->next = NULL; unit->uuid = malloc(37); uuid_generate(uu); uuid_unparse(uu, unit->uuid); unit->name = xstrcpy(opt); unit->air_address = unit->beer_address = unit->io1_address = unit->io2_address = unit->profile = NULL; unit->volume = 0.0; unit->heater_available = unit->cooler_available = unit->fan_available = FALSE; unit->air_temp = unit->beer_temp = unit->beer_set = unit->fridge_set = 20.0; unit->heater_state = unit->cooler_state = unit->fan_state = unit->door_state = unit->mode = 0; unit->temp_set_min = 1.0; unit->temp_set_max = 30.0; unit->idle_rangeH = 1.0; unit->idle_rangeL = -1.0; unit->prof_started = (time_t)0; if (Config.units == NULL) { Config.units = unit; } else { for (tmp = Config.units; tmp; tmp = tmp->next) { id++; if (tmp->next == NULL) { tmp->next = unit; break; } } } syslog(LOG_NOTICE, "Unit %d with uuid %s added", id, unit->uuid); srv_send((char *)"211 Unit %d with uuid %s added", id, unit->uuid); current_unit = id; return 0; } /* * LIST * LIST BUS * LIST PROFILES * LIST UNIT */ int cmd_list(char *buf) { char *opt, *mypath; units_list *unit; int i; DIR *fd; struct dirent *de; opt = strtok(buf, " \0"); opt = strtok(NULL, " \0"); if (opt == NULL) { srv_send((char *)"212 Fermenter list follows:"); i = 0; for (unit = Config.units; unit; unit = unit->next) { i++; srv_send((char *)"%02d %s %-20s %-7s", i, unit->uuid, unit->name, UNITMODE[unit->mode]); } srv_send((char *)"."); } else if (strcmp(opt, (char *)"BUS") == 0) { /* * 1-wire bus */ if ((fd = opendir((char *)"/sys/bus/w1/devices"))) { srv_send((char *)"212 1-wire bus devices:"); while ((de = readdir(fd))) { if (de->d_name[0] != '.') { if (strncmp(de->d_name, (char *)"w1", 2) == 0) { srv_send((char *)"%-15s system device", de->d_name); } else if (strncmp(de->d_name, (char *)"10", 2) == 0) { srv_send((char *)"%-15s 18S20 digital thermometer", de->d_name); } else if (strncmp(de->d_name, (char *)"28", 2) == 0) { srv_send((char *)"%-15s 18B20 digital thermometer", de->d_name); } else if (strncmp(de->d_name, (char *)"29", 2) == 0) { srv_send((char *)"%-15s 2408 8-channel addressable switch/LCD", de->d_name); } else if (strncmp(de->d_name, (char *)"3a", 2) == 0) { srv_send((char *)"%-15s 2413 dual channel addressable switchs", de->d_name); } else { srv_send((char *)"%-15s unknown device", de->d_name); } } } srv_send((char *)"."); closedir(fd); } else { srv_send((char *)"503 directory /sys/bus/w1/devices: %s", strerror(errno)); } } else if (strcmp(opt, (char *)"PROFILES") == 0) { /* * User profiles directory */ if (getenv((char *)"USER") == NULL) { mypath = xstrcpy((char *)"/root"); } else { mypath = xstrcpy(getenv((char *)"HOME")); } mypath = xstrcat(mypath, (char *)"/.thermferm/profiles/"); mkdirs(mypath, 0755); if ((fd = opendir(mypath))) { srv_send((char *)"212 profiles:"); while ((de = readdir(fd))) { if (de->d_name[0] != '.') { srv_send((char *)"%s", de->d_name); } } srv_send((char *)"."); closedir(fd); } else { srv_send((char *)"503 directory %s: %s", mypath, strerror(errno)); } free(mypath); mypath = NULL; } else if (strcmp(opt, (char *)"UNIT") == 0) { /* * List configured and selected fermenter unit */ if (current_unit == -1) { srv_send((char *)"401 No fermenter unit selected"); } else { srv_send((char *)"213 Unit %d listing follows:", current_unit); i = 0; for (unit = Config.units; unit; unit = unit->next) { i++; if (i == current_unit) { srv_send((char *)"Name of the unit/beer %s", unit->name); srv_send((char *)"UUID of this unit %s", unit->uuid); if (unit->air_address) { srv_send((char *)"1-wire address air sensor %s", unit->air_address); srv_send((char *)"Air temperature %.1f", unit->air_temp); } if (unit->beer_address) { srv_send((char *)"1-wire address beer sensor %s", unit->air_address); srv_send((char *)"Beer temperature %.1f", unit->air_temp); } if (unit->io1_address) { srv_send((char *)"1-wire address cool/heat %s", unit->io1_address); } if (unit->io2_address) { srv_send((char *)"1-wire address fan/door %s", unit->io2_address); } if (unit->heater_available) { srv_send((char *)"Heater available %s", unit->heater_available); } if (unit->cooler_available) { srv_send((char *)"Cooler available %s", unit->cooler_available); } if (unit->fan_available) { srv_send((char *)"Fan available %s", unit->fan_available); } srv_send((char *)"Unit mode %s", UNITMODE[unit->mode]); srv_send((char *)"Fridge temperature set to %.1f", unit->fridge_set); srv_send((char *)"Beer temperature set to %.1f", unit->fridge_set); if (unit->profile) { srv_send((char *)"Profile name %s", unit->profile); } srv_send((char *)"Temperature range %.1f .. %.1f", unit->temp_set_min, unit->temp_set_max); srv_send((char *)"Idle temperature range %.1f .. %.1f", unit->idle_rangeL, unit->idle_rangeH); } } srv_send((char *)"."); } } else { srv_send((char *)"502 Unknown command option"); } return 0; } /* * UNIT n * UNIT uuid */ int cmd_unit(char *buf) { char *opt; units_list *tmp; int i, unit_no; opt = strtok(buf, " \0"); opt = strtok(NULL, " \0"); if (opt == NULL) { srv_send((char *)"501 Parameter missing"); return 1; } i = 0; if (strlen(opt) == 36) { /* * Search using uuid */ for (tmp = Config.units; tmp; tmp = tmp->next) { i++; if (strcmp(opt, tmp->uuid) == 0) { srv_send((char *)"210 Unit %d selected", i); current_unit = i; return 0; } } srv_send((char *)"410 No such unit"); return 1; } if (sscanf(opt, "%d", &unit_no) == 1) { /* * We got a number, see if it is valid. */ for (tmp = Config.units; tmp; tmp = tmp->next) { i++; if (unit_no == i) { srv_send((char *)"210 Unit %d selected", i); current_unit = i; return 0; } } srv_send((char *)"410 No such unit"); return 1; } srv_send((char *)"502 Unknown command option"); return 1; } void cmd_server(void) { char *inp, *p, *q, buf[SS_BUFSIZE], obuf[SS_BUFSIZE]; int i, rc, rlen; socklen_t fromlen; float newtemp; #ifdef HAVE_WIRINGPI_H int j; #endif // if (debug) { // char *hostname = inet_ntoa(peeraddr_in.sin_addr); // syslog(LOG_NOTICE, "Start new client connection from %s port %u", hostname, ntohs(peeraddr_in.sin_port)); // fprintf(stdout, "Start new client connection from %s port %u\n", hostname, ntohs(peeraddr_in.sin_port)); // } memset((char *)&buf, 0, SS_BUFSIZE); fromlen = sizeof(peeraddr_in); rlen = recvfrom(s, buf, sizeof(buf) -1, 0, (struct sockaddr *)&peeraddr_in, &fromlen); if (rlen == -1) { syslog(LOG_NOTICE, "recvfrom(): %s", strerror(errno)); } else { for (i = 0; i < strlen(buf); i++) { if (buf[i] == '\n') buf[i] = '\0'; if (buf[i] == '\r') buf[i] = '\0'; } for (i = strlen(buf) -1; i > 0; i--) { if (buf[i] == ' ') buf[i] = '\0'; else break; } if (strlen(buf)) { if (debug) { syslog(LOG_NOTICE, "recv: \"%s\"", buf); fprintf(stdout, "recv: \"%s\"\n", buf); } /* * Process commands from the client */ if (strncmp(buf, "ADD", 3) == 0) { unit_add(buf); } else if (strncmp(buf, "HELP", 4) == 0) { srv_send((char *)"100 Help text follows"); srv_send((char *)"Recognized commands:"); srv_send((char *)""); srv_send((char *)"ADD name Add a new unit with \"name\""); srv_send((char *)"LCD Get LCD screen (allways 4 rows of 20 characters)"); srv_send((char *)"LIST List all fermenter units"); srv_send((char *)"LIST BUS List 1-wire bus"); srv_send((char *)"LIST PROFILES List available profiles"); srv_send((char *)"LIST UNIT List fermenter unit"); // srv_send((char *)"MODE off|none|beer|fridge|profile"); // srv_send((char *)"PROFILE Profile status"); // srv_send((char *)"PROFILE start|stop|pause Profile start, stop or pause"); // srv_send((char *)"SET BEER Set beer temperature"); // srv_send((char *)"SET FRIDGE Set fridge temperature"); // srv_send((char *)"SET IDLE LOW val Set idle temperature low"); // srv_send((char *)"SET IDLE HIGH val Set idle temperature high"); // srv_send((char *)"SET NAME name Set name or beername for the unit"); // srv_send((char *)"SET NONE Set unit to none, but still logs"); // srv_send((char *)"SET OFF Set unit off"); // srv_send((char *)"SET PROFILE name Set named profile"); // srv_send((char *)"SET TEMP MIN val Set unit minimum temperature"); // srv_send((char *)"SET TEMP MAX val Set unit maximum temperature"); // srv_send((char *)"SET VOLUME Set unit volume"); srv_send((char *)"UNIT n|uuid Select unit by number or uuid"); srv_send((char *)"."); } else if (strncmp(buf, "LCD", 3) == 0) { #ifdef HAVE_WIRINGPI_H srv_send((char *)"201 information follows"); for (j = 0; j < 4; j++) { sprintf(obuf, " "); obuf[20] = '\0'; for (i = 0; i < 20; i++) obuf[i] = lcdbuf[lcdHandle][i][j]; srv_send(obuf); } srv_send((char *)"."); #else srv_send((char *)"403 LCD not available"); #endif } else if (strncmp(buf, "LIST", 4) == 0) { cmd_list(buf); } else if (strncmp(buf, "UNIT", 4) == 0) { cmd_unit(buf); } else if (strncmp(buf, "ack", 3) == 0) { srv_send((char *)"ack"); } else if (strncmp(buf, "lcd", 3) == 0) { sprintf(obuf, "[\" \", \" \", \" \", \" \"]"); #ifdef HAVE_WIRINGPI_H for (i = 0; i < 20; i++) { obuf[i+2] = lcdbuf[lcdHandle][i][0]; obuf[i+26] = lcdbuf[lcdHandle][i][1]; obuf[i+50] = lcdbuf[lcdHandle][i][2]; obuf[i+74] = lcdbuf[lcdHandle][i][3]; } #endif srv_send(obuf); } else if (strncmp(buf, "getMode", 7) == 0) { srv_send("%c", Config.cs_mode); } else if (strncmp(buf, "getFridge", 9) == 0) { srv_send("%.1f", Config.cs_fridgeSet); } else if (strncmp(buf, "getBeer", 7) == 0) { srv_send("%.1f", Config.cs_beerSet); } else if (strncmp(buf, "getControlConstants", 19) == 0) { srv_send("{ \"tempFormat\":\"%c\", \"tempSetMin\":%.1f, \"tempSetMax\":%.1f, \"idleRangeH\":%.3f, \"idleRangeL\":%.3f }", Config.tempFormat, Config.cc_tempSetMin, Config.cc_tempSetMax, Config.cc_idleRangeH, Config.cc_idleRangeL ); } else if (strncmp(buf, "getControlSettings", 18) == 0) { srv_send("{ \"mode\":\"%c\", \"beerSet\":%.1f, \"fridgeSet\":%.1f, \"heatEstimator\":%.1f, \"coolEstimator\":%.1f }", Config.cs_mode, Config.cs_beerSet, Config.cs_fridgeSet, Config.cs_heatEstimator, Config.cs_coolEstimator); } else if (strncmp(buf, "getControlVariables", 19) == 0) { srv_send("{ \"beerDiff\":%.2f }", cv_beerDiff); } else if (strncmp(buf, "loadDefaultControlSettings", 26) == 0) { defaultControlSettings(); srv_send("ack"); } else if (strncmp(buf, "loadDefaultControlConstants", 27) == 0) { defaultControlConstants(); srv_send("ack"); } else if (strncmp(buf, "setBeer=", 8) == 0) { inp = xstrcpy(buf+8); rc = sscanf(inp, "%f", &newtemp); if (debug) fprintf(stdout, "new temp from %s, %.1f, rc=%d\n", inp, newtemp, rc); if (rc == 1) { if ((Config.cc_tempSetMin <= newtemp) && (newtemp <= Config.cc_tempSetMax)) { syslog(LOG_NOTICE, "Beer temperature set to %.1f degrees in web interface", newtemp); srv_send("ack"); Config.cs_mode = 'b'; Config.cs_beerSet = newtemp; } else { syslog(LOG_NOTICE, "Beer temperature setting %.1f is outside of allowed range %.1f - %.1f", newtemp, Config.cc_tempSetMin, Config.cc_tempSetMax); srv_send("err"); } } else { syslog(LOG_NOTICE, "Cannot convert temperature '%s' to float", inp); srv_send("err"); } free(inp); } else if (strncmp(buf, "setFridge=", 10) == 0) { inp = xstrcpy(buf+10); rc = sscanf(inp, "%f", &newtemp); if (debug) fprintf(stdout, "new temp from %s, %.1f, rc=%d\n", inp, newtemp, rc); if (rc == 1) { if ((Config.cc_tempSetMin <= newtemp) && (newtemp <= Config.cc_tempSetMax)) { syslog(LOG_NOTICE, "Fridge temperature set to %.1f degrees in web interface", newtemp); srv_send("ack"); Config.cs_mode = 'f'; Config.cs_fridgeSet = newtemp; } else { syslog(LOG_NOTICE, "Fridge temperature setting %.1f is outside of allowed range %.1f - %.1f", newtemp, Config.cc_tempSetMin, Config.cc_tempSetMax); srv_send("err"); } } else { syslog(LOG_NOTICE, "Cannot convert temperature '%s' to float", inp); srv_send("err"); } free(inp); } else if (strncmp(buf, "setOff", 6) == 0) { if (debug) fprintf(stdout, "temperature control disabled\n"); syslog(LOG_NOTICE, "Notification: Temperature control disabled"); Config.cs_mode = 'o'; srv_send("ack"); } else if (strncmp(buf, "setParameters=", 14) == 0) { inp = xstrcpy(buf+14); /* {"tempSetMax":30.5} */ if (debug) fprintf(stdout, "setParameters: %s\n", inp); strtok(inp, (char *)"\""); p = strtok(NULL, (char *)"\""); q = strtok(NULL, (char *)":,}"); if (strcmp(p, (char *)"tempSetMin") == 0) { rc = sscanf(q, "%f", &newtemp); if (rc == 1) { syslog(LOG_NOTICE, "cc_tempSetMin = %1.f", newtemp); Config.cc_tempSetMin = newtemp; srv_send("ack"); } else { srv_send("ERR"); } } else if (strcmp(p, (char *)"tempSetMax") == 0) { rc = sscanf(q, "%f", &newtemp); if (rc == 1) { syslog(LOG_NOTICE, "cc_tempSetMax = %1.f", newtemp); Config.cc_tempSetMax = newtemp; srv_send("ack"); } else { srv_send("ERR"); } } else { fprintf(stdout, "p='%s' q='%s' inp='%s'\n", p, q, inp); srv_send("ERR"); } free(inp); // stopScript // quit // eraseLogs // interval // startNewBrew // pauseLogging // stopLogging // resumeLogging // dateTimeFormatDisplay } else if (strncmp(buf, "setActiveProfile", 16) == 0) { syslog(LOG_NOTICE, "Setting profile '%s' as active profile", "undefined"); // programArduino // refreshDeviceList // getDeviceList // applyDevice } else { if (debug) fprintf(stdout, "unknown command \"%s\"\n", buf); srv_send((char *)"ERR"); } } } // if (debug) { // syslog(LOG_NOTICE, "End connection from %s port %u", hostname, ntohs(peeraddr_in.sin_port)); // fprintf(stdout, "End connection from %s port %u\n", hostname, ntohs(peeraddr_in.sin_port)); // } close(s); } #ifdef HAVE_WIRINGPI_H PI_THREAD (my_server_loop) #else void *my_server_loop(void *threadid) #endif { socklen_t addrlen; int optval = 1; syslog(LOG_NOTICE, "Thread my_server_loop started"); if (debug) fprintf(stdout, "Thread my_server_loop started\n"); memset((char *)&myaddr_in, 0, sizeof(struct sockaddr_in)); memset((char *)&peeraddr_in, 0, sizeof(struct sockaddr_in)); myaddr_in.sin_family = AF_INET; myaddr_in.sin_addr.s_addr = INADDR_ANY; myaddr_in.sin_port = htons(Config.my_port); ls = socket(AF_INET, SOCK_STREAM, 0); if (ls == -1) { syslog(LOG_NOTICE, "Can't create listen socket: %s", strerror(errno)); fprintf(stderr, "Can't create listen socket: %s\n", strerror(errno)); return 0; } if (setsockopt(ls, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) == -1) { syslog(LOG_NOTICE, "Can't setsockopt SO_KEEPALIVE socket: %s", strerror(errno)); close(ls); return 0; } if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) { syslog(LOG_NOTICE, "Can't setsockopt SO_REUSEADDR socket: %s", strerror(errno)); close(ls); return 0; } if (bind(ls, (struct sockaddr *)&myaddr_in, sizeof(struct sockaddr_in)) == -1) { syslog(LOG_NOTICE, "Can't bind to listen socket: %s", strerror(errno)); close(ls); return 0; } if (listen(ls, 5) == -1) { syslog(LOG_NOTICE, "Can't listen on listen socket: %s", strerror(errno)); close(ls); return 0; } syslog(LOG_NOTICE, "listen socket created %d", ls); if (debug) fprintf(stdout, "listen socket created %d\n", ls); /* * Loop forever until the external shutdown variable is set. */ for (;;) { addrlen = sizeof(struct sockaddr_in); /* * This call will block until a new connection * arrives. Then it will return the address of * the connecting peer, and a new socket * descriptor, s, for that connection. */ s = accept(ls, (struct sockaddr *)&peeraddr_in, &addrlen); if (s == -1) { syslog(LOG_NOTICE, "my_server_loop accept failed %s", strerror(errno)); if (debug) fprintf(stdout, "my_server_loop accept failed %s\n", strerror(errno)); return 0; } cmd_server(); if (my_shutdown) { syslog(LOG_NOTICE, "Thread my_server_loop stopped"); if (debug) fprintf(stdout, "Thread my_server_loop stopped\n"); return 0; } } }