diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/Makefile b/Daemons/weatherdaemon_newmeteo.deprecated/Makefile new file mode 100644 index 0000000..1da0763 --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/Makefile @@ -0,0 +1,42 @@ +# run `make DEF=...` to add extra defines +PROGRAM := weatherdaemon +LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all -pthread +LDFLAGS += -flto `pkg-config --libs usefull_macros` -lm +SRCS := $(wildcard *.c) +DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111 +#DEFINES += -DEBUG +OBJDIR := mk +CFLAGS += `pkg-config --cflags usefull_macros` -O3 -Wall -Werror -Wextra -Wno-trampolines -flto +OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o)) +DEPS := $(OBJS:.o=.d) +CC = gcc + +all : $(OBJDIR) $(PROGRAM) + +$(PROGRAM) : $(OBJS) + @echo -e "\t\tLD $(PROGRAM)" + $(CC) $(OBJS) $(LDFLAGS) -o $(PROGRAM) + +$(OBJDIR): + mkdir $(OBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +-include $(DEPS) +endif + +$(OBJDIR)/%.o: %.c + @echo -e "\t\tCC $<" + $(CC) -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ $< + +clean: + @echo -e "\t\tCLEAN" + @rm -f $(OBJS) $(DEPS) + @rmdir $(OBJDIR) 2>/dev/null || true + +xclean: clean + @rm -f $(PROGRAM) + +gentags: + CFLAGS="$(CFLAGS) $(DEFINES)" geany -g $(PROGRAM).c.tags *[hc] 2>/dev/null + +.PHONY: gentags clean xclean diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/Readme.md b/Daemons/weatherdaemon_newmeteo.deprecated/Readme.md new file mode 100644 index 0000000..3df5edc --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/Readme.md @@ -0,0 +1,22 @@ +Weather daemon +================== + +Weather daemon for new meteostation. + +Open a socket at given port (default: 12345) +Parse weather data and send it to client + +``` +Usage: weatherdaemon [args] + + Where args are: + + -P, --pidfile=arg pidfile name (default: /tmp/weatherdaemon.pid) + -b, --baudrate=arg serial terminal baudrate (default: 9600) + -d, --device=arg serial device name (default: none) + -e, --emulation emulate serial device + -h, --help show this help + -l, --logfile=arg save logs to file (default: none) + -p, --port=arg network port to connect (default: 12345) + -v, --verb logfile verbocity level (each -v increase it) +``` diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/cmdlnopts.c b/Daemons/weatherdaemon_newmeteo.deprecated/cmdlnopts.c new file mode 100644 index 0000000..e9bf534 --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/cmdlnopts.c @@ -0,0 +1,92 @@ +/* + * This file is part of the weatherdaemon project. + * Copyright 2021 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include "cmdlnopts.h" + +/* + * here are global parameters initialisation + */ +int help; +static glob_pars G; + +// default values for Gdefault & help +#define DEFAULT_PORT "12345" +#define DEFAULT_PID "/tmp/weatherdaemon.pid" + +// DEFAULTS +// default global parameters +glob_pars const Gdefault = { + .device = NULL, + .port = DEFAULT_PORT, + .logfile = NULL, + .verb = 0, + .tty_speed = 9600, + .rest_pars = NULL, + .rest_pars_num = 0, + .emul = 0, + .pidfile = DEFAULT_PID +}; + +/* + * Define command line options by filling structure: + * name has_arg flag val type argptr help +*/ +myoption cmdlnopts[] = { +// common options + {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")}, + {"device", NEED_ARG, NULL, 'd', arg_string, APTR(&G.device), _("serial device name (default: none)")}, + {"port", NEED_ARG, NULL, 'p', arg_string, APTR(&G.port), _("network port to connect (default: " DEFAULT_PORT ")")}, + {"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), _("save logs to file (default: none)")}, + {"verb", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verb), _("logfile verbocity level (each -v increase it)")}, + {"baudrate",NEED_ARG, NULL, 'b', arg_int, APTR(&G.tty_speed), _("serial terminal baudrate (default: 9600)")}, + {"emulation",NO_ARGS, NULL, 'e', arg_int, APTR(&G.emul), _("emulate serial device")}, + {"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfile), _("pidfile name (default: " DEFAULT_PID ")")}, + end_option +}; + +/** + * Parse command line options and return dynamically allocated structure + * to global parameters + * @param argc - copy of argc from main + * @param argv - copy of argv from main + * @return allocated structure with global parameters + */ +glob_pars *parse_args(int argc, char **argv){ + int i; + void *ptr; + ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr); + // format of help: "Usage: progname [args]\n" + change_helpstring("Usage: %s [args]\n\n\tWhere args are:\n"); + // parse arguments + parseargs(&argc, &argv, cmdlnopts); + if(help) showhelp(-1, cmdlnopts); + if(argc > 0){ + G.rest_pars_num = argc; + G.rest_pars = calloc(argc, sizeof(char*)); + for (i = 0; i < argc; i++) + G.rest_pars[i] = strdup(argv[i]); + } + return &G; +} + diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/cmdlnopts.h b/Daemons/weatherdaemon_newmeteo.deprecated/cmdlnopts.h new file mode 100644 index 0000000..f4d5408 --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/cmdlnopts.h @@ -0,0 +1,44 @@ +/* + * This file is part of the weatherdaemon project. + * Copyright 2021 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + + +#pragma once +#ifndef __CMDLNOPTS_H__ +#define __CMDLNOPTS_H__ + +/* + * here are some typedef's for global data + */ +typedef struct{ + char *device; // serial device name + char *port; // port to connect + char *logfile; // logfile name + int terminal; // run as terminal + int echo; // echo user commands back + int verb; // verbocity level + int tty_speed; // serial terminal baudrate + int emul; // emulation of serial device + char *pidfile; // pidfile name + int rest_pars_num; // number of rest parameters + char** rest_pars; // the rest parameters: array of char* (path to logfile and thrash) +} glob_pars; + + +glob_pars *parse_args(int argc, char **argv); +#endif // __CMDLNOPTS_H__ diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/main.c b/Daemons/weatherdaemon_newmeteo.deprecated/main.c new file mode 100644 index 0000000..288c5e9 --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/main.c @@ -0,0 +1,88 @@ +/* + * This file is part of the weatherdaemon project. + * Copyright 2021 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include // wait +#include //prctl +#include + +#include "cmdlnopts.h" +#include "socket.h" +#include "term.h" + +glob_pars *GP; + +void signals(int signo){ + restore_console(); + stop_tty(); + LOGERR("exit with status %d", signo); + exit(signo); +} + +int main(int argc, char **argv){ + initial_setup(); + signal(SIGTERM, signals); // kill (-15) - quit + signal(SIGHUP, SIG_IGN); // hup - ignore + signal(SIGINT, signals); // ctrl+C - quit + signal(SIGQUIT, signals); // ctrl+\ - quit + signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z +#ifndef EBUG + char *self = strdup(argv[0]); +#endif + GP = parse_args(argc, argv); + if(GP->logfile){ + sl_loglevel lvl = LOGLEVEL_ERR; + for(; GP->verb && lvl < LOGLEVEL_ANY; --GP->verb) ++lvl; + DBG("Loglevel: %d", lvl); + if(!OPENLOG(GP->logfile, lvl, 1)) ERRX("Can't open log file"); + LOGERR("Started"); + } + #ifndef EBUG + if(daemon(1, 0)){ + ERR("daemon()"); + } + check4running(self, GP->pidfile); + while(1){ // guard for dead processes + pid_t 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 + + if(GP->device) if(!try_connect(GP->device, GP->tty_speed)){ + LOGERR("Can't connect to device"); + ERRX("Can't connect to device"); + } + if(!GP->device && !GP->emul){ + LOGERR("Need serial device name or emulation flag"); + ERRX("Need serial device name or emulation flag"); + } + daemonize(GP->port); + return 0; +} diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/socket.c b/Daemons/weatherdaemon_newmeteo.deprecated/socket.c new file mode 100644 index 0000000..e254ccb --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/socket.c @@ -0,0 +1,407 @@ +/* + * This file is part of the weatherdaemon project. + * Copyright 2021 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include // addrinfo +#include // inet_ntop +#include // INT_xxx +#include // poll +#include +#include // pthread_kill +#include +#include +#include // syscall +#include // daemon +#include + +#include "cmdlnopts.h" // glob_pars +#include "socket.h" +#include "stat.h" +#include "term.h" + +// temporary buffers +#define BUFLEN (1024) +// Max amount of connections +#define BACKLOG (30) + +extern glob_pars *GP; + +static weather_t lastweather = {0}; +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static weatherstat_t wstat; + +typedef enum{ + FORMAT_ERROR, // send user an error message + FORMAT_CURDFULL, // param=value for current data + FORMAT_CURDSHORT, // comma-separated current data + FORMAT_STATFULL, // param=value for stat + FORMAT_STATSHORT // comma-separated stat +} format_t; + + + +/**************** SERVER FUNCTIONS ****************/ +/** + * Send data over socket + * @param sock - socket fd + * @param webquery - ==1 if this is web query + * @param format - data format + * @return 1 if all OK + */ +static int send_data(int sock, int webquery, format_t format){ + char tbuf[BUFSIZ]; // buffer to send + char databuf[BUFSIZ]; // buffer with data + ssize_t Len = 0; + const char *eol = webquery ? "\r\n" : "\n"; + // fill buffer with data + pthread_mutex_lock(&mutex); + if(format == FORMAT_CURDFULL){ // full format + Len = snprintf(databuf, BUFSIZ, + "Wind=%.1f%sDir=%.1f%sPressure=%.1f%sTemperature=%.1f%sHumidity=%.1f%s" + "Rain=%.1f%sTime=%.3f%s", + lastweather.windspeed, eol, lastweather.winddir, eol, lastweather.pressure, eol, + lastweather.temperature, eol, lastweather.humidity, eol, lastweather.rainfall, eol, + lastweather.tmeasure, eol + ); + }else if(format == FORMAT_CURDSHORT){ // short format + Len = snprintf(databuf, BUFSIZ, + "%.3f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f%s", + lastweather.tmeasure, lastweather.windspeed, lastweather.winddir, + lastweather.pressure, lastweather.temperature, lastweather.humidity, + lastweather.rainfall, eol + ); + }else if(format == FORMAT_STATFULL){ + char *ptr = databuf; + int l = BUFSIZ; +#define PRSTAT(field, nm) do{register int lc = snprintf(ptr, l, \ + nm "max=%.1f%s" nm "min=%.1f%s" nm "mean=%.1f%s" nm "rms=%.1f%s", \ + wstat.field.max, eol, wstat.field.min, eol, wstat.field.mean, eol, wstat.field.rms, eol); \ + Len += lc; l -= lc; ptr += lc;}while(0) + PRSTAT(windspeed, "Wind"); + PRSTAT(winddir, "Dir"); + PRSTAT(pressure, "Pressure"); + PRSTAT(temperature, "Temperature"); + PRSTAT(humidity, "Humidity"); + PRSTAT(rainfall, "Rain"); + PRSTAT(tmeasure, "Time"); +#undef PRSTAT + }else if(format == FORMAT_STATSHORT){ + char *ptr = databuf; + int l = BUFSIZ; + register int lc; +#define PRSTAT(field, nm) do{lc = snprintf(ptr, l, \ + "%.1f,%.1f,%.1f,%.1f", \ + wstat.field.max, wstat.field.min, wstat.field.mean, wstat.field.rms); \ + Len += lc; l -= lc; ptr += lc;}while(0) +#define COMMA() do{lc = snprintf(ptr, l, ","); Len += lc; l -= lc; ptr += lc;}while(0) + PRSTAT(windspeed, "Wind"); COMMA(); + PRSTAT(winddir, "Dir"); COMMA(); + PRSTAT(pressure, "Pressure"); COMMA(); + PRSTAT(temperature, "Temperature"); COMMA(); + PRSTAT(humidity, "Humidity"); COMMA(); + PRSTAT(rainfall, "Rain"); COMMA(); + PRSTAT(tmeasure, "Time"); + Len += snprintf(ptr, l, "%s", eol); +#undef PRSTAT + }else{ + Len = snprintf(databuf, BUFSIZ, "Error!"); + } + pthread_mutex_unlock(&mutex); + // OK buffer ready, prepare to send it + if(webquery){ + ssize_t L = snprintf(tbuf, BUFSIZ, + "HTTP/2.0 200 OK\r\n" + "Access-Control-Allow-Origin: *\r\n" + "Access-Control-Allow-Methods: GET, POST\r\n" + "Access-Control-Allow-Credentials: true\r\n" + "Content-type: text/plain\r\nContent-Length: %zd\r\n\r\n", Len); + if(L < 0){ + WARN("sprintf()"); + LOGWARN("sprintf()"); + return FALSE; + } + if(L != write(sock, tbuf, L)){ + LOGWARN("Can't write header"); + WARN("write"); + return FALSE; + } + } + if(Len != write(sock, databuf, Len)){ + WARN("write()"); + LOGERR("send_data(): write() failed"); + return FALSE; + } + //LOGDBG("fd %d, write %s", sock, textbuf); + return TRUE; +} + +// search a first word after needle without spaces +static char* stringscan(char *str, char *needle){ + char *a;//, *e; + char *end = str + strlen(str); + a = strstr(str, needle); + if(!a) return NULL; + a += strlen(needle); + while (a < end && (*a == ' ' || *a == '\r' || *a == '\t' || *a == '\r')) a++; + if(a >= end) return NULL; + return a; +} + +static double getpar(const char *s){ + double x = -1.; + char *eptr = NULL; + while(*s && *s <= ' ') ++s; + x = strtod(s, &eptr); + if(eptr == s) x = -1.; + return x; +} + +/** + * @brief handle_socket - read information from socket + * @param sock - socket fd + * @param chkheader - ==1 on first run + * @return 1 if socket closed + */ +static int handle_socket(int sock){ + FNAME(); + int webquery = 0; // whether query is web or regular + char buff[BUFLEN]; + ssize_t rd; + if(!(rd = read(sock, buff, BUFLEN-1))){ + //LOGMSG("Client %d closed", sock); + return 1; + } + //LOG("client send %zd bytes", rd); + DBG("Got %zd bytes", rd); + if(rd <= 0){ // error + LOGWARN("Client %d close socket on error", sock); + DBG("Nothing to read from fd %d (ret: %zd)", sock, rd); + return 1; + } + // add trailing zero to be on the safe side + buff[rd] = 0; + // now we should check what do user want + char *found = buff; + DBG("user send: %s", buff); + format_t format = FORMAT_CURDFULL; // text format - default for web-queries + + if(0 == strncmp(buff, "GET", 3)){ + DBG("GET, buff=%s", buff); + // GET web query have format GET /some.resource + webquery = 1; + char *slash = strchr(buff, '/'); + if(slash){ + found = slash + 1; + if(strncmp(found, "stat", 4) == 0){ + format = FORMAT_STATFULL; + double dt = getpar(found + 4); + if(dt < 1.) dt = 900.; + if(stat_for(dt, &wstat) < 1.) format = FORMAT_ERROR; + } + char *eol = strstr(found, "HTTP"); + if(eol) *eol = 0; + } + }else if(0 == strncmp(buff, "POST", 4)){ + DBG("POST"); + webquery = 1; + // search content length of POST query + char *cl = stringscan(buff, "Content-Length:"); + if(cl){ + int contlen = atoi(cl); + int l = strlen(buff); + if(contlen && l > contlen) found = &buff[l - contlen]; + } + }else if(0 == strncmp(buff, "simple", 6)) format = FORMAT_CURDSHORT; // simple format + else if(0 == strncmp(buff, "stat", 4)){ // show stat + double dt = -1.; int l = 4; + if(0 == strncmp(buff, "statsimple", 10)){ + l = 10; format = FORMAT_STATSHORT; + }else format = FORMAT_STATFULL; + dt = getpar(buff + l); + if(dt < 1.) dt = 900.; // 15 minutes - default + if(stat_for(dt, &wstat) < 1.) format = FORMAT_ERROR; + } + + // here we can process user data + DBG("found=%s", found); + LOGDBG("sockfd=%d, got %s", sock, buff); + DBG("format=%d", format); + send_data(sock, webquery, format); + if(webquery) return 1; // close web query after message processing + return 0; +} + +// main socket server +static void *server(void *asock){ + LOGMSG("server()"); + int sock = *((int*)asock); + if(listen(sock, BACKLOG) == -1){ + LOGERR("listen() failed"); + WARN("listen"); + return NULL; + } + int nfd = 1; // current fd amount in poll_set + struct pollfd poll_set[MAX_FDS]; + memset(poll_set, 0, sizeof(poll_set)); + poll_set[0].fd = sock; + poll_set[0].events = POLLIN; + while(1){ + poll(poll_set, nfd, 1); // poll for 1ms + for(int fdidx = 0; fdidx < nfd; ++fdidx){ // poll opened FDs + if((poll_set[fdidx].revents & POLLIN) == 0) continue; + poll_set[fdidx].revents = 0; + if(fdidx){ // client + int fd = poll_set[fdidx].fd; + if(handle_socket(fd)){ // socket closed - remove it from list + close(fd); + DBG("Client with fd %d closed", fd); + LOGMSG("Client %d disconnected", fd); + // move last to free space + poll_set[fdidx] = poll_set[nfd - 1]; + --nfd; + } + }else{ // server + socklen_t size = sizeof(struct sockaddr_in); + struct sockaddr_in their_addr; + int newsock = accept(sock, (struct sockaddr*)&their_addr, &size); + if(newsock <= 0){ + LOGERR("server(): accept() failed"); + WARN("accept()"); + continue; + } + struct in_addr ipAddr = their_addr.sin_addr; + char str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &ipAddr, str, INET_ADDRSTRLEN); + DBG("Connection from %s, give fd=%d", str, newsock); + LOGMSG("Got connection from %s, fd=%d", str, newsock); + if(nfd == MAX_FDS){ + LOGWARN("Max amount of connections: disconnect %s (%d)", str, newsock); + int _U_ x = write(newsock, "Max amount of connections reached!\n", 35); + WARNX("Limit of connections reached"); + close(newsock); + }else{ + memset(&poll_set[nfd], 0, sizeof(struct pollfd)); + poll_set[nfd].fd = newsock; + poll_set[nfd].events = POLLIN; + ++nfd; + } + } + } // endfor + } // endwhile(1) + LOGERR("server(): UNREACHABLE CODE REACHED!"); +} + +// poll last weather data +static void *weatherpolling(_U_ void *notused){ + while(1){ + weather_t w; + if(getlastweather(&w) && 0 == pthread_mutex_lock(&mutex)){ + memcpy(&lastweather, &w, sizeof(weather_t)); + addtobuf(&w); + pthread_mutex_unlock(&mutex); + } + usleep(100000); // not more than 10 messages per second + } + return NULL; +} + +// data gathering & socket management +static void daemon_(int sock){ + if(sock < 0) return; + pthread_t sock_thread, parser_thread; + if(pthread_create(&sock_thread, NULL, server, (void*) &sock)){ + LOGERR("daemon_(): pthread_create(sock_thread) failed"); + ERR("pthread_create()"); + } + if(pthread_create(&parser_thread, NULL, weatherpolling, NULL)){ + LOGERR("daemon_(): pthread_create(parser_thread) failed"); + ERR("pthread_create()"); + } + do{ + if(pthread_kill(sock_thread, 0) == ESRCH){ // died + WARNX("Sockets thread died"); + LOGWARN("Sockets thread died"); + pthread_join(sock_thread, NULL); + if(pthread_create(&sock_thread, NULL, server, (void*) &sock)){ + LOGERR("daemon_(): new pthread_create() failed"); + ERR("pthread_create()"); + } + } + if(pthread_kill(parser_thread, 0) == ESRCH){ // died + WARNX("TTY thread died"); + LOGWARN("TTY thread died"); + pthread_join(parser_thread, NULL); + if(pthread_create(&parser_thread, NULL, weatherpolling, NULL)){ + LOGERR("daemon_(): new pthread_create(parser_thread) failed"); + ERR("pthread_create()"); + } + } + usleep(1000); // sleep a little + }while(1); + LOGERR("daemon_(): UNREACHABLE CODE REACHED!"); +} + +/** + * Run daemon service + */ +void daemonize(char *port){ + FNAME(); + int sock = -1; + struct addrinfo hints, *res, *p; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + // To accept only local sockets replace NULL with "127.0.0.1" and remove AI_PASSIVE + if(getaddrinfo(NULL, port, &hints, &res) != 0){ + LOGERR("getaddrinfo"); + ERR("getaddrinfo"); + } + struct sockaddr_in *ia = (struct sockaddr_in*)res->ai_addr; + char str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &(ia->sin_addr), str, INET_ADDRSTRLEN); + // loop through all the results and bind to the first we can + for(p = res; p != NULL; p = p->ai_next){ + if((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){ + WARN("socket"); + continue; + } + int reuseaddr = 1; + if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1){ + LOGERR("setsockopt() error"); + ERR("setsockopt"); + } + if(bind(sock, p->ai_addr, p->ai_addrlen) == -1){ + close(sock); + WARN("bind"); + LOGWARN("bind() error"); + continue; + } + break; // if we get here, we have a successfull connection + } + if(p == NULL){ + LOGERR("daemonize(): failed to bind socket, exit"); + // looped off the end of the list with no successful bind + ERRX("failed to bind socket"); + } + freeaddrinfo(res); + daemon_(sock); + close(sock); + LOGERR("daemonize(): UNREACHABLE CODE REACHED!"); + signals(0); +} + diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/socket.h b/Daemons/weatherdaemon_newmeteo.deprecated/socket.h new file mode 100644 index 0000000..189aabc --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/socket.h @@ -0,0 +1,32 @@ +/* + * This file is part of the weatherdaemon project. + * Copyright 2021 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#ifndef __SOCKET_H__ +#define __SOCKET_H__ + +// time interval for data polling (seconds) +#define T_INTERVAL (10.) +// max amount of opened fd (+1 for server socket) +#define MAX_FDS (11) +// no data timeout +#define NODATA_TMOUT (90.) + +void daemonize(char *port); + +#endif // __SOCKET_H__ diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/stat.c b/Daemons/weatherdaemon_newmeteo.deprecated/stat.c new file mode 100644 index 0000000..cb2a2e8 --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/stat.c @@ -0,0 +1,153 @@ +/* + * This file is part of the weatherdaemon project. + * Copyright 2023 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "stat.h" + +// add BUFSZ_INCR records to buffer each time when no free space left +#define BUFSZ_INCR (2048) +static weather_t *buf = NULL; +// current size of `buf` +static size_t buflen = 0; +// indexes of first and last element in buffer +static size_t firstidx = 0, lastidx = 0; +// maximal current time delta between last and first items of `buf` +static double tdiffmax = 0.; + +// mutex for working with buffer +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +double get_tmax(){ + return tdiffmax; +} + +// add new record to buffer +void addtobuf(weather_t *record){ + if(!record) return; + pthread_mutex_lock(&mutex); + if(!buf){ // first run + buf = MALLOC(weather_t, BUFSZ_INCR); + buflen = BUFSZ_INCR; + memcpy(&buf[0], record, sizeof(weather_t)); + //DBG("init buff to %zd", buflen); + pthread_mutex_unlock(&mutex); + return; + } + if(++lastidx == buflen){ // end of buffer reached: decide wether to increase buffer or not + if(tdiffmax < STATMAXT){ + buflen += BUFSZ_INCR; + buf = realloc(buf, sizeof(weather_t)*buflen); + DBG("realloc buf to %zd", buflen); + }else lastidx = 0; + } + if(lastidx == firstidx){ + if(++firstidx == buflen) firstidx = 0; + } + memcpy(&buf[lastidx], record, sizeof(weather_t)); + tdiffmax = buf[lastidx].tmeasure - buf[firstidx].tmeasure; + //DBG("add record, last=%zd, first=%zd, dT=%.3f", lastidx, firstidx, tdiffmax); + pthread_mutex_unlock(&mutex); +} + + + +// get statistics for last `Tsec` seconds; @return real dT for given interval +double stat_for(double Tsec, weatherstat_t *wstat){ + if(!wstat || Tsec < 1.) return 0.; + double dt = 0., tlast = buf[lastidx].tmeasure; + size_t startfrom = lastidx; + pthread_mutex_lock(&mutex); + if(tdiffmax <= Tsec){ // use all data + startfrom = firstidx; + dt = tdiffmax; + }else while(dt < Tsec && startfrom != firstidx){ // search from which index we should start + if(startfrom == 0) startfrom = buflen - 1; + else --startfrom; + dt = tlast - buf[startfrom].tmeasure; + } + DBG("got indexes: start=%zd, end=%zd, dt=%.2f (dtiffmax=%.1f)", startfrom, lastidx, dt, tdiffmax); + weather_t min, max, sum = {0}, sum2 = {0}; + size_t amount = 0; + memcpy(&min, &buf[lastidx], sizeof(weather_t)); + memcpy(&max, &buf[lastidx], sizeof(weather_t)); + min.tmeasure = buf[startfrom].tmeasure; + max.tmeasure = buf[lastidx].tmeasure; + double tmean = (max.tmeasure+min.tmeasure)/2.; +// DBG("tmean=%.1f, min=%.1f, max=%.1f", tmean, min.tmeasure, max.tmeasure); + size_t st = startfrom; + // calculate amount of north and south winds + size_t north = 0, south = 0; + while(st != lastidx){ + double w = buf[st].winddir; + if(w < 90. || w > 270.) ++north; + else ++south; + if(++st == buflen) st = 0; + } + int minus = 0; + if(north > 2*south) minus = 1; + while(startfrom != lastidx){ + weather_t *curw = &buf[startfrom]; +#define CHK(field) do{register double d = curw->field; if(d > max.field) max.field = d; if(d < min.field) min.field = d; \ + sum.field += d; sum2.field += d*d;}while(0) + CHK(windspeed); + register double dir = curw->winddir, s = curw->windspeed; + if(minus && dir > 180.) dir -= 360.; + register double sd = dir * s; // weighted sum + if(dir > max.winddir) max.winddir = dir; + if(dir < min.winddir) min.winddir = dir; + sum.winddir += sd; + sum2.winddir += sd * dir; // v * dir^2 + CHK(pressure); + CHK(temperature); + CHK(humidity); + CHK(rainfall); + s = curw->tmeasure; + sum.tmeasure += s; s -= tmean; + sum2.tmeasure += s * s; // correct tmeasure by mean time to exclude cumulative error of double +#undef CHK + ++amount; + if(++startfrom == buflen) startfrom = 0; + } + // DBG("Got %zd records; tsum/sum2: %g/%g", amount, sum.tmeasure, sum2.tmeasure); + wstat->winddir.max = max.winddir; + wstat->winddir.min = min.winddir; + register double s2 = sum2.winddir / sum.windspeed, s = sum.winddir / sum.windspeed; + wstat->winddir.mean = s; + wstat->winddir.rms = sqrt(s2 - s*s); +// correct RMS by float epsilon to exclude negative values when near zero +#define STAT(field) do{ register double ms = sum.field/amount, ms2 = sum2.field/amount; \ + wstat->field.min = min.field; wstat->field.max = max.field; wstat->field.mean = ms; \ + wstat->field.rms = sqrt(ms2 - ms*ms + __FLT_EPSILON__); }while(0) + STAT(windspeed); + STAT(pressure); + STAT(temperature); + STAT(humidity); + STAT(rainfall); + wstat->tmeasure.max = max.tmeasure; + wstat->tmeasure.min = min.tmeasure; + wstat->tmeasure.mean = sum.tmeasure/amount; + wstat->tmeasure.rms = sqrt(sum2.tmeasure/amount); // sum2.tmeasure is already corrected by mean + // DBG("tmean=%.1f, min=%.1f, max=%.1f", wstat->tmeasure.mean, wstat->tmeasure.min, wstat->tmeasure.max); + pthread_mutex_unlock(&mutex); + return dt; +} diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/stat.h b/Daemons/weatherdaemon_newmeteo.deprecated/stat.h new file mode 100644 index 0000000..85d4ac5 --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/stat.h @@ -0,0 +1,46 @@ +/* + * This file is part of the weatherdaemon project. + * Copyright 2023 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "term.h" + +// maximal time difference for records in statbuf - one hour +#define STATMAXT (3600.) + +// main statistical data +typedef struct{ + double min; + double max; + double mean; + double rms; +} stat_t; + +typedef struct{ + stat_t windspeed; + stat_t winddir; + stat_t pressure; + stat_t temperature; + stat_t humidity; + stat_t rainfall; + stat_t tmeasure; +} weatherstat_t; + +double stat_for(double Tsec, weatherstat_t *wstat); +void addtobuf(weather_t *record); +double get_tmax(); diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/term.c b/Daemons/weatherdaemon_newmeteo.deprecated/term.c new file mode 100644 index 0000000..c3e4bcc --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/term.c @@ -0,0 +1,156 @@ +/* + * This file is part of the weatherdaemon project. + * Copyright 2021 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include // isspace +#include +#include +#include // strncasecmp +#include // time(NULL) +#include // INT_MAX, INT_MIN +#include + +#include "cmdlnopts.h" +#include "term.h" + +#define BUFLEN (4096) + +static TTY_descr *ttydescr = NULL; +extern glob_pars *GP; + +static char buf[BUFLEN]; +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"; + +/** + * read strings from terminal (ending with '\n') with timeout + * @return NULL if nothing was read or pointer to static buffer + */ +static char *read_string(){ + //static int done = 0; + if(GP->emul){ + strncpy(buf, emultemplate, BUFLEN); + return buf; + } + if(!ttydescr) ERRX("Serial device not initialized"); + size_t r = 0, l; + int LL = BUFLEN - 1; + char *ptr = buf; + double d0 = dtime(); + do{ + if((l = read_tty(ttydescr))){ + strncpy(ptr, ttydescr->buf, LL); + r += l; LL -= l; ptr += l; + //DBG("l=%zd, r=%zd, LL=%d", l, r, LL); + d0 = dtime(); + if(r > 2 && ptr[-1] == '\n') break; + } + }while(dtime() - d0 < WAIT_TMOUT && LL); + if(r){ + //buf[r] = 0; + //DBG("buf: %s", buf); + return buf; + } + return NULL; +} + +/** + * Try to connect to `device` at baudrate speed + * @return 1 if OK + */ +int try_connect(char *device, int baudrate){ + if(!device) return 0; + fflush(stdout); + ttydescr = new_tty(device, baudrate, 1024); + if(ttydescr) ttydescr = tty_open(ttydescr, 1); // exclusive open + if(!ttydescr) return 0; + while(read_tty(ttydescr)); // clear rbuf + LOGMSG("Connected to %s", device); + return 1; +} + +// stop polling thread and close tty +void stop_tty(){ + if(ttydescr) close_tty(&ttydescr); +} + +static weather_t lastweather; +typedef struct{ + const char *parname; + int parlen; + double *weatherpar; +} wpair_t; + +static const wpair_t wpairs[] = { + {"S=", 2, &lastweather.windspeed}, + {"D=", 2, &lastweather.winddir}, + {"P=", 2, &lastweather.pressure}, + {"T=", 2, &lastweather.temperature}, + {"H=", 2, &lastweather.humidity}, + {"R=", 2, &lastweather.rainfall}, + {NULL, 0, NULL} +}; + +static int parseans(char *str, weather_t *w){ + if(strncmp(str, "0R0,", 4)){ + WARNX("Wrong answer"); + LOGWARN("poll_device() get wrong answer: %s", str); + return FALSE; + } + str += 3; + do{ + ++str; + //DBG("start=%s", str); + const wpair_t *el = wpairs; + while(el->parname){ + if(strncmp(str, el->parname, el->parlen) == 0){ // found next parameter + str += el->parlen; + char *endptr; + *el->weatherpar = strtod(str, &endptr); + DBG("found par: %s, val=%g", el->parname, *el->weatherpar); + if(endptr == str){ + DBG("Wrong double value"); + *el->weatherpar = 0.; + //return FALSE; + } + break; + } + ++el; + } + str = strchr(str, ','); + //DBG("next=%s", str); + }while(str && *str); + lastweather.tmeasure = dtime(); + if(w) memcpy(w, &lastweather, sizeof(weather_t)); + return TRUE; +} + +// get weather measurements; return FALSE if something failed +int getlastweather(weather_t *w){ + if(!GP->emul){ + if(write_tty(ttydescr->comfd, "!0R0\r\n", 6)) + return FALSE; + } + double t0 = dtime(); + while(dtime() - t0 < T_POLLING_TMOUT){ + char *r = NULL; + if((r = read_string())){ // parse new data + //DBG("got %s", r); + if(parseans(r, w)) return TRUE; + } + } + return FALSE; +} diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/term.h b/Daemons/weatherdaemon_newmeteo.deprecated/term.h new file mode 100644 index 0000000..bd7d803 --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/term.h @@ -0,0 +1,46 @@ +/* + * This file is part of the weatherdaemon project. + * Copyright 2021 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#ifndef __TERM_H__ +#define __TERM_H__ + +#include + +#define FRAME_MAX_LENGTH (300) +#define MAX_MEMORY_DUMP_SIZE (0x800 * 4) +// Terminal timeout (seconds) +#define WAIT_TMOUT (0.5) +// Terminal polling timeout - 1 second +#define T_POLLING_TMOUT (1.0) + +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 tmeasure; // UNIX-time of last measure +} weather_t; + +int try_connect(char *device, int baudrate); +int getlastweather(weather_t *w); +void stop_tty(); + +#endif // __TERM_H__ diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.cflags b/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.cflags new file mode 100644 index 0000000..68d5165 --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.cflags @@ -0,0 +1 @@ +-std=c17 \ No newline at end of file diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.config b/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.config new file mode 100644 index 0000000..63b475c --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.config @@ -0,0 +1,4 @@ +#define EBUG 1 +#define _GNU_SOURCE +#define _XOPEN_SOURCE 1111 +#define _POSIX_C_SOURCE 200000 diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.creator b/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.creator new file mode 100644 index 0000000..e94cbbd --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.creator @@ -0,0 +1 @@ +[General] diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.creator.user b/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.creator.user new file mode 100644 index 0000000..fd150b7 --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.creator.user @@ -0,0 +1,160 @@ + + + + + + EnvironmentId + {cf63021e-ef53-49b0-b03b-2f2570cdf3b6} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + KOI8-R + false + 4 + false + 80 + true + true + 1 + false + false + false + 1 + true + true + 0 + 8 + true + false + 2 + true + true + true + *.md, *.MD, Makefile + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + true + Builtin.DefaultTidyAndClazy + 4 + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + Desktop + {91347f2c-5221-46a7-80b1-0a054ca02f79} + 0 + 0 + 0 + + /home/eddy/Docs/SAO/10micron/C-sources/weatherdaemon_newmeteo + + + + all + + true + GenericProjectManager.GenericMakeStep + + 1 + Сборка + Сборка + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + GenericProjectManager.GenericMakeStep + + 1 + Очистка + Очистка + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + По умолчанию + GenericProjectManager.GenericBuildConfiguration + + 1 + + + 0 + Развёртывание + Развёртывание + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + 2 + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.creator.user.4.9-pre1 b/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.creator.user.4.9-pre1 new file mode 100644 index 0000000..ebe493b --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.creator.user.4.9-pre1 @@ -0,0 +1,167 @@ + + + + + + EnvironmentId + {7bd84e39-ca37-46d3-be9d-99ebea85bc0d} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + KOI8-R + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + true + 0 + 8 + true + 1 + true + false + true + false + + + + ProjectExplorer.Project.PluginSettings + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + {65a14f9e-e008-4c1b-89df-4eaa4774b6e3} + 0 + 0 + 0 + + /Big/Data/00__Small_tel/C-sources/netdaemon + + + + all + + false + + + false + true + Сборка + + GenericProjectManager.GenericMakeStep + + 1 + Сборка + + ProjectExplorer.BuildSteps.Build + + + + + clean + + false + + + false + true + Сборка + + GenericProjectManager.GenericMakeStep + + 1 + Очистка + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + По умолчанию + По умолчанию + GenericProjectManager.GenericBuildConfiguration + + 1 + + + 0 + Установка + + ProjectExplorer.BuildSteps.Deploy + + 1 + Конфигурация установки + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + false + false + 1000 + + true + 2 + + + Особая программа + + ProjectExplorer.CustomExecutableRunConfiguration + + 3768 + false + true + false + false + true + + + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 20 + + + Version + 20 + + diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.creator.user.7bd84e3 b/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.creator.user.7bd84e3 new file mode 100644 index 0000000..e4de2e7 --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.creator.user.7bd84e3 @@ -0,0 +1,157 @@ + + + + + + EnvironmentId + {7bd84e39-ca37-46d3-be9d-99ebea85bc0d} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + KOI8-R + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + true + 0 + 8 + true + 1 + true + false + true + false + + + + ProjectExplorer.Project.PluginSettings + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + {65a14f9e-e008-4c1b-89df-4eaa4774b6e3} + 0 + 0 + 0 + + /Big/Data/00__Small_tel/C-sources/netdaemon + + + + all + + false + + + false + true + GenericProjectManager.GenericMakeStep + + 1 + Сборка + Сборка + ProjectExplorer.BuildSteps.Build + + + + + clean + + false + + + false + true + GenericProjectManager.GenericMakeStep + + 1 + Очистка + Очистка + ProjectExplorer.BuildSteps.Clean + + 2 + false + + По умолчанию + GenericProjectManager.GenericBuildConfiguration + + 1 + + + 0 + Развёртывание + Развёртывание + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + 2 + + + ProjectExplorer.CustomExecutableRunConfiguration + + + false + + false + true + false + false + true + + + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.cxxflags b/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.cxxflags new file mode 100644 index 0000000..6435dfc --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.cxxflags @@ -0,0 +1 @@ +-std=c++17 \ No newline at end of file diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.files b/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.files new file mode 100644 index 0000000..5d49406 --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.files @@ -0,0 +1,9 @@ +cmdlnopts.c +cmdlnopts.h +main.c +socket.c +socket.h +stat.c +stat.h +term.c +term.h diff --git a/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.includes b/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.includes new file mode 100644 index 0000000..9c558e3 --- /dev/null +++ b/Daemons/weatherdaemon_newmeteo.deprecated/weatherdaemon.includes @@ -0,0 +1 @@ +. diff --git a/Daemons/weatherdaemon_newmeteo/Makefile b/Daemons/weatherdaemon_newmeteo/Makefile index 1da0763..3733f08 100644 --- a/Daemons/weatherdaemon_newmeteo/Makefile +++ b/Daemons/weatherdaemon_newmeteo/Makefile @@ -1,24 +1,44 @@ # run `make DEF=...` to add extra defines -PROGRAM := weatherdaemon -LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all -pthread -LDFLAGS += -flto `pkg-config --libs usefull_macros` -lm +PROGRAM := weatherdaemonN +LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all +LDFLAGS += -lusefull_macros -lm SRCS := $(wildcard *.c) DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111 -#DEFINES += -DEBUG OBJDIR := mk -CFLAGS += `pkg-config --cflags usefull_macros` -O3 -Wall -Werror -Wextra -Wno-trampolines -flto +CFLAGS += -O2 -Wall -Wextra -Wno-trampolines OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o)) DEPS := $(OBJS:.o=.d) +TARGFILE := $(OBJDIR)/TARGET CC = gcc -all : $(OBJDIR) $(PROGRAM) +ifeq ($(shell test -e $(TARGFILE) && echo -n yes),yes) + TARGET := $(file < $(TARGFILE)) +else + TARGET := RELEASE +endif -$(PROGRAM) : $(OBJS) +ifeq ($(TARGET), DEBUG) + .DEFAULT_GOAL := debug +endif + +release: CFLAGS += -flto +release: LDFLAGS += -flto +release: $(PROGRAM) + +debug: CFLAGS += -DEBUG -Werror +debug: TARGET := DEBUG +debug: $(PROGRAM) + +$(TARGFILE): $(OBJDIR) + @echo -e "\t\tTARGET: $(TARGET)" + @echo "$(TARGET)" > $(TARGFILE) + +$(PROGRAM) : $(TARGFILE) $(OBJS) @echo -e "\t\tLD $(PROGRAM)" - $(CC) $(OBJS) $(LDFLAGS) -o $(PROGRAM) + $(CC) $(LDFLAGS) $(OBJS) -o $(PROGRAM) $(OBJDIR): - mkdir $(OBJDIR) + @mkdir $(OBJDIR) ifneq ($(MAKECMDGOALS),clean) -include $(DEPS) @@ -30,13 +50,9 @@ $(OBJDIR)/%.o: %.c clean: @echo -e "\t\tCLEAN" - @rm -f $(OBJS) $(DEPS) - @rmdir $(OBJDIR) 2>/dev/null || true + @rm -rf $(OBJDIR) 2>/dev/null || true xclean: clean @rm -f $(PROGRAM) -gentags: - CFLAGS="$(CFLAGS) $(DEFINES)" geany -g $(PROGRAM).c.tags *[hc] 2>/dev/null - -.PHONY: gentags clean xclean +.PHONY: clean xclean diff --git a/Daemons/weatherdaemon_newmeteo/cmdlnopts.c b/Daemons/weatherdaemon_newmeteo/cmdlnopts.c index e9bf534..1eabc7d 100644 --- a/Daemons/weatherdaemon_newmeteo/cmdlnopts.c +++ b/Daemons/weatherdaemon_newmeteo/cmdlnopts.c @@ -18,9 +18,7 @@ #include #include -#include #include -#include #include #include "cmdlnopts.h" @@ -28,7 +26,6 @@ * here are global parameters initialisation */ int help; -static glob_pars G; // default values for Gdefault & help #define DEFAULT_PORT "12345" @@ -36,7 +33,7 @@ static glob_pars G; // DEFAULTS // default global parameters -glob_pars const Gdefault = { +static glob_pars G = { .device = NULL, .port = DEFAULT_PORT, .logfile = NULL, @@ -52,7 +49,7 @@ glob_pars const Gdefault = { * Define command line options by filling structure: * name has_arg flag val type argptr help */ -myoption cmdlnopts[] = { +sl_option_t cmdlnopts[] = { // common options {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")}, {"device", NEED_ARG, NULL, 'd', arg_string, APTR(&G.device), _("serial device name (default: none)")}, @@ -73,19 +70,15 @@ myoption cmdlnopts[] = { * @return allocated structure with global parameters */ glob_pars *parse_args(int argc, char **argv){ - int i; - void *ptr; - ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr); // format of help: "Usage: progname [args]\n" - change_helpstring("Usage: %s [args]\n\n\tWhere args are:\n"); + sl_helpstring("Usage: %s [args]\n\n\tWhere args are:\n"); // parse arguments - parseargs(&argc, &argv, cmdlnopts); - if(help) showhelp(-1, cmdlnopts); + sl_parseargs(&argc, &argv, cmdlnopts); + if(help) sl_showhelp(-1, cmdlnopts); if(argc > 0){ - G.rest_pars_num = argc; - G.rest_pars = calloc(argc, sizeof(char*)); - for (i = 0; i < argc; i++) - G.rest_pars[i] = strdup(argv[i]); + printf("Ignore flags:\n"); + for(int i = 0; i < argc; i++) + printf("\t%s\n", argv[i]); } return &G; } diff --git a/Daemons/weatherdaemon_newmeteo/cmdlnopts.h b/Daemons/weatherdaemon_newmeteo/cmdlnopts.h index f4d5408..032e67e 100644 --- a/Daemons/weatherdaemon_newmeteo/cmdlnopts.h +++ b/Daemons/weatherdaemon_newmeteo/cmdlnopts.h @@ -16,11 +16,8 @@ * along with this program. If not, see . */ - - #pragma once -#ifndef __CMDLNOPTS_H__ -#define __CMDLNOPTS_H__ + /* * here are some typedef's for global data @@ -41,4 +38,4 @@ typedef struct{ glob_pars *parse_args(int argc, char **argv); -#endif // __CMDLNOPTS_H__ + diff --git a/Daemons/weatherdaemon_newmeteo/main.c b/Daemons/weatherdaemon_newmeteo/main.c index 288c5e9..a05c323 100644 --- a/Daemons/weatherdaemon_newmeteo/main.c +++ b/Daemons/weatherdaemon_newmeteo/main.c @@ -27,38 +27,30 @@ #include "socket.h" #include "term.h" -glob_pars *GP; - void signals(int signo){ - restore_console(); stop_tty(); LOGERR("exit with status %d", signo); exit(signo); } int main(int argc, char **argv){ - initial_setup(); - signal(SIGTERM, signals); // kill (-15) - quit - signal(SIGHUP, SIG_IGN); // hup - ignore - signal(SIGINT, signals); // ctrl+C - quit - signal(SIGQUIT, signals); // ctrl+\ - quit - signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z -#ifndef EBUG + sl_init(); char *self = strdup(argv[0]); -#endif - GP = parse_args(argc, argv); + glob_pars *GP = parse_args(argc, argv); + sl_check4running(self, GP->pidfile); if(GP->logfile){ - sl_loglevel lvl = LOGLEVEL_ERR; + sl_loglevel_e lvl = LOGLEVEL_ERR; for(; GP->verb && lvl < LOGLEVEL_ANY; --GP->verb) ++lvl; DBG("Loglevel: %d", lvl); if(!OPENLOG(GP->logfile, lvl, 1)) ERRX("Can't open log file"); LOGERR("Started"); } #ifndef EBUG - if(daemon(1, 0)){ - ERR("daemon()"); - } - check4running(self, GP->pidfile); + signal(SIGTERM, signals); // kill (-15) - quit + signal(SIGHUP, SIG_IGN); // hup - ignore + signal(SIGINT, signals); // ctrl+C - quit + signal(SIGQUIT, signals); // ctrl+\ - quit + signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z while(1){ // guard for dead processes pid_t childpid = fork(); if(childpid){ @@ -75,14 +67,16 @@ int main(int argc, char **argv){ } #endif - if(GP->device) if(!try_connect(GP->device, GP->tty_speed)){ - LOGERR("Can't connect to device"); - ERRX("Can't connect to device"); - } if(!GP->device && !GP->emul){ LOGERR("Need serial device name or emulation flag"); ERRX("Need serial device name or emulation flag"); } + + if(!try_connect(GP->device, GP->tty_speed, GP->emul)){ + LOGERR("Can't connect to device"); + ERRX("Can't connect to device"); + } + daemonize(GP->port); return 0; } diff --git a/Daemons/weatherdaemon_newmeteo/socket.c b/Daemons/weatherdaemon_newmeteo/socket.c index e254ccb..8fd72f3 100644 --- a/Daemons/weatherdaemon_newmeteo/socket.c +++ b/Daemons/weatherdaemon_newmeteo/socket.c @@ -28,7 +28,6 @@ #include // daemon #include -#include "cmdlnopts.h" // glob_pars #include "socket.h" #include "stat.h" #include "term.h" @@ -38,8 +37,6 @@ // Max amount of connections #define BACKLOG (30) -extern glob_pars *GP; - static weather_t lastweather = {0}; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static weatherstat_t wstat; @@ -72,7 +69,7 @@ static int send_data(int sock, int webquery, format_t format){ if(format == FORMAT_CURDFULL){ // full format Len = snprintf(databuf, BUFSIZ, "Wind=%.1f%sDir=%.1f%sPressure=%.1f%sTemperature=%.1f%sHumidity=%.1f%s" - "Rain=%.1f%sTime=%.3f%s", + "Rain=%.1f%sTime=%.1f%s", lastweather.windspeed, eol, lastweather.winddir, eol, lastweather.pressure, eol, lastweather.temperature, eol, lastweather.humidity, eol, lastweather.rainfall, eol, lastweather.tmeasure, eol @@ -117,6 +114,7 @@ static int send_data(int sock, int webquery, format_t format){ PRSTAT(tmeasure, "Time"); Len += snprintf(ptr, l, "%s", eol); #undef PRSTAT +#undef COMMA }else{ Len = snprintf(databuf, BUFSIZ, "Error!"); } @@ -247,6 +245,7 @@ static int handle_socket(int sock){ // main socket server static void *server(void *asock){ + FNAME(); LOGMSG("server()"); int sock = *((int*)asock); if(listen(sock, BACKLOG) == -1){ @@ -307,6 +306,7 @@ static void *server(void *asock){ // poll last weather data static void *weatherpolling(_U_ void *notused){ + FNAME(); while(1){ weather_t w; if(getlastweather(&w) && 0 == pthread_mutex_lock(&mutex)){ @@ -321,6 +321,7 @@ static void *weatherpolling(_U_ void *notused){ // data gathering & socket management static void daemon_(int sock){ + DBG("sock=%d", sock); if(sock < 0) return; pthread_t sock_thread, parser_thread; if(pthread_create(&sock_thread, NULL, server, (void*) &sock)){ diff --git a/Daemons/weatherdaemon_newmeteo/socket.h b/Daemons/weatherdaemon_newmeteo/socket.h index 189aabc..dd0eca0 100644 --- a/Daemons/weatherdaemon_newmeteo/socket.h +++ b/Daemons/weatherdaemon_newmeteo/socket.h @@ -17,8 +17,6 @@ */ #pragma once -#ifndef __SOCKET_H__ -#define __SOCKET_H__ // time interval for data polling (seconds) #define T_INTERVAL (10.) @@ -29,4 +27,3 @@ void daemonize(char *port); -#endif // __SOCKET_H__ diff --git a/Daemons/weatherdaemon_newmeteo/term.c b/Daemons/weatherdaemon_newmeteo/term.c index c3e4bcc..c361336 100644 --- a/Daemons/weatherdaemon_newmeteo/term.c +++ b/Daemons/weatherdaemon_newmeteo/term.c @@ -29,8 +29,8 @@ #define BUFLEN (4096) -static TTY_descr *ttydescr = NULL; -extern glob_pars *GP; +static sl_tty_t *ttydescr = NULL; +static int emulate = FALSE; static char buf[BUFLEN]; 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"; @@ -41,7 +41,7 @@ static const char *emultemplate = "0R0,S=1.9,D=217.2,P=787.7,T=10.8,H=69.0,R=31. */ static char *read_string(){ //static int done = 0; - if(GP->emul){ + if(emulate){ strncpy(buf, emultemplate, BUFLEN); return buf; } @@ -49,16 +49,16 @@ static char *read_string(){ size_t r = 0, l; int LL = BUFLEN - 1; char *ptr = buf; - double d0 = dtime(); + double d0 = sl_dtime(); do{ - if((l = read_tty(ttydescr))){ + if((l = sl_tty_read(ttydescr))){ strncpy(ptr, ttydescr->buf, LL); r += l; LL -= l; ptr += l; //DBG("l=%zd, r=%zd, LL=%d", l, r, LL); - d0 = dtime(); + d0 = sl_dtime(); if(r > 2 && ptr[-1] == '\n') break; } - }while(dtime() - d0 < WAIT_TMOUT && LL); + }while(sl_dtime() - d0 < WAIT_TMOUT && LL); if(r){ //buf[r] = 0; //DBG("buf: %s", buf); @@ -71,20 +71,25 @@ static char *read_string(){ * Try to connect to `device` at baudrate speed * @return 1 if OK */ -int try_connect(char *device, int baudrate){ - if(!device) return 0; +int try_connect(char *device, int baudrate, int emul){ + if(emul){ + emulate = TRUE; + DBG("Emulation mode"); + return TRUE; + } + if(!device) return FALSE; fflush(stdout); - ttydescr = new_tty(device, baudrate, 1024); - if(ttydescr) ttydescr = tty_open(ttydescr, 1); // exclusive open - if(!ttydescr) return 0; - while(read_tty(ttydescr)); // clear rbuf + ttydescr = sl_tty_new(device, baudrate, 1024); + if(ttydescr) ttydescr = sl_tty_open(ttydescr, 1); // exclusive open + if(!ttydescr) return FALSE; + while(sl_tty_read(ttydescr)); // clear rbuf LOGMSG("Connected to %s", device); - return 1; + return TRUE; } // stop polling thread and close tty void stop_tty(){ - if(ttydescr) close_tty(&ttydescr); + if(ttydescr) sl_tty_close(&ttydescr); } static weather_t lastweather; @@ -120,7 +125,7 @@ static int parseans(char *str, weather_t *w){ str += el->parlen; char *endptr; *el->weatherpar = strtod(str, &endptr); - DBG("found par: %s, val=%g", el->parname, *el->weatherpar); + //DBG("found par: %s, val=%g", el->parname, *el->weatherpar); if(endptr == str){ DBG("Wrong double value"); *el->weatherpar = 0.; @@ -133,19 +138,19 @@ static int parseans(char *str, weather_t *w){ str = strchr(str, ','); //DBG("next=%s", str); }while(str && *str); - lastweather.tmeasure = dtime(); + lastweather.tmeasure = sl_dtime(); if(w) memcpy(w, &lastweather, sizeof(weather_t)); return TRUE; } // get weather measurements; return FALSE if something failed int getlastweather(weather_t *w){ - if(!GP->emul){ - if(write_tty(ttydescr->comfd, "!0R0\r\n", 6)) + if(!emulate){ + if(sl_tty_write(ttydescr->comfd, "!0R0\r\n", 6)) return FALSE; } - double t0 = dtime(); - while(dtime() - t0 < T_POLLING_TMOUT){ + double t0 = sl_dtime(); + while(sl_dtime() - t0 < T_POLLING_TMOUT){ char *r = NULL; if((r = read_string())){ // parse new data //DBG("got %s", r); diff --git a/Daemons/weatherdaemon_newmeteo/term.h b/Daemons/weatherdaemon_newmeteo/term.h index bd7d803..bbc0bad 100644 --- a/Daemons/weatherdaemon_newmeteo/term.h +++ b/Daemons/weatherdaemon_newmeteo/term.h @@ -17,8 +17,6 @@ */ #pragma once -#ifndef __TERM_H__ -#define __TERM_H__ #include @@ -39,8 +37,7 @@ typedef struct{ double tmeasure; // UNIX-time of last measure } weather_t; -int try_connect(char *device, int baudrate); +int try_connect(char *device, int baudrate, int emul); int getlastweather(weather_t *w); void stop_tty(); -#endif // __TERM_H__