seems like it works

This commit is contained in:
2026-04-09 18:35:06 +03:00
parent e551b94499
commit 5be6876f9e
14 changed files with 527 additions and 265 deletions

View File

@@ -18,10 +18,10 @@ option(FDEXAMPLE "Example of file descriptor plugin" ON)
option(HYDREON "Hydreon rain sensor plugin" ON) option(HYDREON "Hydreon rain sensor plugin" ON)
option(BTAMETEO "BTA main meteostation plugin" ON) option(BTAMETEO "BTA main meteostation plugin" ON)
option(REINHARDT "Old Reinhardt meteostation plugin" ON) option(REINHARDT "Old Reinhardt meteostation plugin" ON)
option(WXA100 "WXA100-06 meteostation plugin" ON)
# default flags # default flags
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wextra -pedantic-errors -fPIC") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wextra -fPIC")
message("Install dir prefix: ${CMAKE_INSTALL_PREFIX}") message("Install dir prefix: ${CMAKE_INSTALL_PREFIX}")

View File

@@ -107,13 +107,15 @@ sl_option_t confopts[] = {
COMMON_OPTS COMMON_OPTS
end_option end_option
}; };
#if 0
static int sortstrings(const void *v1, const void *v2){ static int sortstrings(const void *v1, const void *v2){
const char **s1 = (const char **)v1, **s2 = (const char **)v2; const char **s1 = (const char **)v1, **s2 = (const char **)v2;
return strcmp(*s1, *s2); return strcmp(*s1, *s2);
} }
#endif
// compare plugins from configuration and command line; add to command line plugins all new // compare plugins from configuration and command line; add to command line plugins all new
// to use similar stations several times you should point for them different settings
static void compplugins(glob_pars *cmdline, glob_pars *conf){ static void compplugins(glob_pars *cmdline, glob_pars *conf){
if(!cmdline) return; if(!cmdline) return;
char **p; char **p;
@@ -136,10 +138,28 @@ static void compplugins(glob_pars *cmdline, glob_pars *conf){
for(int i = 0; i < nconf; ++i){ newarray[i+ncmd] = conf->plugins[i]; } for(int i = 0; i < nconf; ++i){ newarray[i+ncmd] = conf->plugins[i]; }
FREE(conf->plugins); FREE(conf->plugins);
} }
qsort(newarray, newsize, sizeof(char*), sortstrings); // don't sort: we need leave priority as user pointed
//qsort(newarray, newsize, sizeof(char*), sortstrings);
DBG("NOW together:"); p = newarray; while(*p) printf("\t%s\n", *p++); DBG("NOW together:"); p = newarray; while(*p) printf("\t%s\n", *p++);
p = newarray; for(int i = 0; i < newsize-1; ++i){
int nondobuleidx = 0; if(NULL == newarray[i]) continue;
for(int j = i+1; j < newsize; ++j){
if(NULL == newarray[j]) continue;
if(0 == strcmp(newarray[i], newarray[j])) FREE(newarray[j]);
}
}
// now collect them in order
int nondoubleidx = 0;
for(int i = 0; i < newsize; ++i){
if(newarray[i]){
if(i != nondoubleidx){
newarray[nondoubleidx] = newarray[i];
newarray[i] = NULL;
}
++nondoubleidx;
}
}
#if 0
for(int i = 0; i < newsize;){ for(int i = 0; i < newsize;){
int j = i + 1; int j = i + 1;
for(; j < newsize; ++j){ for(; j < newsize; ++j){
@@ -153,9 +173,10 @@ static void compplugins(glob_pars *cmdline, glob_pars *conf){
++nondobuleidx; ++nondobuleidx;
i = j; i = j;
} }
#endif
DBG("Result:"); p = newarray; while(*p) printf("\t%s\n", *p++); DBG("Result:"); p = newarray; while(*p) printf("\t%s\n", *p++);
cmdline->plugins = newarray; cmdline->plugins = newarray;
cmdline->nplugins = nondobuleidx; cmdline->nplugins = nondoubleidx;
} }
/** /**

View File

@@ -66,7 +66,7 @@ static val_t collected_data[NAMOUNT_OF_DATA] = {
[NAMB_TEMP] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_AMB_TEMP}, [NAMB_TEMP] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_AMB_TEMP},
[NPRESSURE] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_PRESSURE}, [NPRESSURE] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_PRESSURE},
[NPRECIP] = {.sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_PRECIP}, [NPRECIP] = {.sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_PRECIP},
[NPRECIP_LEVEL] = {.sense = VAL_OBLIGATORY, .type = VALT_INT, .meaning = IS_PRECIP_LEVEL}, [NPRECIP_LEVEL] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_PRECIP_LEVEL},
[NMIST] = {.sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_MIST}, [NMIST] = {.sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_MIST},
[NCLOUDS] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_CLOUDS}, [NCLOUDS] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_CLOUDS},
[NSKYTEMP] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_SKYTEMP}, [NSKYTEMP] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_SKYTEMP},
@@ -182,10 +182,10 @@ int get_collected(val_t *val, int N){
} }
static void fix_new_data(val_t *collected, val_t *fresh){ static void fix_new_data(val_t *collected, val_t *fresh){
FNAME();
if(!collected || !fresh) return; if(!collected || !fresh) return;
collected->time = fresh->time; collected->time = fresh->time;
if(collected->type == fresh->type){ // good case if(collected->type == fresh->type){ // good case
DBG("Types are the same");
collected->value = fresh->value; collected->value = fresh->value;
return; return;
} }
@@ -194,30 +194,36 @@ static void fix_new_data(val_t *collected, val_t *fresh){
case VALT_UINT: case VALT_UINT:
switch(fresh->type){ switch(fresh->type){
case VALT_INT: case VALT_INT:
collected->value.u = (uint32_t) collected->value.i; collected->value.u = (uint32_t) fresh->value.i;
DBG("i->u");
break; break;
case VALT_FLOAT: case VALT_FLOAT:
collected->value.u = (uint32_t) collected->value.f; collected->value.u = (uint32_t) fresh->value.f;
DBG("f->u");
default: break; default: break;
} }
break; break;
case VALT_INT: case VALT_INT:
switch(fresh->type){ switch(fresh->type){
case VALT_UINT: case VALT_UINT:
collected->value.i = (int32_t) collected->value.u; collected->value.i = (int32_t) fresh->value.u;
DBG("u->i");
break; break;
case VALT_FLOAT: case VALT_FLOAT:
collected->value.i = (int32_t) collected->value.f; collected->value.i = (int32_t) fresh->value.f;
DBG("f->i");
default: break; default: break;
} }
break; break;
case VALT_FLOAT: case VALT_FLOAT:
switch(fresh->type){ switch(fresh->type){
case VALT_UINT: case VALT_UINT:
collected->value.f = (float) collected->value.u; collected->value.f = (float) fresh->value.u;
DBG("u->f");
break; break;
case VALT_INT: case VALT_INT:
collected->value.f = (float) collected->value.i; collected->value.f = (float) fresh->value.i;
DBG("i->f");
default: break; default: break;
} }
break; break;
@@ -262,7 +268,7 @@ void refresh_sensval(sensordata_t *s){
double dir = -100., dir2 = -100.; // mean wind directions double dir = -100., dir2 = -100.; // mean wind directions
DBG("%d meteo values", s->Nvalues); DBG("%d meteo values", s->Nvalues);
for(int i = 0; i < s->Nvalues; ++i){ for(int i = 0; i < s->Nvalues; ++i){
DBG("Try to get %dth value", i); DBG("\nTry to get %dth value", i);
if(!s->get_value(s, &value, i)) continue; if(!s->get_value(s, &value, i)) continue;
DBG("got value"); DBG("got value");
int idx = -1; int idx = -1;
@@ -321,7 +327,7 @@ void refresh_sensval(sensordata_t *s){
} }
if(idx < 0 || idx >= NAMOUNT_OF_DATA) continue; if(idx < 0 || idx >= NAMOUNT_OF_DATA) continue;
DBG("IDX=%d", idx); 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 freshdelay = (s->PluginNo == 0) ? 0 : 90; // use data of less imrortant plugins only if our data is too old
time_t curmt = collected_data[idx].time + freshdelay; time_t curmt = collected_data[idx].time + freshdelay;
if(value.time < curmt){ if(value.time < curmt){
DBG("Data too old (value: %zd, curr: %zd", value.time, curmt); DBG("Data too old (value: %zd, curr: %zd", value.time, curmt);

View File

@@ -37,4 +37,9 @@ if(REINHARDT)
list(APPEND LIBS reinhardt) list(APPEND LIBS reinhardt)
endif() endif()
if(WXA100)
add_library(wxa100 SHARED wxa100.c)
list(APPEND LIBS wxa100)
endif()
install(TARGETS ${LIBS} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(TARGETS ${LIBS} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})

View File

@@ -19,6 +19,8 @@
#include "bta_shdata.h" #include "bta_shdata.h"
#include "weathlib.h" #include "weathlib.h"
#define SENSOR_NAME "BTA 6-m telescope main meteostation"
enum{ enum{
NWIND, NWIND,
NHUMIDITY, NHUMIDITY,
@@ -28,67 +30,66 @@ enum{
NAMOUNT NAMOUNT
}; };
extern sensordata_t sensor;
static const val_t values[NAMOUNT] = { static const val_t values[NAMOUNT] = {
[NWIND] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WIND}, [NWIND] = {.sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_WIND},
[NHUMIDITY] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_HUMIDITY}, [NHUMIDITY] = {.sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_HUMIDITY},
[NAMB_TEMP] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_AMB_TEMP}, [NAMB_TEMP] = {.sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_AMB_TEMP},
[NPRESSURE] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_PRESSURE}, [NPRESSURE] = {.sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_PRESSURE},
[NPRECIP] = {.sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_PRECIP}, [NPRECIP] = {.sense = VAL_RECOMMENDED, .type = VALT_UINT, .meaning = IS_PRECIP},
}; };
static void *mainthread(void _U_ *U){ static void *mainthread(void *s){
FNAME(); FNAME();
sensordata_t *sensor = (sensordata_t *)s;
while(1){ while(1){
if(check_shm_block(&sdat)){ if(check_shm_block(&sdat)){
DBG("Got next"); DBG("Got next");
time_t tnow = time(NULL); time_t tnow = time(NULL);
pthread_mutex_lock(&sensor.valmutex); pthread_mutex_lock(&sensor->valmutex);
for(int i = 0; i < NAMOUNT; ++i) for(int i = 0; i < NAMOUNT; ++i)
sensor.values[i].time = tnow; sensor->values[i].time = tnow;
sensor.values[NWIND].value.f = val_Wnd; sensor->values[NWIND].value.f = val_Wnd;
sensor.values[NPRESSURE].value.f = val_B; sensor->values[NPRESSURE].value.f = val_B;
sensor.values[NAMB_TEMP].value.f = val_T1; sensor->values[NAMB_TEMP].value.f = val_T1;
sensor.values[NHUMIDITY].value.f = val_Hmd; sensor->values[NHUMIDITY].value.f = val_Hmd;
DBG("Tprecip=%.1f, tnow=%.1f", Precip_time, sl_dtime()); DBG("Tprecip=%.1f, tnow=%.1f", Precip_time, sl_dtime());
sensor.values[NPRECIP].value.u = (tnow - (time_t)Precip_time < 60) ? 1 : 0; sensor->values[NPRECIP].value.u = (tnow - (time_t)Precip_time < 60) ? 1 : 0;
pthread_mutex_unlock(&sensor.valmutex); pthread_mutex_unlock(&sensor->valmutex);
if(sensor.freshdatahandler) sensor.freshdatahandler(&sensor); if(sensor->freshdatahandler) sensor->freshdatahandler(sensor);
}else break; // no connection? }else break; // no connection?
sleep(1); sleep(1);
} }
DBG("Lost connection -> suicide"); DBG("Lost connection -> suicide");
common_kill(&sensor); sensor->kill(sensor);
return NULL; return NULL;
} }
static int init(struct sensordata_t *s, int N, time_t pollt, int _U_ fd){ sensordata_t *sensor_new(int N, time_t pollt, int _U_ fd){
FNAME(); FNAME();
if(!s) return -1; sensordata_t *s = common_new();
sensor.PluginNo = N; if(!s) return NULL;
s->PluginNo = N;
if(pollt) s->tpoll = pollt; if(pollt) s->tpoll = pollt;
if(!get_shm_block(&sdat, ClientSide)){ if(!get_shm_block(&sdat, ClientSide)){
WARNX("Can't get BTA shared memory block"); WARNX("Can't get BTA shared memory block or create main thread");
return -1; s->kill(s);
return NULL;
} }
if(pthread_create(&s->thread, NULL, mainthread, NULL)) return -1;
s->values = MALLOC(val_t, NAMOUNT); s->values = MALLOC(val_t, NAMOUNT);
for(int i = 0; i < NAMOUNT; ++i) s->values[i] = values[i]; for(int i = 0; i < NAMOUNT; ++i) s->values[i] = values[i];
if(!(s->ringbuffer = sl_RB_new(BUFSIZ))){ s->Nvalues = NAMOUNT;
strncpy(s->name, SENSOR_NAME, NAME_LEN);
/*if(!(s->ringbuffer = sl_RB_new(BUFSIZ))){
WARNX("Can't init ringbuffer!"); WARNX("Can't init ringbuffer!");
common_kill(s); common_kill(s);
return -1; return -1;
}*/
if(pthread_create(&s->thread, NULL, mainthread, (void*)s)){
WARN("Can't create main thread");
s->kill(s);
return NULL;
} }
return NAMOUNT; s->fdes = 0;
return s;
} }
sensordata_t sensor = {
.name = "BTA 6-m telescope main meteostation",
.Nvalues = NAMOUNT,
.init = init,
.onrefresh = common_onrefresh,
.valmutex = PTHREAD_MUTEX_INITIALIZER,
.get_value = common_getval,
.kill = common_kill,
};

