From 05a42b0a1014c11016f3b2bee9af6212471318a7 Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Fri, 3 Apr 2026 13:14:45 +0300 Subject: [PATCH] tests works, need real meteo modules --- Daemons/weatherdaemon_multimeteo/cmdlnopts.c | 41 ++- Daemons/weatherdaemon_multimeteo/fd.c | 3 +- Daemons/weatherdaemon_multimeteo/main.c | 54 +++- .../weatherdaemon_multimeteo/mainweather.c | 263 ++++++++++++++++++ .../weatherdaemon_multimeteo/mainweather.h | 58 ++++ .../weatherdaemon_multimeteo/plugins/dummy.c | 23 +- .../plugins/fdexample.c | 11 +- Daemons/weatherdaemon_multimeteo/sensors.c | 24 +- Daemons/weatherdaemon_multimeteo/sensors.h | 1 + Daemons/weatherdaemon_multimeteo/server.c | 72 +++-- .../weatherdaemon.files | 2 + Daemons/weatherdaemon_multimeteo/weathlib.c | 21 +- Daemons/weatherdaemon_multimeteo/weathlib.h | 2 + 13 files changed, 505 insertions(+), 70 deletions(-) create mode 100644 Daemons/weatherdaemon_multimeteo/mainweather.c create mode 100644 Daemons/weatherdaemon_multimeteo/mainweather.h diff --git a/Daemons/weatherdaemon_multimeteo/cmdlnopts.c b/Daemons/weatherdaemon_multimeteo/cmdlnopts.c index 5dea00f..3944c67 100644 --- a/Daemons/weatherdaemon_multimeteo/cmdlnopts.c +++ b/Daemons/weatherdaemon_multimeteo/cmdlnopts.c @@ -21,7 +21,9 @@ #include #include #include + #include "cmdlnopts.h" +#include "mainweather.h" /* * here are global parameters initialisation @@ -38,12 +40,32 @@ static glob_pars defpars = { .port = DEFAULT_PORT, .logfile = NULL, .verb = 0, - .pidfile = DEFAULT_PID + .pidfile = DEFAULT_PID, }; // default config: all values should be wrong or empty to understand than user change them static glob_pars defconf = { .verb = -1, }; +// only for config +weather_conf_t WeatherConf = { + .ahtung_delay = 30*60, // 30 minutes + .wind.good = 5., // < 5m/s - good weather + .wind.bad = 10., // > 10m/s - bad weather + .wind.terrible = 15., // > 15m/s - terrible weather + .wind.negflag = 0, + .humidity.good = 65., + .humidity.bad = 80., + .humidity.terrible = 90., + .humidity.negflag = 0, + .clouds.good = 2500., + .clouds.bad = 2000., + .clouds.terrible = 500., + .clouds.negflag = 1, + .sky.good = -40., + .sky.bad = -10., + .sky.terrible = 0., + .sky.negflag = 0 +}; static glob_pars G; @@ -62,13 +84,26 @@ static glob_pars G; sl_option_t cmdlnopts[] = { {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), "show this help"}, {"conffile",NEED_ARG, NULL, 'c', arg_string, APTR(&G.conffile), "configuration file name (consists all or a part of long-named parameters and their values (e.g. plugin=liboldweather.so:D:/dev/ttyS0:115200)"}, - {"verb", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verb), "logfile verbocity level (each -v increased)"}, \ + {"verb", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verb), "logfile verbocity level (each -v increased)"}, COMMON_OPTS end_option }; sl_option_t confopts[] = { - {"verbose", NEED_ARG, NULL, 'v', arg_int, APTR(&G.verb), "logfile verbocity level"}, \ + {"verbose", NEED_ARG, NULL, 'v', arg_int, APTR(&G.verb), "logfile verbocity level"}, + {"ahtung_delay",NEED_ARG,NULL, 0, arg_int, APTR(&WeatherConf.ahtung_delay),"delay in seconds after bad weather to change to good"}, + {"good_wind",NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.wind.good), "good wind while less this"}, + {"bad_wind", NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.wind.bad), "bad wind if more than this"}, + {"terrible_wind",NEED_ARG, NULL,0, arg_double, APTR(&WeatherConf.wind.terrible), "terrible wind if more than this"}, + {"good_humidity",NEED_ARG, NULL,0, arg_double, APTR(&WeatherConf.humidity.good), "humidity is good until this"}, + {"bad_humidity",NEED_ARG, NULL,0, arg_double, APTR(&WeatherConf.humidity.bad), "humidity is bad if greater"}, + {"terrible_humidity",NEED_ARG,NULL, 0, arg_double, APTR(&WeatherConf.humidity.terrible), "humidity is terrible if greater"}, + {"good_clouds",NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.clouds.good), "good weather when \"clouds value\" greater than this"}, + {"bad_clouds",NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.clouds.bad), "if less than this, clouds are bad"}, + {"terrible_clouds",NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.clouds.terrible), "if less, clouds are terrible"}, + {"good_sky",NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.sky.good), "sky-ambient less than this is good"}, + {"bad_sky",NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.sky.bad), "sky-ambient greater than this is bad"}, + {"terrible_sky",NEED_ARG,NULL, 0, arg_double, APTR(&WeatherConf.sky.terrible), "sky-ambient greater than this is terrible"}, COMMON_OPTS end_option }; diff --git a/Daemons/weatherdaemon_multimeteo/fd.c b/Daemons/weatherdaemon_multimeteo/fd.c index 0a27b34..06509ee 100644 --- a/Daemons/weatherdaemon_multimeteo/fd.c +++ b/Daemons/weatherdaemon_multimeteo/fd.c @@ -136,8 +136,9 @@ static int opensocket(char *path, sl_socktype_e type){ * @return opened file descriptor or -1 in case of error */ int getFD(char *path){ - if(!path || !*path) return -1; + if(!path || !*path || strlen(path) < 2) return -1; char type = *path; + if(path[1] != ':') return -1; // after protocol letter should be delimeter path += 2; switch(type){ case 'D': // serial device diff --git a/Daemons/weatherdaemon_multimeteo/main.c b/Daemons/weatherdaemon_multimeteo/main.c index 94ccf5d..c985cdd 100644 --- a/Daemons/weatherdaemon_multimeteo/main.c +++ b/Daemons/weatherdaemon_multimeteo/main.c @@ -24,40 +24,54 @@ #include #include "cmdlnopts.h" +#include "mainweather.h" #include "sensors.h" #include "server.h" static pid_t childpid = 0; +static glob_pars *GP = NULL; +// SIGUSR1 - FORBID observations +// SIGUSR2 - allow void signals(int signo){ - if(childpid){ + if(signo){ + if(signals != signal(signo, SIG_IGN)) exit(signo); // function called "as is", before sig registration + if(childpid == 0){ // child -> test USR1/USR2 + LOGDBG("Child gotta signal %d", signo); + if(signo == SIGUSR1){ + forbid_observations(1); + LOGMSG("Got signal `observations forbidden`"); + signal(signo, signals); + return; + }else if(signo == SIGUSR2){ + forbid_observations(0); + LOGMSG("Got signal `observations permitted`"); + signal(signo, signals); + return; + } + } + } + if(childpid){ // master + LOGERR("Main process exits with status %d", signo); + if(GP && GP->pidfile) unlink(GP->pidfile); + }else{ // child LOGERR("Killed with status %d", signo); closeplugins(); kill_servers(); - usleep(1000); // let child close everything before dead - }else{ - LOGERR("Main process exits with status %d", signo); } + usleep(1000); // let child close everything before dead exit(signo); } static void getpipe(int _U_ signo){ WARNX("Get sigpipe!"); + LOGWARN("SIGPIPE: something disconnected?"); // TODO: check all sensors for disconnected one signal(SIGPIPE, getpipe); } -extern const char *__progname; - int main(int argc, char **argv){ - glob_pars *GP = NULL; sl_init(); - signal(SIGTERM, signals); // kill (-15) - quit - signal(SIGHUP, SIG_IGN); // hup - ignore - signal(SIGINT, signals); // ctrl+C - quit - signal(SIGQUIT, signals); // ctrl+\ - quit - signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z - signal(SIGPIPE, getpipe); // socket disconnected GP = parse_args(argc, argv); if(!GP) ERRX("Error parsing args"); if(!GP->sockname) ERRX("Point command socket name"); @@ -71,14 +85,23 @@ int main(int argc, char **argv){ if(GP->pollt > 0){ if(!set_pollT((time_t)GP->pollt)) ERRX("Can't set polling time to %d seconds", GP->pollt); } + signal(SIGTERM, signals); // kill (-15) - quit + signal(SIGHUP, SIG_IGN); // hup - ignore + signal(SIGINT, signals); // ctrl+C - quit + signal(SIGQUIT, signals); // ctrl+\ - quit + signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z + signal(SIGPIPE, getpipe); // socket disconnected + signal(SIGUSR1, SIG_IGN); + signal(SIGUSR2, SIG_IGN); int nopened = openplugins(GP->plugins, GP->nplugins); if(nopened < 1){ LOGERR("No plugins found; exit!"); ERRX("Can't find any sensor plugin"); } if(GP->nplugins && GP->nplugins != nopened) LOGWARN("Work without some plugins"); - #ifndef EBUG sl_check4running((char*)__progname, GP->pidfile); + #ifndef EBUG + sl_daemonize(); while(1){ // guard for dead processes childpid = fork(); if(childpid){ @@ -94,6 +117,9 @@ int main(int argc, char **argv){ } } #endif + // react for USRx only in child + signal(SIGUSR1, signals); + signal(SIGUSR2, signals); if(!start_servers(GP->port, GP->sockname)) ERRX("Can't run server's threads"); while(1); //WARNX("TEST ends"); diff --git a/Daemons/weatherdaemon_multimeteo/mainweather.c b/Daemons/weatherdaemon_multimeteo/mainweather.c new file mode 100644 index 0000000..8f861fc --- /dev/null +++ b/Daemons/weatherdaemon_multimeteo/mainweather.c @@ -0,0 +1,263 @@ +/* + * This file is part of the weatherdaemon project. + * Copyright 2026 Edward V. Emelianov . + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +// collect here weather from all weatherstations sorted by importance + +#include + +#include "mainweather.h" +#include "sensors.h" +#include "weathlib.h" + +static pthread_mutex_t datamutex = PTHREAD_MUTEX_INITIALIZER; +static int Forbidden = 0; + +// index of meteodata in array +enum{ + NWIND, + NWINDDIR, + NHUMIDITY, + NABM_TEMP, + NPRESSURE, + NPRECIP, + NPRECIP_LEVEL, + NMIST, + NCLOUDS, + NSKYTEMP, + NCOMMWEATH, + NLASTAHTUNG, + NAMOUNT_OF_DATA +}; + +static val_t collected_data[NAMOUNT_OF_DATA] = { + [NWIND] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WIND}, + [NWINDDIR] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WINDDIR}, + [NHUMIDITY] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_HUMIDITY}, + [NABM_TEMP] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_AMB_TEMP}, + [NPRESSURE] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_PRESSURE}, + [NPRECIP] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_PRECIP}, + [NPRECIP_LEVEL] = {.sense = VAL_OBLIGATORY, .type = VALT_INT, .meaning = IS_PRECIP_LEVEL}, + [NMIST] = {.sense = VAL_OBLIGATORY, .type = VALT_INT, .meaning = IS_MIST}, + [NCLOUDS] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_CLOUDS}, + [NSKYTEMP] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_SKYTEMP}, + [NCOMMWEATH] = {.value.i = 0, .sense = VAL_OBLIGATORY, .type = VALT_INT, .meaning = IS_OTHER, .name = "weather", .comment = "Weather level (0 - good, 3 - obs. prohibited)"}, + [NLASTAHTUNG] = {.value.i = 0, .sense = VAL_RECOMMENDED, .type = VALT_INT, .meaning = IS_OTHER, .name = "evttime", .comment = "UNIX-time of last weather level increasing"}, +// {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_OTHER}, +}; + +int collected_amount(){ + return NAMOUNT_OF_DATA; +} + +int get_collected(val_t *val, int N){ + if(!val || N < 0 || N >= NAMOUNT_OF_DATA){ + DBG("Wrong number (%d) requested or no place for data", N); + return FALSE; + } + pthread_mutex_lock(&datamutex); + DBG("Copied data of %d", N); + *val = collected_data[N]; + pthread_mutex_unlock(&datamutex); + return TRUE; +} + +static void fix_new_data(val_t *collected, val_t *fresh){ + FNAME(); + if(!collected || !fresh) return; + collected->time = fresh->time; + if(collected->type == fresh->type){ // good case + collected->value = fresh->value; + return; + } + // bad case: have different types + switch(collected->type){ + case VALT_UINT: + switch(fresh->type){ + case VALT_INT: + collected->value.u = (uint32_t) collected->value.i; + break; + case VALT_FLOAT: + collected->value.u = (uint32_t) collected->value.f; + default: break; + } + break; + case VALT_INT: + switch(fresh->type){ + case VALT_UINT: + collected->value.i = (int32_t) collected->value.u; + break; + case VALT_FLOAT: + collected->value.i = (int32_t) collected->value.f; + default: break; + } + break; + case VALT_FLOAT: + switch(fresh->type){ + case VALT_UINT: + collected->value.f = (float) collected->value.u; + break; + case VALT_INT: + collected->value.f = (float) collected->value.i; + default: break; + } + break; + } +} + +static void chkweatherlevel(int *curlevel, double curvalue, weather_cond_t *curcond, int *ahtungtime){ + double good = curcond->good, bad = curcond->bad, terrible = curcond->terrible; + if(curcond->negflag){ // negate + curvalue = -curvalue; + good = -good; + bad = -bad; + terrible = -terrible; + } + int newlevel = -1; + if(curvalue > terrible) newlevel = WEATHER_TERRIBLE; + else if(curvalue > bad) newlevel = WEATHER_BAD; + else if(curvalue < good) newlevel = WEATHER_GOOD; + if(newlevel == -1) return; + time_t curt = time(NULL); + if(newlevel > *curlevel){ + DBG("newlevel: %d, current: %d INCREASED", newlevel, *curlevel); + *curlevel = newlevel; + if(*ahtungtime < curt) *ahtungtime = (int) curt; // refresh event time + }else{ // check timeout to make level lower + if(curt - *ahtungtime > WeatherConf.ahtung_delay){ + DBG("newlevel: %d, current: %d DECREASED", newlevel, *curlevel); + *curlevel = newlevel; + } + } +} + +void refresh_sensval(sensordata_t *s){ + FNAME(); + static time_t poll_time = 0; + val_t value; + if(!s || !s->get_value) return; + if(poll_time == 0) poll_time = get_pollT(); + int curlevel = collected_data[NCOMMWEATH].value.i; + int curahtungtime = collected_data[NLASTAHTUNG].value.i; + time_t curtime = time(NULL); + DBG("%d meteo values", s->Nvalues); + for(int i = 0; i < s->Nvalues; ++i){ + DBG("Try to get %dth value", i); + if(!s->get_value(s, &value, i)) continue; + DBG("got value"); + int idx = -1; + double curvalue; + weather_cond_t *curcond = NULL; + switch(value.meaning){ + case IS_WIND: + idx = NWIND; + curvalue = (double) value.value.f; + curcond = &WeatherConf.wind; + break; + case IS_WINDDIR: + idx = NWINDDIR; + break; + case IS_HUMIDITY: + idx = NHUMIDITY; + curvalue = (double) value.value.f; + curcond = &WeatherConf.humidity; + break; + case IS_AMB_TEMP: + idx = NABM_TEMP; + break; + case IS_PRESSURE: + idx = NPRESSURE; + break; + case IS_PRECIP: + idx = NPRECIP; + if(value.value.i && curlevel < WEATHER_TERRIBLE){ + curlevel = WEATHER_TERRIBLE; + curahtungtime = curtime; + } + break; + case IS_PRECIP_LEVEL: + idx = NPRECIP_LEVEL; + break; + case IS_MIST: + idx = NMIST; + if(value.value.i && curlevel < WEATHER_TERRIBLE){ + curahtungtime = curtime; + curlevel = WEATHER_TERRIBLE; + } + break; + case IS_CLOUDS: + idx = NCLOUDS; + curvalue = (double) value.value.f; + curcond = &WeatherConf.clouds; + break; + case IS_SKYTEMP: + idx = NSKYTEMP; + curvalue = (double) value.value.f; + curcond = &WeatherConf.sky; + break; + default : break; + } + if(idx < 0 || idx >= NAMOUNT_OF_DATA) continue; + DBG("IDX=%d", idx); + time_t freshdelay = (s->PluginNo == 0) ? 0 : poll_time; // use data of less imrortant plugins only if our data is too old + time_t curmt = collected_data[idx].time + freshdelay; + if(value.time < curmt){ + DBG("Data too old (value: %zd, curr: %zd", value.time, curmt); + continue; // old data + } + pthread_mutex_lock(&datamutex); + fix_new_data(&collected_data[idx], &value); + pthread_mutex_unlock(&datamutex); + if(!Forbidden && curcond) chkweatherlevel(&curlevel, curvalue, curcond, &curahtungtime); + } + DBG("check ahtung"); + pthread_mutex_lock(&datamutex); + if(Forbidden) collected_data[NCOMMWEATH].value.i = WEATHER_PROHIBITED; + else collected_data[NCOMMWEATH].value.i = curlevel; + if(collected_data[NLASTAHTUNG].value.i < curahtungtime) collected_data[NLASTAHTUNG].value.i = curahtungtime; + collected_data[NCOMMWEATH].time = curtime; + collected_data[NLASTAHTUNG].time = curtime; + pthread_mutex_unlock(&datamutex); + DBG("Refreshed"); +} + +void forbid_observations(int f){ + if(f) Forbidden = 1; + else Forbidden = 0; +} + +#if 0 +// main cycle +void run_mainweather(){ + int N = get_nplugins(); + if(N < 1) return; + poll_time = get_pollT(); + while(1){ + int nactive = 0; + pthread_mutex_lock(&datamutex); + for(int i = N-1; i > -1; --i){ // the most important is the last + sensordata_t *s = get_plugin(i); + if(!s || !sensor_alive(s)) continue; + ++nactive; + } + pthread_mutex_unlock(&datamutex); + if(nactive == 0) break; // no active sensors + usleep(10000); + } + LOGERR("Main weather collector died: all sensors lost"); +} +#endif diff --git a/Daemons/weatherdaemon_multimeteo/mainweather.h b/Daemons/weatherdaemon_multimeteo/mainweather.h new file mode 100644 index 0000000..0743e96 --- /dev/null +++ b/Daemons/weatherdaemon_multimeteo/mainweather.h @@ -0,0 +1,58 @@ +/* + * This file is part of the weatherdaemon project. + * Copyright 2026 Edward V. Emelianov . + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +#pragma once + +#include "weathlib.h" + +// weather conditions +enum{ + WEATHER_GOOD, // good to start observations + WEATHER_BAD, // bad for start, but can run + WEATHER_TERRIBLE, // need close the dome + WEATHER_PROHIBITED, // need close all, park and power off <-- by SIGUSR1/SIGUSR2 +}; + +typedef struct{ + double good; // if value less than this, weather is good + double bad; // if value greater than this, weather is bad + double terrible; // if value greater than this, weather is terrible + int negflag; // reversal flag (good if > val, etc) +} weather_cond_t; + +typedef struct{ + int ahtung_delay; // delay to change "bad weather" to good after last "bad event" + // wind, m/s + weather_cond_t wind; + // humidity, %% + weather_cond_t humidity; + // "clouds", > 2500 - OK -> should be negated when check!!! + weather_cond_t clouds; + // sky temperature minus ambient temperature, degC + weather_cond_t sky; +} weather_conf_t; + +// defined in cmdlnopts.c +extern weather_conf_t WeatherConf; + +int collected_amount(); +int get_collected(val_t *val, int N); + +void forbid_observations(int f); +void refresh_sensval(sensordata_t *s); +//void run_mainweather(); diff --git a/Daemons/weatherdaemon_multimeteo/plugins/dummy.c b/Daemons/weatherdaemon_multimeteo/plugins/dummy.c index 1b50ef9..62a7be2 100644 --- a/Daemons/weatherdaemon_multimeteo/plugins/dummy.c +++ b/Daemons/weatherdaemon_multimeteo/plugins/dummy.c @@ -37,19 +37,23 @@ static void *mainthread(void _U_ *U){ FNAME(); double t0 = sl_dtime(); while(1){ + DBG("locked"); + pthread_mutex_lock(&sensor.valmutex); float f = sensor.values[0].value.f + (drand48() - 0.5) / 2.; if(f >= 0.) sensor.values[0].value.f = f; f = sensor.values[1].value.f + (drand48() - 0.5) * 4.; if(f > 160. && f < 200.) sensor.values[1].value.f = f; - f = sensor.values[2].value.f + (drand48() - 0.5) / 20.; + f = sensor.values[2].value.f + (drand48() - 0.5) / 2.; if(f > 13. && f < 21.) sensor.values[2].value.f = f; f = sensor.values[3].value.f + (drand48() - 0.5) / 100.; if(f > 585. && f < 615.) sensor.values[3].value.f = f; - f = sensor.values[4].value.f + (drand48() - 0.5) / 10.; + f = sensor.values[4].value.f + (drand48() - 0.5)*10.; if(f > 60. && f <= 100.) sensor.values[4].value.f = f; sensor.values[5].value.u = (f > 98.) ? 1 : 0; time_t cur = time(NULL); for(int i = 0; i < NS; ++i) sensor.values[i].time = cur; + pthread_mutex_unlock(&sensor.valmutex); + DBG("unlocked"); if(sensor.freshdatahandler) sensor.freshdatahandler(&sensor); while(sl_dtime() - t0 < sensor.tpoll) usleep(500); t0 = sl_dtime(); @@ -73,23 +77,12 @@ static int init(struct sensordata_t* s, int N, time_t pollt, int _U_ fd){ return NS; } -/** - * @brief getval - value's getter - * @param o (o) - value - * @param N - it's index - * @return FALSE if failed - */ -static int getval(struct sensordata_t* s, val_t *o, int N){ - if(N < 0 || N >= NS) return FALSE; - if(o) *o = s->values[N]; - return TRUE; -} - sensordata_t sensor = { .name = "Dummy weatherstation", .Nvalues = NS, .init = init, .onrefresh = common_onrefresh, - .get_value = getval, + .valmutex = PTHREAD_MUTEX_INITIALIZER, + .get_value = common_getval, .kill = common_kill, }; diff --git a/Daemons/weatherdaemon_multimeteo/plugins/fdexample.c b/Daemons/weatherdaemon_multimeteo/plugins/fdexample.c index 726a4f5..e2acab1 100644 --- a/Daemons/weatherdaemon_multimeteo/plugins/fdexample.c +++ b/Daemons/weatherdaemon_multimeteo/plugins/fdexample.c @@ -43,8 +43,10 @@ static int format_values(char *buf){ DBG("TOKEN: %s", token); if(sl_str2d(&v, token)){ DBG("next value: %g", v); + pthread_mutex_lock(&sensor.valmutex); sensor.values[gotvals].value.f = (float) v; sensor.values[gotvals].time = tnow; + pthread_mutex_unlock(&sensor.valmutex); ++gotvals; } token = strtok(NULL, ","); @@ -135,17 +137,12 @@ static int init(struct sensordata_t *s, int N, time_t pollt, int fd){ return NS; } -static int getval(struct sensordata_t *s, val_t *o, int N){ - if(!s || N < 0 || N >= NS) return FALSE; - if(o) *o = s->values[N]; - return TRUE; -} - sensordata_t sensor = { .name = "Dummy socket or serial device weatherstation", .Nvalues = NS, .init = init, .onrefresh = common_onrefresh, - .get_value = getval, + .valmutex = PTHREAD_MUTEX_INITIALIZER, + .get_value = common_getval, .kill = common_kill, }; diff --git a/Daemons/weatherdaemon_multimeteo/sensors.c b/Daemons/weatherdaemon_multimeteo/sensors.c index c5d851c..eac83cb 100644 --- a/Daemons/weatherdaemon_multimeteo/sensors.c +++ b/Daemons/weatherdaemon_multimeteo/sensors.c @@ -21,7 +21,9 @@ #include #include "fd.h" +#include "mainweather.h" #include "sensors.h" +#include "weathlib.h" #define WARNXL(...) do{ LOGWARN(__VA_ARGS__); WARNX(__VA_ARGS__); } while(0) #define WARNL(...) do{ LOGWARN(__VA_ARGS__); WARN(__VA_ARGS__); } while(0) @@ -45,6 +47,8 @@ int set_pollT(time_t t){ return TRUE; } +time_t get_pollT(){ return poll_interval;} + /** * @brief get_plugin - get link to opened plugin * @param o (o) - plugin with given index @@ -71,11 +75,17 @@ void *open_plugin(const char *name){ return dlh; } -#ifdef EBUG -// in release this function can be used for meteo logging +/** + * @brief dumpsensors - this function called each time some `station` got new data + * + * @param station - pointer to N'th station opened + */ static void dumpsensors(struct sensordata_t* station){ FNAME(); if(!station || !station->get_value || station->Nvalues < 1) return; + refresh_sensval(station); + DBG("New values..."); +#ifdef EBUG char buf[FULL_LEN+1]; uint64_t Tsum = 0; int nsum = 0; int N = (nplugins > 1) ? station->PluginNo : -1; @@ -91,8 +101,8 @@ static void dumpsensors(struct sensordata_t* station){ if(0 < format_msrmttm(last, buf, FULL_LEN+1)){ printf("%s\n\n", buf); } -} #endif +} /** * @brief openplugins - open sensors' plugin and init it @@ -130,9 +140,7 @@ int openplugins(char **paths, int N){ int ns = S->init(S, nplugins, poll_interval, fd); // here nplugins is index in array if(ns < 1) WARNXL("Can't init plugin %s", paths[i]); else{ -#ifdef EBUG - if(!S->onrefresh(S, dumpsensors)) WARNXL("Can't init refresh funtion"); -#endif + if(!S->onrefresh || !S->onrefresh(S, dumpsensors)) WARNXL("Can't init refresh funtion"); LOGMSG("Plugin %s nave %d sensors", paths[i], ns); allplugins[nplugins++] = S; } @@ -172,7 +180,7 @@ int format_sensval(const val_t *v, char *buf, int buflen, int Np){ case VALT_FLOAT: snprintf(strval, VAL_LEN, "%g", v->value.f); break; default: sprintf(strval, "'ERROR'"); } - const char* const NM[] = { // names of standard fields + const char* const NM[IS_OTHER] = { // names of standard fields [IS_WIND] = "WIND", [IS_WINDDIR] = "WINDDIR", [IS_HUMIDITY] = "HUMIDITY", @@ -186,7 +194,7 @@ int format_sensval(const val_t *v, char *buf, int buflen, int Np){ [IS_CLOUDS] = "CLOUDS", [IS_SKYTEMP] = "SKYTEMP" }; - const char* const CMT[] = { // comments for standard fields + const char* const CMT[IS_OTHER] = { // comments for standard fields [IS_WIND] = "Wind, m/s", [IS_WINDDIR] = "Wind direction, degr (CW from north to FROM)", [IS_HUMIDITY] = "Humidity, percent", diff --git a/Daemons/weatherdaemon_multimeteo/sensors.h b/Daemons/weatherdaemon_multimeteo/sensors.h index 2b77da7..14ccb79 100644 --- a/Daemons/weatherdaemon_multimeteo/sensors.h +++ b/Daemons/weatherdaemon_multimeteo/sensors.h @@ -30,3 +30,4 @@ int get_nplugins(); int format_sensval(const val_t *v, char *buf, int buflen, int Np); int format_msrmttm(time_t t, char *buf, int buflen); int set_pollT(time_t t); +time_t get_pollT(); diff --git a/Daemons/weatherdaemon_multimeteo/server.c b/Daemons/weatherdaemon_multimeteo/server.c index 98e5d0d..92738e6 100644 --- a/Daemons/weatherdaemon_multimeteo/server.c +++ b/Daemons/weatherdaemon_multimeteo/server.c @@ -20,6 +20,7 @@ #include #include +#include "mainweather.h" #include "sensors.h" #include "server.h" @@ -55,12 +56,11 @@ static sl_sock_hresult_e listhandler(sl_sock_t *client, _U_ sl_sock_hitem_t *ite } /** - * @brief showdata - send to user meteodata + * @brief showdataN - send to user meteodata of Nth station * @param client - client data * @param N - index of station - * @param showidx - == TRUE to show index in square brackets */ -static void showdata(sl_sock_t *client, int N, int showidx){ +static void showdataN(sl_sock_t *client, int N){ char buf[FULL_LEN+1]; val_t v; sensordata_t *s = NULL; @@ -69,7 +69,6 @@ static void showdata(sl_sock_t *client, int N, int showidx){ sl_sock_sendstrmessage(client, buf); return; } - if(!showidx || get_nplugins() == 1) N = -1; // only one -> don't show indexes time_t oldest = time(NULL) - oldest_interval; uint64_t Tsum = 0; int nsum = 0; for(int i = 0; i < s->Nvalues; ++i){ @@ -81,24 +80,56 @@ static void showdata(sl_sock_t *client, int N, int showidx){ sl_sock_sendbyte(client, '\n'); ++nsum; Tsum += v.time; } - oldest = (time_t)(Tsum / nsum); - if(0 < format_msrmttm(oldest, buf, FULL_LEN+1)){ // send mean measuring time - DBG("Formatted time: '%s'", buf); - sl_sock_sendstrmessage(client, buf); - sl_sock_sendbyte(client, '\n'); + if(nsum > 0){ + oldest = (time_t)(Tsum / nsum); + if(0 < format_msrmttm(oldest, buf, FULL_LEN+1)){ // send mean measuring time + DBG("Formatted time: '%s'", buf); + sl_sock_sendstrmessage(client, buf); + sl_sock_sendbyte(client, '\n'); + } } } +/** + * @brief showdata - send to client common gathered data + * @param client - client data + */ +static void showdata(sl_sock_t *client){ + char buf[FULL_LEN+1]; + val_t v; + int Ncoll = collected_amount(); + time_t oldest = time(NULL) - oldest_interval; + uint64_t Tsum = 0; int nsum = 0; + for(int i = 0; i < Ncoll; ++i){ + if(!get_collected(&v, i)) continue; + if(v.time < oldest) continue; + if(1 > format_sensval(&v, buf, FULL_LEN+1, -1)) continue; + DBG("formatted: '%s'", buf); + sl_sock_sendstrmessage(client, buf); + sl_sock_sendbyte(client, '\n'); + ++nsum; Tsum += v.time; + } + if(nsum > 0){ + oldest = (time_t)(Tsum / nsum); + if(0 < format_msrmttm(oldest, buf, FULL_LEN+1)){ // send mean measuring time + DBG("Formatted time: '%s'", buf); + sl_sock_sendstrmessage(client, buf); + sl_sock_sendbyte(client, '\n'); + } + } +} + + // get meteo data static sl_sock_hresult_e gethandler(sl_sock_t *client, _U_ sl_sock_hitem_t *item, const char *req){ if(!client) return RESULT_FAIL; int N = get_nplugins(); if(N < 1) return RESULT_FAIL; - if(!req) for(int i = 0; i < N; ++i) showdata(client, i, TRUE); + if(!req) showdata(client); else{ int n; if(!sl_str2i(&n, req) || n < 0 || n >= N) return RESULT_BADVAL; - showdata(client, n, FALSE); + showdataN(client, n); } return RESULT_SILENCE; } @@ -140,17 +171,18 @@ static sl_sock_hresult_e defhandler(struct sl_sock *s, const char *str){ return RESULT_SILENCE; } +#define COMMONHANDLERS \ + {gethandler, "get", "get all meteo or only for given plugin number", NULL}, \ + {listhandler, "list", "show all opened plugins", NULL}, \ + {timehandler, "time", "get server's UNIX time", NULL}, + // handlers for network and local (UNIX) sockets static sl_sock_hitem_t nethandlers[] = { // net - only getters and client-only setters - {gethandler, "get", "get all meteo or only for given plugin number", NULL}, - {listhandler, "list", "show all opened plugins", NULL}, - {timehandler, "time", "get server's UNIX time", NULL}, + COMMONHANDLERS {NULL, NULL, NULL, NULL} }; static sl_sock_hitem_t localhandlers[] = { // local - full amount of setters/getters - {gethandler, "get", "get all meteo or only for given plugin number", NULL}, - {listhandler, "list", "show all opened plugins", NULL}, - {timehandler, "time", "get server's UNIX time", NULL}, + COMMONHANDLERS {NULL, NULL, NULL, NULL} }; @@ -223,7 +255,7 @@ void kill_servers(){ sl_sock_delete(&netsocket); LOGMSG("Server sockets destroyed"); //usleep(500); - //pthread_kill(locthread, 9); - //pthread_kill(netthread, 9); - //LOGMSG("Server threads killed"); + //pthread_join(locthread, NULL); + //pthread_join(netthread, NULL); + //LOGMSG("Server threads are dead"); } diff --git a/Daemons/weatherdaemon_multimeteo/weatherdaemon.files b/Daemons/weatherdaemon_multimeteo/weatherdaemon.files index 35c7497..cbb372e 100644 --- a/Daemons/weatherdaemon_multimeteo/weatherdaemon.files +++ b/Daemons/weatherdaemon_multimeteo/weatherdaemon.files @@ -4,6 +4,8 @@ cmdlnopts.h fd.c fd.h main.c +mainweather.c +mainweather.h plugins/dummy.c plugins/fdexample.c sensors.c diff --git a/Daemons/weatherdaemon_multimeteo/weathlib.c b/Daemons/weatherdaemon_multimeteo/weathlib.c index 6393c8c..4c35d7a 100644 --- a/Daemons/weatherdaemon_multimeteo/weathlib.c +++ b/Daemons/weatherdaemon_multimeteo/weathlib.c @@ -50,8 +50,8 @@ int common_onrefresh(sensordata_t *s, void (*handler)(sensordata_t *)){ void common_kill(sensordata_t *s){ FNAME(); if(!s) return; - if(0 == pthread_kill(s->thread, -9)){ - DBG("%s main thread killed, join", s->name); + if(0 == pthread_cancel(s->thread)){ + DBG("%s main thread canceled, join", s->name); pthread_join(s->thread, NULL); DBG("Done"); } @@ -64,3 +64,20 @@ void common_kill(sensordata_t *s){ FREE(s->values); DBG("Sensor %s killed", s->name); } + +/** + * @brief common_getval - common value getter + * @param s (i) - station + * @param o (o) - value or NULL (if you just wants test N) + * @param N - number of sensor + * @return FALSE if failed + */ +int common_getval(struct sensordata_t *s, val_t *o, int N){ + if(!s || N < 0 || N >= s->Nvalues) return FALSE; + if(o){ + pthread_mutex_lock(&s->valmutex); + *o = s->values[N]; + pthread_mutex_unlock(&s->valmutex); + } + return TRUE; +} diff --git a/Daemons/weatherdaemon_multimeteo/weathlib.h b/Daemons/weatherdaemon_multimeteo/weathlib.h index 1ed3f1c..47f9e9b 100644 --- a/Daemons/weatherdaemon_multimeteo/weathlib.h +++ b/Daemons/weatherdaemon_multimeteo/weathlib.h @@ -96,6 +96,7 @@ typedef struct sensordata_t{ // private members: val_t *values; // array of values pthread_t thread; // main thread + pthread_mutex_t valmutex;// value getter/setter mutex void (*freshdatahandler)(struct sensordata_t*); // handler of fresh data int fdes; // file descriptor of device/socket sl_ringbuffer_t *ringbuffer; // ringbuffer for device reading @@ -106,3 +107,4 @@ typedef struct sensordata_t{ int common_onrefresh(sensordata_t*, void (*handler)(sensordata_t*)); void common_kill(sensordata_t *s); int sensor_alive(sensordata_t *s); +int common_getval(struct sensordata_t *s, val_t *o, int N);