mirror of
https://github.com/eddyem/small_tel.git
synced 2026-03-20 08:41:03 +03:00
227 lines
7.6 KiB
C
227 lines
7.6 KiB
C
/*
|
|
* 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 <string.h>
|
|
#include <usefull_macros.h>
|
|
|
|
#include "fd.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)
|
|
|
|
// poll each `poll_interval` seconds
|
|
static time_t poll_interval = 15;
|
|
|
|
static int nplugins = 0;
|
|
static sensordata_t **allplugins = NULL;
|
|
|
|
int get_nplugins(){
|
|
return nplugins;
|
|
}
|
|
|
|
// set polling interval
|
|
int set_pollT(time_t t){
|
|
if(t == 0 || t > MAX_POLLT) return FALSE;
|
|
poll_interval = t;
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief get_plugin - get link to opened plugin
|
|
* @param o (o) - plugin with given index
|
|
* @param N - index in `allplugins`
|
|
* @return NULL if failed or pointer
|
|
*/
|
|
sensordata_t *get_plugin(int N){
|
|
if(N < 0 || N >= nplugins) return NULL;
|
|
return allplugins[N];
|
|
}
|
|
|
|
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(struct sensordata_t* station){
|
|
FNAME();
|
|
if(!station || !station->get_value || station->Nvalues < 1) return;
|
|
char buf[FULL_LEN+1];
|
|
uint64_t Tsum = 0; int nsum = 0;
|
|
int N = (nplugins > 1) ? station->PluginNo : -1;
|
|
for(int i = 0; i < station->Nvalues; ++i){
|
|
val_t v;
|
|
if(!station->get_value(station, &v, i)) continue;
|
|
if(0 < format_sensval(&v, buf, FULL_LEN+1, N)){
|
|
printf("%s\n", buf);
|
|
++nsum; Tsum += v.time;
|
|
}
|
|
}
|
|
time_t last = (time_t)(Tsum / nsum);
|
|
if(0 < format_msrmttm(last, buf, FULL_LEN+1)){
|
|
printf("%s\n\n", buf);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @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){
|
|
char buf[PATH_MAX+1];
|
|
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]);
|
|
snprintf(buf, PATH_MAX, "%s", paths[i]);
|
|
char *colon = strchr(buf, ':');
|
|
if(colon) *colon++ = 0;
|
|
void* dlh = open_plugin(buf);
|
|
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 fd = -1;
|
|
if(colon) fd = getFD(colon);
|
|
int ns = S->init(S, nplugins, poll_interval, fd); // here nplugins is index in array
|
|
if(ns < 1) WARNXL("Can't init plugin %s", paths[i]);
|
|
else{
|
|
#ifdef EBUG
|
|
if(!S->onrefresh(S, 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]->kill) allplugins[i]->kill(allplugins[i]);
|
|
}
|
|
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);
|
|
}
|