View File

@@ -20,9 +20,9 @@
#include "weathlib.h" #include "weathlib.h"
#define NS (6) #define SENSOR_NAME "Dummy weatherstation"
extern sensordata_t sensor; #define NS (6)
static const val_t values[NS] = { // fields `name` and `comment` have no sense until value meaning is `IS_OTHER` static const val_t values[NS] = { // fields `name` and `comment` have no sense until value meaning is `IS_OTHER`
{.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WIND}, {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WIND},
@@ -33,56 +33,55 @@ static const val_t values[NS] = { // fields `name` and `comment` have no sense u
{.sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_PRECIP}, {.sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_PRECIP},
}; };
static void *mainthread(void _U_ *U){ static void *mainthread(void *s){
FNAME(); FNAME();
double t0 = sl_dtime(); double t0 = sl_dtime();
sensordata_t *sensor = (sensordata_t *)s;
while(1){ while(1){
DBG("locked"); DBG("locked");
pthread_mutex_lock(&sensor.valmutex); pthread_mutex_lock(&sensor->valmutex);
float f = sensor.values[0].value.f + (drand48() - 0.5) / 2.; float f = sensor->values[0].value.f + (drand48() - 0.5) / 2.;
if(f >= 0.) sensor.values[0].value.f = f; if(f >= 0.) sensor->values[0].value.f = f;
f = sensor.values[1].value.f + (drand48() - 0.5) * 4.; f = sensor->values[1].value.f + (drand48() - 0.5) * 4.;
if(f > 160. && f < 200.) sensor.values[1].value.f = f; if(f > 160. && f < 200.) sensor->values[1].value.f = f;
f = sensor.values[2].value.f + (drand48() - 0.5) / 2.; f = sensor->values[2].value.f + (drand48() - 0.5) / 2.;
if(f > 13. && f < 21.) sensor.values[2].value.f = f; if(f > 13. && f < 21.) sensor->values[2].value.f = f;
f = sensor.values[3].value.f + (drand48() - 0.5) / 100.; f = sensor->values[3].value.f + (drand48() - 0.5) / 100.;
if(f > 585. && f < 615.) sensor.values[3].value.f = f; 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; if(f > 60. && f <= 100.) sensor->values[4].value.f = f;
sensor.values[5].value.u = (f > 98.) ? 1 : 0; sensor->values[5].value.u = (f > 98.) ? 1 : 0;
time_t cur = time(NULL); time_t cur = time(NULL);
for(int i = 0; i < NS; ++i) sensor.values[i].time = cur; for(int i = 0; i < NS; ++i) sensor->values[i].time = cur;
pthread_mutex_unlock(&sensor.valmutex); pthread_mutex_unlock(&sensor->valmutex);
DBG("unlocked"); DBG("unlocked");
if(sensor.freshdatahandler) sensor.freshdatahandler(&sensor); if(sensor->freshdatahandler) sensor->freshdatahandler(sensor);
while(sl_dtime() - t0 < sensor.tpoll) usleep(500); while(sl_dtime() - t0 < sensor->tpoll) usleep(500);
t0 = sl_dtime(); t0 = sl_dtime();
} }
return NULL; return NULL;
} }
static int init(struct sensordata_t* s, int N, time_t pollt, int _U_ fd){ sensordata_t *sensor_new(int N, time_t pollt, int _U_ fd){
FNAME(); FNAME();
if(pthread_create(&s->thread, NULL, mainthread, NULL)) return 0; sensordata_t *s = common_new();
if(!s) return NULL;
s->Nvalues = NS;
strncpy(s->name, SENSOR_NAME, NAME_LEN);
if(pollt) s->tpoll = pollt; if(pollt) s->tpoll = pollt;
s->values = MALLOC(val_t, NS); s->values = MALLOC(val_t, NS);
for(int i = 0; i < NS; ++i) s->values[i] = values[i]; for(int i = 0; i < NS; ++i) s->values[i] = values[i];
sensor.values[0].value.f = 1.; s->values[0].value.f = 1.;
sensor.values[1].value.f = 180.; s->values[1].value.f = 180.;
sensor.values[2].value.f = 17.; s->values[2].value.f = 17.;
sensor.values[3].value.f = 600.; s->values[3].value.f = 600.;
sensor.values[4].value.f = 80.; s->values[4].value.f = 80.;
sensor.values[5].value.u = 0; s->values[5].value.u = 0;
sensor.PluginNo = N; s->PluginNo = N;
return NS; if(pthread_create(&s->thread, NULL, mainthread, (void*)s)){
s->kill(s);
return NULL;
}
s->fdes = 0;
return s;
} }
sensordata_t sensor = {
.name = "Dummy weatherstation",
.Nvalues = NS,
.init = init,
.onrefresh = common_onrefresh,
.valmutex = PTHREAD_MUTEX_INITIALIZER,
.get_value = common_getval,
.kill = common_kill,
};

