add serial/socket plugin example

This commit is contained in:
2026-02-27 18:31:22 +03:00
parent 54778dcf9a
commit 7c2aaf1cb0
13 changed files with 416 additions and 13 deletions

View File

@@ -14,6 +14,7 @@ message("VER: ${VERSION}")
# list of options # list of options
option(DEBUG "Compile in debug mode" OFF) option(DEBUG "Compile in debug mode" OFF)
option(DUMMY "Dummy device plugin" ON) option(DUMMY "Dummy device plugin" ON)
option(FDEXAMPLE "Example of file descriptor plugin" ON)
# default flags # default flags

View File

@@ -56,11 +56,12 @@ static glob_pars G;
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), "save logs to file (default: none)"}, \ {"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), "save logs to file (default: none)"}, \
{"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfile), "pidfile name (default: " DEFAULT_PID ")"}, \ {"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfile), "pidfile name (default: " DEFAULT_PID ")"}, \
{"sockpath",NEED_ARG, NULL, 0, arg_string, APTR(&G.sockname), "UNIX socket path (starting from '\\0' for anonimous) of command socket"}, \ {"sockpath",NEED_ARG, NULL, 0, arg_string, APTR(&G.sockname), "UNIX socket path (starting from '\\0' for anonimous) of command socket"}, \
{"plugin", MULT_PAR, NULL, 'p', arg_string, APTR(&G.plugins), "add this weather plugin (may be a lot of)"}, {"plugin", MULT_PAR, NULL, 'p', arg_string, APTR(&G.plugins), "add this weather plugin (may be a lot of); FORMAT: \"dlpath:l:dev\", where `dlpath` - path of plugin library; `l` - 'D' for device, 'U' for UNIX-socket or 'N' for INET socket; dev - path to device and speed (like /dev/ttyS0:9600), UNIX socket name or host:port for INET"}, \
{"pollt", NEED_ARG, NULL, 'T', arg_int, APTR(&G.pollt), "set maximal polling interval (seconds, integer)"},
sl_option_t cmdlnopts[] = { sl_option_t cmdlnopts[] = {
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), "show this help"}, {"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)"}, {"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 COMMON_OPTS
end_option end_option

View File

@@ -30,6 +30,7 @@ typedef struct{
char **plugins; // all plugins connected char **plugins; // all plugins connected
int nplugins; // amount of plugins int nplugins; // amount of plugins
char *conffile; // configuration file used instead of long command line char *conffile; // configuration file used instead of long command line
int pollt; // sensors maximal polling interval
} glob_pars; } glob_pars;

View File

