diff --git a/Daemons/weatherdaemon_newmeteo/Makefile b/Daemons/weatherdaemon_newmeteo/Makefile index 1da0763..7504aaf 100644 --- a/Daemons/weatherdaemon_newmeteo/Makefile +++ b/Daemons/weatherdaemon_newmeteo/Makefile @@ -4,7 +4,7 @@ LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-a LDFLAGS += -flto `pkg-config --libs usefull_macros` -lm SRCS := $(wildcard *.c) DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111 -#DEFINES += -DEBUG +DEFINES += -DEBUG OBJDIR := mk CFLAGS += `pkg-config --cflags usefull_macros` -O3 -Wall -Werror -Wextra -Wno-trampolines -flto OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o)) diff --git a/Daemons/weatherdaemon_newmeteo/socket.c b/Daemons/weatherdaemon_newmeteo/socket.c index ae302d3..7c927ae 100644 --- a/Daemons/weatherdaemon_newmeteo/socket.c +++ b/Daemons/weatherdaemon_newmeteo/socket.c @@ -42,23 +42,34 @@ extern glob_pars *GP; static weather_t lastweather = {0}; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static weatherstat_t wstat; + +typedef enum{ + FORMAT_ERROR, // send user an error message + FORMAT_CURDFULL, // param=value for current data + FORMAT_CURDSHORT, // comma-separated current data + FORMAT_STATFULL, // param=value for stat + FORMAT_STATSHORT // comma-separated stat +} format_t; + + /**************** SERVER FUNCTIONS ****************/ /** * Send data over socket * @param sock - socket fd * @param webquery - ==1 if this is web query - * @param format - 1 - full (param=value), 0 - simple (comma-separated) + * @param format - data format * @return 1 if all OK */ -static int send_data(int sock, int webquery, int format){ +static int send_data(int sock, int webquery, format_t format){ char tbuf[BUFSIZ]; // buffer to send char databuf[BUFSIZ]; // buffer with data ssize_t Len = 0; const char *eol = webquery ? "\r\n" : "\n"; // fill buffer with data pthread_mutex_lock(&mutex); - if(format){ // full format + if(format == FORMAT_CURDFULL){ // full format Len = snprintf(databuf, BUFSIZ, "Wind=%.1f%sDir=%.1f%sPressure=%.1f%sTemperature=%.1f%sHumidity=%.1f%s" "Rain=%.1f%sTime=%.3f%s", @@ -66,13 +77,48 @@ static int send_data(int sock, int webquery, int format){ lastweather.temperature, eol, lastweather.humidity, eol, lastweather.rainfall, eol, lastweather.tmeasure, eol ); - }else{ // short format + }else if(format == FORMAT_CURDSHORT){ // short format Len = snprintf(databuf, BUFSIZ, "%.3f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f%s", lastweather.tmeasure, lastweather.windspeed, lastweather.winddir, lastweather.pressure, lastweather.temperature, lastweather.humidity, lastweather.rainfall, eol ); + }else if(format == FORMAT_STATFULL){ + char *ptr = databuf; + int l = BUFSIZ; +#define PRSTAT(field, nm) do{register int lc = snprintf(ptr, l, \ + nm "max=%.1f%s" nm "min=%.1f%s" nm "mean=%.1f%s" nm "rms=%.1f%s", \ + wstat.field.max, eol, wstat.field.min, eol, wstat.field.mean, eol, wstat.field.rms, eol); \ + Len += lc; l -= lc; ptr += lc;}while(0) + PRSTAT(windspeed, "Wind"); + PRSTAT(winddir, "Dir"); + PRSTAT(pressure, "Pressure"); + PRSTAT(temperature, "Temperature"); + PRSTAT(humidity, "Humidity"); + PRSTAT(rainfall, "Rain"); + PRSTAT(tmeasure, "Time"); +#undef PRSTAT + }else if(format == FORMAT_STATSHORT){ + char *ptr = databuf; + int l = BUFSIZ; + register int lc; +#define PRSTAT(field, nm) do{lc = snprintf(ptr, l, \ + "%.1f,%.1f,%.1f,%.1f", \ + wstat.field.max, wstat.field.min, wstat.field.mean, wstat.field.rms); \ + Len += lc; l -= lc; ptr += lc;}while(0) +#define COMMA() do{lc = snprintf(ptr, l, ","); Len += lc; l -= lc; ptr += lc;}while(0) + PRSTAT(windspeed, "Wind"); COMMA(); + PRSTAT(winddir, "Dir"); COMMA(); + PRSTAT(pressure, "Pressure"); COMMA(); + PRSTAT(temperature, "Temperature"); COMMA(); + PRSTAT(humidity, "Humidity"); COMMA(); + PRSTAT(rainfall, "Rain"); COMMA(); + PRSTAT(tmeasure, "Time"); + Len += snprintf(ptr, l, "%s", eol); +#undef PRSTAT + }else{ + Len = snprintf(databuf, BUFSIZ, "Error!"); } pthread_mutex_unlock(&mutex); // OK buffer ready, prepare to send it @@ -86,21 +132,21 @@ static int send_data(int sock, int webquery, int format){ if(L < 0){ WARN("sprintf()"); LOGWARN("sprintf()"); - return 0; + return FALSE; } if(L != write(sock, tbuf, L)){ LOGWARN("Can't write header"); WARN("write"); - return 0; + return FALSE; } } if(Len != write(sock, databuf, Len)){ WARN("write()"); LOGERR("send_data(): write() failed"); - return 0; + return FALSE; } //LOGDBG("fd %d, write %s", sock, textbuf); - return 1; + return TRUE; } // search a first word after needle without spaces @@ -115,6 +161,15 @@ static char* stringscan(char *str, char *needle){ return a; } +static double getpar(const char *s){ + double x = -1.; + char *eptr = NULL; + while(*s && *s <= ' ') ++s; + x = strtod(s, &eptr); + if(eptr == s) x = -1.; + return x; +} + /** * @brief handle_socket - read information from socket * @param sock - socket fd @@ -142,7 +197,7 @@ static int handle_socket(int sock){ // now we should check what do user want char *found = buff; DBG("user send: %s", buff); - int format = 1; // text format - default for web-queries + format_t format = FORMAT_CURDFULL; // text format - default for web-queries if(0 == strncmp(buff, "GET", 3)){ DBG("GET"); @@ -164,10 +219,15 @@ static int handle_socket(int sock){ int l = strlen(buff); if(contlen && l > contlen) found = &buff[l - contlen]; } - }else if(0 == strncmp(buff, "simple", 6)) format = 0; // simple format + }else if(0 == strncmp(buff, "simple", 6)) format = FORMAT_CURDSHORT; // simple format else if(0 == strncmp(buff, "stat", 4)){ // show stat - stat_for(90.); - return 0; + double dt = -1.; int l = 4; + if(0 == strncmp(buff, "statsimple", 10)){ + l = 10; format = FORMAT_STATSHORT; + }else format = FORMAT_STATFULL; + dt = getpar(buff + l); + if(dt < 1.) dt = 900.; // 15 minutes - default + if(stat_for(dt, &wstat) < 1.) format = FORMAT_ERROR; } // here we can process user data diff --git a/Daemons/weatherdaemon_newmeteo/stat.c b/Daemons/weatherdaemon_newmeteo/stat.c index fec951f..e41816b 100644 --- a/Daemons/weatherdaemon_newmeteo/stat.c +++ b/Daemons/weatherdaemon_newmeteo/stat.c @@ -69,8 +69,11 @@ void addtobuf(weather_t *record){ pthread_mutex_unlock(&mutex); } + + // get statistics for last `Tsec` seconds; @return real dT for given interval -double stat_for(double Tsec/*, weather_t *w*/){ +double stat_for(double Tsec, weatherstat_t *wstat){ + if(!wstat || Tsec < 1.) return 0.; double dt = 0., tlast = buf[lastidx].tmeasure; size_t startfrom = lastidx; pthread_mutex_lock(&mutex); @@ -80,26 +83,64 @@ double stat_for(double Tsec/*, weather_t *w*/){ else --startfrom; } dt = tlast - buf[startfrom].tmeasure; - DBG("got indexes: start=%zd, end=%zd, dt=%.2f", startfrom, lastidx, dt); + // DBG("got indexes: start=%zd, end=%zd, dt=%.2f", startfrom, lastidx, dt); weather_t min = {0}, max = {0}, sum = {0}, sum2 = {0}; size_t amount = 0; memcpy(&min, &buf[lastidx], sizeof(weather_t)); + min.tmeasure = buf[startfrom].tmeasure; + max.tmeasure = buf[lastidx].tmeasure; + double tmean = (max.tmeasure+min.tmeasure)/2.; +// DBG("tmean=%.1f, min=%.1f, max=%.1f", tmean, min.tmeasure, max.tmeasure); + size_t st = startfrom; + // calculate amount of north and south winds + size_t north = 0, south = 0; + while(st != lastidx){ + double w = buf[st].winddir; + if(w < 90. || w > 300.) ++north; + else ++south; + if(++st == buflen) st = 0; + } + double wminus = 0.; + if(north > 2*south) wminus = 360.; while(startfrom != lastidx){ weather_t *curw = &buf[startfrom]; -#define CHK(field) do{register double d = curw->field; if(d > max.field) max.field = d; else if(d < min.field) min.field = d; \ +#define CHK(field) do{register double d = curw->field; if(d > max.field) max.field = d; if(d < min.field) min.field = d; \ sum.field += d; sum2.field += d*d;}while(0) CHK(windspeed); + register double s = curw->windspeed, sd = (curw->winddir - wminus) * s; + if(s > max.winddir) max.winddir = s; + if(s < min.winddir) min.winddir = s; + sum.winddir += sd; + sum2.winddir += sd * s; // v * dir^2 CHK(pressure); CHK(temperature); CHK(humidity); CHK(rainfall); + s = curw->tmeasure; + sum.tmeasure += s; s -= tmean; + sum2.tmeasure += s * s; // correct tmeasure by mean time to exclude cumulative error of double +#undef CHK ++amount; if(++startfrom == buflen) startfrom = 0; } - DBG("Got %zd records", amount); - double wmean = sum.windspeed/amount; - DBG("wind min/max/mean/rms=%.1f/%.1f/%.1f/%.1f", min.windspeed, max.windspeed, - wmean, sqrt(sum2.windspeed/amount - wmean*wmean)); + // DBG("Got %zd records; tsum/sum2: %g/%g", amount, sum.tmeasure, sum2.tmeasure); + wstat->winddir.max = max.winddir; + wstat->winddir.min = min.winddir; + register double s2 = sum2.winddir / sum.windspeed, s = sum.winddir / sum.windspeed; + wstat->winddir.mean = s; + wstat->winddir.rms = sqrt(s2 - s*s); +#define STAT(field) do{ register double ms = sum.field/amount, ms2 = sum2.field/amount; \ + wstat->field.min = min.field; wstat->field.max = max.field; wstat->field.mean = ms; wstat->field.rms = sqrt(ms2 - ms*ms); }while(0) + STAT(windspeed); + STAT(pressure); + STAT(temperature); + STAT(humidity); + STAT(rainfall); + wstat->tmeasure.max = max.tmeasure; + wstat->tmeasure.min = min.tmeasure; + wstat->tmeasure.mean = sum.tmeasure/amount; + wstat->tmeasure.rms = sqrt(sum2.tmeasure/amount); + // DBG("tmean=%.1f, min=%.1f, max=%.1f", wstat->tmeasure.mean, wstat->tmeasure.min, wstat->tmeasure.max); pthread_mutex_unlock(&mutex); return dt; } diff --git a/Daemons/weatherdaemon_newmeteo/stat.h b/Daemons/weatherdaemon_newmeteo/stat.h index 73da1c7..85d4ac5 100644 --- a/Daemons/weatherdaemon_newmeteo/stat.h +++ b/Daemons/weatherdaemon_newmeteo/stat.h @@ -23,6 +23,24 @@ // maximal time difference for records in statbuf - one hour #define STATMAXT (3600.) -double stat_for(double Tsec/*, weather_t *w*/); +// main statistical data +typedef struct{ + double min; + double max; + double mean; + double rms; +} stat_t; + +typedef struct{ + stat_t windspeed; + stat_t winddir; + stat_t pressure; + stat_t temperature; + stat_t humidity; + stat_t rainfall; + stat_t tmeasure; +} weatherstat_t; + +double stat_for(double Tsec, weatherstat_t *wstat); void addtobuf(weather_t *record); double get_tmax();