View File

@@ -23,10 +23,9 @@
// dummy example of file descriptors usage // dummy example of file descriptors usage
#define SENSOR_NAME "Dummy socket or serial device weatherstation"
#define NS (4) #define NS (4)
extern sensordata_t sensor;
static const val_t values[NS] = { // fields `name` and `comment` have no sense until value meaning is `IS_OTHER` static const val_t values[NS] = { // fields `name` and `comment` have no sense until value meaning is `IS_OTHER`
{.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WIND}, {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WIND},
{.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_AMB_TEMP}, {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_AMB_TEMP},
@@ -34,23 +33,23 @@ static const val_t values[NS] = { // fields `name` and `comment` have no sense u
{.sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_HUMIDITY}, {.sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_HUMIDITY},
}; };
static int format_values(char *buf){ static int format_values(sensordata_t *sensor, char *buf){
int gotvals = 0; int gotvals = 0;
char *token = strtok(buf, ","); char *token = strtok(buf, ",");
time_t tnow = time(NULL); time_t tnow = time(NULL);
pthread_mutex_lock(&sensor->valmutex);
while(token && gotvals < NS){ while(token && gotvals < NS){
double v; double v;
DBG("TOKEN: %s", token); DBG("TOKEN: %s", token);
if(sl_str2d(&v, token)){ if(sl_str2d(&v, token)){
DBG("next value: %g", v); DBG("next value: %g", v);
pthread_mutex_lock(&sensor.valmutex); sensor->values[gotvals].value.f = (float) v;
sensor.values[gotvals].value.f = (float) v; sensor->values[gotvals].time = tnow;
sensor.values[gotvals].time = tnow;
pthread_mutex_unlock(&sensor.valmutex);
++gotvals; ++gotvals;
} }
token = strtok(NULL, ","); token = strtok(NULL, ",");
} }
pthread_mutex_unlock(&sensor->valmutex);
DBG("GOT: %d", gotvals); DBG("GOT: %d", gotvals);
return gotvals; return gotvals;
} }
@@ -79,72 +78,68 @@ static ssize_t writedata(int fd, const char *str, size_t size){
return sent; return sent;
} }
static void *mainthread(void _U_ *U){ static void *mainthread(void *s){
FNAME(); FNAME();
time_t task = 0; time_t task = 0;
const char begging[] = "Enter comma-separated data: wind, exttemp, pressure, humidity\n"; const char begging[] = "Enter comma-separated data: wind, exttemp, pressure, humidity\n";
char buf[128]; char buf[128];
while(sensor.fdes > -1){ sensordata_t *sensor = (sensordata_t *)s;
while(sensor->fdes > -1){
time_t tnow = time(NULL); time_t tnow = time(NULL);
int canread = sl_canread(sensor.fdes); int canread = sl_canread(sensor->fdes);
if(canread < 0){ if(canread < 0){
WARNX("Disconnected fd %d", sensor.fdes); WARNX("Disconnected fd %d", sensor->fdes);
break; break;
}else if(canread == 1){ }else if(canread == 1){
ssize_t got = read(sensor.fdes, buf, 128); ssize_t got = read(sensor->fdes, buf, 128);
if(got > 0){ if(got > 0){
sl_RB_write(sensor.ringbuffer, (uint8_t*)buf, got); sl_RB_write(sensor->ringbuffer, (uint8_t*)buf, got);
}else if(got < 0){ }else if(got < 0){
DBG("Disconnected?"); DBG("Disconnected?");
break; break;
} }
} }
if(sl_RB_readline(sensor.ringbuffer, buf, 127) > 0){ if(sl_RB_readline(sensor->ringbuffer, buf, 127) > 0){
if(NS == format_values(buf) && sensor.freshdatahandler) if(NS == format_values(sensor, buf) && sensor->freshdatahandler)
sensor.freshdatahandler(&sensor); sensor->freshdatahandler(sensor);
} }
if(sensor.tpoll){ if(sensor->tpoll){
if(tnow >= task){ if(tnow >= task){
DBG("write %s", begging); DBG("write %s", begging);
ssize_t got = writedata(sensor.fdes, begging, sizeof(begging)-1); ssize_t got = writedata(sensor->fdes, begging, sizeof(begging)-1);
if(got > 0) task = tnow + sensor.tpoll; if(got > 0) task = tnow + sensor->tpoll;
else if(got < 0){ else if(got < 0){
close(sensor.fdes); close(sensor->fdes);
sensor.fdes = -1; sensor->fdes = -1;
} }
} }
} }
} }
DBG("OOOOps!"); DBG("OOOOps!");
common_kill(&sensor); sensor->kill(sensor);
return NULL; return NULL;
} }
static int init(struct sensordata_t *s, int N, time_t pollt, int fd){ sensordata_t *sensor_new(int N, time_t pollt, int fd){
FNAME(); FNAME();
if(!s) return -1; if(fd < 0) return NULL;
sensordata_t *s = common_new();
if(!s) return NULL;
s->fdes = fd; s->fdes = fd;
if(s->fdes < 0) return -1; s->PluginNo = N;
sensor.PluginNo = N;
if(pollt) s->tpoll = pollt; if(pollt) s->tpoll = pollt;
if(pthread_create(&s->thread, NULL, mainthread, NULL)) return -1; strncpy(s->name, SENSOR_NAME, NAME_LEN);
s->values = MALLOC(val_t, NS); s->values = MALLOC(val_t, NS);
// don't use memcpy, as `values` could be aligned // don't use memcpy, as `values` could be aligned
for(int i = 0; i < NS; ++i) s->values[i] = values[i]; for(int i = 0; i < NS; ++i) s->values[i] = values[i];
if(!(s->ringbuffer = sl_RB_new(BUFSIZ))){ if(!(s->ringbuffer = sl_RB_new(BUFSIZ))){
WARNX("Can't init ringbuffer!"); WARNX("Can't init ringbuffer!");
common_kill(s); s->kill(s);
return -1; return NULL;
} }
return NS; if(pthread_create(&s->thread, NULL, mainthread, (void*)s)){
s->kill(s);
return NULL;
}
return s;
} }
sensordata_t sensor = {
.name = "Dummy socket or serial device weatherstation",
.Nvalues = NS,
.init = init,
.onrefresh = common_onrefresh,
.valmutex = PTHREAD_MUTEX_INITIALIZER,
.get_value = common_getval,
.kill = common_kill,
};