@@ -0,0 +1,154 @@
/*
* 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/>.
*/
// try to open device or socket that user pointed for plugin
// WARNING!!! The `getFD` function intended for single use for each plugin!
// WARNING!!! If you will try to close some plugins in running mode and open others, it
// WARNING!!! would cause to a memory leak!
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h> // unix socket
#include <usefull_macros.h>
#include "fd.h"
/**
* @brief openserial - try to open serial device
* @param path - path to device and speed, colon-separated (without given speed assume 9600)
* @return -1 if failed or opened FD
* WARNING!!! Memory leakage danger. Don't call this function too much times!
*/
static int openserial(char *path){
FNAME();
int speed = 9600; // default speed
char *colon = strchr(path, ':');
if(colon){
*colon++ = 0;
if(!sl_str2i(&speed, colon)){
WARNX("Wrong speed settings: '%s'", colon);
return -1;
}
}
sl_tty_t *serial = sl_tty_new(path, speed, BUFSIZ);
if(!serial || !sl_tty_open(serial, TRUE)){
WARN("Can't open %s @ speed %d", path, speed);
return -1;
}
return serial->comfd;
}
static char *convunsname(const char *path){
char *apath = MALLOC(char, 106);
if(*path == 0 || *path == '@'){
DBG("convert name starting from 0 or @");
apath[0] = 0;
strncpy(apath+1, path+1, 104);
}else if(strncmp("\\0", path, 2) == 0){
DBG("convert name starting from \\0");
apath[0] = 0;
strncpy(apath+1, path+2, 104);
}else strncpy(apath, path, 105);
return apath;
}
/**
* @brief opensocket - try to open socket
* @param sock - UNIX socket path or hostname:port for INET socket
* @param type - UNIX or INET
* @return -1 if failed or opened FD
*/
static int opensocket(char *path, sl_socktype_e type){
FNAME();
DBG("path: '%s'", path);
int sock = -1;
struct addrinfo ai = {0}, *res = &ai;
struct sockaddr_un unaddr = {0};
char *node = path, *service = NULL;
ai.ai_socktype = SOCK_STREAM;
switch(type){
case SOCKT_UNIX:
{
char *str = convunsname(path);
if(!str) return -1;
unaddr.sun_family = AF_UNIX;
ai.ai_addr = (struct sockaddr*) &unaddr;
ai.ai_addrlen = sizeof(unaddr);
memcpy(unaddr.sun_path, str, 106);
FREE(str);
ai.ai_family = AF_UNIX;
//ai.ai_socktype = SOCK_SEQPACKET;
}
break;
case SOCKT_NET:
case SOCKT_NETLOCAL:
//ai.ai_socktype = SOCK_DGRAM;
ai.ai_family = AF_INET;
char *delim = strchr(path, ':');
if(delim){
*delim = 0;
service = delim+1;
if(delim == path) node = NULL; // only port
}
DBG("node: '%s', service: '%s'", node, service);
int e = getaddrinfo(node, service, &ai, &res);
if(e){
WARNX("getaddrinfo(): %s", gai_strerror(e));
return -1;
}
for(struct addrinfo *p = res; p; p = p->ai_next){
if((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) continue;
DBG("Try proto %d, type %d", p->ai_protocol, p->ai_socktype);
if(connect(sock, p->ai_addr, p->ai_addrlen) == -1){
WARN("connect()");
close(sock); sock = -1;
} else break;
}
break;
default: // never reached
WARNX("Unsupported socket type %d", type);
return -1;
}
DBG("FD: %d", sock);
return sock;
}
/**
* @brief getFD - try to open given device/socket
* @param path - rest of string for --plugin= (e.g. "N:host.com:12345")
* WARNING!!! Contents of `path` would be modified in this function!
* @return opened file descriptor or -1 in case of error
*/
int getFD(char *path){
if(!path || !*path) return -1;
char type = *path;
path += 2;
switch(type){
case 'D': // serial device
return openserial(path);
case 'N': // INET socket
return opensocket(path, SOCKT_NET);
case 'U': // UNIX socket
return opensocket(path, SOCKT_UNIX);
}
WARNX("Wrong plugin format: '%c', should be 'D', 'N' or 'U'", type);
return -1;
}

View File

@@ -0,0 +1,21 @@
/*
* 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/>.
*/
#pragma once
int getFD(char *path);

View File

@@ -41,6 +41,12 @@ void signals(int signo){
exit(signo); exit(signo);
} }
static void getpipe(int _U_ signo){
WARNX("Get sigpipe!");
// TODO: check all sensors for disconnected one
signal(SIGPIPE, getpipe);
}
extern const char *__progname; extern const char *__progname;
int main(int argc, char **argv){ int main(int argc, char **argv){
@@ -51,6 +57,7 @@ int main(int argc, char **argv){
signal(SIGINT, signals); // ctrl+C - quit signal(SIGINT, signals); // ctrl+C - quit
signal(SIGQUIT, signals); // ctrl+\ - quit signal(SIGQUIT, signals); // ctrl+\ - quit
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
signal(SIGPIPE, getpipe); // socket disconnected
GP = parse_args(argc, argv); GP = parse_args(argc, argv);
if(!GP) ERRX("Error parsing args"); if(!GP) ERRX("Error parsing args");
if(!GP->sockname) ERRX("Point command socket name"); if(!GP->sockname) ERRX("Point command socket name");
@@ -61,6 +68,9 @@ int main(int argc, char **argv){
if(!OPENLOG(GP->logfile, lvl, 1)) ERRX("Can't open log file"); if(!OPENLOG(GP->logfile, lvl, 1)) ERRX("Can't open log file");
LOGMSG("Started"); LOGMSG("Started");
} }
if(GP->pollt > 0){
if(!set_pollT((time_t)GP->pollt)) ERRX("Can't set polling time to %d seconds", GP->pollt);
}
int nopened = openplugins(GP->plugins, GP->nplugins); int nopened = openplugins(GP->plugins, GP->nplugins);
if(nopened < 1){ if(nopened < 1){
LOGERR("No plugins found; exit!"); LOGERR("No plugins found; exit!");

View File

@@ -6,7 +6,7 @@ find_package(PkgConfig REQUIRED)
pkg_check_modules(PLUGINS REQUIRED usefull_macros) pkg_check_modules(PLUGINS REQUIRED usefull_macros)
include_directories(${PLUGINS_INCLUDE_DIRS} ..) include_directories(${PLUGINS_INCLUDE_DIRS} ..)
link_directories(${PLUGINSLIBRARY_DIRS}) link_directories(${PLUGINS_LIBRARY_DIRS})
link_libraries(${$PLUGINS_LIBRARIES} -fPIC) link_libraries(${$PLUGINS_LIBRARIES} -fPIC)
set(LIBS "") set(LIBS "")
@@ -14,6 +14,11 @@ set(LIBS "")
if(DUMMY) if(DUMMY)
add_library(wsdummy SHARED dummy.c) add_library(wsdummy SHARED dummy.c)
list(APPEND LIBS wsdummy) list(APPEND LIBS wsdummy)
endif(DUMMY) endif()
if(FDEXAMPLE)
add_library(fdex SHARED fdexample.c)
list(APPEND LIBS fdex)
endif()
install(TARGETS ${LIBS} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(TARGETS ${LIBS} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})

View File

@@ -16,6 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
// dummy meteostation sending data each `dt` seconds
#include <pthread.h> #include <pthread.h>
#include <signal.h> #include <signal.h>
#include <usefull_macros.h> #include <usefull_macros.h>
@@ -25,6 +27,7 @@
#define NS (6) #define NS (6)
extern sensordata_t sensor; extern sensordata_t sensor;
static double dt = 1.;
static void (*freshdatahandler)(const struct sensordata_t* const) = NULL; // do nothing with fresh data static void (*freshdatahandler)(const struct sensordata_t* const) = NULL; // do nothing with fresh data
static pthread_t thread; static pthread_t thread;
@@ -38,7 +41,7 @@ static val_t values[NS] = { // fields `name` and `comment` have no sense until v
{.name = "PRECIP", .comment = "precipitations flag (0 - no, 1 - yes)", .sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_PRECIP}, {.name = "PRECIP", .comment = "precipitations flag (0 - no, 1 - yes)", .sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_PRECIP},
}; };
void *mainthread(void _U_ *U){ static void *mainthread(void _U_ *U){
FNAME(); FNAME();
double t0 = sl_dtime(); double t0 = sl_dtime();
while(1){ while(1){
@@ -56,13 +59,13 @@ void *mainthread(void _U_ *U){
time_t cur = time(NULL); time_t cur = time(NULL);
for(int i = 0; i < NS; ++i) values[i].time = cur; for(int i = 0; i < NS; ++i) values[i].time = cur;
if(freshdatahandler) freshdatahandler(&sensor); if(freshdatahandler) freshdatahandler(&sensor);
while(sl_dtime() - t0 < 1.) usleep(500); while(sl_dtime() - t0 < dt) usleep(500);
t0 = sl_dtime(); t0 = sl_dtime();
} }
return NULL; return NULL;
} }
static int init(int N){ static int init(int N, time_t pollt, int _U_ fd){
FNAME(); FNAME();
values[0].value.f = 1.; values[0].value.f = 1.;
values[1].value.f = 180.; values[1].value.f = 180.;
@@ -72,6 +75,7 @@ static int init(int N){
values[5].value.u = 0; values[5].value.u = 0;
if(pthread_create(&thread, NULL, mainthread, NULL)) return 0; if(pthread_create(&thread, NULL, mainthread, NULL)) return 0;
sensor.PluginNo = N; sensor.PluginNo = N;
if(pollt) dt = (double) pollt; // refresh each `pollt` seconds
return NS; return NS;
} }

View File

@@ -0,0 +1,178 @@
/*
* 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 <pthread.h>
#include <signal.h> // pthread_kill
#include <string.h>
#include <time.h>
#include <usefull_macros.h>
#include "weathlib.h"
// dummy example of file descriptors usage
#define NS (4)
static time_t Tpoll = 10;
extern sensordata_t sensor;
static int fdes = -1;
static sl_ringbuffer_t *rb;
static pthread_t thread;
static void (*freshdatahandler)(const struct sensordata_t* const) = NULL;
static void die();
static val_t values[NS] = { // fields `name` and `comment` have no sense until value meaning is `IS_OTHER`
{.name = "WIND", .sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WIND},
{.name = "EXTTEMP", .sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_AMB_TEMP},
{.name = "PRESSURE", .sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_PRESSURE},
{.name = "HUMIDITY", .sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_HUMIDITY},
};
static int format_values(char *buf){
int gotvals = 0;
char *token = strtok(buf, ",");
time_t tnow = time(NULL);
while(token && gotvals < NS){
double v;
DBG("TOKEN: %s", token);
if(sl_str2d(&v, token)){
DBG("next value: %g", v);
values[gotvals].value.f = (float) v;
values[gotvals].time = tnow;
++gotvals;
}
token = strtok(NULL, ",");
}
DBG("GOT: %d", gotvals);
return gotvals;
}
static ssize_t writedata(int fd, const char *str, size_t size){
ssize_t sent = 0;
do{
DBG("try to write %zd bytes", size);
int canwrite = sl_canwrite(fd);
if(canwrite < 0){
WARNX("Disconnected?!");
return -1;
}else if(canwrite){
ssize_t r = write(fd, str+sent, size);
if(r < 0){
sent = -1;
WARNX("Disconnected??");
break;
}else{
sent += r;
size -= r;
}
DBG("sent %zd bytes; total send %zd, leave %zd", r, sent, size);
}
}while(size);
return sent;
}
static void *mainthread(void _U_ *U){
FNAME();
time_t task = 0;
const char begging[] = "Enter comma-separated data: wind, exttemp, pressure, humidity\n";
char buf[128];
while(fdes > -1){
time_t tnow = time(NULL);
int canread = sl_canread(fdes);
if(canread < 0){
WARNX("Disconnected fd %d", fdes);
break;
}else if(canread == 1){
ssize_t got = read(fdes, buf, 128);
if(got > 0){
sl_RB_write(rb, (uint8_t*)buf, got);
}else if(got < 0){
DBG("Disconnected?");
break;
}
}
if(sl_RB_readline(rb, buf, 127) > 0){
if(NS == format_values(buf) && freshdatahandler)
freshdatahandler(&sensor);
}
if(tnow >= task){
DBG("write %s", begging);
ssize_t got = writedata(fdes, begging, sizeof(begging)-1);
if(got > 0) task = tnow + Tpoll;
else if(got < 0){
close(fdes);
fdes = -1;
}
}
}
DBG("OOOOps!");
return NULL;
}
static int init(int N, time_t pollt, int fd){
FNAME();
if(!(rb = sl_RB_new(BUFSIZ))){
WARNX("Can't init ringbuffer!");
return 0;
}
if(pthread_create(&thread, NULL, mainthread, NULL)) return 0;
sensor.PluginNo = N;
if(pollt) Tpoll = pollt;
fdes = fd;
if(fdes < 0) return -1;
return NS;
}
static int onrefresh(void (*handler)(const struct sensordata_t* const)){
FNAME();
if(!handler) return FALSE;
freshdatahandler = handler;
return TRUE;
}
static int getval(val_t *o, int N){
if(N < 0 || N >= NS) return FALSE;
if(o) *o = values[N];
return TRUE;
}
static void die(){
FNAME();
if(0 == pthread_kill(thread, -9)){
DBG("Killed, join");
pthread_join(thread, NULL);
DBG("Done");
}
DBG("Delete RB");
sl_RB_delete(&rb);
if(fdes > -1){
close(fdes);
DBG("FD closed");
}
DBG("FDEXAMPLE: died");
}
sensordata_t sensor = {
.name = "Dummy socket or serial device weatherstation",
.Nvalues = NS,
.init = init,
.onrefresh = onrefresh,
.get_value = getval,
.die = die,
};

View File

@@ -17,8 +17,10 @@
*/ */
#include <dlfcn.h> #include <dlfcn.h>
#include <string.h>
#include <usefull_macros.h> #include <usefull_macros.h>
#include "fd.h"
#include "sensors.h" #include "sensors.h"
#define WARNXL(...) do{ LOGWARN(__VA_ARGS__); WARNX(__VA_ARGS__); } while(0) #define WARNXL(...) do{ LOGWARN(__VA_ARGS__); WARNX(__VA_ARGS__); } while(0)
@@ -26,6 +28,9 @@
#define ERRXL(...) do{ LOGERR(__VA_ARGS__); ERRX(__VA_ARGS__); } while(0) #define ERRXL(...) do{ LOGERR(__VA_ARGS__); ERRX(__VA_ARGS__); } while(0)
#define ERRL(...) do{ LOGERR(__VA_ARGS__); ERR(__VA_ARGS__); } while(0) #define ERRL(...) do{ LOGERR(__VA_ARGS__); ERR(__VA_ARGS__); } while(0)
// poll each `poll_interval` seconds
static time_t poll_interval = 15;
static int nplugins = 0; static int nplugins = 0;
static sensordata_t **allplugins = NULL; static sensordata_t **allplugins = NULL;
@@ -33,6 +38,13 @@ int get_nplugins(){
return nplugins; return nplugins;
} }
// set polling interval
int set_pollT(time_t t){
if(t == 0 || t > MAX_POLLT) return FALSE;
poll_interval = t;
return TRUE;
}
/** /**
* @brief get_plugin - get copy of opened plugin * @brief get_plugin - get copy of opened plugin
* @param o (o) - plugin with given index * @param o (o) - plugin with given index
@@ -66,14 +78,20 @@ static void dumpsensors(const struct sensordata_t* const station){
FNAME(); FNAME();
if(!station || !station->get_value || station->Nvalues < 1) return; if(!station || !station->get_value || station->Nvalues < 1) return;
char buf[FULL_LEN+1]; char buf[FULL_LEN+1];
uint64_t Tsum = 0; int nsum = 0;
int N = (nplugins > 1) ? station->PluginNo : -1; int N = (nplugins > 1) ? station->PluginNo : -1;
for(int i = 0; i < station->Nvalues; ++i){ for(int i = 0; i < station->Nvalues; ++i){
val_t v; val_t v;
if(!station->get_value(&v, i)) continue; if(!station->get_value(&v, i)) continue;
if(0 < format_sensval(&v, buf, FULL_LEN+1, N)){ if(0 < format_sensval(&v, buf, FULL_LEN+1, N)){
printf("%s\n", buf); printf("%s\n", buf);
++nsum; Tsum += v.time;
} }
} }
time_t last = (time_t)(Tsum / nsum);
if(0 < format_msrmttm(last, buf, FULL_LEN+1)){
printf("%s\n\n", buf);
}
} }
#endif #endif
@@ -85,6 +103,7 @@ static void dumpsensors(const struct sensordata_t* const station){
* This function should be runned only once at start * This function should be runned only once at start
*/ */
int openplugins(char **paths, int N){ int openplugins(char **paths, int N){
char buf[PATH_MAX+1];
if(!paths || !*paths || N < 1) return 0; if(!paths || !*paths || N < 1) return 0;
if(allplugins || nplugins){ if(allplugins || nplugins){
WARNXL("Plugins already opened"); return 0; WARNXL("Plugins already opened"); return 0;
@@ -93,7 +112,10 @@ int openplugins(char **paths, int N){
green("Try to open plugins:\n"); green("Try to open plugins:\n");
for(int i = 0; i < N; ++i){ for(int i = 0; i < N; ++i){
printf("\tplugin[%d]=%s\n", i, paths[i]); printf("\tplugin[%d]=%s\n", i, paths[i]);
void* dlh = open_plugin(paths[i]); snprintf(buf, PATH_MAX, "%s", paths[i]);
char *colon = strchr(buf, ':');
if(colon) *colon++ = 0;
void* dlh = open_plugin(buf);
if(!dlh) continue; if(!dlh) continue;
DBG("OPENED"); DBG("OPENED");
void *s = dlsym(dlh, "sensor"); void *s = dlsym(dlh, "sensor");
@@ -104,7 +126,9 @@ int openplugins(char **paths, int N){
WARNXL("Sensor library %s have no init funtion"); WARNXL("Sensor library %s have no init funtion");
continue; continue;
} }
int ns = S->init(nplugins); int fd = -1;
if(colon) fd = getFD(colon);
int ns = S->init(nplugins, poll_interval, fd); // here nplugins is index in array
if(ns < 1) WARNXL("Can't init plugin %s", paths[i]); if(ns < 1) WARNXL("Can't init plugin %s", paths[i]);
else{ else{
#ifdef EBUG #ifdef EBUG

View File

@@ -20,9 +20,13 @@
#include "weathlib.h" #include "weathlib.h"
// don't allow to set polling time more than 10 minutes
#define MAX_POLLT (600)
int openplugins(char **paths, int N); int openplugins(char **paths, int N);
void closeplugins(); void closeplugins();
int get_plugin(sensordata_t *o, int N); int get_plugin(sensordata_t *o, int N);
int get_nplugins(); int get_nplugins();
int format_sensval(const val_t *v, char *buf, int buflen, int Np); int format_sensval(const val_t *v, char *buf, int buflen, int Np);
int format_msrmttm(time_t t, char *buf, int buflen); int format_msrmttm(time_t t, char *buf, int buflen);
int set_pollT(time_t t);

View File

@@ -42,13 +42,13 @@ static sl_sock_hresult_e timehandler(sl_sock_t *client, _U_ sl_sock_hitem_t *ite
// show all connected libraries // show all connected libraries
static sl_sock_hresult_e listhandler(sl_sock_t *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){ static sl_sock_hresult_e listhandler(sl_sock_t *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){
if(!client) return RESULT_FAIL; if(!client) return RESULT_FAIL;
char buf[128]; char buf[256];
int N = get_nplugins(); int N = get_nplugins();
if(N < 1) return RESULT_FAIL; if(N < 1) return RESULT_FAIL;
sensordata_t d; sensordata_t d;
for(int i = 0; i < N; ++i){ for(int i = 0; i < N; ++i){
if(!get_plugin(&d, i)) continue; if(!get_plugin(&d, i)) continue;
snprintf(buf, 127, "PLUGIN[%d]=%s\nNVALUES[%d]=%d\n", i, d.name, i, d.Nvalues); snprintf(buf, 255, "PLUGIN[%d]=%s\nNVALUES[%d]=%d\n", i, d.name, i, d.Nvalues);
sl_sock_sendstrmessage(client, buf); sl_sock_sendstrmessage(client, buf);
} }
return RESULT_SILENCE; return RESULT_SILENCE;

View File

@@ -28,7 +28,7 @@
// maximal full length of "KEY=val / comment" (as for sfitsio) // maximal full length of "KEY=val / comment" (as for sfitsio)
#define FULL_LEN (81) #define FULL_LEN (81)
// name of meteo-plugin // name of meteo-plugin
#define NAME_LEN (31) #define NAME_LEN (127)
// importance of values // importance of values
typedef enum{ typedef enum{
@@ -85,7 +85,7 @@ 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)(int); // init meteostation with given PluginNo; return amount of parameters found int (*init)(int, time_t, int); // init meteostation with given PluginNo, poll_interval and fd; return amount of parameters found
int (*onrefresh)(void (*handler)(const struct sensordata_t* const)); // handler of new data; return TRUE if OK int (*onrefresh)(void (*handler)(const struct sensordata_t* const)); // handler of new data; return TRUE if OK
int (*get_value)(val_t *, int); // getter of Nth value int (*get_value)(val_t *, int); // getter of Nth value
void (*die)(); // close everything and die void (*die)(); // close everything and die