Compare commits

...

3 Commits

Author SHA1 Message Date
5acd1cd97d some fixes 2026-04-10 15:52:55 +03:00
af33a036c8 some fixes 2026-04-10 14:51:40 +03:00
987cf022fe added 'level' changing 2026-04-10 14:21:25 +03:00
11 changed files with 489 additions and 245 deletions

View File

@@ -1,6 +1,6 @@
CC = gcc CC = gcc
CFLAGS = -Wall -Wextra -fPIC CFLAGS = -Wall -Wextra -fPIC -DEBUG
LDFLAGS = -lrt -pthread LDFLAGS = -lrt -pthread -lusefull_macros
all: weather_daemon libweather.so weather_clt_example all: weather_daemon libweather.so weather_clt_example

View File

@@ -1,3 +1,21 @@
/*
* This file is part of the weather_proxy 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 "weather_data.h" #include "weather_data.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>

View File

@@ -1,10 +1,28 @@
/*
* This file is part of the weather_proxy 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 "weather_data.h" #include "weather_data.h"
#include <stdio.h> #include <stdio.h>
int main() { int main() {
weather_data_t wd; weather_data_t wd;
if(get_weather_data(&wd) == 0){ if(get_weather_data(&wd) == 0){
printf("Weather: %d, Max wind: %.1f, Wind: %.1f, Temp: %.1f; updated @%.1f\n", printf("Weather: %d, Max wind: %.1f, Wind: %.1f, Temp: %.1f; updated @%zd\n",
wd.weather, wd.windmax, wd.wind, wd.exttemp, wd.last_update); wd.weather, wd.windmax, wd.wind, wd.exttemp, wd.last_update);
}else{ }else{
fprintf(stderr, "Failed to get weather data\n"); fprintf(stderr, "Failed to get weather data\n");

View File

@@ -1,44 +1,109 @@
#include "weather_data.h" /*
#include <stdio.h> * This file is part of the weather_proxy 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/>.
*/
#if 0
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h> #include <errno.h>
#include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/mman.h> #include <stdarg.h>
#include <sys/stat.h> #endif
#include <sys/types.h>
#include <fcntl.h> #include <fcntl.h>
#include <semaphore.h> #include <semaphore.h>
#include <signal.h> #include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h> #include <time.h>
#include <stdarg.h> #include <unistd.h>
#include <usefull_macros.h>
#include "weather_data.h"
#define DEFAULT_PID "/tmp/weather_proxy.pid"
#define DEAD_TMOUT 15 #define DEAD_TMOUT 15
#define RECONN_TMOUT 5 #define RECONN_TMOUT 5
#define STAT_TMOUT 60 #define WEAT_TMOUT 1
#define WEAT_TMOUT 5
#define SHM_NAME "/weather_shm" #define SHM_NAME "/weather_shm"
#define SEM_NAME "/weather_sem" #define SEM_NAME "/weather_sem"
typedef struct{
char *node; // node of server
int isunix; // use UNIX-sockets instead of net
char *logfile; // logfile name
int verb; // verbocity level
char *pidfile; // pidfile name
} glob_pars;
static pid_t childpid;
static int shm_fd = -1; static int shm_fd = -1;
static int forbidden = 0;
static sem_t *sem = NULL; static sem_t *sem = NULL;
static weather_data_t *shared_data = NULL; static weather_data_t *shared_data = NULL;
static volatile int running = 1; static volatile int running = 1;
static glob_pars G = {0};
#define log_message(...) do{printf("message: "); printf(__VA_ARGS__); printf("\n");}while(0) static sl_option_t opts[] = {
#define log_error(...) do{printf("error: "); printf(__VA_ARGS__); printf("\n");}while(0) {"node", NEED_ARG, NULL, 'n', arg_string, APTR(&G.node), "node to connect (host:port or UNIX socket name)"},
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), "save logs to file"},
{"pidfile", NEED_ARG, NULL, 'p', arg_string, APTR(&G.pidfile), "pidfile name (default: " DEFAULT_PID ")"},
{"isunix", NO_ARGS, NULL, 'u', arg_string, APTR(&G.isunix), "use UNIX socket instead of network"},
{"verbose", NO_ARGS, NULL, 'v', arg_int, APTR(&G.verb), "verbose level (each -v increases)"},
end_option
};
static void signal_handler(int sig) { void signals(int signo){
if (sig == SIGTERM || sig == SIGINT) { if(signo){
running = 0; if(signals != signal(signo, SIG_IGN)) exit(signo); // function called "as is", before sig registration
log_message("Received signal %d, shutting down", sig); if(childpid == 0){ // child -> test USR1/USR2
LOGDBG("Child gotta signal %d", signo);
if(signo == SIGUSR1){
forbidden = 1;
LOGMSG("Got signal `observations forbidden`");
signal(signo, signals);
return;
}else if(signo == SIGUSR2){
forbidden = 0;
LOGMSG("Got signal `observations permitted`");
signal(signo, signals);
return;
} }
} }
}
if(childpid){ // master
LOGERR("Main process exits with status %d", signo);
if(G.pidfile) unlink(G.pidfile);
exit(1);
}else{ // child
LOGERR("Killed with status %d", signo);
running = 0; // let make cleanup
}
sleep(1);
exit(signo); // force exit if stubs
}
static int init_ipc(void){ static int init_ipc(void){
umask(0); // for read-write semaphore umask(0); // for read-write semaphore
@@ -48,37 +113,34 @@ static int init_ipc(void) {
// no - create new // no - create new
shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0644); shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0644);
if(shm_fd == -1){ if(shm_fd == -1){
log_error("shm_open (create) failed: %s", strerror(errno)); LOGERR("shm_open (create) failed: %s", strerror(errno));
return -1; return -1;
} }
if(ftruncate(shm_fd, sizeof(weather_data_t)) == -1){ if(ftruncate(shm_fd, sizeof(weather_data_t)) == -1){
log_error("ftruncate failed: %s", strerror(errno)); LOGERR("ftruncate failed: %s", strerror(errno));
return -1; return -1;
} }
shared_data = mmap(NULL, sizeof(weather_data_t), PROT_READ | PROT_WRITE, shared_data = mmap(NULL, sizeof(weather_data_t), PROT_READ | PROT_WRITE,
MAP_SHARED, shm_fd, 0); MAP_SHARED, shm_fd, 0);
if(shared_data == MAP_FAILED){ if(shared_data == MAP_FAILED){
log_error("mmap failed: %s", strerror(errno)); LOGERR("mmap failed: %s", strerror(errno));
return -1; return -1;
} }
// default values to data // default values to data
}else{ }else{
printf("Use existant SHM\n"); DBG("Use existant SHM\n");
// use existant SHM
shared_data = mmap(NULL, sizeof(weather_data_t), PROT_READ | PROT_WRITE, shared_data = mmap(NULL, sizeof(weather_data_t), PROT_READ | PROT_WRITE,
MAP_SHARED, shm_fd, 0); MAP_SHARED, shm_fd, 0);
if(shared_data == MAP_FAILED){ if(shared_data == MAP_FAILED){
log_error("mmap failed: %s", strerror(errno)); LOGERR("mmap failed: %s", strerror(errno));
return -1; return -1;
} }
} }
memset(shared_data, 0, sizeof(weather_data_t)); memset(shared_data, 0, sizeof(weather_data_t));
// create samaphore if no // create samaphore if no
sem = sem_open(SEM_NAME, O_CREAT, 0666, 1); sem = sem_open(SEM_NAME, O_CREAT, 0666, 1);
if(sem == SEM_FAILED){ if(sem == SEM_FAILED){
log_error("sem_open failed: %s", strerror(errno)); LOGERR("sem_open failed: %s", strerror(errno));
return -1; return -1;
} }
return 0; return 0;
@@ -88,18 +150,18 @@ static int init_ipc(void) {
static void cleanup_ipc(void){ static void cleanup_ipc(void){
if (sem != NULL) { if (sem != NULL) {
sem_close(sem); sem_close(sem);
printf("semaphore closed\n"); DBG("semaphore closed\n");
if(-1 == sem_unlink(SEM_NAME)) perror("Can't delete semaphore"); if(-1 == sem_unlink(SEM_NAME)) LOGERR("Can't delete semaphore");
} }
if(shared_data != NULL){ if(shared_data != NULL){
printf("memory unmapped\n"); DBG("memory unmapped\n");
munmap(shared_data, sizeof(weather_data_t)); munmap(shared_data, sizeof(weather_data_t));
} }
if(shm_fd != -1){ if(shm_fd != -1){
close(shm_fd); close(shm_fd);
printf("close shared mem\n"); DBG("close shared mem\n");
if(shm_unlink(SHM_NAME) == -1){ if(shm_unlink(SHM_NAME) == -1){
perror("can't unlink SHM"); LOGERR("can't unlink SHM");
} }
} }
} }
@@ -108,177 +170,141 @@ static void parse_line(const char *line, weather_data_t *data) {
char key[64]; char key[64];
char value[256]; char value[256];
if (sscanf(line, "%63[^=]=%255s", key, value) == 2) { if (sscanf(line, "%63[^=]=%255s", key, value) == 2) {
if (strcmp(key, "weather") == 0) { if (strcmp(key, "WEATHER") == 0) {
if (strcmp(value, "good") == 0) data->weather = (weather_condition_t) atoi(value);
data->weather = WEATHER_GOOD;
else if (strcmp(value, "bad") == 0)
data->weather = WEATHER_BAD;
else if (strcmp(value, "terrible") == 0)
data->weather = WEATHER_TERRIBLE;
printf("got weather: %d\n", data->weather); printf("got weather: %d\n", data->weather);
} else if (strcmp(key, "Windmax") == 0) { } else if (strcmp(key, "WINDMAX1") == 0) {
data->windmax = atof(value); data->windmax = atof(value);
printf("got windmax: %g\n", data->windmax); printf("got windmax: %g\n", data->windmax);
} else if (strcmp(key, "Rain") == 0) { } else if (strcmp(key, "PRECIP") == 0) {
data->rain = atoi(value); data->rain = atoi(value);
printf("got rain: %d\n", data->rain); printf("got rain: %d\n", data->rain);
} else if (strcmp(key, "Clouds") == 0) { } else if (strcmp(key, "CLOUDS") == 0) {
data->clouds = atof(value); data->clouds = atof(value);
printf("got clouds: %g\n", data->clouds); printf("got clouds: %g\n", data->clouds);
} else if (strcmp(key, "Wind") == 0) { } else if (strcmp(key, "WIND") == 0) {
data->wind = atof(value); data->wind = atof(value);
printf("got wind: %g\n", data->wind); printf("got wind: %g\n", data->wind);
} else if (strcmp(key, "Temperature") == 0) { } else if (strcmp(key, "EXTTEMP") == 0) {
data->exttemp = atof(value); data->exttemp = atof(value);
printf("got temp: %g\n", data->exttemp); printf("got temp: %g\n", data->exttemp);
} else if (strcmp(key, "Pressure") == 0) { } else if (strcmp(key, "PRESSURE") == 0) {
data->pressure = atof(value); data->pressure = atof(value);
printf("got pressure: %g\n", data->pressure); printf("got pressure: %g\n", data->pressure);
} else if (strcmp(key, "Humidity") == 0) { } else if (strcmp(key, "HUMIDITY") == 0) {
data->humidity = atof(value); data->humidity = atof(value);
printf("got humidity: %g\n", data->humidity); printf("got humidity: %g\n", data->humidity);
} else if (strcmp(key, "prohibited") == 0) { } else if (strcmp(key, "PROHIBIT") == 0) {
data->prohibited = atoi(value); data->prohibited = atoi(value);
} else if (strcmp(key, "Time") == 0) { } else if (strcmp(key, "TMEAS") == 0) {
data->last_update = atof(value); data->last_update = atof(value);
if(data->weather == WEATHER_PROHIBITED || forbidden) data->prohibited = 1;
else if(data->weather < WEATHER_TERRIBLE) data->prohibited = 0;
// update all // update all
if (sem_wait(sem) == -1) { if (sem_wait(sem) == -1) {
log_error("sem_wait failed: %s", strerror(errno)); LOGWARN("sem_wait failed: %s", strerror(errno));
} else { } else {
memcpy(shared_data, data, sizeof(weather_data_t)); memcpy(shared_data, data, sizeof(weather_data_t));
sem_post(sem); sem_post(sem);
log_message("Weather data updated"); LOGMSG("Weather data updated");
} }
} }
} }
} }
static int sock = -1; static int request_weather_data(sl_sock_t *sock){
static FILE *sock_file = NULL; static time_t tcur = 0;
char *request = "get\n";
static int opensock(const char *server_ip, int port){
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
log_error("socket creation failed: %s", strerror(errno));
return -1;
}
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
if (inet_pton(AF_INET, server_ip, &server_addr.sin_addr) <= 0) {
log_error("invalid address: %s", server_ip);
close(sock);
sock = -1;
return -1;
}
if (connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
log_error("connect failed: %s", strerror(errno));
close(sock);
sock = -1;
return -1;
}
int flags = fcntl(sock, F_GETFL, 0);
if (flags == -1){
perror("fcntl F_GETFL");
}else{
if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) perror("fcntl F_SETFL");
}
sock_file = fdopen(sock, "r");
if (!sock_file) {
log_error("fdopen failed: %s", strerror(errno));
close(sock);
sock = -1;
return -1;
}
return 0;
}
static int request_weather_data() {
static time_t tstat = 0, tcur = 0;
char *request = "\n";
time_t tnow = time(NULL); time_t tnow = time(NULL);
if(tnow - tcur >= WEAT_TMOUT){ if(tnow - tcur >= WEAT_TMOUT){
tcur = tnow; tcur = tnow;
}else if(tnow - tstat >= STAT_TMOUT){
tstat = tnow;
request = "stat60\n";
}else return 1; // not now }else return 1; // not now
printf("try to send request: '%s", request); DBG("try to send request: '%s", request);
if (send(sock, request, strlen(request), 0) < 0) { if(sl_sock_sendstrmessage(sock, request) < 1){
log_error("send failed: %s", strerror(errno)); LOGERR("Can't poll new data");
close(sock);
sock = -1;
return -1; return -1;
} }
return 0; return 0;
} }
static void run_daemon(const char *server_ip, int port) { static void run_daemon(){
char line[256]; char line[256];
weather_data_t new_data; weather_data_t new_data;
opensock(server_ip, port); // just try: in case of error reopen next time sl_socktype_e stype = (G.isunix) ? SOCKT_UNIX : SOCKT_NET;
DBG("Try to connect to %s", G.node);
sl_sock_t *sock = sl_sock_run_client(stype, G.node, 4096);
if(!sock){
DBG("Can't connect");
LOGERR("Can't connect to meteodaemon over socket with node %s", G.node);
return;
}
memcpy(&new_data, shared_data, sizeof(weather_data_t)); memcpy(&new_data, shared_data, sizeof(weather_data_t));
time_t lastert = time(NULL); time_t lastert = time(NULL);
while(running){ while(running){
time_t tnow = time(NULL); time_t tnow = time(NULL);
if (-1 == sock || request_weather_data() == -1) { if(!sock || request_weather_data(sock) == -1){
if(tnow - lastert > RECONN_TMOUT){ // try to reconnect if(tnow - lastert > RECONN_TMOUT){ // try to reconnect
log_error("Failed to request weather data, retry"); LOGERR("Failed to request weather data, retry");
if(-1 == opensock(server_ip, port)) lastert += 5; if(sock) sl_sock_delete(&sock);
if(!(sock = sl_sock_run_client(stype, G.node, 4096))) lastert += 5;
else lastert = tnow; else lastert = tnow;
} }
}else lastert = tnow; }else lastert = tnow;
while (fgets(line, sizeof(line), sock_file)) { while(sl_sock_readline(sock, line, 255) > 0){
line[strcspn(line, "\r\n")] = '\0'; DBG("Parse '%s'", line);
printf("parse '%s'\n", line);
parse_line(line, &new_data); parse_line(line, &new_data);
} }
} }
close(sock); sl_sock_delete(&sock); // disconnect and clear memory
} }
int main(int argc, char *argv[]){ int main(int argc, char *argv[]){
if (argc != 3) { sl_init();
fprintf(stderr, "Usage: %s <server_ip> <port>\n", argv[0]); sl_parseargs(&argc, &argv, opts);
exit(EXIT_FAILURE); if(!G.node) ERRX("Point node to connect");
sl_check4running(NULL, G.pidfile);
if(G.logfile){
sl_loglevel_e lvl = LOGLEVEL_ERR + G.verb;
if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1;
DBG("Loglevel: %d", lvl);
if(!OPENLOG(G.logfile, lvl, 1)) ERRX("Can't open log file %s", G.logfile);
LOGMSG("Started");
} }
signal(SIGTERM, signals);
const char *server_ip = argv[1]; signal(SIGINT, signals);
int port = atoi(argv[2]); signal(SIGUSR1, SIG_IGN);
if (port <= 0 || port > 65535) { signal(SIGUSR2, SIG_IGN);
fprintf(stderr, "Invalid port\n");
exit(EXIT_FAILURE);
}
//daemonize();
log_message("Starting weather daemon, server %s:%d", server_ip, port);
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
#ifndef EBUG
sl_daemonize();
while(1){ // guard for dead processes
childpid = fork();
if(childpid){
LOGDBG("create child with PID %d\n", childpid);
DBG("Created child with PID %d\n", childpid);
wait(NULL);
WARNX("Child %d died\n", childpid);
LOGWARN("Child %d died\n", childpid);
sleep(1);
}else{
prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies
break; // go out to normal functional
}
}
#endif
// react for USRx only in child
signal(SIGUSR1, signals);
signal(SIGUSR2, signals);
if(init_ipc() != 0){ if(init_ipc() != 0){
log_error("IPC initialization failed"); LOGERR("IPC initialization failed");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
run_daemon();
run_daemon(server_ip, port);
cleanup_ipc(); cleanup_ipc();
log_message("Weather daemon stopped"); LOGMSG("Daemon is dead");
return 0; return 0;
} }

View File

@@ -6,20 +6,21 @@
typedef enum { typedef enum {
WEATHER_GOOD = 0, WEATHER_GOOD = 0,
WEATHER_BAD = 1, WEATHER_BAD = 1,
WEATHER_TERRIBLE = 2 WEATHER_TERRIBLE = 2,
WEATHER_PROHIBITED = 3,
} weather_condition_t; } weather_condition_t;
typedef struct { typedef struct {
weather_condition_t weather; weather_condition_t weather; // conditions: field "WEATHER"
float windmax; float windmax; // maximal wind for last hour, m/s: "WINDMAX1"
int rain; float wind; // current wind speed, m/s: "WIND"
float clouds; float clouds; // sky "quality" (>2500 - OK): "CLOUDS"
float wind; float exttemp; // external temperature, degC: "EXTTEMP"
float exttemp; float pressure; // atm. pressure, mmHg: "PRESSURE"
float pressure; float humidity; // humidity, percents: "HUMIDITY"
float humidity; int rain; // ==1 when rainy: "PRECIP"
int prohibited; int prohibited; // ==1 if "weather == prohibited" or rain == 1
double last_update; time_t last_update; // value of "TMEAS"
} weather_data_t; } weather_data_t;
int get_weather_data(weather_data_t *data); int get_weather_data(weather_data_t *data);

View File

@@ -55,21 +55,24 @@ enum{
NAMOUNT_OF_DATA NAMOUNT_OF_DATA
}; };
// starting sense values are VAL_BROKEN except of calculated values
// they would be changed later in `fix_new_data` to lowest level
static val_t collected_data[NAMOUNT_OF_DATA] = { static val_t collected_data[NAMOUNT_OF_DATA] = {
[NWIND] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WIND}, [NWIND] = {.sense = VAL_BROKEN, .type = VALT_FLOAT, .meaning = IS_WIND},
[NWINDMAX] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_OTHER, .name = "WINDMAX", .comment = "Maximal wind speed for last 24 hours"}, [NWINDMAX] = {.sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_OTHER, .name = "WINDMAX", .comment = "Maximal wind speed for last 24 hours"},
[NWINDMAX1] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_OTHER, .name = "WINDMAX1", .comment = "Maximal wind speed for last hour"}, [NWINDMAX1] = {.sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_OTHER, .name = "WINDMAX1", .comment = "Maximal wind speed for last hour"},
[NWINDDIR] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WINDDIR}, [NWINDDIR] = {.sense = VAL_BROKEN, .type = VALT_FLOAT, .meaning = IS_WINDDIR},
[NWINDDIR1] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_OTHER, .name = "WINDDIR1", .comment = "Mean wind speed direction for last hour"}, [NWINDDIR1] = {.sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_OTHER, .name = "WINDDIR1", .comment = "Mean wind speed direction for last hour"},
[NWINDDIR2] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_OTHER, .name = "WINDDIR2", .comment = "Mean wind speed^2 direction for last hour"}, [NWINDDIR2] = {.sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_OTHER, .name = "WINDDIR2", .comment = "Mean wind speed^2 direction for last hour"},
[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_BROKEN, .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},
[NPRECIP] = {.sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_PRECIP}, [NPRECIP] = {.sense = VAL_BROKEN, .type = VALT_UINT, .meaning = IS_PRECIP},
[NPRECIP_LEVEL] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_PRECIP_LEVEL}, [NPRECIP_LEVEL] = {.sense = VAL_BROKEN, .type = VALT_FLOAT, .meaning = IS_PRECIP_LEVEL},
[NMIST] = {.sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_MIST}, [NMIST] = {.sense = VAL_BROKEN, .type = VALT_UINT, .meaning = IS_MIST},
[NCLOUDS] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_CLOUDS}, [NCLOUDS] = {.sense = VAL_BROKEN, .type = VALT_FLOAT, .meaning = IS_CLOUDS},
[NSKYTEMP] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_SKYTEMP}, [NSKYTEMP] = {.sense = VAL_BROKEN, .type = VALT_FLOAT, .meaning = IS_SKYTEMP},
// these are calculated values
[NCOMMWEATH] = {.value.i = 0, .sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_OTHER, .name = "WEATHER", .comment = "Weather level (0 - good, 3 - obs. prohibited)"}, [NCOMMWEATH] = {.value.i = 0, .sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_OTHER, .name = "WEATHER", .comment = "Weather level (0 - good, 3 - obs. prohibited)"},
[NLASTAHTUNG] = {.value.i = 0, .sense = VAL_RECOMMENDED, .type = VALT_UINT, .meaning = IS_OTHER, .name = "EVTTIME", .comment = "UNIX-time of last weather level increasing"}, [NLASTAHTUNG] = {.value.i = 0, .sense = VAL_RECOMMENDED, .type = VALT_UINT, .meaning = IS_OTHER, .name = "EVTTIME", .comment = "UNIX-time of last weather level increasing"},
// {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_OTHER}, // {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_OTHER},
@@ -181,11 +184,18 @@ int get_collected(val_t *val, int N){
return TRUE; return TRUE;
} }
// take only data with `sense value` less than collected have
static void fix_new_data(val_t *collected, val_t *fresh){ static void fix_new_data(val_t *collected, val_t *fresh){
if(!collected || !fresh) return; if(!collected || !fresh) return;
if(collected->time > fresh->time) return;
// lower `collected` level if data is too old
if(fresh->time - collected->time > 60) collected->sense = VAL_UNNECESSARY;
if(collected->sense < fresh->sense) return;
if(collected->sense != fresh->sense) collected->sense = fresh->sense; // take new lower level
//DBG("Refresh collected value");
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"); //DBG("Types are the same");
collected->value = fresh->value; collected->value = fresh->value;
return; return;
} }
@@ -195,11 +205,11 @@ static void fix_new_data(val_t *collected, val_t *fresh){
switch(fresh->type){ switch(fresh->type){
case VALT_INT: case VALT_INT:
collected->value.u = (uint32_t) fresh->value.i; collected->value.u = (uint32_t) fresh->value.i;
DBG("i->u"); //DBG("i->u");
break; break;
case VALT_FLOAT: case VALT_FLOAT:
collected->value.u = (uint32_t) fresh->value.f; collected->value.u = (uint32_t) fresh->value.f;
DBG("f->u"); //DBG("f->u");
default: break; default: break;
} }
break; break;
@@ -207,11 +217,11 @@ static void fix_new_data(val_t *collected, val_t *fresh){
switch(fresh->type){ switch(fresh->type){
case VALT_UINT: case VALT_UINT:
collected->value.i = (int32_t) fresh->value.u; collected->value.i = (int32_t) fresh->value.u;
DBG("u->i"); //DBG("u->i");
break; break;
case VALT_FLOAT: case VALT_FLOAT:
collected->value.i = (int32_t) fresh->value.f; collected->value.i = (int32_t) fresh->value.f;
DBG("f->i"); //DBG("f->i");
default: break; default: break;
} }
break; break;
@@ -219,11 +229,11 @@ static void fix_new_data(val_t *collected, val_t *fresh){
switch(fresh->type){ switch(fresh->type){
case VALT_UINT: case VALT_UINT:
collected->value.f = (float) fresh->value.u; collected->value.f = (float) fresh->value.u;
DBG("u->f"); //DBG("u->f");
break; break;
case VALT_INT: case VALT_INT:
collected->value.f = (float) fresh->value.i; collected->value.f = (float) fresh->value.i;
DBG("i->f"); //DBG("i->f");
default: break; default: break;
} }
break; break;
@@ -257,7 +267,7 @@ static void chkweatherlevel(int *curlevel, double curvalue, weather_cond_t *curc
} }
void refresh_sensval(sensordata_t *s){ void refresh_sensval(sensordata_t *s){
FNAME(); //FNAME();
static time_t poll_time = 0; static time_t poll_time = 0;
val_t value; val_t value;
if(!s || !s->get_value) return; if(!s || !s->get_value) return;
@@ -266,11 +276,11 @@ void refresh_sensval(sensordata_t *s){
int curahtungtime = collected_data[NLASTAHTUNG].value.i; int curahtungtime = collected_data[NLASTAHTUNG].value.i;
time_t curtime = time(NULL); time_t curtime = time(NULL);
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("\nTry 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) || value.sense > VAL_RECOMMENDED) continue;
DBG("got value"); //DBG("got value");
int idx = -1; int idx = -1;
double curvalue; double curvalue;
weather_cond_t *curcond = NULL; weather_cond_t *curcond = NULL;
@@ -326,13 +336,7 @@ void refresh_sensval(sensordata_t *s){
default : break; default : break;
} }
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 : 90; // 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); pthread_mutex_lock(&datamutex);
fix_new_data(&collected_data[idx], &value); fix_new_data(&collected_data[idx], &value);
pthread_mutex_unlock(&datamutex); pthread_mutex_unlock(&datamutex);
@@ -352,14 +356,14 @@ void refresh_sensval(sensordata_t *s){
collected_data[NWINDMAX].time = curtime; collected_data[NWINDMAX].time = curtime;
collected_data[NWINDMAX1].value.f = (float) get_max_forT(&windspeeds, curtime - T_ONE_HOUR); collected_data[NWINDMAX1].value.f = (float) get_max_forT(&windspeeds, curtime - T_ONE_HOUR);
collected_data[NWINDMAX1].time = curtime; collected_data[NWINDMAX1].time = curtime;
DBG("check ahtung"); //DBG("check ahtung");
if(Forbidden) collected_data[NCOMMWEATH].value.i = WEATHER_PROHIBITED; if(Forbidden) collected_data[NCOMMWEATH].value.i = WEATHER_PROHIBITED;
else collected_data[NCOMMWEATH].value.i = curlevel; else collected_data[NCOMMWEATH].value.i = curlevel;
if(collected_data[NLASTAHTUNG].value.i < curahtungtime) collected_data[NLASTAHTUNG].value.i = curahtungtime; if(collected_data[NLASTAHTUNG].value.i < curahtungtime) collected_data[NLASTAHTUNG].value.i = curahtungtime;
collected_data[NCOMMWEATH].time = curtime; collected_data[NCOMMWEATH].time = curtime;
collected_data[NLASTAHTUNG].time = curtime; collected_data[NLASTAHTUNG].time = curtime;
pthread_mutex_unlock(&datamutex); pthread_mutex_unlock(&datamutex);
DBG("Refreshed"); //DBG("Refreshed");
} }
void forbid_observations(int f){ void forbid_observations(int f){

View File

@@ -43,7 +43,7 @@ static void *mainthread(void *s){
sensordata_t *sensor = (sensordata_t *)s; 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)
@@ -52,7 +52,7 @@ static void *mainthread(void *s){
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);

View File

@@ -85,9 +85,10 @@ void *open_plugin(const char *name){
* @param station - pointer to N'th station opened * @param station - pointer to N'th station opened
*/ */
static void dumpsensors(struct sensordata_t* station){ static void dumpsensors(struct sensordata_t* station){
FNAME(); //FNAME();
if(!station || !station->get_value || station->Nvalues < 1) return; if(!station || !station->get_value || station->Nvalues < 1) return;
refresh_sensval(station); refresh_sensval(station);
#if 0
DBG("New values..."); DBG("New values...");
#ifdef EBUG #ifdef EBUG
char buf[FULL_LEN+1]; char buf[FULL_LEN+1];
@@ -110,6 +111,7 @@ static void dumpsensors(struct sensordata_t* station){
} }
} }
#endif #endif
#endif
} }
/** /**
@@ -164,25 +166,7 @@ void closeplugins(){
nplugins = 0; nplugins = 0;
} }
/** static const char* const NM[IS_OTHER] = { // names of standard fields
* @brief format_sensval - snprintf sensor's value into buffer
* @param v - value to get
* @param buf - buffer
* @param buflen - full length of `buf`
* @param Np - if Np>-1, show it as plugin number (added to field name in square brackets, like WIND[1]);
* @return amount of symbols printed or -1 if error
*/
int format_sensval(const val_t *v, char *buf, int buflen, int Np){
--buflen; // for trailing zero
if(!v || !buf || buflen < FULL_LEN) return -1;
char strval[VAL_LEN+1];
switch(v->type){
case VALT_UINT: snprintf(strval, VAL_LEN, "%u", v->value.u); break;
case VALT_INT: snprintf(strval, VAL_LEN, "%d", v->value.i); break;
case VALT_FLOAT: snprintf(strval, VAL_LEN, "%.2f", v->value.f); break;
default: sprintf(strval, "'ERROR'");
}
const char* const NM[IS_OTHER] = { // names of standard fields
[IS_WIND] = "WIND", [IS_WIND] = "WIND",
[IS_WINDDIR] = "WINDDIR", [IS_WINDDIR] = "WINDDIR",
[IS_HUMIDITY] = "HUMIDITY", [IS_HUMIDITY] = "HUMIDITY",
@@ -196,6 +180,35 @@ int format_sensval(const val_t *v, char *buf, int buflen, int Np){
[IS_CLOUDS] = "CLOUDS", [IS_CLOUDS] = "CLOUDS",
[IS_SKYTEMP] = "SKYTEMP" [IS_SKYTEMP] = "SKYTEMP"
}; };
// format "sense" of sensor, like "[WIND][1]=2"
int format_senssense(const val_t *v, char *buf, int buflen, int Np){
if(!v || !buf || buflen < 1) return -1;
int idx = v->meaning;
const char *name = (idx < IS_OTHER) ? NM[idx] : v->name;
int got;
if(Np > -1) got = snprintf(buf, buflen, "[%s][%d]=%d", name, Np, v->sense);
else got = snprintf(buf, buflen, "[%s]=%d", name, v->sense);
return (got < buflen) ? got : buflen; // full or truncated
}
/**
* @brief format_sensval - snprintf sensor's value into buffer
* @param v - value to get
* @param buf - buffer
* @param buflen - full length of `buf`
* @param Np - if Np>-1, show it as plugin number (added to field name in square brackets, like WIND[1]);
* @return amount of symbols printed or -1 if error
*/
int format_sensval(const val_t *v, char *buf, int buflen, int Np){
if(!v || !buf || buflen < 1) return -1;
char strval[VAL_LEN+1];
switch(v->type){
case VALT_UINT: snprintf(strval, VAL_LEN, "%u", v->value.u); break;
case VALT_INT: snprintf(strval, VAL_LEN, "%d", v->value.i); break;
case VALT_FLOAT: snprintf(strval, VAL_LEN, "%.2f", v->value.f); break;
default: sprintf(strval, "'ERROR'");
}
const char* const CMT[IS_OTHER] = { // comments for standard fields const char* const CMT[IS_OTHER] = { // comments for standard fields
[IS_WIND] = "Wind, m/s", [IS_WIND] = "Wind, m/s",
[IS_WINDDIR] = "Instant wind direction, degr (CW from north to FROM)", [IS_WINDDIR] = "Instant wind direction, degr (CW from north to FROM)",
@@ -222,15 +235,47 @@ int format_sensval(const val_t *v, char *buf, int buflen, int Np){
int got; int got;
if(Np > -1) got = snprintf(buf, buflen, "%s[%d]=%s / %s", name, Np, strval, comment); if(Np > -1) got = snprintf(buf, buflen, "%s[%d]=%s / %s", name, Np, strval, comment);
else got = snprintf(buf, buflen, "%s=%s / %s", name, strval, comment); else got = snprintf(buf, buflen, "%s=%s / %s", name, strval, comment);
return got; return (got < buflen) ? got : buflen; // full or truncated
} }
// the same for measurement time formatting // the same for measurement time formatting
int format_msrmttm(time_t t, char *buf, int buflen){ int format_msrmttm(time_t t, char *buf, int buflen){
--buflen; // for trailing zero if(!buf || buflen < 1) return -1;
if(!buf || buflen < FULL_LEN) return -1;
char cmt[COMMENT_LEN+1]; char cmt[COMMENT_LEN+1];
struct tm *T = localtime(&t); struct tm *T = localtime(&t);
strftime(cmt, COMMENT_LEN, "%F %T", T); strftime(cmt, COMMENT_LEN, "%F %T", T);
return snprintf(buf, buflen, "TMEAS=%zd / Last measurement time: %s", t, cmt); int got = snprintf(buf, buflen, "TMEAS=%zd / Last measurement time: %s", t, cmt);
return (got < buflen) ? got : buflen;
}
// find sensor's value by its name; @return index or -1 if not found
int find_val_by_name(sensordata_t *s, const char *name){
if(!s || !name) return -1;
if(s->Nvalues < 1) return -1;
// check standard "meaning"
valmeaning_t mnng = 0;
for(; mnng < IS_OTHER; ++mnng){
if(0 == strcmp(NM[mnng], name)) break; // found in standard
}
for(int i = 0; i < s->Nvalues; ++i){
val_t val;
if(!s->get_value(s, &val, i) || val.meaning != mnng) continue;
if(mnng != IS_OTHER){ // found in standard values
return i;
}else{ // non-standard: check by name
if(0 == strcmp(val.name, name)){ // found by name
return i;
}
}
}
return -1; // not found
}
// chane 'sense' field of given meteostation for value with index=`idx`; @return FALSE if failed
int change_val_sense(sensordata_t *s, int idx, valsense_t sense){
if(!s || sense < 0 || sense >= VAL_AMOUNT) return FALSE;
int N = s->Nvalues;
if(idx < 0 || idx >= N) return FALSE;
s->values[idx].sense = sense;
return TRUE;
} }

View File

@@ -27,7 +27,10 @@ int openplugins(char **paths, int N);
void closeplugins(); void closeplugins();
sensordata_t *get_plugin(int N); sensordata_t *get_plugin(int N);
int get_nplugins(); int get_nplugins();
int find_val_by_name(sensordata_t *s, const char *name);
int format_senssense(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_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 change_val_sense(sensordata_t *s, int idx, valsense_t sense);
int set_pollT(time_t t); int set_pollT(time_t t);
time_t get_pollT(); time_t get_pollT();

View File

@@ -16,7 +16,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <ctype.h>
#include <signal.h> #include <signal.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <usefull_macros.h> #include <usefull_macros.h>
@@ -55,26 +57,34 @@ static sl_sock_hresult_e listhandler(sl_sock_t *client, _U_ sl_sock_hitem_t *ite
return RESULT_SILENCE; return RESULT_SILENCE;
} }
// get N'th plugin or send error message
sensordata_t *get_plugin_w_message(sl_sock_t *client, int N){
char buf[FULL_LEN];
sensordata_t *s = NULL;
if(!(s = get_plugin(N)) || (s->Nvalues < 1)){
snprintf(buf, FULL_LEN, "Can't get plugin[%d]\n", N);
sl_sock_sendstrmessage(client, buf);
return NULL;
}
return s;
}
/** /**
* @brief showdataN - send to user meteodata of Nth station * @brief showdataN - send to user meteodata of Nth station
* @param client - client data * @param client - client data
* @param N - index of station * @param N - index of station
*/ */
static void showdataN(sl_sock_t *client, int N){ static void showdataN(sl_sock_t *client, int N){
char buf[FULL_LEN+1]; char buf[FULL_LEN];
val_t v; val_t v;
sensordata_t *s = NULL; sensordata_t *s = get_plugin_w_message(client, N);
if(!(s = get_plugin(N)) || (s->Nvalues < 1)){ if(!s) return;
snprintf(buf, FULL_LEN, "Can't get plugin[%d]\n", N);
sl_sock_sendstrmessage(client, buf);
return;
}
time_t oldest = time(NULL) - oldest_interval; time_t oldest = time(NULL) - oldest_interval;
uint64_t Tsum = 0; int nsum = 0; uint64_t Tsum = 0; int nsum = 0;
for(int i = 0; i < s->Nvalues; ++i){ for(int i = 0; i < s->Nvalues; ++i){
if(!s->get_value(s, &v, i)) continue; if(!s->get_value(s, &v, i)) continue;
if(v.time < oldest) continue; if(v.time < oldest) continue;
if(1 > format_sensval(&v, buf, FULL_LEN+1, N)) continue; if(1 > format_sensval(&v, buf, FULL_LEN, N)) continue;
DBG("formatted: '%s'", buf); DBG("formatted: '%s'", buf);
sl_sock_sendstrmessage(client, buf); sl_sock_sendstrmessage(client, buf);
sl_sock_sendbyte(client, '\n'); sl_sock_sendbyte(client, '\n');
@@ -82,7 +92,7 @@ static void showdataN(sl_sock_t *client, int N){
} }
if(nsum > 0){ if(nsum > 0){
oldest = (time_t)(Tsum / nsum); oldest = (time_t)(Tsum / nsum);
if(0 < format_msrmttm(oldest, buf, FULL_LEN+1)){ // send mean measuring time if(0 < format_msrmttm(oldest, buf, FULL_LEN)){ // send mean measuring time
DBG("Formatted time: '%s'", buf); DBG("Formatted time: '%s'", buf);
sl_sock_sendstrmessage(client, buf); sl_sock_sendstrmessage(client, buf);
sl_sock_sendbyte(client, '\n'); sl_sock_sendbyte(client, '\n');
@@ -95,7 +105,7 @@ static void showdataN(sl_sock_t *client, int N){
* @param client - client data * @param client - client data
*/ */
static void showdata(sl_sock_t *client){ static void showdata(sl_sock_t *client){
char buf[FULL_LEN+1]; char buf[FULL_LEN];
val_t v; val_t v;
int Ncoll = collected_amount(); int Ncoll = collected_amount();
time_t oldest = time(NULL) - oldest_interval; time_t oldest = time(NULL) - oldest_interval;
@@ -103,15 +113,16 @@ static void showdata(sl_sock_t *client){
for(int i = 0; i < Ncoll; ++i){ for(int i = 0; i < Ncoll; ++i){
if(!get_collected(&v, i)){ DBG("Can't get %dth value", i); continue; } if(!get_collected(&v, i)){ DBG("Can't get %dth value", i); continue; }
if(v.time < oldest){ DBG("%dth value is too old", i); continue; } if(v.time < oldest){ DBG("%dth value is too old", i); continue; }
if(1 > format_sensval(&v, buf, FULL_LEN+1, -1)){ DBG("Can't format"); continue; } if(1 > format_sensval(&v, buf, FULL_LEN, -1)){ DBG("Can't format"); continue; }
DBG("formatted: '%s'", buf); DBG("formatted: '%s'", buf);
sl_sock_sendstrmessage(client, buf); sl_sock_sendstrmessage(client, buf);
sl_sock_sendbyte(client, '\n'); sl_sock_sendbyte(client, '\n');
++nsum; Tsum += v.time; ++nsum; Tsum += v.time;
} }
DBG("nsum=%d", nsum);
if(nsum > 0){ if(nsum > 0){
oldest = (time_t)(Tsum / nsum); oldest = (time_t)(Tsum / nsum);
if(0 < format_msrmttm(oldest, buf, FULL_LEN+1)){ // send mean measuring time if(0 < format_msrmttm(oldest, buf, FULL_LEN)){ // send mean measuring time
DBG("Formatted time: '%s'", buf); DBG("Formatted time: '%s'", buf);
sl_sock_sendstrmessage(client, buf); sl_sock_sendstrmessage(client, buf);
sl_sock_sendbyte(client, '\n'); sl_sock_sendbyte(client, '\n');
@@ -134,6 +145,121 @@ static sl_sock_hresult_e gethandler(sl_sock_t *client, _U_ sl_sock_hitem_t *item
return RESULT_SILENCE; return RESULT_SILENCE;
} }
// get parameters' level
static sl_sock_hresult_e getlvlhandler(sl_sock_t *client, _U_ sl_sock_hitem_t *item, const char *req){
if(!client) if(!client) return RESULT_FAIL;
int N = get_nplugins();
if(N < 1) return RESULT_FAIL;
val_t v;
char buf[FULL_LEN];
if(!req){ // level of collected parameters
DBG("User asks for collected");
int Ncoll = collected_amount();
if(Ncoll < 1) return RESULT_FAIL;
for(int i = 0; i < Ncoll; ++i){
if(!get_collected(&v, i)){ DBG("Can't get %dth value", i); continue; }
if(1 > format_senssense(&v, buf, FULL_LEN, -1)){ DBG("Can't format"); continue; }
sl_sock_sendstrmessage(client, buf);
sl_sock_sendbyte(client, '\n');
}
}else{
int n;
if(!sl_str2i(&n, req) || n < 0 || n >= N) return RESULT_BADVAL;
DBG("User asks for %d", n);
sensordata_t *s = get_plugin_w_message(client, n);
if(!s) return RESULT_SILENCE;
for(int i = 0; i < s->Nvalues; ++i){
if(!s->get_value(s, &v, i)) continue;
if(1 > format_senssense(&v, buf, FULL_LEN, n)){ DBG("Can't format"); continue; }
sl_sock_sendstrmessage(client, buf);
sl_sock_sendbyte(client, '\n');
}
}
return RESULT_SILENCE;
}
// set parameters' level; format: setlevel=N1:par=level,...,N1:par=level...
// Nx - "station" number, par - parameter name (like "HUMIDITY"), level - new level (0..3)
static sl_sock_hresult_e setlvlhandler(sl_sock_t *client, _U_ sl_sock_hitem_t *item, const char *req){
if(!client) if(!client) return RESULT_FAIL;
int N = get_nplugins();
if(N < 1) return RESULT_FAIL;
if(!req) return RESULT_BADVAL;
sl_sock_hresult_e result = RESULT_OK;
char *s = (char *)req;
while(*s){
while (isspace(*s) || *s == ',') s++;
if (!*s) break;
// get station number
char *end;
long st_num = strtol(s, &end, 10);
if(s == end) break;
s = end;
// wait for ':'
while (isspace(*s)) s++;
if(*s != ':') break;
++s;
DBG("ST %ld:\n", st_num);
sensordata_t *sd;
if(st_num < 0 || st_num >= N || !(sd = get_plugin((int)st_num))){
result = RESULT_BADVAL;
break;
}
while(1){
while(isspace(*s)) ++s;
if(*s == '\0') break;
if(isdigit((unsigned char)*s)) break; // - next block
const char *par_start = s;
while(isalnum(*s)) ++s;
if(par_start == s) break;
int l = (int)(s - par_start);
DBG(" par: %.*s = ", l, par_start);
if(l > KEY_LEN){
result = RESULT_BADVAL;
break;
}
char buf[KEY_LEN + 1];
memcpy(buf, par_start, l);
buf[l] = 0;
int validx = find_val_by_name(sd, buf);
// search for given value
if(validx < 0){
result = RESULT_BADVAL;
break;
}
while(isspace(*s)) ++s;
// fait for '='
if(*s != '='){
result = RESULT_BADVAL;
break;
}
++s;
// get new level
while(isspace(*s)) s++;
long val = strtol(s, &end, 10);
if(s == end){
result = RESULT_BADVAL;
break;
}
s = end;
DBG("%ld\n", val);
if(!change_val_sense(sd, validx, (valsense_t)val)){
result = RESULT_BADVAL;
break;
}
while(isspace((unsigned char)*s)) s++;
if(*s == ','){ // omit delimeter
++s;
continue;
}else{
if(*s == ';') ++s; // omit ; as block's end
break;
}
}
}
return result;
}
// graceful closing socket: let client know that he's told to fuck off // graceful closing socket: let client know that he's told to fuck off
static void toomuch(int fd){ static void toomuch(int fd){
const char *m = "Try later: too much clients connected\n"; const char *m = "Try later: too much clients connected\n";
@@ -173,6 +299,7 @@ static sl_sock_hresult_e defhandler(struct sl_sock *s, const char *str){
#define COMMONHANDLERS \ #define COMMONHANDLERS \
{gethandler, "get", "get all meteo or only for given plugin number", NULL}, \ {gethandler, "get", "get all meteo or only for given plugin number", NULL}, \
{getlvlhandler,"chklevel", "check 'sense level' of given plugin parameters", NULL}, \
{listhandler, "list", "show all opened plugins", NULL}, \ {listhandler, "list", "show all opened plugins", NULL}, \
{timehandler, "time", "get server's UNIX time", NULL}, {timehandler, "time", "get server's UNIX time", NULL},
@@ -182,6 +309,7 @@ static sl_sock_hitem_t nethandlers[] = { // net - only getters and client-only s
{NULL, NULL, NULL, NULL} {NULL, NULL, NULL, NULL}
}; };
static sl_sock_hitem_t localhandlers[] = { // local - full amount of setters/getters static sl_sock_hitem_t localhandlers[] = { // local - full amount of setters/getters
{setlvlhandler, "setlevel", "set 'sense level' (0..3) for given plugin parameters, e.g. setlevel=1:WIND=3,HUMIDITY=3 - disable fields for station 1", NULL},
COMMONHANDLERS COMMONHANDLERS
{NULL, NULL, NULL, NULL} {NULL, NULL, NULL, NULL}
}; };

View File

@@ -39,7 +39,8 @@ typedef enum{
VAL_OBLIGATORY, // can't be omitted VAL_OBLIGATORY, // can't be omitted
VAL_RECOMMENDED, // recommended to show VAL_RECOMMENDED, // recommended to show
VAL_UNNECESSARY, // may be shown by user request VAL_UNNECESSARY, // may be shown by user request
VAL_BROKEN // sensor is broken, omit it VAL_BROKEN, // sensor is broken, omit it
VAL_AMOUNT // amount of values
} valsense_t; } valsense_t;
// meaning of values // meaning of values