View File

@@ -23,6 +23,8 @@
// HYDREON rain sensor // HYDREON rain sensor
#define SENSOR_NAME "Hydreon RG-11 rain sensor"
// amount of datafields // amount of datafields
#define RREGNUM 6 #define RREGNUM 6
#define RGBITNUM 8 #define RGBITNUM 8
@@ -83,8 +85,6 @@ typedef struct{
uint8_t RainThr; // (??? == 12) uint8_t RainThr; // (??? == 12)
} slowregs; } slowregs;
extern sensordata_t sensor;
enum{ enum{
NPRECIP = 0, NPRECIP = 0,
NPRECIP_LEVEL, NPRECIP_LEVEL,
@@ -146,78 +146,71 @@ static int encodepacket(const char *buf, int len, rg11 *Rregs, slowregs *Sregs){
return TRUE; return TRUE;
} }
static void *mainthread(void _U_ *U){ static void *mainthread(void *s){
FNAME(); FNAME();
char buf[128]; char buf[128];
rg11 Rregs; rg11 Rregs;
slowregs Sregs; slowregs Sregs;
while(sensor.fdes > -1){ sensordata_t *sensor = (sensordata_t *)s;
while(sensor->fdes > -1){
time_t tnow = time(NULL); time_t tnow = time(NULL);
int canread = sl_canread(sensor.fdes); int canread = sl_canread(sensor->fdes);
if(canread < 0){ if(canread < 0){
WARNX("Disconnected fd %d", sensor.fdes); WARNX("Disconnected fd %d", sensor->fdes);
break; break;
}else if(canread == 1){ }else if(canread == 1){
ssize_t got = read(sensor.fdes, buf, 128); ssize_t got = read(sensor->fdes, buf, 128);
if(got > 0){ if(got > 0){
//DBG("write into buffer: %s[%zd]", buf, got); //DBG("write into buffer: %s[%zd]", buf, got);
sl_RB_write(sensor.ringbuffer, (uint8_t*)buf, got); sl_RB_write(sensor->ringbuffer, (uint8_t*)buf, got);
}else if(got < 0){ }else if(got < 0){
DBG("Disconnected?"); DBG("Disconnected?");
break; break;
} }
} }
int got = sl_RB_readto(sensor.ringbuffer, 's', (uint8_t*)buf, 127); int got = sl_RB_readto(sensor->ringbuffer, 's', (uint8_t*)buf, 127);
if(got > 0){ if(got > 0){
buf[--got] = 0; buf[--got] = 0;
if(encodepacket(buf, got, &Rregs, &Sregs)){ if(encodepacket(buf, got, &Rregs, &Sregs)){
DBG("refresh..."); DBG("refresh...");
pthread_mutex_lock(&sensor.valmutex); pthread_mutex_lock(&sensor->valmutex);
for(int i = 0; i < NAMOUNT; ++i) for(int i = 0; i < NAMOUNT; ++i)
sensor.values[i].time = tnow; sensor->values[i].time = tnow;
sensor.values[NPRECIP].value.u = (Rregs.RGBits & (Raining | Storm)) ? 1 : 0; sensor->values[NPRECIP].value.u = (Rregs.RGBits & (Raining | Storm)) ? 1 : 0;
float f = Sregs.Barrel * 256.f + Sregs.Bucket - 14.f; float f = Sregs.Barrel * 256.f + Sregs.Bucket - 14.f;
sensor.values[NPRECIP_LEVEL].value.f = (f > 0.f) ? f : 0.f; sensor->values[NPRECIP_LEVEL].value.f = (f > 0.f) ? f : 0.f;
sensor.values[NSINCERN].value.u = Sregs.SinceRn; sensor->values[NSINCERN].value.u = Sregs.SinceRn;
sensor.values[NPOW].value.u = Rregs.PeakRS; sensor->values[NPOW].value.u = Rregs.PeakRS;
sensor.values[NAVG].value.u = Rregs.LRA; sensor->values[NAVG].value.u = Rregs.LRA;
sensor.values[NAMBL].value.u = Sregs.AmbLight; sensor->values[NAMBL].value.u = Sregs.AmbLight;
sensor.values[NFREEZ].value.u = (Rregs.RGBits & Freeze) ? 1 : 0; sensor->values[NFREEZ].value.u = (Rregs.RGBits & Freeze) ? 1 : 0;
pthread_mutex_unlock(&sensor.valmutex); pthread_mutex_unlock(&sensor->valmutex);
if(sensor.freshdatahandler) sensor.freshdatahandler(&sensor); if(sensor->freshdatahandler) sensor->freshdatahandler(sensor);
} }
} }
} }
DBG("OOOOps!"); DBG("OOOOps!");
common_kill(&sensor); sensor->kill(sensor);
return NULL; return NULL;
} }
static int init(struct sensordata_t *s, int N, time_t pollt, int fd){ sensordata_t *sensor_new(int N, time_t pollt, int fd){
FNAME(); FNAME();
if(!s) return -1; if(fd < 0) return NULL;
sensordata_t *s = common_new();
if(!s) return NULL;
strncpy(s->name, SENSOR_NAME, NAME_LEN);
s->fdes = fd; s->fdes = fd;
if(s->fdes < 0) return -1; s->PluginNo = N;
sensor.PluginNo = N; s->Nvalues = NAMOUNT;
if(pollt) s->tpoll = pollt; if(pollt) s->tpoll = pollt;
if(pthread_create(&s->thread, NULL, mainthread, NULL)) return -1;
s->values = MALLOC(val_t, NAMOUNT); s->values = MALLOC(val_t, NAMOUNT);
// don't use memcpy, as `values` could be aligned // don't use memcpy, as `values` could be aligned
for(int i = 0; i < NAMOUNT; ++i) s->values[i] = values[i]; for(int i = 0; i < NAMOUNT; ++i) s->values[i] = values[i];
if(!(s->ringbuffer = sl_RB_new(BUFSIZ))){ if(!(s->ringbuffer = sl_RB_new(BUFSIZ)) ||
WARNX("Can't init ringbuffer!"); pthread_create(&s->thread, NULL, mainthread, (void*)s)){
common_kill(s); s->kill(s);
return -1; return NULL;
} }
return NAMOUNT; return s;
} }
sensordata_t sensor = {
.name = "Hydreon RG-11 rain sensor",
.Nvalues = NAMOUNT,
.init = init,
.onrefresh = common_onrefresh,
.valmutex = PTHREAD_MUTEX_INITIALIZER,
.get_value = common_getval,
.kill = common_kill,
};

View File

@@ -20,6 +20,8 @@
#include "weathlib.h" #include "weathlib.h"
#define SENSOR_NAME "Old Reinhard meteostation"
//static const char *emultemplate = "<?U> 06:50:36, 20.01.00, TE-2.20, DR1405.50, WU2057.68, RT0.00, WK1.00, WR177.80, WT-2.20, FE0.69, RE0.00, WG7.36, WV260.03, TI0.00, FI0.00,"; //static const char *emultemplate = "<?U> 06:50:36, 20.01.00, TE-2.20, DR1405.50, WU2057.68, RT0.00, WK1.00, WR177.80, WT-2.20, FE0.69, RE0.00, WG7.36, WV260.03, TI0.00, FI0.00,";
enum{ enum{
@@ -34,17 +36,15 @@ enum{
NAMOUNT NAMOUNT
}; };
extern sensordata_t sensor;
static const val_t values[NAMOUNT] = { static const val_t values[NAMOUNT] = {
[NWIND] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WIND}, [NWIND] = {.sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_WIND},
[NWINDDIR] = {.sense = VAL_RECOMMENDED,.type = VALT_FLOAT, .meaning = IS_WINDDIR}, [NWINDDIR] = {.sense = VAL_RECOMMENDED,.type = VALT_FLOAT, .meaning = IS_WINDDIR},
[NHUMIDITY] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_HUMIDITY}, [NHUMIDITY] = {.sense = VAL_BROKEN, .type = VALT_FLOAT, .meaning = IS_HUMIDITY},
[NAMB_TEMP] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_AMB_TEMP}, [NAMB_TEMP] = {.sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_AMB_TEMP},
[NPRESSURE] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_PRESSURE}, [NPRESSURE] = {.sense = VAL_BROKEN, .type = VALT_FLOAT, .meaning = IS_PRESSURE},
[NCLOUDS] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_CLOUDS}, [NCLOUDS] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_CLOUDS},
[NPRECIP] = {.sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_PRECIP}, [NPRECIP] = {.sense = VAL_RECOMMENDED, .type = VALT_UINT, .meaning = IS_PRECIP},
[NPRECIPLVL]= {.sense = VAL_RECOMMENDED,.type = VALT_FLOAT, .meaning = IS_PRECIP_LEVEL}, [NPRECIPLVL]= {.sense = VAL_UNNECESSARY,.type = VALT_FLOAT, .meaning = IS_PRECIP_LEVEL},
}; };
/** /**
@@ -70,124 +70,118 @@ static int getpar(char *string, double *Val, char *Name){
return TRUE; return TRUE;
} }
static void *mainthread(void _U_ *U){ static void *mainthread(void *s){
FNAME(); FNAME();
char buf[BUFSIZ]; char buf[BUFSIZ];
time_t tpoll = 0; time_t tpoll = 0;
while(sensor.fdes > -1){ sensordata_t *sensor = (sensordata_t *)s;
while(sensor->fdes > -1){
time_t tnow = time(NULL); time_t tnow = time(NULL);
if(tnow - tpoll > sensor.tpoll){ if(tnow - tpoll > sensor->tpoll){
if(sl_tty_write(sensor.fdes, "?U\r\n", 4)){ if(sl_tty_write(sensor->fdes, "?U\r\n", 4)){
WARN("Can't ask new data"); WARN("Can't ask new data");
break; break;
} }
DBG("poll @%zd, pollt=%zd", tnow, sensor.tpoll); DBG("poll @%zd, pollt=%zd", tnow, sensor->tpoll);
tpoll = tnow; tpoll = tnow;
} }
int canread = sl_canread(sensor.fdes); int canread = sl_canread(sensor->fdes);
if(canread < 0){ if(canread < 0){
WARNX("Disconnected fd %d", sensor.fdes); WARNX("Disconnected fd %d", sensor->fdes);
break; break;
}else if(canread == 1){ }else if(canread == 1){
ssize_t got = read(sensor.fdes, buf, BUFSIZ); ssize_t got = read(sensor->fdes, buf, BUFSIZ);
if(got > 0){ if(got > 0){
sl_RB_write(sensor.ringbuffer, (uint8_t*)buf, got); sl_RB_write(sensor->ringbuffer, (uint8_t*)buf, got);
}else if(got < 0){ }else if(got < 0){
DBG("Disconnected?"); DBG("Disconnected?");
break; break;
} }
} }
if(sl_RB_datalen(sensor.ringbuffer) > BUFSIZ-1){ if(sl_RB_datalen(sensor->ringbuffer) > BUFSIZ-1){
WARNX("Overfull? Clear data from ring buffer"); WARNX("Overfull? Clear data from ring buffer");
sl_RB_clearbuf(sensor.ringbuffer); sl_RB_clearbuf(sensor->ringbuffer);
} }
if(sl_RB_readto(sensor.ringbuffer, '\n', (uint8_t*)buf, BUFSIZ-1) > 0){ if(sl_RB_readto(sensor->ringbuffer, '\n', (uint8_t*)buf, BUFSIZ-1) > 0){
tnow = time(NULL); tnow = time(NULL);
DBG("Got next: %s", buf); DBG("Got next: %s", buf);
pthread_mutex_lock(&sensor.valmutex); pthread_mutex_lock(&sensor->valmutex);
double d; double d;
//int Ngot = 0; //int Ngot = 0;
if(getpar(buf, &d, "RE")){ if(getpar(buf, &d, "RE")){
//++Ngot; //++Ngot;
sensor.values[NPRECIPLVL].value.f = (float) d; sensor->values[NPRECIPLVL].value.f = (float) d;
sensor.values[NPRECIPLVL].time = tnow; sensor->values[NPRECIPLVL].time = tnow;
DBG("Got precip. lvl: %g", d); DBG("Got precip. lvl: %g", d);
} }
if(getpar(buf, &d, "RT")){ if(getpar(buf, &d, "RT")){
//++Ngot; //++Ngot;
sensor.values[NPRECIP].value.u = (d > 0.) ? 1 : 0; sensor->values[NPRECIP].value.u = (d > 0.) ? 1 : 0;
sensor.values[NPRECIP].time = tnow; sensor->values[NPRECIP].time = tnow;
DBG("Got precip.: %g", d); DBG("Got precip.: %g", d);
} }
if(getpar(buf, &d, "WU")){ if(getpar(buf, &d, "WU")){
//++Ngot; //++Ngot;
sensor.values[NCLOUDS].value.f = (float) d; sensor->values[NCLOUDS].value.f = (float) d;
sensor.values[NCLOUDS].time = tnow; sensor->values[NCLOUDS].time = tnow;
DBG("Got clouds.: %g", d); DBG("Got clouds.: %g", d);
} }
if(getpar(buf, &d, "TE")){ if(getpar(buf, &d, "TE")){
//++Ngot; //++Ngot;
sensor.values[NAMB_TEMP].value.f = (float) d; sensor->values[NAMB_TEMP].value.f = (float) d;
sensor.values[NAMB_TEMP].time = tnow; sensor->values[NAMB_TEMP].time = tnow;
DBG("Got ext. T: %g", d); DBG("Got ext. T: %g", d);
} }
if(getpar(buf, &d, "WG")){ if(getpar(buf, &d, "WG")){
//++Ngot; //++Ngot;
d /= 3.6; d /= 3.6;
DBG("Wind: %g", d); DBG("Wind: %g", d);
sensor.values[NWIND].value.f = (float) d; sensor->values[NWIND].value.f = (float) d;
sensor.values[NWIND].time = tnow; sensor->values[NWIND].time = tnow;
} }
if(getpar(buf, &d, "WR")){ if(getpar(buf, &d, "WR")){
//++Ngot; //++Ngot;
sensor.values[NWINDDIR].value.f = (float) d; sensor->values[NWINDDIR].value.f = (float) d;
sensor.values[NWINDDIR].time = tnow; sensor->values[NWINDDIR].time = tnow;
DBG("Winddir: %g", d); DBG("Winddir: %g", d);
} }
if(getpar(buf, &d, "DR")){ if(getpar(buf, &d, "DR")){
//++Ngot; //++Ngot;
sensor.values[NPRESSURE].value.f = (float) (d * 0.7500616); sensor->values[NPRESSURE].value.f = (float) (d * 0.7500616);
sensor.values[NPRESSURE].time = tnow; sensor->values[NPRESSURE].time = tnow;
DBG("Pressure: %g", d); DBG("Pressure: %g", d);
} }
if(getpar(buf, &d, "FE")){ if(getpar(buf, &d, "FE")){
//++Ngot; //++Ngot;
sensor.values[NHUMIDITY].value.f = (float) d; sensor->values[NHUMIDITY].value.f = (float) d;
sensor.values[NHUMIDITY].time = tnow; sensor->values[NHUMIDITY].time = tnow;
DBG("Humidity: %g", d); DBG("Humidity: %g", d);
} }
pthread_mutex_unlock(&sensor.valmutex); pthread_mutex_unlock(&sensor->valmutex);
if(sensor.freshdatahandler) sensor.freshdatahandler(&sensor); if(sensor->freshdatahandler) sensor->freshdatahandler(sensor);
} }
} }
common_kill(&sensor); sensor->kill(sensor);
return NULL; return NULL;
} }
static int init(struct sensordata_t *s, int N, time_t pollt, int fd){ sensordata_t *sensor_new(int N, time_t pollt, int fd){
FNAME(); FNAME();
if(!s || fd < 0) return -1; if(fd < 0) return NULL;
sensor.PluginNo = N; sensordata_t *s = common_new();
sensor.fdes = fd; if(!s) return NULL;
s->Nvalues = NAMOUNT;
s->PluginNo = N;
s->fdes = fd;
strncpy(s->name, SENSOR_NAME, NAME_LEN);
if(pollt) s->tpoll = pollt; if(pollt) s->tpoll = pollt;
if(pthread_create(&s->thread, NULL, mainthread, NULL)) return -1;
s->values = MALLOC(val_t, NAMOUNT); s->values = MALLOC(val_t, NAMOUNT);
for(int i = 0; i < NAMOUNT; ++i) s->values[i] = values[i]; for(int i = 0; i < NAMOUNT; ++i) s->values[i] = values[i];
if(!(s->ringbuffer = sl_RB_new(BUFSIZ))){ if(!(s->ringbuffer = sl_RB_new(BUFSIZ)) ||
WARNX("Can't init ringbuffer!"); pthread_create(&s->thread, NULL, mainthread, (void*)s)){
common_kill(s); s->kill(s);
return -1; return NULL;
} }
return NAMOUNT; return s;
} }
sensordata_t sensor = {
.name = "Old Reinhard meteostation",
.Nvalues = NAMOUNT,
.init = init,
.onrefresh = common_onrefresh,
.valmutex = PTHREAD_MUTEX_INITIALIZER,
.get_value = common_getval,
.kill = common_kill,
};

View File

@@ -0,0 +1,216 @@
/*
* This file is part of the weatherdaemon project.
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <math.h>
#include <string.h>
#include "weathlib.h"
#define SENSOR_NAME "WXA100-06 ultrasonic meteostation"
// static const char *emultemplate = "0R0,S=1.9,D=217.2,P=787.7,T=10.8,H=69.0,R=31.0,Ri=0.0,Rs=Y";
enum{
NWIND,
NWINDDIR,
NHUMIDITY,
NAMB_TEMP,
NPRESSURE,
NPRECIP,
NPRECIPLVL,
NPRECIPINT,
NAMOUNT
};
static const val_t values[NAMOUNT] = {
[NWIND] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WIND},
[NWINDDIR] = {.sense = VAL_RECOMMENDED,.type = VALT_FLOAT, .meaning = IS_WINDDIR},
[NHUMIDITY] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_HUMIDITY},
[NAMB_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_UINT, .meaning = IS_PRECIP},
[NPRECIPLVL]= {.sense = VAL_RECOMMENDED,.type = VALT_FLOAT, .meaning = IS_PRECIP_LEVEL},
[NPRECIPINT]= {.sense = VAL_RECOMMENDED,.type = VALT_FLOAT, .meaning = IS_OTHER, .name = "PRECRATE", .comment = "Precipitation rate, mm/h"},
};
typedef struct{
double windspeed; // speed in m/s
double winddir; // direction from north
double pressure; // pressure in hPa
double temperature; // outern temperature in degC
double humidity; // humidity in percents
double rainfall; // cumulative rain level (mm)
double rainrate; // rain rate, mm/h
double israin; // ==1 if it's raining
} weather_t;
typedef struct{
const char *parname; // parameter started value
int isboolean; // ==1 if answer Y/N
int parlen; // parameter length in bytes
double *weatherpar; // data target
} wpair_t;
static weather_t lastweather;
static const wpair_t wpairs[] = {
{"S=", 0, 2, &lastweather.windspeed},
{"D=", 0, 2, &lastweather.winddir},
{"P=", 0, 2, &lastweather.pressure},
{"T=", 0, 2, &lastweather.temperature},
{"H=", 0, 2, &lastweather.humidity},
{"R=", 0, 2, &lastweather.rainfall},
{"Ri=",0, 3, &lastweather.rainrate},
{"Rs=",1, 3, &lastweather.israin},
{NULL, 0, 0, NULL}
};
static int parseans(char *str){
int ncollected = 0;
if(strncmp(str, "0R0,", 4)){
WARNX("Wrong answer");
LOGWARN("poll_device() get wrong answer: %s", str);
return 0;
}
// init with NaNs
const wpair_t *el = wpairs;
while(el->parname){
*el->weatherpar = NAN;
++el;
}
char *token = strtok(str, ",");
while(token){
el = wpairs;
while(el->parname){
if(strncmp(token, el->parname, el->parlen) == 0){ // found next parameter
token += el->parlen;
char *endptr;
if(el->isboolean){
*el->weatherpar = (*token == 'Y') ? 1. : 0.;
++ncollected;
}else{
*el->weatherpar = strtod(token, &endptr);
if(endptr == token){
DBG("Wrong double value %s", token);
}else ++ncollected;
}
break;
}
++el;
}
token = strtok(NULL, ",");
}
DBG("Got %d values", ncollected);
return ncollected;
}
static void *mainthread(void *s){
FNAME();
char buf[BUFSIZ];
time_t tpoll = 0;
sensordata_t *sensor = (sensordata_t *)s;
while(sensor->fdes > -1){
time_t tnow = time(NULL);
if(tnow - tpoll > sensor->tpoll){
if(sl_tty_write(sensor->fdes, "!0R0\r\n", 6)){
WARN("Can't ask new data");
break;
}
DBG("poll @%zd, pollt=%zd", tnow, sensor->tpoll);
tpoll = tnow;
}
int canread = sl_canread(sensor->fdes);
if(canread < 0){
WARNX("Disconnected fd %d", sensor->fdes);
break;
}else if(canread == 1){
ssize_t got = read(sensor->fdes, buf, BUFSIZ);
if(got > 0){
sl_RB_write(sensor->ringbuffer, (uint8_t*)buf, got);
}else if(got < 0){
DBG("Disconnected?");
break;
}
}
if(sl_RB_datalen(sensor->ringbuffer) > BUFSIZ-1){
WARNX("Overfull? Clear data from ring buffer");
sl_RB_clearbuf(sensor->ringbuffer);
}
if(sl_RB_readline(sensor->ringbuffer, buf, BUFSIZ-1) > 0 && parseans(buf) > 0){
tnow = time(NULL);
pthread_mutex_lock(&sensor->valmutex);
if(!isnan(lastweather.rainfall)){
sensor->values[NPRECIPLVL].value.f = (float) lastweather.rainfall;
sensor->values[NPRECIPLVL].time = tnow;
}
if(!isnan(lastweather.rainrate)){
sensor->values[NPRECIPINT].value.f = (float) lastweather.rainrate;
sensor->values[NPRECIPINT].time = tnow;
}
if(!isnan(lastweather.israin)){
sensor->values[NPRECIP].value.u = (lastweather.israin > 0.) ? 1 : 0;
sensor->values[NPRECIP].time = tnow;
}
if(!isnan(lastweather.temperature)){
sensor->values[NAMB_TEMP].value.f = (float) lastweather.temperature;
sensor->values[NAMB_TEMP].time = tnow;
}
if(!isnan(lastweather.windspeed)){
sensor->values[NWIND].value.f = (float) lastweather.windspeed;
sensor->values[NWIND].time = tnow;
}
if(!isnan(lastweather.winddir)){
sensor->values[NWINDDIR].value.f = (float) lastweather.winddir;
sensor->values[NWINDDIR].time = tnow;
}
if(!isnan(lastweather.pressure)){
sensor->values[NPRESSURE].value.f = (float) (lastweather.pressure * 0.7500616); // mmHg instead of hPa!
sensor->values[NPRESSURE].time = tnow;
}
if(!isnan(lastweather.humidity)){
sensor->values[NHUMIDITY].value.f = (float) lastweather.humidity;
sensor->values[NHUMIDITY].time = tnow;
}
pthread_mutex_unlock(&sensor->valmutex);
if(sensor->freshdatahandler) sensor->freshdatahandler(sensor);
}
}
sensor->kill(sensor);
return NULL;
}
sensordata_t *sensor_new(int N, time_t pollt, int fd){
FNAME();
if(fd < 0) return NULL;
sensordata_t *s = common_new();
if(!s) return NULL;
strncpy(s->name, SENSOR_NAME, NAME_LEN);
s->PluginNo = N;
s->fdes = fd;
s->Nvalues = NAMOUNT;
if(pollt) s->tpoll = pollt;
s->values = MALLOC(val_t, NAMOUNT);
for(int i = 0; i < NAMOUNT; ++i) s->values[i] = values[i];
if(!(s->ringbuffer = sl_RB_new(BUFSIZ)) ||
pthread_create(&s->thread, NULL, mainthread, (void*)s)){
s->kill(s);
return NULL;
}
return s;
}

View File

@@ -60,12 +60,16 @@ sensordata_t *get_plugin(int N){
return allplugins[N]; return allplugins[N];
} }
// TODO: fix for usage with several identical meteostations
void *open_plugin(const char *name){ void *open_plugin(const char *name){
DBG("try to open lib %s", name); DBG("try to open lib %s", name);
void* dlh = dlopen(name, RTLD_NOLOAD); // library may be already opened void* dlh = dlopen(name, RTLD_NOW | RTLD_NOLOAD); // library may be already opened
if(!dlh){ if(!dlh){
DBG("Not loaded - load"); DBG("Not loaded - load");
dlh = dlopen(name, RTLD_NOW); dlh = dlopen(name, RTLD_NOW);
}else{
WARNX("Library %s already opened", name);
//return NULL;
} }
if(!dlh){ if(!dlh){
char *e = dlerror(); char *e = dlerror();
@@ -131,24 +135,18 @@ int openplugins(char **paths, int N){
void* dlh = open_plugin(buf); void* dlh = open_plugin(buf);
if(!dlh) continue; if(!dlh) continue;
DBG("OPENED"); DBG("OPENED");
void *s = dlsym(dlh, "sensor"); sensor_new_t sensnew = (sensor_new_t) dlsym(dlh, "sensor_new");
if(s){ if(sensnew){
sensordata_t *S = (sensordata_t*) s;
if(!S->get_value) WARNXL("Sensor library %s have no values' getter!", paths[i]);
if(!S->init){
WARNXL("Sensor library %s have no init funtion");
continue;
}
int fd = -1; int fd = -1;
if(colon) fd = getFD(colon); if(colon) fd = getFD(colon);
int ns = S->init(S, nplugins, poll_interval, fd); // here nplugins is index in array sensordata_t *S = sensnew(nplugins, poll_interval, fd); // here nplugins is index in array
if(ns < 1) WARNXL("Can't init plugin %s", paths[i]); if(!S) WARNXL("Can't init plugin %s", paths[i]);
else{ else{
if(!S->onrefresh || !S->onrefresh(S, dumpsensors)) WARNXL("Can't init refresh funtion"); if(!S->onrefresh || !S->onrefresh(S, dumpsensors)) WARNXL("Can't init refresh funtion");
LOGMSG("Plugin %s nave %d sensors", paths[i], ns); LOGMSG("Plugin %s nave %d sensors", paths[i], S->Nvalues);
allplugins[nplugins++] = S; allplugins[nplugins++] = S;
} }
}else WARNXL("Can't find field `sensor` in plugin %s: %s", paths[i], dlerror()); }else WARNXL("Can't find initing function in plugin %s: %s", paths[i], dlerror());
} }
return nplugins; return nplugins;
} }
@@ -207,9 +205,9 @@ int format_sensval(const val_t *v, char *buf, int buflen, int Np){
[IS_HW_TEMP] = "Hardware (mirror?) termperature, degC", [IS_HW_TEMP] = "Hardware (mirror?) termperature, degC",
[IS_PRESSURE] = "Atmospheric pressure, mmHg", [IS_PRESSURE] = "Atmospheric pressure, mmHg",
[IS_PRECIP] = "Precipitation (1 - yes, 0 - no)", [IS_PRECIP] = "Precipitation (1 - yes, 0 - no)",
[IS_PRECIP_LEVEL]="Precipitation level (mm)", [IS_PRECIP_LEVEL]="Cumulative precipitation level (mm)",
[IS_MIST] = "Mist (1 - yes, 0 - no)", [IS_MIST] = "Mist (1 - yes, 0 - no)",
[IS_CLOUDS] = "Integral clouds value (bigger - better)", [IS_CLOUDS] = "Integral sky quality value (bigger - better)",
[IS_SKYTEMP] = "Mean sky temperatyre" [IS_SKYTEMP] = "Mean sky temperatyre"
}; };
const char *name = NULL, *comment = NULL; const char *name = NULL, *comment = NULL;

View File

@@ -13,6 +13,7 @@ plugins/dummy.c
plugins/fdexample.c plugins/fdexample.c
plugins/hydreon.c plugins/hydreon.c
plugins/reinhardt.c plugins/reinhardt.c
plugins/wxa100.c
sensors.c sensors.c
sensors.h sensors.h
server.c server.c

View File

@@ -18,15 +18,37 @@
// Some common functions and handlers for sensors // Some common functions and handlers for sensors
#include <pthread.h>
#include "weathlib.h" #include "weathlib.h"
// private functions (for plugins usage only)
static int common_onrefresh(sensordata_t*, void (*handler)(sensordata_t*));
static void common_kill(sensordata_t *);
static int common_getval(sensordata_t*, val_t*, int);
//static int common_init(sensordata_t*, int, time_t, int);
/**
* @brief common_new - call this function from your plugin's `sensor_new`
* @return
*/
sensordata_t *common_new(){
sensordata_t *s = MALLOC(sensordata_t, 1);
s->fdes = -1; // not inited
s->onrefresh = common_onrefresh;
s->get_value = common_getval;
s->kill = common_kill;
pthread_mutex_init(&s->valmutex, NULL);
return s;
}
/** /**
* @brief sensor_alive - test if sensor's thread isn't dead * @brief sensor_alive - test if sensor's thread isn't dead
* @param s - sensor * @param s - sensor
* @return FALSE if thread is dead * @return FALSE if thread is dead
*/ */
int sensor_alive(sensordata_t *s){ int sensor_alive(sensordata_t *s){
if(!s) return FALSE; if(!s || s->fdes < 0) return FALSE;
if(pthread_kill(s->thread, 0)) return FALSE; if(pthread_kill(s->thread, 0)) return FALSE;
return TRUE; return TRUE;
} }
@@ -50,19 +72,23 @@ int common_onrefresh(sensordata_t *s, void (*handler)(sensordata_t *)){
void common_kill(sensordata_t *s){ void common_kill(sensordata_t *s){
FNAME(); FNAME();
if(!s) return; if(!s) return;
if(s->fdes > -1){ // inited and maybe have opened file/socket
if(0 == pthread_cancel(s->thread)){ if(0 == pthread_cancel(s->thread)){
DBG("%s main thread canceled, join", s->name); DBG("%s main thread canceled, join", s->name);
pthread_join(s->thread, NULL); pthread_join(s->thread, NULL);
DBG("Done"); DBG("Done");
} }
DBG("Delete RB");
sl_RB_delete(&s->ringbuffer);
if(s->fdes > -1){
close(s->fdes); close(s->fdes);
DBG("FD closed");
} }
DBG("Delete RB");
if(s->ringbuffer) sl_RB_delete(&s->ringbuffer);
FREE(s->values); FREE(s->values);
DBG("Sensor %s killed", s->name); if(s->privdatafree) s->privdatafree(s->privdata);
else FREE(s->privdata);
DBG("Sensor '%s' killed", s->name);
LOGERR("Sensor '%s' killed", s->name);
FREE(s);
DBG("There's no more this sensor");
} }
/** /**

View File

@@ -21,6 +21,7 @@
#include <pthread.h> #include <pthread.h>
#include <signal.h> // pthread_kill #include <signal.h> // pthread_kill
#include <stdint.h> #include <stdint.h>
#include <string.h>
#include <time.h> #include <time.h>
#include <usefull_macros.h> #include <usefull_macros.h>
@@ -89,7 +90,6 @@ typedef struct sensordata_t{
char name[NAME_LEN+1]; // max 31 symbol of sensor's name (e.g. "rain sensor") char name[NAME_LEN+1]; // max 31 symbol of sensor's name (e.g. "rain sensor")
int Nvalues; // amount of values int Nvalues; // amount of values
int PluginNo; // plugin number in array (if several) int PluginNo; // plugin number in array (if several)
int (*init)(struct sensordata_t*, int, time_t, int); // init meteostation with given PluginNo, poll_interval and fd; return amount of parameters found or -1 if error
int (*onrefresh)(struct sensordata_t*, void (*handler)(struct sensordata_t*)); // handler of new data; return TRUE if OK int (*onrefresh)(struct sensordata_t*, void (*handler)(struct sensordata_t*)); // handler of new data; return TRUE if OK
int (*get_value)(struct sensordata_t*, val_t*, int); // getter of Nth value int (*get_value)(struct sensordata_t*, val_t*, int); // getter of Nth value
void (*kill)(struct sensordata_t*); // close everything and remove sensor void (*kill)(struct sensordata_t*); // close everything and remove sensor
@@ -97,14 +97,21 @@ typedef struct sensordata_t{
val_t *values; // array of values val_t *values; // array of values
pthread_t thread; // main thread pthread_t thread; // main thread
pthread_mutex_t valmutex;// value getter/setter mutex pthread_mutex_t valmutex;// value getter/setter mutex
void (*freshdatahandler)(struct sensordata_t*); // handler of fresh data // !!! if your plugin don't use file descriptor, you should set fdes to any non-negative value after running main thread
int fdes; // file descriptor of device/socket int fdes; // file descriptor of device/socket or "init" flag (should be > -1)
sl_ringbuffer_t *ringbuffer; // ringbuffer for device reading sl_ringbuffer_t *ringbuffer; // ringbuffer for device reading
time_t tpoll; // forced polling time for sensor time_t tpoll; // forced polling time for sensor
void (*freshdatahandler)(struct sensordata_t*); // handler of fresh data
void (*privdatafree)(void*); // free private data (if don't wanna write own `kill` instead of `common kill`
void *privdata; // some private data like struct
} sensordata_t; } sensordata_t;
// library functions and other // type for function extraction
int common_onrefresh(sensordata_t*, void (*handler)(sensordata_t*)); typedef sensordata_t* (*sensor_new_t)(int, time_t, int);
void common_kill(sensordata_t *s);
// init meteostation with given PluginNo, poll_interval and fd
sensordata_t *sensor_new(int PluginNo, time_t poll_interval, int fd); // external initial function for any plugin
int sensor_alive(sensordata_t *s); int sensor_alive(sensordata_t *s);
int common_getval(struct sensordata_t *s, val_t *o, int N);
// private function (for plugins usage only)
sensordata_t *common_new();