thermferm/server.c

Tue, 24 Jun 2014 22:38:46 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Tue, 24 Jun 2014 22:38:46 +0200
changeset 75
4b976601737d
parent 74
879bd09e2b96
child 77
143077e54255
permissions
-rw-r--r--

Writes a basic xml configuration next to the plain ascii config file

/*****************************************************************************
 * 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;

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.cc_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;
}



void cmd_die(int onsig)
{
    syslog(LOG_NOTICE, "Server process die on signal %d", onsig);
    close(s);
    exit(0);
}



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';
	}
	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, "HELP", 4) == 0) {
		srv_send((char *)"100 Help text follows");
		srv_send((char *)"Recognized commands:");
		srv_send((char *)"");
		srv_send((char *)"LCD                       Get LCD screen (allways 4 rows of 20 characters)");
		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, "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.cc_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;
	}

    }
}

mercurial