mirror of
https://github.com/eddyem/small_tel.git
synced 2026-03-20 00:31:00 +03:00
add pre-pre-alpha
This commit is contained in:
64
Daemons/weatherdaemon_multimeteo/CMakeLists.txt
Normal file
64
Daemons/weatherdaemon_multimeteo/CMakeLists.txt
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
set(PROJ weatherdaemon)
|
||||||
|
set(MAJOR_VERSION "0")
|
||||||
|
set(MID_VERSION "0")
|
||||||
|
set(MINOR_VERSION "1")
|
||||||
|
|
||||||
|
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
|
||||||
|
#set(SOURCES main.c cmdlnopts.c sensors.c)
|
||||||
|
|
||||||
|
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
|
||||||
|
project(${PROJ} VERSION ${VERSION} LANGUAGES C)
|
||||||
|
message("VER: ${VERSION}")
|
||||||
|
|
||||||
|
# list of options
|
||||||
|
option(DEBUG "Compile in debug mode" OFF)
|
||||||
|
option(DUMMY "Dummy device plugin" ON)
|
||||||
|
|
||||||
|
|
||||||
|
# default flags
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wextra -fno-builtin-strlen")
|
||||||
|
|
||||||
|
message("Install dir prefix: ${CMAKE_INSTALL_PREFIX}")
|
||||||
|
|
||||||
|
add_definitions(-D_XOPEN_SOURCE=1234 -D_DEFAULT_SOURCE -D_GNU_SOURCE
|
||||||
|
-DPACKAGE_VERSION=\"${VERSION}\" -DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\"
|
||||||
|
-DMAJOR_VERSION=\"${MAJOR_VESION}\")
|
||||||
|
|
||||||
|
set(CMAKE_COLOR_MAKEFILE ON)
|
||||||
|
|
||||||
|
# cmake -DDEBUG=on -> debugging
|
||||||
|
if(DEBUG)
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Og -g3 -ggdb -Werror")
|
||||||
|
add_definitions(-DEBUG)
|
||||||
|
set(CMAKE_BUILD_TYPE DEBUG)
|
||||||
|
set(CMAKE_VERBOSE_MAKEFILE "ON")
|
||||||
|
else()
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -march=native -fdata-sections -ffunction-sections -flto=auto")
|
||||||
|
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections -flto=auto")
|
||||||
|
set(CMAKE_BUILD_TYPE RELEASE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(${PROJ} REQUIRED usefull_macros>=0.3.2)
|
||||||
|
|
||||||
|
#include(FindOpenMP)
|
||||||
|
#if(OPENMP_FOUND)
|
||||||
|
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
|
||||||
|
# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
|
||||||
|
# add_definitions(-DOMP_FOUND)
|
||||||
|
#endif()
|
||||||
|
|
||||||
|
# exe & lib files
|
||||||
|
add_executable(${PROJ} ${SOURCES})
|
||||||
|
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} -lm ${CMAKE_DL_LIBS})
|
||||||
|
target_include_directories(${PROJ} PUBLIC ${${PROJ}_INCLUDE_DIRS} .)
|
||||||
|
target_link_directories(${PROJ} PUBLIC ${${PROJ}_LIBRARY_DIRS} )
|
||||||
|
set_target_properties(${PROJLIB} PROPERTIES VERSION ${VERSION})
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
# Installation of the program
|
||||||
|
install(TARGETS ${PROJ} DESTINATION "bin")
|
||||||
|
|
||||||
|
add_subdirectory("plugins")
|
||||||
22
Daemons/weatherdaemon_multimeteo/Readme.md
Normal file
22
Daemons/weatherdaemon_multimeteo/Readme.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Weather daemon for several different weather stations
|
||||||
|
=====================================================
|
||||||
|
|
||||||
|
## Usage:
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage: weatherdaemon [args]
|
||||||
|
Be careful: command line options have priority over config
|
||||||
|
Where args are:
|
||||||
|
|
||||||
|
-P, --pidfile=arg pidfile name (default: /tmp/weatherdaemon.pid)
|
||||||
|
-c, --conffile=arg configuration file name (consists all or a part of long-named parameters and their values (e.g. plugin=liboldweather.so)
|
||||||
|
-h, --help show this help
|
||||||
|
-l, --logfile=arg save logs to file (default: none)
|
||||||
|
-p, --plugin=arg add this weather plugin (may be a lot of) (can occur multiple times)
|
||||||
|
-v, --verb logfile verbocity level (each -v increased)
|
||||||
|
--port=arg network port to connect (default: 12345); hint: use "localhost:port" to make local net socket
|
||||||
|
--sockpath=arg UNIX socket path (starting from '\0' for anonimous) of command socket
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
TODO: brief documentation will be here
|
||||||
163
Daemons/weatherdaemon_multimeteo/cmdlnopts.c
Normal file
163
Daemons/weatherdaemon_multimeteo/cmdlnopts.c
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the weatherdaemon project.
|
||||||
|
* Copyright 2025 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 <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <usefull_macros.h>
|
||||||
|
#include "cmdlnopts.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* here are global parameters initialisation
|
||||||
|
*/
|
||||||
|
int help;
|
||||||
|
|
||||||
|
// default values for Gdefault & help
|
||||||
|
#define DEFAULT_PORT "12345"
|
||||||
|
#define DEFAULT_PID "/tmp/weatherdaemon.pid"
|
||||||
|
|
||||||
|
// DEFAULTS
|
||||||
|
// default global parameters
|
||||||
|
static glob_pars defpars = {
|
||||||
|
.port = DEFAULT_PORT,
|
||||||
|
.logfile = NULL,
|
||||||
|
.verb = 0,
|
||||||
|
.pidfile = DEFAULT_PID
|
||||||
|
};
|
||||||
|
// default config: all values should be wrong or empty to understand than user change them
|
||||||
|
static glob_pars defconf = {
|
||||||
|
.verb = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static glob_pars G;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define command line options by filling structure:
|
||||||
|
* name has_arg flag val type argptr help
|
||||||
|
*/
|
||||||
|
#define COMMON_OPTS \
|
||||||
|
{"port", NEED_ARG, NULL, 0, arg_string, APTR(&G.port), "network port to connect (default: " DEFAULT_PORT "); hint: use \"localhost:port\" to make local net socket"}, \
|
||||||
|
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), "save logs to file (default: none)"}, \
|
||||||
|
{"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfile), "pidfile name (default: " DEFAULT_PID ")"}, \
|
||||||
|
{"sockpath",NEED_ARG, NULL, 0, arg_string, APTR(&G.sockname), "UNIX socket path (starting from '\\0' for anonimous) of command socket"}, \
|
||||||
|
{"plugin", MULT_PAR, NULL, 'p', arg_string, APTR(&G.plugins), "add this weather plugin (may be a lot of)"},
|
||||||
|
|
||||||
|
sl_option_t cmdlnopts[] = {
|
||||||
|
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), "show this help"},
|
||||||
|
{"conffile",NEED_ARG, NULL, 'c', arg_string, APTR(&G.conffile), "configuration file name (consists all or a part of long-named parameters and their values (e.g. plugin=liboldweather.so)"},
|
||||||
|
{"verb", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verb), "logfile verbocity level (each -v increased)"}, \
|
||||||
|
COMMON_OPTS
|
||||||
|
end_option
|
||||||
|
};
|
||||||
|
|
||||||
|
sl_option_t confopts[] = {
|
||||||
|
{"verbose", NEED_ARG, NULL, 'v', arg_int, APTR(&G.verb), "logfile verbocity level"}, \
|
||||||
|
COMMON_OPTS
|
||||||
|
end_option
|
||||||
|
};
|
||||||
|
|
||||||
|
static int sortstrings(const void *v1, const void *v2){
|
||||||
|
const char **s1 = (const char **)v1, **s2 = (const char **)v2;
|
||||||
|
return strcmp(*s1, *s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare plugins from configuration and command line; add to command line plugins all new
|
||||||
|
static void compplugins(glob_pars *cmdline, glob_pars *conf){
|
||||||
|
if(!cmdline) return;
|
||||||
|
char **p;
|
||||||
|
int nconf = 0;
|
||||||
|
if(conf){
|
||||||
|
p = conf->plugins;
|
||||||
|
if(p && *p) while(*p++) ++nconf;
|
||||||
|
}
|
||||||
|
int ncmd = 0;
|
||||||
|
p = cmdline->plugins;
|
||||||
|
if(p && *p) while(*p++) ++ncmd;
|
||||||
|
DBG("Got %d plugins in conf and %d in cmdline", nconf, ncmd);
|
||||||
|
// compare plugins and rebuild new list
|
||||||
|
int newsize = ncmd + nconf;
|
||||||
|
if(newsize == 0) return; // no plugins in both
|
||||||
|
char **newarray = MALLOC(char*, newsize + 1); // +1 for ending NULL
|
||||||
|
for(int i = 0; i < ncmd; ++i){ newarray[i] = cmdline->plugins[i]; }
|
||||||
|
FREE(cmdline->plugins);
|
||||||
|
if(conf){
|
||||||
|
for(int i = 0; i < nconf; ++i){ newarray[i+ncmd] = conf->plugins[i]; }
|
||||||
|
FREE(conf->plugins);
|
||||||
|
}
|
||||||
|
qsort(newarray, newsize, sizeof(char*), sortstrings);
|
||||||
|
DBG("NOW together:"); p = newarray; while(*p) printf("\t%s\n", *p++);
|
||||||
|
p = newarray;
|
||||||
|
int nondobuleidx = 0;
|
||||||
|
for(int i = 0; i < newsize;){
|
||||||
|
int j = i + 1;
|
||||||
|
for(; j < newsize; ++j){
|
||||||
|
if(strcmp(newarray[i], newarray[j])) break;
|
||||||
|
FREE(newarray[j]);
|
||||||
|
}
|
||||||
|
if(nondobuleidx != i){
|
||||||
|
newarray[nondobuleidx] = newarray[i];
|
||||||
|
newarray[i] = NULL;
|
||||||
|
}
|
||||||
|
++nondobuleidx;
|
||||||
|
i = j;
|
||||||
|
}
|
||||||
|
DBG("Result:"); p = newarray; while(*p) printf("\t%s\n", *p++);
|
||||||
|
cmdline->plugins = newarray;
|
||||||
|
cmdline->nplugins = nondobuleidx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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){
|
||||||
|
G = defpars; // copy defaults
|
||||||
|
// format of help: "Usage: progname [args]\n"
|
||||||
|
sl_helpstring("Usage: %s [args]\n\t" COLOR_RED "Be careful: command line options have priority over config" COLOR_OLD "\n\tWhere args are:\n");
|
||||||
|
// parse arguments
|
||||||
|
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||||
|
DBG("verb: %d", G.verb);
|
||||||
|
if(help) sl_showhelp(-1, cmdlnopts);
|
||||||
|
if(argc > 0){
|
||||||
|
WARNX("You give %d unused parameters:", argc);
|
||||||
|
while(argc) printf("\t%s\n", argv[--argc]);
|
||||||
|
}
|
||||||
|
DBG("PARS: \n-------------\n%s-------------\n\n", sl_print_opts(cmdlnopts, 1));
|
||||||
|
if(G.conffile){ // read conffile and fix parameters (cmdline args are in advantage)
|
||||||
|
glob_pars oldpars = G; // save cmdline opts
|
||||||
|
G = defconf;
|
||||||
|
if(!sl_conf_readopts(oldpars.conffile, confopts)) ERRX("Can't get options from %s", G.conffile);
|
||||||
|
DBG("CONF: \n-------------\n%s-------------\n\n", sl_print_opts(confopts, 1));
|
||||||
|
if((0 == strcmp(oldpars.port, DEFAULT_PORT)) && G.port) oldpars.port = G.port;
|
||||||
|
if(!oldpars.logfile && G.logfile) oldpars.logfile = G.logfile;
|
||||||
|
if(!oldpars.verb && G.verb > -1) oldpars.verb = G.verb;
|
||||||
|
if((0 == strcmp(oldpars.pidfile, DEFAULT_PID)) && G.pidfile) oldpars.pidfile = G.pidfile;
|
||||||
|
if(!oldpars.sockname && G.sockname) oldpars.sockname = G.sockname;
|
||||||
|
// now check plugins
|
||||||
|
compplugins(&oldpars, &G);
|
||||||
|
G = oldpars;
|
||||||
|
}else compplugins(&G, NULL);
|
||||||
|
DBG("RESULT: \n-------------\n%s-------------\n\n", sl_print_opts(cmdlnopts, 1));
|
||||||
|
DBG("Nplugins = %d", G.nplugins);
|
||||||
|
return &G;
|
||||||
|
}
|
||||||
|
|
||||||
36
Daemons/weatherdaemon_multimeteo/cmdlnopts.h
Normal file
36
Daemons/weatherdaemon_multimeteo/cmdlnopts.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the weatherdaemon project.
|
||||||
|
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
* here are some typedef's for global data
|
||||||
|
*/
|
||||||
|
typedef struct{
|
||||||
|
char *sockname; // UNIX socket name for internal connections (commands etc)
|
||||||
|
char *port; // port for external clients
|
||||||
|
char *logfile; // logfile name
|
||||||
|
int verb; // verbocity level
|
||||||
|
char *pidfile; // pidfile name
|
||||||
|
char **plugins; // all plugins connected
|
||||||
|
int nplugins; // amount of plugins
|
||||||
|
char *conffile; // configuration file used instead of long command line
|
||||||
|
} glob_pars;
|
||||||
|
|
||||||
|
|
||||||
|
glob_pars *parse_args(int argc, char **argv);
|
||||||
92
Daemons/weatherdaemon_multimeteo/main.c
Normal file
92
Daemons/weatherdaemon_multimeteo/main.c
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the weatherdaemon project.
|
||||||
|
* Copyright 2025 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 <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/wait.h> // wait
|
||||||
|
#include <sys/prctl.h> //prctl
|
||||||
|
#include <usefull_macros.h>
|
||||||
|
|
||||||
|
#include "cmdlnopts.h"
|
||||||
|
#include "sensors.h"
|
||||||
|
#include "server.h"
|
||||||
|
|
||||||
|
static pid_t childpid = 0;
|
||||||
|
|
||||||
|
void signals(int signo){
|
||||||
|
if(childpid){
|
||||||
|
LOGERR("Killed with status %d", signo);
|
||||||
|
closeplugins();
|
||||||
|
kill_servers();
|
||||||
|
usleep(1000); // let child close everything before dead
|
||||||
|
}else{
|
||||||
|
LOGERR("Main process exits with status %d", signo);
|
||||||
|
}
|
||||||
|
exit(signo);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern const char *__progname;
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
glob_pars *GP = NULL;
|
||||||
|
sl_init();
|
||||||
|
signal(SIGTERM, signals); // kill (-15) - quit
|
||||||
|
signal(SIGHUP, SIG_IGN); // hup - ignore
|
||||||
|
signal(SIGINT, signals); // ctrl+C - quit
|
||||||
|
signal(SIGQUIT, signals); // ctrl+\ - quit
|
||||||
|
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
|
||||||
|
GP = parse_args(argc, argv);
|
||||||
|
if(!GP) ERRX("Error parsing args");
|
||||||
|
if(!GP->sockname) ERRX("Point command socket name");
|
||||||
|
if(GP->logfile){
|
||||||
|
sl_loglevel_e lvl = LOGLEVEL_ERR + GP->verb;
|
||||||
|
if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1;
|
||||||
|
DBG("Loglevel: %d", lvl);
|
||||||
|
if(!OPENLOG(GP->logfile, lvl, 1)) ERRX("Can't open log file");
|
||||||
|
LOGMSG("Started");
|
||||||
|
}
|
||||||
|
int nopened = openplugins(GP->plugins, GP->nplugins);
|
||||||
|
if(nopened < 1){
|
||||||
|
LOGERR("No plugins found; exit!");
|
||||||
|
ERRX("Can't find any sensor plugin");
|
||||||
|
}
|
||||||
|
if(GP->nplugins && GP->nplugins != nopened) LOGWARN("Work without some plugins");
|
||||||
|
#ifndef EBUG
|
||||||
|
sl_check4running((char*)__progname, GP->pidfile);
|
||||||
|
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
|
||||||
|
if(!start_servers(GP->port, GP->sockname)) ERRX("Can't run server's threads");
|
||||||
|
while(1);
|
||||||
|
//WARNX("TEST ends");
|
||||||
|
//signals(0);
|
||||||
|
return 0; // never reached
|
||||||
|
}
|
||||||
19
Daemons/weatherdaemon_multimeteo/plugins/CMakeLists.txt
Normal file
19
Daemons/weatherdaemon_multimeteo/plugins/CMakeLists.txt
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
|
||||||
|
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(PLUGINS REQUIRED usefull_macros)
|
||||||
|
|
||||||
|
include_directories(${PLUGINS_INCLUDE_DIRS} ..)
|
||||||
|
link_directories(${PLUGINSLIBRARY_DIRS})
|
||||||
|
link_libraries(${$PLUGINS_LIBRARIES} -fPIC)
|
||||||
|
|
||||||
|
set(LIBS "")
|
||||||
|
|
||||||
|
if(DUMMY)
|
||||||
|
add_library(wsdummy SHARED dummy.c)
|
||||||
|
list(APPEND LIBS wsdummy)
|
||||||
|
endif(DUMMY)
|
||||||
|
|
||||||
|
install(TARGETS ${LIBS} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||||
113
Daemons/weatherdaemon_multimeteo/plugins/dummy.c
Normal file
113
Daemons/weatherdaemon_multimeteo/plugins/dummy.c
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the weatherdaemon project.
|
||||||
|
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <usefull_macros.h>
|
||||||
|
|
||||||
|
#include "weathlib.h"
|
||||||
|
|
||||||
|
#define NS (6)
|
||||||
|
|
||||||
|
extern sensordata_t sensor;
|
||||||
|
|
||||||
|
static void (*freshdatahandler)(const struct sensordata_t* const) = NULL; // do nothing with fresh data
|
||||||
|
static pthread_t thread;
|
||||||
|
|
||||||
|
static val_t values[NS] = { // fields `name` and `comment` have no sense until value meaning is `IS_OTHER`
|
||||||
|
{.name = "WIND", .comment = "wind speed, m/s", .sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WIND},
|
||||||
|
{.name = "WINDDIR", .comment = "wind direction azimuth (from south over west), deg", .sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_WINDDIR},
|
||||||
|
{.name = "EXTTEMP", .comment = "external temperature, degC", .sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_AMB_TEMP},
|
||||||
|
{.name = "PRESSURE", .comment = "atmospheric pressure, hPa", .sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_PRESSURE},
|
||||||
|
{.name = "HUMIDITY", .comment = "air relative humidity, %%", .sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_HUMIDITY},
|
||||||
|
{.name = "PRECIP", .comment = "precipitations flag (0 - no, 1 - yes)", .sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_PRECIP},
|
||||||
|
};
|
||||||
|
|
||||||
|
void *mainthread(void _U_ *U){
|
||||||
|
FNAME();
|
||||||
|
double t0 = sl_dtime();
|
||||||
|
while(1){
|
||||||
|
float f = values[0].value.f + (drand48() - 0.5) / 2.;
|
||||||
|
if(f >= 0.) values[0].value.f = f;
|
||||||
|
f = values[1].value.f + (drand48() - 0.5) * 4.;
|
||||||
|
if(f > 160. && f < 200.) values[1].value.f = f;
|
||||||
|
f = values[2].value.f + (drand48() - 0.5) / 20.;
|
||||||
|
if(f > 13. && f < 21.) values[2].value.f = f;
|
||||||
|
f = values[3].value.f + (drand48() - 0.5) / 100.;
|
||||||
|
if(f > 585. && f < 615.) values[3].value.f = f;
|
||||||
|
f = values[4].value.f + (drand48() - 0.5) / 10.;
|
||||||
|
if(f > 60. && f <= 100.) values[4].value.f = f;
|
||||||
|
values[5].value.u = (f > 98.) ? 1 : 0;
|
||||||
|
time_t cur = time(NULL);
|
||||||
|
for(int i = 0; i < NS; ++i) values[i].time = cur;
|
||||||
|
if(freshdatahandler) freshdatahandler(&sensor);
|
||||||
|
while(sl_dtime() - t0 < 1.) usleep(500);
|
||||||
|
t0 = sl_dtime();
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int init(int N){
|
||||||
|
FNAME();
|
||||||
|
values[0].value.f = 1.;
|
||||||
|
values[1].value.f = 180.;
|
||||||
|
values[2].value.f = 17.;
|
||||||
|
values[3].value.f = 600.;
|
||||||
|
values[4].value.f = 80.;
|
||||||
|
values[5].value.u = 0;
|
||||||
|
if(pthread_create(&thread, NULL, mainthread, NULL)) return 0;
|
||||||
|
sensor.PluginNo = N;
|
||||||
|
return NS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int onrefresh(void (*handler)(const struct sensordata_t* const)){
|
||||||
|
FNAME();
|
||||||
|
if(!handler) return FALSE;
|
||||||
|
freshdatahandler = handler;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void die(){
|
||||||
|
FNAME();
|
||||||
|
if(0 == pthread_kill(thread, 9)){
|
||||||
|
DBG("Killed, join");
|
||||||
|
pthread_join(thread, NULL);
|
||||||
|
DBG("Done");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief getval - value's getter
|
||||||
|
* @param o (o) - value
|
||||||
|
* @param N - it's index
|
||||||
|
* @return FALSE if failed
|
||||||
|
*/
|
||||||
|
static int getval(val_t *o, int N){
|
||||||
|
if(N < 0 || N >= NS) return FALSE;
|
||||||
|
if(o) *o = values[N];
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
sensordata_t sensor = {
|
||||||
|
.name = "Dummy weatherstation",
|
||||||
|
.Nvalues = NS,
|
||||||
|
.init = init,
|
||||||
|
.onrefresh = onrefresh,
|
||||||
|
.get_value = getval,
|
||||||
|
.die = die,
|
||||||
|
};
|
||||||
203
Daemons/weatherdaemon_multimeteo/sensors.c
Normal file
203
Daemons/weatherdaemon_multimeteo/sensors.c
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the weatherdaemon project.
|
||||||
|
* Copyright 2025 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 <dlfcn.h>
|
||||||
|
#include <usefull_macros.h>
|
||||||
|
|
||||||
|
#include "sensors.h"
|
||||||
|
|
||||||
|
#define WARNXL(...) do{ LOGWARN(__VA_ARGS__); WARNX(__VA_ARGS__); } while(0)
|
||||||
|
#define WARNL(...) do{ LOGWARN(__VA_ARGS__); WARN(__VA_ARGS__); } while(0)
|
||||||
|
#define ERRXL(...) do{ LOGERR(__VA_ARGS__); ERRX(__VA_ARGS__); } while(0)
|
||||||
|
#define ERRL(...) do{ LOGERR(__VA_ARGS__); ERR(__VA_ARGS__); } while(0)
|
||||||
|
|
||||||
|
static int nplugins = 0;
|
||||||
|
static sensordata_t **allplugins = NULL;
|
||||||
|
|
||||||
|
int get_nplugins(){
|
||||||
|
return nplugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get_plugin - get copy of opened plugin
|
||||||
|
* @param o (o) - plugin with given index
|
||||||
|
* @param N - index in `allplugins`
|
||||||
|
* @return TRUE if OK
|
||||||
|
*/
|
||||||
|
int get_plugin(sensordata_t *o, int N){
|
||||||
|
if(!o || N < 0 || N >= nplugins) return FALSE;
|
||||||
|
*o = *allplugins[N];
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *open_plugin(const char *name){
|
||||||
|
DBG("try to open lib %s", name);
|
||||||
|
void* dlh = dlopen(name, RTLD_NOLOAD); // library may be already opened
|
||||||
|
if(!dlh){
|
||||||
|
DBG("Not loaded - load");
|
||||||
|
dlh = dlopen(name, RTLD_NOW);
|
||||||
|
}
|
||||||
|
if(!dlh){
|
||||||
|
char *e = dlerror();
|
||||||
|
WARNXL("Can't find plugin! %s", (e) ? e : "");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return dlh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef EBUG
|
||||||
|
// in release this function can be used for meteo logging
|
||||||
|
static void dumpsensors(const struct sensordata_t* const station){
|
||||||
|
FNAME();
|
||||||
|
if(!station || !station->get_value || station->Nvalues < 1) return;
|
||||||
|
char buf[FULL_LEN+1];
|
||||||
|
int N = (nplugins > 1) ? station->PluginNo : -1;
|
||||||
|
for(int i = 0; i < station->Nvalues; ++i){
|
||||||
|
val_t v;
|
||||||
|
if(!station->get_value(&v, i)) continue;
|
||||||
|
if(0 < format_sensval(&v, buf, FULL_LEN+1, N)){
|
||||||
|
printf("%s\n", buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief openplugins - open sensors' plugin and init it
|
||||||
|
* @param paths - paths to plugins
|
||||||
|
* @param N - amount of plugins
|
||||||
|
* @return amount of opened and inited plugins
|
||||||
|
* This function should be runned only once at start
|
||||||
|
*/
|
||||||
|
int openplugins(char **paths, int N){
|
||||||
|
if(!paths || !*paths || N < 1) return 0;
|
||||||
|
if(allplugins || nplugins){
|
||||||
|
WARNXL("Plugins already opened"); return 0;
|
||||||
|
}
|
||||||
|
allplugins = MALLOC(sensordata_t*, N);
|
||||||
|
green("Try to open plugins:\n");
|
||||||
|
for(int i = 0; i < N; ++i){
|
||||||
|
printf("\tplugin[%d]=%s\n", i, paths[i]);
|
||||||
|
void* dlh = open_plugin(paths[i]);
|
||||||
|
if(!dlh) continue;
|
||||||
|
DBG("OPENED");
|
||||||
|
void *s = dlsym(dlh, "sensor");
|
||||||
|
if(s){
|
||||||
|
sensordata_t *S = (sensordata_t*) s;
|
||||||
|
if(!S->get_value) WARNXL("Sensor library %s have no values' getter!", paths[i]);
|
||||||
|
if(!S->init){
|
||||||
|
WARNXL("Sensor library %s have no init funtion");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int ns = S->init(nplugins);
|
||||||
|
if(ns < 1) WARNXL("Can't init plugin %s", paths[i]);
|
||||||
|
else{
|
||||||
|
#ifdef EBUG
|
||||||
|
if(!S->onrefresh(dumpsensors)) WARNXL("Can't init refresh funtion");
|
||||||
|
#endif
|
||||||
|
LOGMSG("Plugin %s nave %d sensors", paths[i], ns);
|
||||||
|
allplugins[nplugins++] = S;
|
||||||
|
}
|
||||||
|
}else WARNXL("Can't find field `sensor` in plugin %s: %s", paths[i], dlerror());
|
||||||
|
}
|
||||||
|
return nplugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief closeplugins - call `die` function for all sensors
|
||||||
|
* This function should be runned at exit
|
||||||
|
*/
|
||||||
|
void closeplugins(){
|
||||||
|
if(!allplugins || nplugins < 1) return;
|
||||||
|
for(int i = 0; i < nplugins; ++i){
|
||||||
|
if(allplugins[i]->die) allplugins[i]->die();
|
||||||
|
}
|
||||||
|
FREE(allplugins);
|
||||||
|
nplugins = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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, "%g", v->value.f); break;
|
||||||
|
default: sprintf(strval, "'ERROR'");
|
||||||
|
}
|
||||||
|
const char* const NM[] = { // names of standard fields
|
||||||
|
[IS_WIND] = "WIND",
|
||||||
|
[IS_WINDDIR] = "WINDDIR",
|
||||||
|
[IS_HUMIDITY] = "HUMIDITY",
|
||||||
|
[IS_AMB_TEMP] = "EXTTEMP",
|
||||||
|
[IS_INNER_TEMP] = "INTTEMP",
|
||||||
|
[IS_HW_TEMP] = "HWTEMP", // mirror?
|
||||||
|
[IS_PRESSURE] = "PRESSURE",
|
||||||
|
[IS_PRECIP] = "PRECIP",
|
||||||
|
[IS_PRECIP_LEVEL]="PRECIPLV",
|
||||||
|
[IS_MIST] = "MIST",
|
||||||
|
[IS_CLOUDS] = "CLOUDS",
|
||||||
|
[IS_SKYTEMP] = "SKYTEMP"
|
||||||
|
};
|
||||||
|
const char* const CMT[] = { // comments for standard fields
|
||||||
|
[IS_WIND] = "Wind, m/s",
|
||||||
|
[IS_WINDDIR] = "Wind direction, degr (CW from north to FROM)",
|
||||||
|
[IS_HUMIDITY] = "Humidity, percent",
|
||||||
|
[IS_AMB_TEMP] = "Ambient temperature, degC",
|
||||||
|
[IS_INNER_TEMP] = "In-dome temperature, degC",
|
||||||
|
[IS_HW_TEMP] = "Hardware (mirror?) termperature, degC",
|
||||||
|
[IS_PRESSURE] = "Atmospheric pressure, mmHg",
|
||||||
|
[IS_PRECIP] = "Precipitation (1 - yes, 0 - no)",
|
||||||
|
[IS_PRECIP_LEVEL]="Precipitation level (mm)",
|
||||||
|
[IS_MIST] = "Mist (1 - yes, 0 - no)",
|
||||||
|
[IS_CLOUDS] = "Integral clouds value (bigger - better)",
|
||||||
|
[IS_SKYTEMP] = "Mean sky temperatyre"
|
||||||
|
};
|
||||||
|
const char *name = NULL, *comment = NULL;
|
||||||
|
int idx = v->meaning;
|
||||||
|
if(idx < IS_OTHER){
|
||||||
|
name = NM[idx];
|
||||||
|
comment = CMT[idx];
|
||||||
|
}else{
|
||||||
|
name = v->name;
|
||||||
|
comment = v->comment;
|
||||||
|
}
|
||||||
|
int got;
|
||||||
|
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);
|
||||||
|
return got;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the same for measurement time formatting
|
||||||
|
int format_msrmttm(time_t t, char *buf, int buflen){
|
||||||
|
--buflen; // for trailing zero
|
||||||
|
if(!buf || buflen < FULL_LEN) return -1;
|
||||||
|
char cmt[COMMENT_LEN+1];
|
||||||
|
struct tm *T = localtime(&t);
|
||||||
|
strftime(cmt, COMMENT_LEN, "%F %T", T);
|
||||||
|
return snprintf(buf, buflen, "TMEAS=%zd / Last measurement time: %s", t, cmt);
|
||||||
|
}
|
||||||
28
Daemons/weatherdaemon_multimeteo/sensors.h
Normal file
28
Daemons/weatherdaemon_multimeteo/sensors.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the weatherdaemon project.
|
||||||
|
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "weathlib.h"
|
||||||
|
|
||||||
|
int openplugins(char **paths, int N);
|
||||||
|
void closeplugins();
|
||||||
|
int get_plugin(sensordata_t *o, int N);
|
||||||
|
int get_nplugins();
|
||||||
|
int format_sensval(const val_t *v, char *buf, int buflen, int Np);
|
||||||
|
int format_msrmttm(time_t t, char *buf, int buflen);
|
||||||
229
Daemons/weatherdaemon_multimeteo/server.c
Normal file
229
Daemons/weatherdaemon_multimeteo/server.c
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the weatherdaemon project.
|
||||||
|
* Copyright 2025 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 <signal.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <usefull_macros.h>
|
||||||
|
|
||||||
|
#include "sensors.h"
|
||||||
|
#include "server.h"
|
||||||
|
|
||||||
|
// if measurement time oldest than now minus `oldest_interval`, we think measurement are too old
|
||||||
|
static time_t oldest_interval = 60;
|
||||||
|
|
||||||
|
// server's sockets: net and local (UNIX)
|
||||||
|
static sl_sock_t *netsocket = NULL, *localsocket;
|
||||||
|
//static pthread_t netthread, locthread;
|
||||||
|
|
||||||
|
// show user current time
|
||||||
|
static sl_sock_hresult_e timehandler(sl_sock_t *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
||||||
|
if(!client) return RESULT_FAIL;
|
||||||
|
char buf[32];
|
||||||
|
snprintf(buf, 31, "UNIXT=%.3f\n", sl_dtime());
|
||||||
|
sl_sock_sendstrmessage(client, buf);
|
||||||
|
return RESULT_SILENCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// show all connected libraries
|
||||||
|
static sl_sock_hresult_e listhandler(sl_sock_t *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
||||||
|
if(!client) return RESULT_FAIL;
|
||||||
|
char buf[128];
|
||||||
|
int N = get_nplugins();
|
||||||
|
if(N < 1) return RESULT_FAIL;
|
||||||
|
sensordata_t d;
|
||||||
|
for(int i = 0; i < N; ++i){
|
||||||
|
if(!get_plugin(&d, i)) continue;
|
||||||
|
snprintf(buf, 127, "PLUGIN[%d]=%s\nNVALUES[%d]=%d\n", i, d.name, i, d.Nvalues);
|
||||||
|
sl_sock_sendstrmessage(client, buf);
|
||||||
|
}
|
||||||
|
return RESULT_SILENCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief showdata - send to user meteodata
|
||||||
|
* @param client - client data
|
||||||
|
* @param N - index of station
|
||||||
|
* @param showidx - == TRUE to show index in square brackets
|
||||||
|
*/
|
||||||
|
static void showdata(sl_sock_t *client, int N, int showidx){
|
||||||
|
char buf[FULL_LEN+1];
|
||||||
|
val_t v;
|
||||||
|
sensordata_t s;
|
||||||
|
if(!get_plugin(&s, N) || (s.Nvalues < 1)){
|
||||||
|
snprintf(buf, FULL_LEN, "Can't get plugin[%d]\n", N);
|
||||||
|
sl_sock_sendstrmessage(client, buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!showidx || get_nplugins() == 1) N = -1; // only one -> don't show indexes
|
||||||
|
time_t oldest = time(NULL) - oldest_interval;
|
||||||
|
uint64_t Tsum = 0; int nsum = 0;
|
||||||
|
for(int i = 0; i < s.Nvalues; ++i){
|
||||||
|
if(!s.get_value(&v, i)) continue;
|
||||||
|
if(v.time < oldest) continue;
|
||||||
|
if(1 > format_sensval(&v, buf, FULL_LEN+1, N)) continue;
|
||||||
|
DBG("formatted: '%s'", buf);
|
||||||
|
sl_sock_sendstrmessage(client, buf);
|
||||||
|
sl_sock_sendbyte(client, '\n');
|
||||||
|
++nsum; Tsum += v.time;
|
||||||
|
}
|
||||||
|
oldest = (time_t)(Tsum / nsum);
|
||||||
|
if(0 < format_msrmttm(oldest, buf, FULL_LEN+1)){ // send mean measuring time
|
||||||
|
DBG("Formatted time: '%s'", buf);
|
||||||
|
sl_sock_sendstrmessage(client, buf);
|
||||||
|
sl_sock_sendbyte(client, '\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get meteo data
|
||||||
|
static sl_sock_hresult_e gethandler(sl_sock_t *client, _U_ sl_sock_hitem_t *item, const char *req){
|
||||||
|
if(!client) return RESULT_FAIL;
|
||||||
|
int N = get_nplugins();
|
||||||
|
if(N < 1) return RESULT_FAIL;
|
||||||
|
if(!req) for(int i = 0; i < N; ++i) showdata(client, i, TRUE);
|
||||||
|
else{
|
||||||
|
int n;
|
||||||
|
if(!sl_str2i(&n, req) || n < 0 || n >= N) return RESULT_BADVAL;
|
||||||
|
showdata(client, n, FALSE);
|
||||||
|
}
|
||||||
|
return RESULT_SILENCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// graceful closing socket: let client know that he's told to fuck off
|
||||||
|
static void toomuch(int fd){
|
||||||
|
const char *m = "Try later: too much clients connected\n";
|
||||||
|
send(fd, m, sizeof(m)-1, MSG_NOSIGNAL);
|
||||||
|
shutdown(fd, SHUT_WR);
|
||||||
|
DBG("shutdown, wait");
|
||||||
|
double t0 = sl_dtime();
|
||||||
|
uint8_t buf[8];
|
||||||
|
while(sl_dtime() - t0 < 90.){ // change this value to smaller for real work
|
||||||
|
if(sl_canread(fd)){
|
||||||
|
ssize_t got = read(fd, buf, 8);
|
||||||
|
DBG("Got=%zd", got);
|
||||||
|
if(got < 1) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DBG("Disc after %gs", sl_dtime() - t0);
|
||||||
|
LOGWARN("Client fd=%d tried to connect after MAX reached", fd);
|
||||||
|
}
|
||||||
|
// new connections handler (return FALSE to reject client)
|
||||||
|
static int connected(sl_sock_t *c){
|
||||||
|
if(c->type == SOCKT_UNIX) LOGMSG("New local client fd=%d connected", c->fd);
|
||||||
|
else LOGMSG("New client fd=%d, IP=%s connected", c->fd, c->IP);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
// disconnected handler
|
||||||
|
static void disconnected(sl_sock_t *c){
|
||||||
|
if(c->type == SOCKT_UNIX) LOGMSG("Disconnected local client fd=%d", c->fd);
|
||||||
|
else LOGMSG("Disconnected client fd=%d, IP=%s", c->fd, c->IP);
|
||||||
|
}
|
||||||
|
static sl_sock_hresult_e defhandler(struct sl_sock *s, const char *str){
|
||||||
|
if(!s || !str) return RESULT_FAIL;
|
||||||
|
sl_sock_sendstrmessage(s, "You entered wrong command:\n```\n");
|
||||||
|
sl_sock_sendstrmessage(s, str);
|
||||||
|
sl_sock_sendstrmessage(s, "\n```\nTry \"help\"\n");
|
||||||
|
return RESULT_SILENCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handlers for network and local (UNIX) sockets
|
||||||
|
static sl_sock_hitem_t nethandlers[] = { // net - only getters and client-only setters
|
||||||
|
{gethandler, "get", "get all meteo or only for given plugin number", NULL},
|
||||||
|
{listhandler, "list", "show all opened plugins", NULL},
|
||||||
|
{timehandler, "time", "get server's UNIX time", NULL},
|
||||||
|
{NULL, NULL, NULL, NULL}
|
||||||
|
};
|
||||||
|
static sl_sock_hitem_t localhandlers[] = { // local - full amount of setters/getters
|
||||||
|
{gethandler, "get", "get all meteo or only for given plugin number", NULL},
|
||||||
|
{listhandler, "list", "show all opened plugins", NULL},
|
||||||
|
{timehandler, "time", "get server's UNIX time", NULL},
|
||||||
|
{NULL, NULL, NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// common parsers for both net and local sockets
|
||||||
|
static void *cmdparser(void *U){
|
||||||
|
if(!U) return NULL;
|
||||||
|
sl_sock_t *s = (sl_sock_t*) U;
|
||||||
|
while(s && s->connected){
|
||||||
|
if(!s->rthread){
|
||||||
|
LOGERR("Server's handlers' thread is dead");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOGDBG("cmdparser(): exit");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int start_servers(const char *netnode, const char *sockpath){
|
||||||
|
if(!netnode || !sockpath){
|
||||||
|
LOGERR("start_servers(): need arguments");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
netsocket = sl_sock_run_server(SOCKT_NET, netnode, BUFSIZ, nethandlers);
|
||||||
|
if(!netsocket){
|
||||||
|
LOGERR("start_servers(): can't run network socket");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
localsocket = sl_sock_run_server(SOCKT_UNIX, sockpath, BUFSIZ, localhandlers);
|
||||||
|
if(!localsocket){
|
||||||
|
LOGERR("start_servers(): can't run local socket");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
sl_sock_changemaxclients(netsocket, MAX_CLIENTS);
|
||||||
|
sl_sock_changemaxclients(localsocket, 1);
|
||||||
|
sl_sock_maxclhandler(netsocket, toomuch);
|
||||||
|
sl_sock_maxclhandler(localsocket, toomuch);
|
||||||
|
sl_sock_connhandler(netsocket, connected);
|
||||||
|
sl_sock_connhandler(localsocket, connected);
|
||||||
|
sl_sock_dischandler(netsocket, disconnected);
|
||||||
|
sl_sock_dischandler(localsocket, disconnected);
|
||||||
|
sl_sock_defmsghandler(netsocket, defhandler);
|
||||||
|
sl_sock_defmsghandler(localsocket, defhandler);
|
||||||
|
#if 0
|
||||||
|
if(pthread_create(&netthread, NULL, cmdparser, (void*)netsocket)){
|
||||||
|
LOGERR("Can't run server's net thread");
|
||||||
|
goto errs;
|
||||||
|
}
|
||||||
|
if(pthread_create(&locthread, NULL, cmdparser, (void*)localsocket)){
|
||||||
|
LOGERR("Can't run server's local thread");
|
||||||
|
goto errs;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return TRUE;
|
||||||
|
#if 0
|
||||||
|
errs:
|
||||||
|
sl_sock_delete(&localsocket);
|
||||||
|
sl_sock_delete(&netsocket);
|
||||||
|
return FALSE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void kill_servers(){
|
||||||
|
//pthread_cancel(locthread);
|
||||||
|
//pthread_cancel(netthread);
|
||||||
|
//LOGMSG("Server threads canceled");
|
||||||
|
//usleep(500);
|
||||||
|
sl_sock_delete(&localsocket);
|
||||||
|
sl_sock_delete(&netsocket);
|
||||||
|
LOGMSG("Server sockets destroyed");
|
||||||
|
//usleep(500);
|
||||||
|
//pthread_kill(locthread, 9);
|
||||||
|
//pthread_kill(netthread, 9);
|
||||||
|
//LOGMSG("Server threads killed");
|
||||||
|
}
|
||||||
25
Daemons/weatherdaemon_multimeteo/server.h
Normal file
25
Daemons/weatherdaemon_multimeteo/server.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the weatherdaemon project.
|
||||||
|
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// maximal amount of simultaneous clients connected
|
||||||
|
#define MAX_CLIENTS (30)
|
||||||
|
|
||||||
|
int start_servers(const char *netnode, const char *sockpath);
|
||||||
|
void kill_servers();
|
||||||
1
Daemons/weatherdaemon_multimeteo/weatherdaemon.cflags
Normal file
1
Daemons/weatherdaemon_multimeteo/weatherdaemon.cflags
Normal file
@@ -0,0 +1 @@
|
|||||||
|
-std=c17
|
||||||
4
Daemons/weatherdaemon_multimeteo/weatherdaemon.config
Normal file
4
Daemons/weatherdaemon_multimeteo/weatherdaemon.config
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#define EBUG 1
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#define _XOPEN_SOURCE 1111
|
||||||
|
#define _POSIX_C_SOURCE 200000
|
||||||
1
Daemons/weatherdaemon_multimeteo/weatherdaemon.creator
Normal file
1
Daemons/weatherdaemon_multimeteo/weatherdaemon.creator
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[General]
|
||||||
1
Daemons/weatherdaemon_multimeteo/weatherdaemon.cxxflags
Normal file
1
Daemons/weatherdaemon_multimeteo/weatherdaemon.cxxflags
Normal file
@@ -0,0 +1 @@
|
|||||||
|
-std=c++17
|
||||||
9
Daemons/weatherdaemon_multimeteo/weatherdaemon.files
Normal file
9
Daemons/weatherdaemon_multimeteo/weatherdaemon.files
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
cmdlnopts.c
|
||||||
|
cmdlnopts.h
|
||||||
|
main.c
|
||||||
|
plugins/dummy.c
|
||||||
|
sensors.c
|
||||||
|
sensors.h
|
||||||
|
server.c
|
||||||
|
server.h
|
||||||
|
weathlib.h
|
||||||
1
Daemons/weatherdaemon_multimeteo/weatherdaemon.includes
Normal file
1
Daemons/weatherdaemon_multimeteo/weatherdaemon.includes
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.
|
||||||
92
Daemons/weatherdaemon_multimeteo/weathlib.h
Normal file
92
Daemons/weatherdaemon_multimeteo/weathlib.h
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the weatherdaemon project.
|
||||||
|
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
// length (in symbols) of key, value and comment
|
||||||
|
#define KEY_LEN (8)
|
||||||
|
#define VAL_LEN (31)
|
||||||
|
#define COMMENT_LEN (63)
|
||||||
|
// maximal full length of "KEY=val / comment" (as for sfitsio)
|
||||||
|
#define FULL_LEN (81)
|
||||||
|
// name of meteo-plugin
|
||||||
|
#define NAME_LEN (31)
|
||||||
|
|
||||||
|
// importance of values
|
||||||
|
typedef enum{
|
||||||
|
VAL_OBLIGATORY, // can't be omitted
|
||||||
|
VAL_RECOMMENDED, // recommended to show
|
||||||
|
VAL_UNNECESSARY, // may be shown by user request
|
||||||
|
VAL_BROKEN // sensor is broken, omit it
|
||||||
|
} valsense_t;
|
||||||
|
|
||||||
|
// meaning of values
|
||||||
|
typedef enum{
|
||||||
|
IS_WIND, // wind, m/s
|
||||||
|
IS_WINDDIR, // wind direction, degr
|
||||||
|
IS_HUMIDITY, // humidity, percent
|
||||||
|
IS_AMB_TEMP, // ambient temperature, degC
|
||||||
|
IS_INNER_TEMP, // in-dome temperature, degC
|
||||||
|
IS_HW_TEMP, // hardware (?) termperature, degC
|
||||||
|
IS_PRESSURE, // atmospheric pressure, mmHg
|
||||||
|
IS_PRECIP, // precipitation (1 - yes, 0 - no)
|
||||||
|
IS_PRECIP_LEVEL, // precipitation level (mm)
|
||||||
|
IS_MIST, // mist (1 - yes, 0 - no)
|
||||||
|
IS_CLOUDS, // integral clouds value (bigger - better)
|
||||||
|
IS_SKYTEMP, // mean sky temperatyre
|
||||||
|
IS_OTHER // something other - read "name" and "comment"
|
||||||
|
} valmeaning_t;
|
||||||
|
|
||||||
|
typedef union{
|
||||||
|
uint32_t u;
|
||||||
|
int32_t i;
|
||||||
|
float f;
|
||||||
|
} num_t;
|
||||||
|
|
||||||
|
// type of value
|
||||||
|
typedef enum{
|
||||||
|
VALT_UINT,
|
||||||
|
VALT_INT,
|
||||||
|
VALT_FLOAT,
|
||||||
|
//VALT_STRING,
|
||||||
|
} valtype_t;
|
||||||
|
|
||||||
|
// value
|
||||||
|
typedef struct{
|
||||||
|
char name[KEY_LEN+1]; // max VAL_LEN symbols FITS header keyword name
|
||||||
|
char comment[COMMENT_LEN+1];// max COMMENT_LEN symbols of comment to FITS header
|
||||||
|
valsense_t sense; // importance
|
||||||
|
valtype_t type; // type of given value
|
||||||
|
valmeaning_t meaning; // what type of sensor is it
|
||||||
|
num_t value; // value itself
|
||||||
|
time_t time; // last changing time
|
||||||
|
} val_t;
|
||||||
|
|
||||||
|
// all sensor's data
|
||||||
|
typedef struct sensordata_t{
|
||||||
|
char name[NAME_LEN+1]; // max 31 symbol of sensor's name (e.g. "rain sensor")
|
||||||
|
int Nvalues; // amount of values
|
||||||
|
int PluginNo; // plugin number in array (if several)
|
||||||
|
int (*init)(int); // init meteostation with given PluginNo; return amount of parameters found
|
||||||
|
int (*onrefresh)(void (*handler)(const struct sensordata_t* const)); // handler of new data; return TRUE if OK
|
||||||
|
int (*get_value)(val_t *, int); // getter of Nth value
|
||||||
|
void (*die)(); // close everything and die
|
||||||
|
} sensordata_t;
|
||||||
Reference in New Issue
Block a user