Compare commits

...

9 Commits

Author SHA1 Message Date
46de782019 Add readme, make some fixes 2026-05-07 10:56:21 +03:00
63f87d2283 add dead sensors' reinit, gathering all fresh data, fixed some little bugs 2026-05-06 17:13:00 +03:00
c5b9d43797 add lightning module 2026-05-05 17:57:42 +03:00
2413661e19 add UPS monitoring over SNMP 2026-04-30 16:16:12 +03:00
05e57ef012 .. 2026-04-29 17:47:23 +03:00
27cfe60fe8 some fixes; add forced shutdown 2026-04-28 18:14:34 +03:00
347d02e748 . 2026-04-27 17:45:29 +03:00
68febd02c4 add config example 2026-04-27 09:45:48 +03:00
Edward Emelianov
7b2d93299d add fits-header to weather_proxy, fixed something 2026-04-24 23:12:29 +03:00
30 changed files with 1834 additions and 501 deletions

117
Daemons/2DO.txt Normal file
View File

@@ -0,0 +1,117 @@
(хорошо бы нигде не ориентироваться на DNS, везде писать IP)
### superweatherdaemon ###
Все "станции" (* - готово):
- старая метео *
- новая метео *
- датчик дождя *
- датчик грозы (молния <=5км -> FORCEOFF=1; молния >=10км - FORCEOFF=0)
поля: LIGTPWR, LIGTDIST, LIGTTIME
- ИБП в стойке (через минуту после выключения света FORCEOFF=1; через пять минут после включения - FORCEOFF=0)
поля: POWERED (0 - внешнее питание комплекса отключено, 1 - включено)
- ИК-allsky (как будет готов - получение "процента облачности" и "температуры неба")
поля: CLOUDS, SKYTEMP
В выдаваемую клиентам информацию добавить "стандартное" поле FORCEOFF (если ==1 - парковаться, закрываться и выключаться,
в т.ч. компьютер).
Только ИБП и датчик грозы влияют на поле FORCEOFF. FORCEOFF автоматом ставит WEATHER=3.
Флаг FORCEOFF - исключительная прерогатива "супердемона", как и поле WEATHER. Значение флага снимается лишь по устареванию,
как для прочих полей с проверкой на слишком старые данные. Лишь если снят FORCEOFF, поле WEATHER может начать понижать
уровень. Сразу со снятием флага FORCEOFF, снижаем уровень WEATHER до 2. Следовательно, запускать наблюдения можно будет
лишь через 2N секунд (кстати, добавить в конфиг время ожидания понижения уровня погоды) после подачи питания.
??? Добавить "стандартные" поля: LIGTPWR, LIGTDIST, LIGTTIME (например, если гроза еще дальше 5км, писать эти данные
в FITS-шапку).
Обязательно логгировать источники повышения и понижения уровня погоды, а также источник установки флага FORCEOFF.
В управляющий UNIX-сокет добавить комады:
- forceoff - чтобы можно было вручную ставить/снимать флаг
- weath - вручную сменять уровень погоды
- mute/unmute плагинов (по номеру из list); если плагин mute, то вся информация от него игнорируется
- ?
### Демон общего питания (robometeo) ###
Мониторит FORCEOFF. При ==1 выжидает OFF_T_WAIT секунд, затем начинает проверять все 5 "роботелов": если купол закрыт и не пингуется,
отключаем питание оборудования и питание купола (кроме первого). Через OFF_T_FORCE секунд после сигнала даже если комп пингуется,
но купол закрыт, питание отключаем. Интервалы времени - в конфигурационный файл.
Если все отключено, ждет, пока не наступит 0. Потом выжидает еще ON_WAIT секунд, после чего последовательно подает питание
на все купола, выжидает ON_FORCE секунд и поочередно включает нагрузку всех ИБП куполов. Проверяет пингуемость компов и
пишет в лог, во сколько какой запинговался.
Аналогично с приборами в стойке: после OFF_R_WAIT секунд пинговать roboserv, robonas и robostorage; как не пингуются все -
выключать питание стойки. Либо же принудительно отключать после OFF_R_FORCE секунд. При снятии флага выжидаем ON_WAIT секунд,
затем включаем напряжение стойки и пингуем ее компы, логгируем.
В конфиг-файл: IP-адреса и номера релюшек оборудования телескопов и стойки, например,
telescopes=Astro-M1, Astro-M2...
Astro-M1=192.168.70.33:1:-1 # Название из telescopes = IP:номер реле оборудования:номер реле питания или -1
Astro-M2=192.168.70.35:2:22
...
equipment=roboserv,robonas,robostorage
roboserv=192.168.70.6:11:-1 # аналогично должен вести себя 192.168.70.6:-1:11
...
(при разборе конфига группировать по номеру реле оборудования и номеру реле питания; не отключать реле питания, пока
не отключены все компы, кроме выхода таймаута)
### weather proxy ###
Добавить флаг forceoff.
### Clients ###
Все клиентские демоны используют weather_proxy и его библиотеку, чтобы получать из SHM погодные данные.
Демоны купола, телескопа и монтировки обязаны создавать в /tmp файлы с FITS-шапками (по ним "демон питания" узнает, что
можно выключать компьютер).
Клиенты реагируют на WEATHER:
0 - можно работать
1 - нельзя открываться, но можно продолжать работать
2 - форсированное закрывание купола, останов телескопа, остальное без изменений
3 - закрывание всего, парковка телескопа
## Демон купола ##
Форсированное закрывание при WEATHER>1 или FORCEOFF=1.
## Демон телескопа ##
Форсированное закрывание при WEATHER>2 или FORCEOFF=1.
## Демон монтировки ##
Останов при WEATHER=2 или FORCEOFF=1, парковка при WEATHER=3 (в случае FORCEOFF=1 ни в коем случае не парковать).
ДОБАВИТЬ ключ HDRTIME // UNIX-time of last activity -> в этом ключе содержится sl_dtime() обновления шапки (нужно для
демона питания, чтобы контролировать, когда отмерла монтировка).
## Демон питания оборудования и компьютера ##
Этот демон запускается из-под рута. Принимает от пользователя команды вклчения-выключения оборудования (сюда же можно
воткнуть управление плоским полем, пинание монтировки на включение, управление светом).
При получении FORCEOFF=1, сначала щелкается кнопка отключения монтировки. Далее
проверяются DOMFITSHDR (DOMESTAT= closed), TELFITSHDR (TELSTAT = closed) и MOUNTFITSHDR (HDRTIME должен быть минимум
на 30 секунд старше текущего времени).
Если все ОК, то выключается навесное оборудование и монтировка, а следом - и сам компьютер получает сигнал poweroff.
sync(); // Flush disk buffer to prevent data loss
reboot(RB_POWER_OFF);
или (правильней?): system("shutdown -P now")

View File

@@ -16,7 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "weather_data.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -27,8 +26,7 @@
#include <semaphore.h>
#include <unistd.h>
#define SHM_NAME "/weather_shm"
#define SEM_NAME "/weather_sem"
#include "weather_data.h"
int get_weather_data(weather_data_t *data) {
int shm_fd;

View File

@@ -22,8 +22,12 @@
int main() {
weather_data_t wd;
if(get_weather_data(&wd) == 0){
printf("Weather: %d, Max wind: %.1f, Wind: %.1f, Temp: %.1f; updated @%zd\n",
wd.weather, wd.windmax, wd.wind, wd.exttemp, wd.last_update);
char strt[64];
struct tm *T = localtime(&wd.last_update);
strftime(strt, 63, "%F %T", T);
printf("Prohibited: %d\nWeather: %d\nMax wind: %.1f\nWind: %.1f\nTemp: %.1f\nPressure: %.1f\nHumidity: %.1f\nupdated @%zd (%s)\n",
wd.prohibited, wd.weather, wd.windmax, wd.wind, wd.exttemp, wd.pressure, wd.humidity,
wd.last_update, strt);
}else{
fprintf(stderr, "Failed to get weather data\n");
}

View File

@@ -15,19 +15,12 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if 0
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdarg.h>
#endif
#include <fcntl.h>
#include <semaphore.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/prctl.h>
@@ -43,19 +36,18 @@
#define DEFAULT_PID "/tmp/weather_proxy.pid"
#define DEAD_TMOUT 15
// if we have no fresh data more than `RECONN_TMOUT`, try to reconect
#define RECONN_TMOUT 5
// don't ask new data less than `WEAT_TMOUT` seconds
#define WEAT_TMOUT 1
#define SHM_NAME "/weather_shm"
#define SEM_NAME "/weather_sem"
typedef struct{
char *node; // node of server
int isunix; // use UNIX-sockets instead of net
char *logfile; // logfile name
int verb; // verbocity level
char *pidfile; // pidfile name
char *fitsheader; // FITS-header with collected weather data
} glob_pars;
static pid_t childpid;
@@ -64,14 +56,15 @@ static int forbidden = 0;
static sem_t *sem = NULL;
static weather_data_t *shared_data = NULL;
static volatile int running = 1;
static glob_pars G = {0};
static glob_pars G = {.pidfile = DEFAULT_PID};
static sl_option_t opts[] = {
{"node", NEED_ARG, NULL, 'n', arg_string, APTR(&G.node), "node to connect (host:port or UNIX socket name)"},
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), "save logs to file"},
{"pidfile", NEED_ARG, NULL, 'p', arg_string, APTR(&G.pidfile), "pidfile name (default: " DEFAULT_PID ")"},
{"isunix", NO_ARGS, NULL, 'u', arg_string, APTR(&G.isunix), "use UNIX socket instead of network"},
{"verbose", NO_ARGS, NULL, 'v', arg_int, APTR(&G.verb), "verbose level (each -v increases)"},
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verb), "verbose level (each -v increases)"},
{"fitsheader",NEED_ARG, NULL, 'f', arg_string, APTR(&G.fitsheader),"fits-header for weather data"},
end_option
};
@@ -96,13 +89,14 @@ void signals(int signo){
if(childpid){ // master
LOGERR("Main process exits with status %d", signo);
if(G.pidfile) unlink(G.pidfile);
if(G.fitsheader) unlink(G.fitsheader);
exit(1);
}else{ // child
LOGERR("Killed with status %d", signo);
running = 0; // let make cleanup
if(running){
LOGERR("Stop running");
running = 0; // let make cleanup
}
}
sleep(1);
exit(signo); // force exit if stubs
}
static int init_ipc(void){
@@ -166,48 +160,90 @@ static void cleanup_ipc(void){
}
}
// update record of FITS header
static void FITS_update(const char *line, int finish){
static FILE *tmp = NULL;
static char templ[32]; // temporary file name
if(!tmp){ // try to create new temporary file
sprintf(templ, "/tmp/fitshdrXXXXXX");
int fd = mkstemp(templ);
if(fd < 0){
WARN("mkstemp()");
LOGERR("Can't create temporary file!");
return;
}
tmp = fdopen(fd, "w");
if(!tmp){
WARN("fdopen()");
LOGERR("Error in fdopen()");
return;
}
}
fprintf(tmp, "%s\n", line);
if(finish){ // move temporary file into new location
fclose(tmp);
tmp = NULL;
chmod(templ, 0644);
if(rename(templ, G.fitsheader) < 0){
WARN("rename(%s, %s)", templ, G.fitsheader);
LOGERR("Error in rename()");
}
}
}
static void update_shm(weather_data_t *data){
if(sem_wait(sem) == -1){
LOGWARN("sem_wait failed: %s", strerror(errno));
}else{
memcpy(shared_data, data, sizeof(weather_data_t));
sem_post(sem);
LOGDBG("Weather data updated");
}
}
static void parse_line(const char *line, weather_data_t *data) {
char key[64];
char value[256];
if (sscanf(line, "%63[^=]=%255s", key, value) == 2) {
if (strcmp(key, "WEATHER") == 0) {
char key[SL_KEY_LEN];
char value[SL_VAL_LEN];
int update = 0; // 0 for updating, 1 for finishing, -1 for error
if(sl_get_keyval(line, key, value)){
if(strcmp(key, "WEATHER") == 0){
data->weather = (weather_condition_t) atoi(value);
printf("got weather: %d\n", data->weather);
} else if (strcmp(key, "WINDMAX1") == 0) {
}else if (strcmp(key, "WINDMAX1") == 0){
data->windmax = atof(value);
printf("got windmax: %g\n", data->windmax);
} else if (strcmp(key, "PRECIP") == 0) {
}else if (strcmp(key, "PRECIP") == 0){
data->rain = atoi(value);
printf("got rain: %d\n", data->rain);
} else if (strcmp(key, "CLOUDS") == 0) {
}else if (strcmp(key, "CLOUDS") == 0){
data->clouds = atof(value);
printf("got clouds: %g\n", data->clouds);
} else if (strcmp(key, "WIND") == 0) {
}else if (strcmp(key, "WIND") == 0){
data->wind = atof(value);
printf("got wind: %g\n", data->wind);
} else if (strcmp(key, "EXTTEMP") == 0) {
}else if (strcmp(key, "EXTTEMP") == 0){
data->exttemp = atof(value);
printf("got temp: %g\n", data->exttemp);
} else if (strcmp(key, "PRESSURE") == 0) {
}else if (strcmp(key, "PRESSURE") == 0){
data->pressure = atof(value);
printf("got pressure: %g\n", data->pressure);
} else if (strcmp(key, "HUMIDITY") == 0) {
}else if (strcmp(key, "HUMIDITY") == 0){
data->humidity = atof(value);
printf("got humidity: %g\n", data->humidity);
} else if (strcmp(key, "PROHIBIT") == 0) {
}else if (strcmp(key, "PROHIBIT") == 0){
data->prohibited = atoi(value);
} else if (strcmp(key, "TMEAS") == 0) {
}else if (strcmp(key, "TMEAS") == 0){ // last line in message -> update
data->last_update = atof(value);
if(data->weather == WEATHER_PROHIBITED || forbidden) data->prohibited = 1;
else if(data->weather < WEATHER_TERRIBLE) data->prohibited = 0;
else if(data->weather < WEATHER_PROHIBITED) data->prohibited = 0;
// update all
if (sem_wait(sem) == -1) {
LOGWARN("sem_wait failed: %s", strerror(errno));
} else {
memcpy(shared_data, data, sizeof(weather_data_t));
sem_post(sem);
LOGMSG("Weather data updated");
}
update_shm(data);
update = 1;
}else update = -1;
if(update > -1 && G.fitsheader){
FITS_update(line, update);
}
}
}
@@ -222,7 +258,7 @@ static int request_weather_data(sl_sock_t *sock){
DBG("try to send request: '%s", request);
if(sl_sock_sendstrmessage(sock, request) < 1){
LOGERR("Can't poll new data");
LOGWARN("Can't poll new data");
return -1;
}
return 0;
@@ -239,32 +275,52 @@ static void run_daemon(){
LOGERR("Can't connect to meteodaemon over socket with node %s", G.node);
return;
}
LOGMSG("Connected to meteodaemon %s", G.node);
memcpy(&new_data, shared_data, sizeof(weather_data_t));
time_t lastert = time(NULL);
while(running){
time_t tnow = time(NULL);
if(!sock || request_weather_data(sock) == -1){
if(tnow - lastert > RECONN_TMOUT){ // try to reconnect
int req = -1;
if(sock) req = request_weather_data(sock);
if(req == -1){
int diff = tnow - lastert;
DBG("diff = %d", diff);
if(diff > RECONN_TMOUT){ // try to reconnect
LOGERR("Failed to request weather data, retry");
if(sock) sl_sock_delete(&sock);
if(!(sock = sl_sock_run_client(stype, G.node, 4096))) lastert += 5;
else lastert = tnow;
if(!(sock = sl_sock_run_client(stype, G.node, 4096))){
new_data.weather = WEATHER_TERRIBLE; // no connection to weather server, don't allow to open
update_shm(&new_data);
lastert += RECONN_TMOUT;
}else{
LOGMSG("Reconnected to %s", G.node);
lastert = tnow;
}
}
}else lastert = tnow;
}else if(req == 0) lastert = tnow;
while(sl_sock_readline(sock, line, 255) > 0){
DBG("Parse '%s'", line);
parse_line(line, &new_data);
}
usleep(500000);
}
sl_sock_delete(&sock); // disconnect and clear memory
DBG("run_daemon() exited");
}
int main(int argc, char *argv[]){
sl_init();
sl_parseargs(&argc, &argv, opts);
if(!G.node) ERRX("Point node to connect");
if(G.fitsheader){
FILE *fitsfile = fopen(G.fitsheader, "w");
if(!fitsfile){
WARN("Can't create FITS header %s", G.fitsheader);
FREE(G.fitsheader);
}else fclose(fitsfile);
}
sl_check4running(NULL, G.pidfile);
if(G.logfile){
sl_loglevel_e lvl = LOGLEVEL_ERR + G.verb;
@@ -304,7 +360,8 @@ int main(int argc, char *argv[]){
exit(EXIT_FAILURE);
}
run_daemon();
LOGDBG("Daemon is dead");
cleanup_ipc();
LOGMSG("Daemon is dead");
LOGDBG("IPC cleaned");
return 0;
}

View File

@@ -3,11 +3,14 @@
#include <stdint.h>
#include <time.h>
#define SHM_NAME "/weather_shm"
#define SEM_NAME "/weather_sem"
typedef enum {
WEATHER_GOOD = 0,
WEATHER_BAD = 1,
WEATHER_TERRIBLE = 2,
WEATHER_PROHIBITED = 3,
WEATHER_GOOD = 0, // may start observations
WEATHER_BAD = 1, // cannot start but can continue if want
WEATHER_TERRIBLE = 2, // close & park: wind, precipitation, humidity etc.
WEATHER_PROHIBITED = 3, // force closing & parking; power off equipment, ready to power off computer
} weather_condition_t;
typedef struct {
@@ -19,7 +22,7 @@ typedef struct {
float pressure; // atm. pressure, mmHg: "PRESSURE"
float humidity; // humidity, percents: "HUMIDITY"
int rain; // ==1 when rainy: "PRECIP"
int prohibited; // ==1 if "weather == prohibited" or rain == 1
int prohibited; // ==1 if "weather == prohibited" or got `prohibited` signal -> ready to power off
time_t last_update; // value of "TMEAS"
} weather_data_t;

View File

@@ -5,7 +5,7 @@ set(MAJOR_VERSION "0")
set(MID_VERSION "0")
set(MINOR_VERSION "1")
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
set(SOURCES cmdlnopts.c main.c mainweather.c sensors.c server.c)
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
project(${PROJ} VERSION ${VERSION} LANGUAGES C)
@@ -19,6 +19,8 @@ option(HYDREON "Hydreon rain sensor plugin" ON)
option(BTAMETEO "BTA main meteostation plugin" ON)
option(REINHARDT "Old Reinhardt meteostation plugin" ON)
option(WXA100 "WXA100-06 meteostation plugin" ON)
option(SNMP "SNMP UPS monitoring module" ON)
option(LIGHTNING "AS3935-based lightning sensor" ON)
# default flags
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wextra -fPIC")
@@ -27,7 +29,7 @@ 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}\")
-DMAJOR_VERSION=\"${MAJOR_VERSION}\")
set(CMAKE_COLOR_MAKEFILE ON)
@@ -55,8 +57,8 @@ pkg_check_modules(${PROJ} REQUIRED usefull_macros>=0.3.5)
#endif()
# static lib for sensors
set(LIBSRC "weathlib.c")
set(LIBHEADER "weathlib.h")
set(LIBSRC fd.c weathlib.c)
set(LIBHEADER weathlib.h)
add_library(${PROJLIB} STATIC ${LIBSRC})
set_target_properties(${PROJLIB} PROPERTIES VERSION ${VERSION})

View File

@@ -1,22 +1,324 @@
Weather daemon for several different weather stations
=====================================================
# superweatherdaemon Documentation
## Usage:
## Overview
```
Usage: weatherdaemon [args]
Be careful: command line options have priority over config
Where args are:
**superweatherdaemon** is a weather monitoring daemon designed for astronomical observatories. It
collects data from multiple heterogeneous weather stations (meteostations) via a plugin
architecture, computes a unified weather status, and provides control interfaces over TCP and local
UNIX sockets. The daemon can issue warnings, change weather level, and even trigger a forced
shutdown of instruments (e.g., close dome) when conditions become dangerous.
-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
The project is written in C, uses CMake as its build system, and relies on the
[usefull_macros](https://github.com/eddyem/snippets_library) library for utilities and socket
management.
## Dependencies
- **Build tools**: CMake >= 4.0, C compiler with C11 support, `pkg-config`.
- **Library**: `usefull_macros` >= 0.3.5 (`sl_*` functions for logging, sockets, command-line parsing, etc.).
- **Optional**: `net-snmp` (for the SNMP UPS plugin).
On Gentoo/Calculate Linux, install the basic build dependencies:
```bash
emerge dev-build/cmake dev-util/pkgconf
# usefull_macros must be installed from its own source; follow its documentation.
# For SNMP support:
emerge net-analyzer/net-snmp
```
## Building and Installation
TODO: brief documentation will be here
1. Clone or download the source tree.
2. Create a build directory and run CMake:
```bash
mkdir build && cd build
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local
```
3. Customise enabled plugins with `-D` options (all are `ON` by default):
```bash
cmake .. -DDUMMY=OFF -DFDEXAMPLE=OFF -DHYDREON=ON -DBTAMETEO=ON ...
```
Available plugin options:
- `DUMMY`  Dummy weather station for testing.
- `FDEXAMPLE`  Example file descriptor plugin.
- `HYDREON`  Hydreon RG-11 rain sensor.
- `BTAMETEO`  BTA 6-m telescope main meteostation (shared memory).
- `REINHARDT`  Old Reinhardt meteostation.
- `WXA100`  Vaisala WXA100 ultrasonic station.
- `SNMP`  UPS monitoring via SNMP.
- `LIGHTNING`  AS3935-based lightning sensor.
4. Building:
```bash
make
su -c "make install"
```
The main executable `superweatherdaemon` is installed into `bin`; the plugin shared libraries
(`lib*.so`) go to the library directory.
## Configuration
The daemon can be configured entirely via command-line options, a configuration file, or both.
Command-line options take precedence.
### Command-line Options
| Option | Description |
|--------|-------------|
| `-h`, `--help` | Show help message and exit. |
| `-c <file>`, `--conffile <file>` | Use a configuration file. Point non-existant file to get help. |
| `-l <path>`, `--logfile=<path>` | Write logs to a file (default: none). |
| `-P <path>`, `--pidfile=<path>` | PID file (default `/tmp/superweatherdaemon.pid`). |
| `-p <spec>`, `--plugin=<spec>` | Add a weather plugin; can be repeated for different plugins. Format: `path:type:device` (see below). |
| `--port=<node>` | Network port for clients (default `12345`). Use `localhost:port` for local access only. |
| `--sockpath=<path>` | UNIX socket path (start with `@` for an abstract socket). |
| `-T <seconds>`, `--pollt <seconds>` | Max polling interval in seconds (integer). |
| `-v`, `--verb` | Increase verbosity level (each `-v` adds 1). |
### Plugin Specification
A plugin is a shared library (`.so`) that provides a `sensor_init` function. It is loaded with the
`--plugin` option.
Format:
```
--plugin=library:type:parameter
```
- `library`: path to the shared library, e.g. `libwxa100.so`.
- `type`: Connection type  `D` for serial device, `U` for UNIX socket, `N` for INET socket.
- `parameter`: device path and optional speed (`/dev/ttyS0:9600`), UNIX socket name, or `host:port` for INET.
Examples:
```bash
--plugin=libreinhardt.so:D:/dev/ttyS0
--plugin=libwxa100.so:D:/dev/pl2303_0
--plugin=libhydreon.so:D:/dev/ch340_0:1200
--plugin=libbtameteo.so (no device, uses shared memory)
```
Multiple plugins are listed in order of **importance** (first ones are considered primary for
weather level calculation).
### Configuration File
The configuration file uses a simple `key = value` syntax, with `#` for comments. Example:
```ini
# network port for clients
port = 4444
logfile = /var/log/meteo/superweather.log
verbose = 2
sockpath = "@weather"
pollt = 1
reinit_delay = 10
# Weather thresholds
ahtung_delay = 1800 # in seconds
good_wind = 5.0 # m/s
bad_wind = 10.0
terrible_wind = 15.0
good_humidity = 65.0 # percents
bad_humidity = 87.0
terrible_humidity = 94.0
good_clouds = 2500.0 # for reinhardt sensor in its units
bad_clouds = 2000.0
terrible_clouds = 500.0
clouds_negflag = 1 # 1 means the higher the value the better
good_sky = -40.0 # sky minus ambient temperature, degC
bad_sky = -10.0
terrible_sky = 0.0
# plugins - most important first
plugin = libwxa100.so:D:/dev/pl2303_0
plugin = libhydreon.so:D:/dev/ch340_0:1200
plugin = libbtameteo.so
plugin = libreinhardt.so:D:/dev/ttyS0
```
Run with:
```bash
superweatherdaemon -c /etc/weather.conf
```
To run on system start you can use OpenRó `rc.local` mechanism.
## Plugins
Each weather station is implemented as a shared library exporting a single function:
```c
int sensor_init(sensordata_t *s);
```
The `sensordata_t` structure contains all required callbacks, data pointers, and private fields.
See `weathlib.h` for the full definition.
### Plugin Lifecycle
1. **Loading**: The main daemon calls `s = sensor_new(...)` to create `sensordata_t` structure,
opens given library with `dlopen` and calls `sensor_init` on that new `s`.
2. **Initialisation**:
- Set `s->name`, `s->Nvalues`, `s->values` array.
- Configure the communication channel (file descriptor) using `getFD(s->path)`.
- Create a worker thread that periodically reads sensor data and updates `s->values`.
Don't forget to lock `s->valmutex` on any operation with `s->values`.
3. **Data delivery**: Each time new data is available, call `s->freshdatahandler(s)`
(this is set by the daemon to `dumpsensors`). The main daemon then merges the data into the
global weather evaluation.
4. **Shutdown**: The daemon calls `s->kill(s)`, which must join the thread, close the file
descriptor, and free resources. Default `common_kill` handles most of this; plugins can override it.
If the plugin is disconnected for some reason (for example, the network connection is lost), the
daemon will try to reconnect every `reinit_delay` seconds.
### Available Plugins
| Library | Sensor | Type |
|---------|--------|------|
| `libwsdummy.so` | Dummy station  outputs random walk data around realistic values. | Test / Development |
| `libfdex.so` | Example of a filedescriptor based plugin. Prompts for commaseparated values. | Example |
| `libhydreon.so` | Hydreon RG-11 optical rain sensor. | Serial |
| `libbtameteo.so` | BTA 6-m telescope main meteostation (shared memory). | Shared Memory |
| `libreinhardt.so` | Old Reinhardt meteostation (serial, `?U` command). | Serial |
| `libwxa100.so` | Vaisala WXA100 ultrasonic meteostation (serial, `0R0` command). | Serial |
| `libsnmp.so` | UPS monitor via SNMP (requires net-snmp). | Network |
| `liblightning.so` | AS3935-based lightning sensor (serial). | Serial |
### Writing a New Plugin
Use the existing plugins as templates. A minimal plugin must:
- Include `weathlib.h`.
- Define an array of `val_t` describing each measured quantity.
- Implement `sensor_init`:
- Allocate `s->values`, copy the template array.
- Set `s->Nvalues`, `s->name`.
- Open the device (use `getFD(s->path)` for serial/sockets) and set `s->fdes`.
If your plugin don't need file descriptor, you must set `s->fdes` to any non-negative value.
- Create a ring buffer if needed (`sl_RB_new`).
- Start the worker thread that reads data, updates values inside `pthread_mutex_lock(&s->valmutex)`,
and calls `s->freshdatahandler(s)` (outside mutex locked).
- Return `TRUE` on success, `FALSE` on failure (call `s->kill(s)` to clean up).
- The `weathlib.h` provides helper functions: `common_onrefresh`, `common_getval`, `common_kill`.
## Weather Level Calculation
The daemon combines data from all active plugins and continuously evaluates a global **weather
level**:
- `0`  **GOOD**: observations can start safely.
- `1`  **BAD**: risky to start, but can continue.
- `2`  **TERRIBLE**: dome must close, instruments park.
- `3`  **PROHIBITED**: complete shutdown, power off equipment.
### Criteria
Each weather parameter (wind speed, humidity, clouds, sky temperature, lightning distance,
precipitation, etc.) has configurable thresholds:
- `good`  below/above this (depending on sign) the condition is good.
- `bad`  above this the condition is bad.
- `terrible`  above this the condition is terrible.
- `prohibited`  (if defined) above this the condition goes directly to PROHIBITED.
- `negflag`  if `1`, a smaller value is worse (e.g., clouds).
- `shtdnflag`  if `1`, entering the terrible/prohibited range also sets the **FORCE SHUTDOWN** flag.
### Special Flags
- **FORCE SHUTDOWN**: Some parameters (e.g., lightning within <= 5km, UPS on battery) carry the
`IS_FORCEDSHTDN` meaning and have `shtdnflag = 1`. When their value exceeds the terrible threshold,
the forced shutdown flag is raised, which immediately sets the weather level to PROHIBITED and is
typically used to cut power.
- **Manual FORBID**: An operator can send a signal (`SIGUSR1`) or a socket command to forbid
observations, which also forces PROHIBITED. `SIGUSR2` or a socket command clears forbidden flag.
### Hysteresis
Once a bad/terrible state is reached, the level is not lowered until `ahtung_delay` seconds have
passed since the last serious event. This prevents rapid toggling.
## Server Commands (Socket API)
The daemon listens on two interfaces:
1. **Network socket** (TCP, default port 12345)  read-only (in meaning they cannot change any
parameters) access for remote clients.
2. **Local UNIX socket** (abstract or filesystem, default `@weather`)  full control for local applications.
Commands are sent as plain text strings, terminated by a newline.
### Common (read-only) Commands
| Command | Description |
|---------|-------------|
| `get` | Return all collected weather data (all stations). |
| `get=<N>` | Return data from plugin `<N>`. |
| `list` | List all loaded plugins with their names and value counts. |
| `time` | Return server UNIX time (float seconds). |
| `chklevel` | Show the `sense` (importance) level of every collected parameter. |
| `chklevel=<N>` | Same for a specific plugin. |
### Local-only (read-write) Commands
| Command | Description |
|---------|-------------|
| `forbid` | Get current FORBID flag (0/1). |
| `forbid=<0/1>` | Set/clear manual FORBID. |
| `forceoff` | Get FORCE SHUTDOWN flag. |
| `forceoff=<0/1>` | Set/clear it manually. |
| `weathlevel` | Get current weather level (0-3 for GOOD-PROHIBITED). |
| `weathlevel=<0..3>` | Force weather level (use with caution). |
| `setlevel=<plugin>:<param>=<sense>,...` | Change the `sense` field of one or more sensor parameters. Example: `setlevel=1:WIND=3,HUMIDITY=3` disables wind and humidity from station 1. |
| `mute=<N>` | Stop refreshing data from plugin `<N>` (mute). |
| `unmute=<N>` | Resume refreshing. |
| `ismuted=<N>` | Return 1 if muted, 0 otherwise. |
Reply format: each line is a FITS-like `KEY = value / comment` string; commands that set something
usually echo back the variable and its new value. For `get=<N>` each `KEY` have a suffix in square
brackets  number of plugin, e.g. `WIND[1]= 10.1 / Wind speed, m/s`.
## Signals
| Signal | Effect |
|--------|--------|
| `SIGTERM`, `SIGINT`, `SIGQUIT` | Clean shutdown (removes PID file, kills plugins, destroys sockets). |
| `SIGHUP` | Ignored. |
| `SIGUSR1` | Set manual FORBID (weather level == PROHIBITED). |
| `SIGUSR2` | Clear manual FORBID. |
| `SIGPIPE` | Logged, used to detect network plugins disconnections. |
## Files
| File | Purpose |
|------|---------|
| `CMakeLists.txt` | Top-level build definition. |
| `cmdlnopts.c/.h` | Command-line and configuration file parsing. |
| `main.c` | Daemon entry point, signal handlers, forking. |
| `mainweather.c/.h` | Global weather evaluation, data collection, forced shutdown. |
| `sensors.c/.h` | Plugin management (load, unload, getters). |
| `server.c/.h` | TCP and UNIX socket servers. |
| `weathlib.c/.h` | Common plugin API, value definitions, helper functions. |
| `fd.c` | Function `getFD()` to open serial devices or sockets for plugins. |
| `example.config` | Sample configuration file. |
| `plugins/CMakeLists.txt` | Build file for all plugins. |
| `plugins/*.c` | Individual plugin source files. |
## License
The project is released under the **GNU General Public License v3.0** or later. See the headers in
the source files for the full legal text.
---
*For further assistance or to report issues, please contact the maintainer: Edward V. Emelianov
<edward.emelianoff@gmail.com>.*

View File

@@ -32,7 +32,7 @@ int help;
// default values for Gdefault & help
#define DEFAULT_PORT "12345"
#define DEFAULT_PID "/tmp/weatherdaemon.pid"
#define DEFAULT_PID "/tmp/superweatherdaemon.pid"
// DEFAULTS
// default global parameters
@@ -49,22 +49,25 @@ static glob_pars defconf = {
// only for config
weather_conf_t WeatherConf = {
.ahtung_delay = 30*60, // 30 minutes
.reinit_delay = 60, // each 1 minute
.wind.good = 5., // < 5m/s - good weather
.wind.bad = 10., // > 10m/s - bad weather
.wind.terrible = 15., // > 15m/s - terrible weather
.wind.negflag = 0,
.humidity.good = 65.,
.humidity.bad = 80.,
.humidity.terrible = 90.,
.humidity.negflag = 0,
.humidity.bad = 87.,
.humidity.terrible = 94.,
.clouds.good = 2500.,
.clouds.bad = 2000.,
.clouds.terrible = 500.,
.clouds.negflag = 1,
.clouds.negflag = 1, // the higher values is the better
.sky.good = -40.,
.sky.bad = -10.,
.sky.terrible = 0.,
.sky.negflag = 0
.ligtdist.good = 60., // no lightnings near
.ligtdist.bad = 10., // 10km
.ligtdist.terrible = 5., // <=5km - ahtung!
.ligtdist.negflag = 1, // the nearest is the worse
.ligtdist.shtdnflag = 1, // force shutdown if too close
};
static glob_pars G;
@@ -77,7 +80,7 @@ static glob_pars G;
{"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"}, \
{"sockpath",NEED_ARG, NULL, 0, arg_string, APTR(&G.sockname), "UNIX socket path (starting from '@' for anonimous) of command socket"}, \
{"plugin", MULT_PAR, NULL, 'p', arg_string, APTR(&G.plugins), "add this weather plugin (may be a lot of); FORMAT: \"dlpath:l:dev\", where `dlpath` - path of plugin library; `l` - 'D' for device, 'U' for UNIX-socket or 'N' for INET socket; dev - path to device and speed (like /dev/ttyS0:9600), UNIX socket name or host:port for INET"}, \
{"pollt", NEED_ARG, NULL, 'T', arg_int, APTR(&G.pollt), "set maximal polling interval (seconds, integer)"},
@@ -90,20 +93,22 @@ sl_option_t cmdlnopts[] = {
};
sl_option_t confopts[] = {
{"verbose", NEED_ARG, NULL, 'a', arg_int, APTR(&G.verb), "logfile verbocity level"},
{"ahtung_delay",NEED_ARG,NULL, 'b', arg_int, APTR(&WeatherConf.ahtung_delay),"delay in seconds after bad weather to change to good"},
{"good_wind",NEED_ARG, NULL, 'c', arg_double, APTR(&WeatherConf.wind.good), "good wind while less this"},
{"bad_wind", NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.wind.bad), "bad wind if more than this"},
{"terrible_wind",NEED_ARG, NULL,0, arg_double, APTR(&WeatherConf.wind.terrible), "terrible wind if more than this"},
{"good_humidity",NEED_ARG, NULL,0, arg_double, APTR(&WeatherConf.humidity.good), "humidity is good until this"},
{"bad_humidity",NEED_ARG, NULL,0, arg_double, APTR(&WeatherConf.humidity.bad), "humidity is bad if greater"},
{"terrible_humidity",NEED_ARG,NULL, 0, arg_double, APTR(&WeatherConf.humidity.terrible), "humidity is terrible if greater"},
{"good_clouds",NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.clouds.good), "good weather when \"clouds value\" greater than this"},
{"bad_clouds",NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.clouds.bad), "if less than this, clouds are bad"},
{"terrible_clouds",NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.clouds.terrible), "if less, clouds are terrible"},
{"good_sky",NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.sky.good), "sky-ambient less than this is good"},
{"bad_sky",NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.sky.bad), "sky-ambient greater than this is bad"},
{"terrible_sky",NEED_ARG,NULL, 0, arg_double, APTR(&WeatherConf.sky.terrible), "sky-ambient greater than this is terrible"},
{"verbose", NEED_ARG, NULL, 0, arg_int, APTR(&G.verb), "logfile verbocity level"},
{"ahtung_delay",NEED_ARG, NULL, 0, arg_int, APTR(&WeatherConf.ahtung_delay), "delay in seconds after bad weather to change to good"},
{"good_wind", NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.wind.good), "good wind while less this"},
{"bad_wind", NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.wind.bad), "bad wind if more than this"},
{"terrible_wind",NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.wind.terrible), "terrible wind if more than this"},
{"good_humidity",NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.humidity.good), "humidity is good until this"},
{"bad_humidity",NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.humidity.bad), "humidity is bad if greater"},
{"terrible_humidity",NEED_ARG,NULL, 0, arg_double, APTR(&WeatherConf.humidity.terrible), "humidity is terrible if greater"},
{"good_clouds", NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.clouds.good), "good weather when \"clouds value\" less than this"},
{"bad_clouds", NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.clouds.bad), "if greater than this, clouds are bad"},
{"terrible_clouds",NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.clouds.terrible), "if greater, clouds are terrible"},
{"clouds_negflag",NEED_ARG, NULL, 0, arg_int, APTR(&WeatherConf.clouds.negflag), "==1 to invert sign (lesser value is worst)"},
{"good_sky", NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.sky.good), "sky-ambient less than this is good"},
{"bad_sky", NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.sky.bad), "sky-ambient greater than this is bad"},
{"terrible_sky",NEED_ARG, NULL, 0, arg_double, APTR(&WeatherConf.sky.terrible), "sky-ambient greater than this is terrible"},
{"reinit_delay",NEED_ARG, NULL, 0, arg_int, APTR(&WeatherConf.reinit_delay), "delay (s) to reinit dead sensors"},
COMMON_OPTS
end_option
};
@@ -140,7 +145,9 @@ static void compplugins(glob_pars *cmdline, glob_pars *conf){
}
// don't sort: we need leave priority as user pointed
//qsort(newarray, newsize, sizeof(char*), sortstrings);
#ifdef EBUG
DBG("NOW together:"); p = newarray; while(*p) printf("\t%s\n", *p++);
#endif
for(int i = 0; i < newsize-1; ++i){
if(NULL == newarray[i]) continue;
for(int j = i+1; j < newsize; ++j){
@@ -174,7 +181,9 @@ static void compplugins(glob_pars *cmdline, glob_pars *conf){
i = j;
}
#endif
#ifdef EBUG
DBG("Result:"); p = newarray; while(*p) printf("\t%s\n", *p++);
#endif
cmdline->plugins = newarray;
cmdline->nplugins = nondoubleidx;
}

View File

@@ -0,0 +1,19 @@
logfile = /var/log/meteo/superweather.log
# node for clients connection, if you point localost:xxxx, then NET socket would have only local access
port = 4444
# logging level: 0 - ERR, 1 - WARN, 2 - MSG, 3 - DBG, 4 - all shit
verbose = 2
# path to local command UNIX-socket (first '@' means anonymous)
sockpath = "@weather"
# sensors polling time - 1s
pollt = 1
# try to reinit dead sensors each 10s
reinit_delay = 10
# !!! Point plugins in order of meaning: the most important are first !!!
# see help for plugins format
plugin = libwxa100.so:D:/dev/pl2303_0
plugin = libhydreon.so:D:/dev/ch340_0:1200
plugin = libbtameteo.so
# this should be last as almost a half of its sensors are broken
plugin = libreinhardt.so:D:/dev/ttyS0

View File

@@ -29,105 +29,39 @@
#include <sys/un.h> // unix socket
#include <usefull_macros.h>
#include "fd.h"
/**
* @brief openserial - try to open serial device
* @param path - path to device and speed, colon-separated (without given speed assume 9600)
* @return -1 if failed or opened FD
* WARNING!!! Memory leakage danger. Don't call this function too much times!
*/
static int openserial(char *path){
static int openserial(const char *path){
FNAME();
int speed = 9600; // default speed
char *colon = strchr(path, ':');
char *str = strdup(path);
char *colon = strchr(str, ':');
if(colon){
*colon++ = 0;
if(!sl_str2i(&speed, colon)){
WARNX("Wrong speed settings: '%s'", colon);
FREE(str);
return -1;
}
}
sl_tty_t *serial = sl_tty_new(path, speed, BUFSIZ);
sl_tty_t *serial = sl_tty_new(str, speed, BUFSIZ);
if(!serial || !sl_tty_open(serial, TRUE)){
WARN("Can't open %s @ speed %d", path, speed);
return -1;
}
DBG("Opened %s @ %d", path, speed);
return serial->comfd;
}
static char *convunsname(const char *path){
char *apath = MALLOC(char, 106);
if(*path == 0 || *path == '@'){
DBG("convert name starting from 0 or @");
apath[0] = 0;
strncpy(apath+1, path+1, 104);
}else if(strncmp("\\0", path, 2) == 0){
DBG("convert name starting from \\0");
apath[0] = 0;
strncpy(apath+1, path+2, 104);
}else strncpy(apath, path, 105);
return apath;
}
/**
* @brief opensocket - try to open socket
* @param sock - UNIX socket path or hostname:port for INET socket
* @param type - UNIX or INET
* @return -1 if failed or opened FD
*/
static int opensocket(char *path, sl_socktype_e type){
FNAME();
DBG("path: '%s'", path);
int sock = -1;
struct addrinfo ai = {0}, *res = &ai;
struct sockaddr_un unaddr = {0};
char *node = path, *service = NULL;
ai.ai_socktype = 0; // try to get socket type from `getaddrinfo`
switch(type){
case SOCKT_UNIX:
{
char *str = convunsname(path);
if(!str) return -1;
unaddr.sun_family = AF_UNIX;
ai.ai_addr = (struct sockaddr*) &unaddr;
ai.ai_addrlen = sizeof(unaddr);
memcpy(unaddr.sun_path, str, 106);
WARN("Can't open %s @ speed %d", str, speed);
FREE(str);
ai.ai_family = AF_UNIX;
}
break;
case SOCKT_NET:
case SOCKT_NETLOCAL:
ai.ai_family = AF_INET;
char *delim = strchr(path, ':');
if(delim){
*delim = 0;
service = delim+1;
if(delim == path) node = NULL; // only port
}
DBG("node: '%s', service: '%s'", node, service);
int e = getaddrinfo(node, service, &ai, &res);
if(e){
WARNX("getaddrinfo(): %s", gai_strerror(e));
return -1;
}
for(struct addrinfo *p = res; p; p = p->ai_next){
if((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) continue;
DBG("Try proto %d, type %d", p->ai_protocol, p->ai_socktype);
if(connect(sock, p->ai_addr, p->ai_addrlen) == -1){
WARN("connect()");
close(sock); sock = -1;
} else break;
}
break;
default: // never reached
WARNX("Unsupported socket type %d", type);
return -1;
}
DBG("FD: %d", sock);
return sock;
DBG("Opened %s @ %d", str, speed);
FREE(str);
int comfd = serial->comfd;
FREE(serial->portname);
FREE(serial->buf);
FREE(serial->format);
FREE(serial);
return comfd;
}
/**
@@ -136,18 +70,21 @@ static int opensocket(char *path, sl_socktype_e type){
* WARNING!!! Contents of `path` would be modified in this function!
* @return opened file descriptor or -1 in case of error
*/
int getFD(char *path){
int getFD(const char *path){
if(!path || !*path || strlen(path) < 2) return -1;
char type = *path;
if(path[1] != ':') return -1; // after protocol letter should be delimeter
path += 2;
if(!*path) return -1; // empty path
switch(type){
case 'D': // serial device
return openserial(path);
case 'N': // INET socket
return opensocket(path, SOCKT_NET);
//return opensocket(path, SOCKT_NET);
return sl_sock_open(SOCKT_NET, path, 0, 0);
case 'U': // UNIX socket
return opensocket(path, SOCKT_UNIX);
//return opensocket(path, SOCKT_UNIX);
return sl_sock_open(SOCKT_UNIX, path, 0, 0);
}
WARNX("Wrong plugin format: '%c', should be 'D', 'N' or 'U'", type);
return -1;

View File

@@ -1,21 +0,0 @@
/*
* This file is part of the weatherdaemon project.
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
int getFD(char *path);

View File

@@ -40,12 +40,12 @@ void signals(int signo){
LOGDBG("Child gotta signal %d", signo);
if(signo == SIGUSR1){
forbid_observations(1);
LOGMSG("Got signal `observations forbidden`");
LOGWARN("Got signal `observations forbidden`, set FORBIDDEN");
signal(signo, signals);
return;
}else if(signo == SIGUSR2){
forbid_observations(0);
LOGMSG("Got signal `observations permitted`");
LOGWARN("Got signal `observations permitted`, clear FORBIDDEN");
signal(signo, signals);
return;
}
@@ -74,6 +74,7 @@ int main(int argc, char **argv){
sl_init();
GP = parse_args(argc, argv);
if(!GP) ERRX("Error parsing args");
sl_check4running((char*)__progname, GP->pidfile);
if(!GP->sockname) ERRX("Point command socket name");
if(GP->logfile){
sl_loglevel_e lvl = LOGLEVEL_ERR + GP->verb;
@@ -99,7 +100,6 @@ int main(int argc, char **argv){
ERRX("Can't find any sensor plugin");
}
if(GP->nplugins && GP->nplugins != nopened) LOGWARN("Work without some plugins");
sl_check4running((char*)__progname, GP->pidfile);
#ifndef EBUG
sl_daemonize();
while(1){ // guard for dead processes
@@ -121,7 +121,7 @@ int main(int argc, char **argv){
signal(SIGUSR1, signals);
signal(SIGUSR2, signals);
if(!start_servers(GP->port, GP->sockname)) ERRX("Can't run server's threads");
while(1);
//while(1) pause();
//WARNX("TEST ends");
//signals(0);
return 0; // never reached

View File

@@ -52,6 +52,11 @@ enum{
NSKYTEMP,
NCOMMWEATH,
NLASTAHTUNG,
NAHTUNGRSN,
// NLIGHTDIST,
NBADWEATH,
NTERRWEATH,
NFORCEDSHTDN,
NAMOUNT_OF_DATA
};
@@ -72,12 +77,73 @@ static val_t collected_data[NAMOUNT_OF_DATA] = {
[NMIST] = {.sense = VAL_BROKEN, .type = VALT_UINT, .meaning = IS_MIST},
[NCLOUDS] = {.sense = VAL_BROKEN, .type = VALT_FLOAT, .meaning = IS_CLOUDS},
[NSKYTEMP] = {.sense = VAL_BROKEN, .type = VALT_FLOAT, .meaning = IS_SKYTEMP},
// [NLIGHTDIST] = {.sense = VAL_FORCEDSHTDN, .type = VALT_FLOAT, .meaning = IS_LIGTDIST},
// these are calculated values
[NCOMMWEATH] = {.value.i = 0, .sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_OTHER, .name = "WEATHER", .comment = "Weather level (0 - good, 3 - obs. prohibited)"},
[NLASTAHTUNG] = {.value.i = 0, .sense = VAL_RECOMMENDED, .type = VALT_UINT, .meaning = IS_OTHER, .name = "EVTTIME", .comment = "UNIX-time of last weather level increasing"},
[NCOMMWEATH] = {.sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_OTHER, .name = "WEATHER", .comment = "Weather (0..3: good/bad/terrible/prohibited)"},
[NLASTAHTUNG] = {.sense = VAL_RECOMMENDED, .type = VALT_UINT, .meaning = IS_OTHER, .name = "EVTTIME", .comment = "UNIX-time of last weather level changing"},
[NAHTUNGRSN] = {.sense = VAL_RECOMMENDED, .type = VALT_STRING, .meaning = IS_OTHER, .name = "EVTRSN", .comment = "Last weather level increasing reason"},
// virtual values for weather level / flags changing
[NBADWEATH] = {.sense = VAL_BROKEN, .type = VALT_UINT, .meaning = IS_BADWEATH, .name = "BADWEATH", .comment = "Flag changing weather level to 'BAD'"},
[NTERRWEATH] = {.sense = VAL_BROKEN, .type = VALT_UINT, .meaning = IS_TERRIBLEWEATH, .name = "TERWEATH", .comment = "Flag changing weather level to 'TERRIBLE'"},
[NFORCEDSHTDN] = {.sense = VAL_FORCEDSHTDN, .type = VALT_UINT, .meaning = IS_FORCEDSHTDN, .name = "FORCEOFF", .comment = "All should be powered off NOW"},
// {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_OTHER},
};
// additional fields marked as `IS_OTHER` gathered from different stations
static int Nadditional = 0;
static val_t *additional_data = NULL;
/**
* @brief weather_level - set/clear weather level
* @param newlvl - -1 for getter or 0..3 for setter
* @return current weather level
*/
int weather_level(int newlvl){
if(newlvl > -1 && newlvl <= WEATHER_PROHIBITED){
pthread_mutex_lock(&datamutex);
uint32_t curt = time(NULL);
int oldlvl = collected_data[NCOMMWEATH].value.u;
collected_data[NCOMMWEATH].value.u = newlvl;
collected_data[NCOMMWEATH].time = curt;
sprintf(collected_data[NAHTUNGRSN].value.str, "MANUAL");
collected_data[NAHTUNGRSN].time = curt;
collected_data[NLASTAHTUNG].value.u = curt;
collected_data[NLASTAHTUNG].time = curt;
pthread_mutex_unlock(&datamutex);
LOGWARN("Manual changing of weather level from %d to %d", oldlvl, newlvl);
}
pthread_mutex_lock(&datamutex);
int curlevel = collected_data[NCOMMWEATH].value.u;
pthread_mutex_unlock(&datamutex);
return curlevel;
}
/**
* @brief force_off - set/clear `force off` flag
* @param flag - 1 to set, 0 to clear or -1 to get
* @return current value
*/
int force_off(int flag){
DBG("Force OFF to %d", flag);
if(flag > -1 && flag < 2){
pthread_mutex_lock(&datamutex);
uint32_t curt = time(NULL);
int oldval = collected_data[NFORCEDSHTDN].value.u;
collected_data[NFORCEDSHTDN].value.u = (uint32_t)flag;
if(flag) collected_data[NFORCEDSHTDN].time = curt;
sprintf(collected_data[NAHTUNGRSN].value.str, "MANUAL");
collected_data[NAHTUNGRSN].time = curt;
collected_data[NLASTAHTUNG].value.u = curt;
collected_data[NLASTAHTUNG].time = curt;
pthread_mutex_unlock(&datamutex);
LOGWARN("Manual changing of FORCED SHUTDOWN from %d to %d", oldval, flag);
}
pthread_mutex_lock(&datamutex);
flag = collected_data[NFORCEDSHTDN].value.u;
pthread_mutex_unlock(&datamutex);
return flag;
}
typedef struct{
double array[MAX_HISTORY];
double sum;
@@ -169,37 +235,53 @@ static double get_max_forT(sliding_max_t *sm, time_t tcutoff){
}
int collected_amount(){
return NAMOUNT_OF_DATA;
return NAMOUNT_OF_DATA + Nadditional;
}
int get_collected(val_t *val, int N){
if(!val || N < 0 || N >= NAMOUNT_OF_DATA){
if(!val || N < 0 || N >= NAMOUNT_OF_DATA + Nadditional){
DBG("Wrong number (%d) requested or no place for data", N);
return FALSE;
}
pthread_mutex_lock(&datamutex);
DBG("Copied data of %d", N);
*val = collected_data[N];
val_t *dptr = (N < NAMOUNT_OF_DATA) ? &collected_data[N] : &additional_data[N-NAMOUNT_OF_DATA];
#ifdef EBUG
char buf[KEY_LEN+1];
get_fieldname(dptr, buf);
DBG("Copied data of %d (u=%d, nm=%s, t=%zd)", N, dptr->value.u, buf, dptr->time);
#endif
*val = *dptr;
pthread_mutex_unlock(&datamutex);
return TRUE;
}
// take only data with `sense value` less than collected have
static void fix_new_data(val_t *collected, val_t *fresh){
/**
* @brief fix_new_data - take only data with `sense value` less than collected have and more recent
* @param collected - pointer to collected data
* @param fresh - pointer to fresh data
* @param force - ==1 to force changing even if this data is older
*/
static void fix_new_data(val_t *collected, const val_t *fresh, int force){
if(!collected || !fresh) return;
if(collected->time > fresh->time) return;
if(collected->time >= fresh->time){
if(!force) return;
//DBG("Forced, collected=%g, fresh=%g", val2d(collected), val2d(fresh));
if(collected->time - fresh->time > 60) return;
//DBG("Not too old");
}
// lower `collected` level if data is too old
if(fresh->time - collected->time > 60) collected->sense = VAL_UNNECESSARY;
if(fresh->time - collected->time > WeatherConf.ahtung_delay) collected->sense = VAL_UNNECESSARY;
if(collected->sense < fresh->sense) return;
if(collected->sense != fresh->sense) collected->sense = fresh->sense; // take new lower level
if(collected->sense != fresh->sense) collected->sense = fresh->sense; // take new level
//DBG("Refresh collected value");
collected->time = fresh->time;
if(collected->type == fresh->type){ // good case
memcpy(&collected->value, &fresh->value, sizeof(num_t));
//DBG("Types are the same");
collected->value = fresh->value;
return;
}
// bad case: have different types
// DON'T convert between string and number types!
switch(collected->type){
case VALT_UINT:
switch(fresh->type){
@@ -237,44 +319,97 @@ static void fix_new_data(val_t *collected, val_t *fresh){
default: break;
}
break;
default: break;
}
}
static void chkweatherlevel(int *curlevel, double curvalue, weather_cond_t *curcond, int *ahtungtime){
double good = curcond->good, bad = curcond->bad, terrible = curcond->terrible;
// find value.name in `additional_data`, if need - allocate new memory and update
static void update_additional(val_t *value){
if(!value) return;
int idx = 0;
for(; idx < Nadditional; ++idx){
if(0 == strcmp(additional_data[idx].name, value->name)) break;
}
if(idx == Nadditional){ // not found -> allocate
additional_data = realloc(additional_data, sizeof(val_t) * (++Nadditional));
if(!additional_data){
LOGERR("update_additional() can't realloc()");
ERR("realloc()");
}
memcpy(&additional_data[idx], value, sizeof(val_t));
DBG("Allocated new field: %s", value->name);
}else fix_new_data(&additional_data[idx], value, 0);
}
/**
* @brief chkweatherlevel - increase weather level if need, also check force shutdown flags
* @param curlevel - current max weather level
* @param curvalue - current value of sensor data
* @param curcond - conditions for given value
* @return 0 if level wasn't changed, or +1 if was increased
*/
static int chkweatherlevel(uint32_t *curlevel, double curvalue, weather_cond_t const *curcond){
int rtn = 0;
double good = curcond->good, bad = curcond->bad, terrible = curcond->terrible, prohibited = curcond->prohibited;
int haveproh = (prohibited > terrible) ? 1 : 0; // value have `prohibited` field
if(curcond->negflag){ // negate
curvalue = -curvalue;
good = -good;
bad = -bad;
terrible = -terrible;
prohibited = -prohibited;
}
int newlevel = -1;
if(curvalue > terrible) newlevel = WEATHER_TERRIBLE;
else if(curvalue > bad) newlevel = WEATHER_BAD;
if(haveproh && curvalue > prohibited){
newlevel = WEATHER_PROHIBITED;
DBG("---> new level is PROHIBITED, val=%g", (curcond->negflag) ? -curvalue : curvalue);
}else if(curvalue > terrible){
newlevel = WEATHER_TERRIBLE;
DBG("---> new level is TERRIBLE, val=%g", (curcond->negflag) ? -curvalue : curvalue);
}else if(curvalue > bad) newlevel = WEATHER_BAD;
else if(curvalue < good) newlevel = WEATHER_GOOD;
if(newlevel == -1) return;
if(newlevel == -1) return 0;
time_t curt = time(NULL);
if(newlevel > *curlevel){
DBG("newlevel: %d, current: %d INCREASED", newlevel, *curlevel);
*curlevel = newlevel;
if(*ahtungtime < curt) *ahtungtime = (int) curt; // refresh event time
}else if(newlevel < *curlevel){ // check timeout to make level lower
if(curt - *ahtungtime > WeatherConf.ahtung_delay){
DBG("newlevel: %d, current: %d DECREASED", newlevel, *curlevel);
*curlevel = newlevel;
}
if(curcond->shtdnflag && newlevel >= WEATHER_TERRIBLE){
DBG("Forced shutdown flag is set, curvalue: %g", (curcond->negflag) ? -curvalue : curvalue);
// set to one collected data flag and its time
val_t *f = &collected_data[NFORCEDSHTDN];
f->value.u = 1;
f->time = (int) curt;
DBG("forced = %u", collected_data[NFORCEDSHTDN].value.u);
// and set current weather level to prohibited
newlevel = WEATHER_PROHIBITED;
rtn = 1;
}
if((uint32_t)newlevel > *curlevel){
// TODO: add logging
DBG("local level increased to %d", newlevel);
*curlevel = (uint32_t)newlevel;
rtn = 1;
}
return rtn;
}
// conditions for "bad weather" flag (if it ==1 set BAD WEATH)
static weather_cond_t const badweathflag = {.good = 0.1, .bad = 0.5, .terrible = 2.};
// conditions for "terrible weather" flag
static weather_cond_t const terrweathflag = {.good = 0.1, .bad = 0.5, .terrible = 0.7};
// conditions for "prohibited weather" flag
static weather_cond_t const prohibweathflag = {.good = 0.1, .bad = 0.5, .terrible = 0.6, .prohibited = 0.7};
// conditions for "force shutdown" flag
static weather_cond_t const shtdnflag = {.good = 0.1, .bad = 0.5, .terrible = 0.7, .shtdnflag = 1, .prohibited = 0.8};
void refresh_sensval(sensordata_t *s){
//FNAME();
static time_t poll_time = 0;
//static time_t poll_time = 0;
static char reason[KEY_LEN+1] = {0}; // reason of weather level increasing
val_t value;
if(!s || !s->get_value) return;
if(poll_time == 0) poll_time = get_pollT();
int curlevel = collected_data[NCOMMWEATH].value.i;
int curahtungtime = collected_data[NLASTAHTUNG].value.i;
//if(poll_time == 0) poll_time = get_pollT();
static uint32_t curlevel = 0; // this is worse weather leavel, start from best (collect by all sensors through 3*tpoll)
static time_t lasttupdate = 0; // last update time of weather level
time_t curtime = time(NULL);
time_t tpoll = get_pollT(), _3tpoll = 3*tpoll;
double dir = -100., dir2 = -100.; // mean wind directions
//DBG("%d meteo values", s->Nvalues);
for(int i = 0; i < s->Nvalues; ++i){
@@ -282,22 +417,25 @@ void refresh_sensval(sensordata_t *s){
if(!s->get_value(s, &value, i) || value.sense > VAL_RECOMMENDED) continue;
//DBG("got value");
int idx = -1;
double curvalue;
weather_cond_t *curcond = NULL;
double curvalue = val2d(&value);
const weather_cond_t *curcond = NULL;
switch(value.meaning){
case IS_WIND:
idx = NWIND;
curvalue = (double) value.value.f;
curcond = &WeatherConf.wind;
// protect collected wind speeds from destruction in case of simultaneous acces from different plugins
pthread_mutex_lock(&datamutex);
add_windspeed(&windspeeds, curvalue, curtime);
pthread_mutex_unlock(&datamutex);
break;
case IS_WINDDIR:
idx = NWINDDIR;
pthread_mutex_lock(&datamutex);
wind_dir_add(collected_data[NWIND].value.f, value.value.f, &dir, &dir2);
pthread_mutex_unlock(&datamutex);
break;
case IS_HUMIDITY:
idx = NHUMIDITY;
curvalue = (double) value.value.f;
curcond = &WeatherConf.humidity;
break;
case IS_AMB_TEMP:
@@ -308,39 +446,67 @@ void refresh_sensval(sensordata_t *s){
break;
case IS_PRECIP:
idx = NPRECIP;
if(value.value.i && curlevel < WEATHER_TERRIBLE){
curlevel = WEATHER_TERRIBLE;
curahtungtime = curtime;
}
curcond = &prohibweathflag;
if(curvalue > 0.) DBG("IS_PRECIP == 1 !!!");
break;
case IS_PRECIP_LEVEL:
idx = NPRECIP_LEVEL;
curcond = &terrweathflag;
break;
case IS_MIST:
idx = NMIST;
if(value.value.i && curlevel < WEATHER_TERRIBLE){
curahtungtime = curtime;
curlevel = WEATHER_TERRIBLE;
}
curcond = &terrweathflag;
break;
case IS_CLOUDS:
idx = NCLOUDS;
curvalue = (double) value.value.f;
curcond = &WeatherConf.clouds;
break;
case IS_SKYTEMP:
idx = NSKYTEMP;
curvalue = (double) value.value.f;
curcond = &WeatherConf.sky;
break;
/*case IS_LIGTDIST:
idx = NLIGHTDIST;
curcond = &WeatherConf.ligtdist;
break;*/
case IS_BADWEATH:
idx = NBADWEATH;
curcond = &badweathflag;
break;
case IS_TERRIBLEWEATH:
idx = NTERRWEATH;
curcond = &terrweathflag;
break;
case IS_FORCEDSHTDN:
idx = NFORCEDSHTDN;
curcond = &shtdnflag;
//DBG("%s have shtdn flag", value.name);
break;
default : break;
}
if(value.meaning == IS_OTHER){ // check for new or existant field in `additional_data`
update_additional(&value);
continue;
}
if(idx < 0 || idx >= NAMOUNT_OF_DATA) continue;
//DBG("IDX=%d", idx);
pthread_mutex_lock(&datamutex);
fix_new_data(&collected_data[idx], &value);
int force = 0;
if(curcond){
int oldshtdn = collected_data[NFORCEDSHTDN].value.u;
if(1 == chkweatherlevel(&curlevel, curvalue, curcond)){
get_fieldname(&value, reason); // copy to `reason` reason of last level increasing
force = 1;
DBG("reason: %s; forceflag=%u, old=%d", reason, collected_data[NFORCEDSHTDN].value.u, oldshtdn);
if(collected_data[NFORCEDSHTDN].value.u - oldshtdn == 1){ // got shutdown
LOGWARN("Forced shutdown flag is set by '%s' of '%s'", reason, s->name);
}
}
}
if(idx == NFORCEDSHTDN && value.value.u == 0){
//DBG("Don't clear forced flag by other station");
}else fix_new_data(&collected_data[idx], &value, force);
pthread_mutex_unlock(&datamutex);
if(!Forbidden && curcond) chkweatherlevel(&curlevel, curvalue, curcond, &curahtungtime);
}
pthread_mutex_lock(&datamutex);
// refresh max
@@ -352,43 +518,69 @@ void refresh_sensval(sensordata_t *s){
collected_data[NWINDDIR2].value.f = (float) dir2;
collected_data[NWINDDIR2].time = curtime;
}
collected_data[NWINDMAX].value.f = (float) get_current_max(&windspeeds);
collected_data[NWINDMAX].time = curtime;
collected_data[NWINDMAX1].value.f = (float) get_max_forT(&windspeeds, curtime - T_ONE_HOUR);
collected_data[NWINDMAX1].time = curtime;
if(curtime - collected_data[NWIND].time < tpoll + 1){
collected_data[NWINDMAX].value.f = (float) get_current_max(&windspeeds);
collected_data[NWINDMAX].time = curtime;
collected_data[NWINDMAX1].value.f = (float) get_max_forT(&windspeeds, curtime - T_ONE_HOUR);
collected_data[NWINDMAX1].time = curtime;
}
//DBG("check ahtung");
if(Forbidden) collected_data[NCOMMWEATH].value.i = WEATHER_PROHIBITED;
else collected_data[NCOMMWEATH].value.i = curlevel;
if(collected_data[NLASTAHTUNG].value.i < curahtungtime) collected_data[NLASTAHTUNG].value.i = curahtungtime;
collected_data[NCOMMWEATH].time = curtime;
collected_data[NLASTAHTUNG].time = curtime;
time_t _2update = lasttupdate + _3tpoll;
if(Forbidden){
collected_data[NCOMMWEATH].value.u = WEATHER_PROHIBITED;
collected_data[NCOMMWEATH].time = curtime;
}else if(curtime >= _2update){ // need to update weather level
if(collected_data[NCOMMWEATH].value.u > curlevel){ // check timeout to make level lower
// DBG("curtime: %zd, curahtt: %d, diff: %zd, delay: %d", curtime, collected_data[NLASTAHTUNG].value.u, curtime - collected_data[NLASTAHTUNG].value.u, WeatherConf.ahtung_delay);
if(curtime - collected_data[NLASTAHTUNG].value.u > WeatherConf.ahtung_delay){
DBG("newlevel: %d, current: %d DECREASED", curlevel, collected_data[NCOMMWEATH].value.u);
if(curlevel < WEATHER_TERRIBLE){ // clear forced shutdown flag
if(collected_data[NFORCEDSHTDN].value.u){
LOGMSG("Clear forced shutdown flag by '%s' and set weather level to %d", s->name, WEATHER_TERRIBLE);
DBG("Clear FORCED SHUTDOWN flag");
collected_data[NCOMMWEATH].value.u = WEATHER_TERRIBLE;
collected_data[NFORCEDSHTDN].value.u = 0;
}else collected_data[NCOMMWEATH].value.u = curlevel;
}else --collected_data[NCOMMWEATH].value.u;
collected_data[NLASTAHTUNG].value.u = curtime;
LOGMSG("Station '%s', decrease weather level to %d", s->name, collected_data[NCOMMWEATH].value.u);
}
}else{
if(collected_data[NCOMMWEATH].value.u < curlevel){ // set to worse
DBG("newlevel: %d, current: %d INCREASED", curlevel, collected_data[NCOMMWEATH].value.u);
LOGWARN("Station '%s', sensor '%s', increase weather level to %d", s->name, reason, curlevel);
collected_data[NCOMMWEATH].value.u = curlevel;
if(1 < snprintf(collected_data[NAHTUNGRSN].value.str, STRT_LEN+1, "%s", reason))
collected_data[NAHTUNGRSN].time = curtime;
}
if(curlevel){
collected_data[NLASTAHTUNG].value.u = curtime; // refresh last ahtung time only for level > good
collected_data[NLASTAHTUNG].time = curtime;
collected_data[NAHTUNGRSN].time = curtime;
}
}
lasttupdate = curtime;
curlevel = 0; // wait for next collected max level
collected_data[NCOMMWEATH].time = curtime; // refresh `common weather` updating time
}
pthread_mutex_unlock(&datamutex);
//DBG("Refreshed");
}
// set/clear `forbid` flag (by signals USR1 and USR2)
void forbid_observations(int f){
pthread_mutex_lock(&datamutex);
if(f) Forbidden = 1;
else Forbidden = 0;
int curt = (int) time(NULL);
// don't use mutexes here as this function called from signal handler
collected_data[NLASTAHTUNG].value.u = curt;
collected_data[NLASTAHTUNG].time = curt;
sprintf(collected_data[NAHTUNGRSN].value.str, "FORBID");
collected_data[NAHTUNGRSN].time = curt;
pthread_mutex_unlock(&datamutex);
DBG("Change FORBID status to %d", f);
}
#if 0
// main cycle
void run_mainweather(){
int N = get_nplugins();
if(N < 1) return;
poll_time = get_pollT();
while(1){
int nactive = 0;
pthread_mutex_lock(&datamutex);
for(int i = N-1; i > -1; --i){ // the most important is the last
sensordata_t *s = get_plugin(i);
if(!s || !sensor_alive(s)) continue;
++nactive;
}
pthread_mutex_unlock(&datamutex);
if(nactive == 0) break; // no active sensors
usleep(10000);
}
LOGERR("Main weather collector died: all sensors lost");
}
#endif
// `forbid` flag getter
int is_forbidden(){ return Forbidden; }

View File

@@ -25,18 +25,21 @@ enum{
WEATHER_GOOD, // good to start observations
WEATHER_BAD, // bad for start, but can run
WEATHER_TERRIBLE, // need close the dome
WEATHER_PROHIBITED, // need close all, park and power off <-- by SIGUSR1/SIGUSR2
WEATHER_PROHIBITED, // need close all, park and power off equipment <-- by SIGUSR1/SIGUSR2 + by FORCEOFF
};
typedef struct{
double good; // if value less than this, weather is good
double bad; // if value greater than this, weather is bad
double terrible; // if value greater than this, weather is terrible
double prohibited; // ...
int negflag; // reversal flag (good if > val, etc)
int shtdnflag; // ==1 to shut down if `terrible`
} weather_cond_t;
typedef struct{
int ahtung_delay; // delay to change "bad weather" to good after last "bad event"
int reinit_delay; // delay to check all sensors and reinit dead
// wind, m/s
weather_cond_t wind;
// humidity, %%
@@ -45,6 +48,8 @@ typedef struct{
weather_cond_t clouds;
// sky temperature minus ambient temperature, degC
weather_cond_t sky;
// distance to lightning
weather_cond_t ligtdist;
} weather_conf_t;
// defined in cmdlnopts.c
@@ -54,5 +59,11 @@ int collected_amount();
int get_collected(val_t *val, int N);
void forbid_observations(int f);
int is_forbidden();
void refresh_sensval(sensordata_t *s);
int force_off(int flag);
int weather_level(int new);
//void run_mainweather();

View File

@@ -27,19 +27,40 @@ if(HYDREON)
endif()
if(BTAMETEO)
add_library(btameteo SHARED btameteo.c bta_shdata.c)
add_library(btameteo SHARED btameteo.c bta_shdata.c)
target_link_libraries(btameteo -lcrypt)
list(APPEND LIBS btameteo)
endif()
if(REINHARDT)
add_library(reinhardt SHARED reinhardt.c)
add_library(reinhardt SHARED reinhardt.c)
list(APPEND LIBS reinhardt)
endif()
if(WXA100)
add_library(wxa100 SHARED wxa100.c)
add_library(wxa100 SHARED wxa100.c)
list(APPEND LIBS wxa100)
endif()
if(SNMP)
add_library(snmp SHARED snmp.c)
find_program(NETSNMP_CONFIG_BIN net-snmp-config)
if(NETSNMP_CONFIG_BIN)
# Capture linker libraries
execute_process(COMMAND ${NETSNMP_CONFIG_BIN} --libs
OUTPUT_VARIABLE NETSNMP_LIBS
OUTPUT_STRIP_TRAILING_WHITESPACE)
else()
message(FATAL_ERROR "net-snmp-config not found. Please install net-snmp package.")
endif()
message("SNMP: ${NETSNMP_LIBS}")
target_link_libraries(snmp PRIVATE ${NETSNMP_LIBS})
list(APPEND LIBS snmp)
endif()
if(LIGHTNING)
add_library(lightning SHARED lightning.c)
list(APPEND LIBS lightning)
endif()
install(TARGETS ${LIBS} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})

View File

@@ -0,0 +1,149 @@
OBJECT_NAME,OBJECT_IDENTIFIER,OBJECT_DATA_TYPE,OBJECT_PREMISSIONS,OBJECT_CLASS,OBJECT_NODE_TYPE,OBJECT_DESCRIPTION
upsMIB,1.3.6.1.2.1.33,,,moduleidentity,,"The MIB module to describe Uninterruptible Power Supplies."
upsObjects,1.3.6.1.2.1.33.1,,,objectidentity,,""
upsIdent,1.3.6.1.2.1.33.1.1,,,objectidentity,,""
upsIdentManufacturer,1.3.6.1.2.1.33.1.1.1,displaystring,read-only,objecttype,scalar,"The name of the UPS manufacturer."
upsIdentModel,1.3.6.1.2.1.33.1.1.2,displaystring,read-only,objecttype,scalar,"The UPS Model designation."
upsIdentUPSSoftwareVersion,1.3.6.1.2.1.33.1.1.3,displaystring,read-only,objecttype,scalar,"The UPS firmware/software version(s). This variable may or may not have the same value as upsIdentAgentSoftwareVersion in some implementations."
upsIdentAgentSoftwareVersion,1.3.6.1.2.1.33.1.1.4,displaystring,read-only,objecttype,scalar,"The UPS agent software version. This variable may or may not have the same value as upsIdentUPSSoftwareVersion in some implementations."
upsIdentName,1.3.6.1.2.1.33.1.1.5,displaystring,read-write,objecttype,scalar,"A string identifying the UPS. This object should be set by the administrator."
upsIdentAttachedDevices,1.3.6.1.2.1.33.1.1.6,displaystring,read-write,objecttype,scalar,"A string identifying the devices attached to the output(s) of the UPS. This object should be set by the administrator."
upsBattery,1.3.6.1.2.1.33.1.2,,,objectidentity,,""
upsBatteryStatus,1.3.6.1.2.1.33.1.2.1,integer,read-only,objecttype,scalar,"The indication of the capacity remaining in the UPS system's batteries. A value of batteryNormal indicates that the remaining run-time is greater than upsConfigLowBattTime. A value of batteryLow indicates that the remaining battery run-time is less than or equal to upsConfigLowBattTime. A value of batteryDepleted indicates that the UPS will be unable to sustain the present load when and if the utility power is lost (including the possibility that the utility power is currently absent and the UPS is unable to sustain the output). Enumeration: 'unknown': 1, 'batteryNormal': 2, 'batteryDepleted': 4, 'batteryLow': 3."
upsSecondsOnBattery,1.3.6.1.2.1.33.1.2.2,nonnegativeinteger,read-only,objecttype,scalar,"If the unit is on battery power, the elapsed time since the UPS last switched to battery power, or the time since the network management subsystem was last restarted, whichever is less. Zero shall be returned if the unit is not on battery power."
upsEstimatedMinutesRemaining,1.3.6.1.2.1.33.1.2.3,positiveinteger,read-only,objecttype,scalar,"An estimate of the time to battery charge depletion under the present load conditions if the utility power is off and remains off, or if it were to be lost and remain off."
upsEstimatedChargeRemaining,1.3.6.1.2.1.33.1.2.4,integer,read-only,objecttype,scalar,"An estimate of the battery charge remaining expressed as a percent of full charge."
upsBatteryVoltage,1.3.6.1.2.1.33.1.2.5,nonnegativeinteger,read-only,objecttype,scalar,"The magnitude of the present battery voltage."
upsBatteryCurrent,1.3.6.1.2.1.33.1.2.6,integer32,read-only,objecttype,scalar,"The present battery current."
upsBatteryTemperature,1.3.6.1.2.1.33.1.2.7,integer32,read-only,objecttype,scalar,"The ambient temperature at or near the UPS Battery casing."
upsInput,1.3.6.1.2.1.33.1.3,,,objectidentity,,""
upsInputLineBads,1.3.6.1.2.1.33.1.3.1,counter32,read-only,objecttype,scalar,"A count of the number of times the input entered an out-of-tolerance condition as defined by the manufacturer. This count is incremented by one each time the input transitions from zero out-of-tolerance lines to one or more input lines out-of-tolerance."
upsInputNumLines,1.3.6.1.2.1.33.1.3.2,nonnegativeinteger,read-only,objecttype,scalar,"The number of input lines utilized in this device. This variable indicates the number of rows in the input table."
upsInputTable,1.3.6.1.2.1.33.1.3.3,,no-access,objecttype,table,"A list of input table entries. The number of entries is given by the value of upsInputNumLines."
upsInputEntry,1.3.6.1.2.1.33.1.3.3.1,,no-access,objecttype,row,"An entry containing information applicable to a particular input line."
upsInputLineIndex,1.3.6.1.2.1.33.1.3.3.1.1,positiveinteger,no-access,objecttype,column,"The input line identifier."
upsInputFrequency,1.3.6.1.2.1.33.1.3.3.1.2,nonnegativeinteger,read-only,objecttype,column,"The present input frequency."
upsInputVoltage,1.3.6.1.2.1.33.1.3.3.1.3,nonnegativeinteger,read-only,objecttype,column,"The magnitude of the present input voltage."
upsInputCurrent,1.3.6.1.2.1.33.1.3.3.1.4,nonnegativeinteger,read-only,objecttype,column,"The magnitude of the present input current."
upsInputTruePower,1.3.6.1.2.1.33.1.3.3.1.5,nonnegativeinteger,read-only,objecttype,column,"The magnitude of the present input true power."
upsOutput,1.3.6.1.2.1.33.1.4,,,objectidentity,,""
upsOutputSource,1.3.6.1.2.1.33.1.4.1,integer,read-only,objecttype,scalar,"The present source of output power. The enumeration none(2) indicates that there is no source of output power (and therefore no output power), for example, the system has opened the output breaker. Enumeration: 'none': 2, 'normal': 3, 'battery': 5, 'reducer': 7, 'other': 1, 'bypass': 4, 'booster': 6."
upsOutputFrequency,1.3.6.1.2.1.33.1.4.2,nonnegativeinteger,read-only,objecttype,scalar,"The present output frequency."
upsOutputNumLines,1.3.6.1.2.1.33.1.4.3,nonnegativeinteger,read-only,objecttype,scalar,"The number of output lines utilized in this device. This variable indicates the number of rows in the output table."
upsOutputTable,1.3.6.1.2.1.33.1.4.4,,no-access,objecttype,table,"A list of output table entries. The number of entries is given by the value of upsOutputNumLines."
upsOutputEntry,1.3.6.1.2.1.33.1.4.4.1,,no-access,objecttype,row,"An entry containing information applicable to a particular output line."
upsOutputLineIndex,1.3.6.1.2.1.33.1.4.4.1.1,positiveinteger,no-access,objecttype,column,"The output line identifier."
upsOutputVoltage,1.3.6.1.2.1.33.1.4.4.1.2,nonnegativeinteger,read-only,objecttype,column,"The present output voltage."
upsOutputCurrent,1.3.6.1.2.1.33.1.4.4.1.3,nonnegativeinteger,read-only,objecttype,column,"The present output current."
upsOutputPower,1.3.6.1.2.1.33.1.4.4.1.4,nonnegativeinteger,read-only,objecttype,column,"The present output true power."
upsOutputPercentLoad,1.3.6.1.2.1.33.1.4.4.1.5,integer,read-only,objecttype,column,"The percentage of the UPS power capacity presently being used on this output line, i.e., the greater of the percent load of true power capacity and the percent load of VA."
upsBypass,1.3.6.1.2.1.33.1.5,,,objectidentity,,""
upsBypassFrequency,1.3.6.1.2.1.33.1.5.1,nonnegativeinteger,read-only,objecttype,scalar,"The present bypass frequency."
upsBypassNumLines,1.3.6.1.2.1.33.1.5.2,nonnegativeinteger,read-only,objecttype,scalar,"The number of bypass lines utilized in this device. This entry indicates the number of rows in the bypass table."
upsBypassTable,1.3.6.1.2.1.33.1.5.3,,no-access,objecttype,table,"A list of bypass table entries. The number of entries is given by the value of upsBypassNumLines."
upsBypassEntry,1.3.6.1.2.1.33.1.5.3.1,,no-access,objecttype,row,"An entry containing information applicable to a particular bypass input."
upsBypassLineIndex,1.3.6.1.2.1.33.1.5.3.1.1,positiveinteger,no-access,objecttype,column,"The bypass line identifier."
upsBypassVoltage,1.3.6.1.2.1.33.1.5.3.1.2,nonnegativeinteger,read-only,objecttype,column,"The present bypass voltage."
upsBypassCurrent,1.3.6.1.2.1.33.1.5.3.1.3,nonnegativeinteger,read-only,objecttype,column,"The present bypass current."
upsBypassPower,1.3.6.1.2.1.33.1.5.3.1.4,nonnegativeinteger,read-only,objecttype,column,"The present true power conveyed by the bypass."
upsAlarm,1.3.6.1.2.1.33.1.6,,,objectidentity,,""
upsAlarmsPresent,1.3.6.1.2.1.33.1.6.1,gauge32,read-only,objecttype,scalar,"The present number of active alarm conditions."
upsAlarmTable,1.3.6.1.2.1.33.1.6.2,,no-access,objecttype,table,"A list of alarm table entries. The table contains zero, one, or many rows at any moment, depending upon the number of alarm conditions in effect. The table is initially empty at agent startup. The agent creates a row in the table each time a condition is detected and deletes that row when that condition no longer pertains. The agent creates the first row with upsAlarmId equal to 1, and increments the value of upsAlarmId each time a new row is created, wrapping to the first free value greater than or equal to 1 when the maximum value of upsAlarmId would otherwise be exceeded. Consequently, after multiple operations, the table may become sparse, e.g., containing entries for rows 95, 100, 101, and 203 and the entries should not be assumed to be in chronological order because upsAlarmId might have wrapped. Alarms are named by an AutonomousType (OBJECT IDENTIFIER), upsAlarmDescr, to allow a single table to reflect well known alarms plus alarms defined by a particular implementation, i.e., as documented in the private enterprise MIB definition for the device. No two rows will have the same value of upsAlarmDescr, since alarms define conditions. In order to meet this requirement, care should be taken in the definition of alarm conditions to insure that a system cannot enter the same condition multiple times simultaneously. The number of rows in the table at any given time is reflected by the value of upsAlarmsPresent."
upsAlarmEntry,1.3.6.1.2.1.33.1.6.2.1,,no-access,objecttype,row,"An entry containing information applicable to a particular alarm."
upsAlarmId,1.3.6.1.2.1.33.1.6.2.1.1,positiveinteger,no-access,objecttype,column,"A unique identifier for an alarm condition. This value must remain constant."
upsAlarmDescr,1.3.6.1.2.1.33.1.6.2.1.2,autonomoustype,read-only,objecttype,column,"A reference to an alarm description object. The object referenced should not be accessible, but rather be used to provide a unique description of the alarm condition."
upsAlarmTime,1.3.6.1.2.1.33.1.6.2.1.3,timestamp,read-only,objecttype,column,"The value of sysUpTime when the alarm condition was detected. If the alarm condition was detected at the time of agent startup and presumably existed before agent startup, the value of upsAlarmTime shall equal 0."
upsWellKnownAlarms,1.3.6.1.2.1.33.1.6.3,,,objectidentity,,""
upsAlarmBatteryBad,1.3.6.1.2.1.33.1.6.3.1,,,objectidentity,,"One or more batteries have been determined to require replacement."
upsAlarmOnBattery,1.3.6.1.2.1.33.1.6.3.2,,,objectidentity,,"The UPS is drawing power from the batteries."
upsAlarmLowBattery,1.3.6.1.2.1.33.1.6.3.3,,,objectidentity,,"The remaining battery run-time is less than or equal to upsConfigLowBattTime."
upsAlarmDepletedBattery,1.3.6.1.2.1.33.1.6.3.4,,,objectidentity,,"The UPS will be unable to sustain the present load when and if the utility power is lost."
upsAlarmTempBad,1.3.6.1.2.1.33.1.6.3.5,,,objectidentity,,"A temperature is out of tolerance."
upsAlarmInputBad,1.3.6.1.2.1.33.1.6.3.6,,,objectidentity,,"An input condition is out of tolerance."
upsAlarmOutputBad,1.3.6.1.2.1.33.1.6.3.7,,,objectidentity,,"An output condition (other than OutputOverload) is out of tolerance."
upsAlarmOutputOverload,1.3.6.1.2.1.33.1.6.3.8,,,objectidentity,,"The output load exceeds the UPS output capacity."
upsAlarmOnBypass,1.3.6.1.2.1.33.1.6.3.9,,,objectidentity,,"The Bypass is presently engaged on the UPS."
upsAlarmBypassBad,1.3.6.1.2.1.33.1.6.3.10,,,objectidentity,,"The Bypass is out of tolerance."
upsAlarmOutputOffAsRequested,1.3.6.1.2.1.33.1.6.3.11,,,objectidentity,,"The UPS has shutdown as requested, i.e., the output is off."
upsAlarmUpsOffAsRequested,1.3.6.1.2.1.33.1.6.3.12,,,objectidentity,,"The entire UPS has shutdown as commanded."
upsAlarmChargerFailed,1.3.6.1.2.1.33.1.6.3.13,,,objectidentity,,"An uncorrected problem has been detected within the UPS charger subsystem."
upsAlarmUpsOutputOff,1.3.6.1.2.1.33.1.6.3.14,,,objectidentity,,"The output of the UPS is in the off state."
upsAlarmUpsSystemOff,1.3.6.1.2.1.33.1.6.3.15,,,objectidentity,,"The UPS system is in the off state."
upsAlarmFanFailure,1.3.6.1.2.1.33.1.6.3.16,,,objectidentity,,"The failure of one or more fans in the UPS has been detected."
upsAlarmFuseFailure,1.3.6.1.2.1.33.1.6.3.17,,,objectidentity,,"The failure of one or more fuses has been detected."
upsAlarmGeneralFault,1.3.6.1.2.1.33.1.6.3.18,,,objectidentity,,"A general fault in the UPS has been detected."
upsAlarmDiagnosticTestFailed,1.3.6.1.2.1.33.1.6.3.19,,,objectidentity,,"The result of the last diagnostic test indicates a failure."
upsAlarmCommunicationsLost,1.3.6.1.2.1.33.1.6.3.20,,,objectidentity,,"A problem has been encountered in the communications between the agent and the UPS."
upsAlarmAwaitingPower,1.3.6.1.2.1.33.1.6.3.21,,,objectidentity,,"The UPS output is off and the UPS is awaiting the return of input power."
upsAlarmShutdownPending,1.3.6.1.2.1.33.1.6.3.22,,,objectidentity,,"A upsShutdownAfterDelay countdown is underway."
upsAlarmShutdownImminent,1.3.6.1.2.1.33.1.6.3.23,,,objectidentity,,"The UPS will turn off power to the load in less than 5 seconds; this may be either a timed shutdown or a low battery shutdown."
upsAlarmTestInProgress,1.3.6.1.2.1.33.1.6.3.24,,,objectidentity,,"A test is in progress, as initiated and indicated by the Test Group. Tests initiated via other implementation-specific mechanisms can indicate the presence of the testing in the alarm table, if desired, via a OBJECT-IDENTITY macro in the MIB document specific to that implementation and are outside the scope of this OBJECT-IDENTITY."
upsTest,1.3.6.1.2.1.33.1.7,,,objectidentity,,""
upsTestId,1.3.6.1.2.1.33.1.7.1,object identifier,read-write,objecttype,scalar,"The test is named by an OBJECT IDENTIFIER which allows a standard mechanism for the initiation of tests, including the well known tests identified in this document as well as those introduced by a particular implementation, i.e., as documented in the private enterprise MIB definition for the device. Setting this variable initiates the named test. Sets to this variable require the presence of upsTestSpinLock in the same SNMP message. The set request will be rejected with an appropriate error message if the requested test cannot be performed, including attempts to start a test when another test is already in progress. The status of the current or last test is maintained in upsTestResultsSummary. Tests in progress may be aborted by setting the upsTestId variable to upsTestAbortTestInProgress. Read operations return the value of the name of the test in progress if a test is in progress or the name of the last test performed if no test is in progress, unless no test has been run, in which case the well known value upsTestNoTestsInitiated is returned."
upsTestSpinLock,1.3.6.1.2.1.33.1.7.2,testandincr,read-write,objecttype,scalar,"A spin lock on the test subsystem. The spinlock is used as follows. Before starting a test, a manager-station should make sure that a test is not in progress as follows: try_again: get (upsTestSpinLock) while (upsTestResultsSummary == inProgress) { /* loop while a test is running for another manager */ short delay get (upsTestSpinLock) } lock_value = upsTestSpinLock /* no test in progress, start the test */ set (upsTestSpinLock = lock_value, upsTestId = requested_test) if (error_index == 1) { /* (upsTestSpinLock failed) */ /* if problem is not access control, then some other manager slipped in ahead of us */ goto try_again } if (error_index == 2) { /* (upsTestId) */ /* cannot perform the test */ give up } /* test started ok */ /* wait for test completion by polling upsTestResultsSummary */ get (upsTestSpinLock, upsTestResultsSummary, upsTestResultsDetail) while (upsTestResultsSummary == inProgress) { short delay get (upsTestSpinLock, upsTestResultsSummary, upsTestResultsDetail) } /* when test completes, retrieve any additional test results */ /* if upsTestSpinLock == lock_value + 1, then these are our test */ /* results (as opposed to another manager's */ The initial value of upsTestSpinLock at agent initialization shall be 1."
upsTestResultsSummary,1.3.6.1.2.1.33.1.7.3,integer,read-only,objecttype,scalar,"The results of the current or last UPS diagnostics test performed. The values for donePass(1), doneWarning(2), and doneError(3) indicate that the test completed either successfully, with a warning, or with an error, respectively. The value aborted(4) is returned for tests which are aborted by setting the value of upsTestId to upsTestAbortTestInProgress. Tests which have not yet concluded are indicated by inProgress(5). The value noTestsInitiated(6) indicates that no previous test results are available, such as is the case when no tests have been run since the last reinitialization of the network management subsystem and the system has no provision for non- volatile storage of test results. Enumeration: 'doneError': 3, 'noTestsInitiated': 6, 'donePass': 1, 'doneWarning': 2, 'aborted': 4, 'inProgress': 5."
upsTestResultsDetail,1.3.6.1.2.1.33.1.7.4,displaystring,read-only,objecttype,scalar,"Additional information about upsTestResultsSummary. If no additional information available, a zero length string is returned."
upsTestStartTime,1.3.6.1.2.1.33.1.7.5,timestamp,read-only,objecttype,scalar,"The value of sysUpTime at the time the test in progress was initiated, or, if no test is in progress, the time the previous test was initiated. If the value of upsTestResultsSummary is noTestsInitiated(6), upsTestStartTime has the value 0."
upsTestElapsedTime,1.3.6.1.2.1.33.1.7.6,timeinterval,read-only,objecttype,scalar,"The amount of time, in TimeTicks, since the test in progress was initiated, or, if no test is in progress, the previous test took to complete. If the value of upsTestResultsSummary is noTestsInitiated(6), upsTestElapsedTime has the value 0."
upsWellKnownTests,1.3.6.1.2.1.33.1.7.7,,,objectidentity,,""
upsTestNoTestsInitiated,1.3.6.1.2.1.33.1.7.7.1,,,objectidentity,,"No tests have been initiated and no test is in progress."
upsTestAbortTestInProgress,1.3.6.1.2.1.33.1.7.7.2,,,objectidentity,,"The test in progress is to be aborted / the test in progress was aborted."
upsTestGeneralSystemsTest,1.3.6.1.2.1.33.1.7.7.3,,,objectidentity,,"The manufacturer's standard test of UPS device systems."
upsTestQuickBatteryTest,1.3.6.1.2.1.33.1.7.7.4,,,objectidentity,,"A test that is sufficient to determine if the battery needs replacement."
upsTestDeepBatteryCalibration,1.3.6.1.2.1.33.1.7.7.5,,,objectidentity,,"The system is placed on battery to a discharge level, set by the manufacturer, sufficient to determine battery replacement and battery run-time with a high degree of confidence. WARNING: this test will leave the battery in a low charge state and will require time for recharging to a level sufficient to provide normal battery duration for the protected load."
upsControl,1.3.6.1.2.1.33.1.8,,,objectidentity,,""
upsShutdownType,1.3.6.1.2.1.33.1.8.1,integer,read-write,objecttype,scalar,"This object determines the nature of the action to be taken at the time when the countdown of the upsShutdownAfterDelay and upsRebootWithDuration objects reaches zero. Setting this object to output(1) indicates that shutdown requests should cause only the output of the UPS to turn off. Setting this object to system(2) indicates that shutdown requests will cause the entire UPS system to turn off. Enumeration: 'output': 1, 'system': 2."
upsShutdownAfterDelay,1.3.6.1.2.1.33.1.8.2,integer,read-write,objecttype,scalar,"Setting this object will shutdown (i.e., turn off) either the UPS output or the UPS system (as determined by the value of upsShutdownType at the time of shutdown) after the indicated number of seconds, or less if the UPS batteries become depleted. Setting this object to 0 will cause the shutdown to occur immediately. Setting this object to -1 will abort the countdown. If the system is already in the desired state at the time the countdown reaches 0, then nothing will happen. That is, there is no additional action at that time if upsShutdownType = system and the system is already off. Similarly, there is no additional action at that time if upsShutdownType = output and the output is already off. When read, upsShutdownAfterDelay will return the number of seconds remaining until shutdown, or -1 if no shutdown countdown is in effect. On some systems, if the agent is restarted while a shutdown countdown is in effect, the countdown may be aborted. Sets to this object override any upsShutdownAfterDelay already in effect."
upsStartupAfterDelay,1.3.6.1.2.1.33.1.8.3,integer,read-write,objecttype,scalar,"Setting this object will start the output after the indicated number of seconds, including starting the UPS, if necessary. Setting this object to 0 will cause the startup to occur immediately. Setting this object to -1 will abort the countdown. If the output is already on at the time the countdown reaches 0, then nothing will happen. Sets to this object override the effect of any upsStartupAfterDelay countdown or upsRebootWithDuration countdown in progress. When read, upsStartupAfterDelay will return the number of seconds until startup, or -1 if no startup countdown is in effect. If the countdown expires during a utility failure, the startup shall not occur until the utility power is restored. On some systems, if the agent is restarted while a startup countdown is in effect, the countdown is aborted."
upsRebootWithDuration,1.3.6.1.2.1.33.1.8.4,integer,read-write,objecttype,scalar,"Setting this object will immediately shutdown (i.e., turn off) either the UPS output or the UPS system (as determined by the value of upsShutdownType at the time of shutdown) for a period equal to the indicated number of seconds, after which time the output will be started, including starting the UPS, if necessary. If the number of seconds required to perform the request is greater than the requested duration, then the requested shutdown and startup cycle shall be performed in the minimum time possible, but in no case shall this require more than the requested duration plus 60 seconds. When read, upsRebootWithDuration shall return the number of seconds remaining in the countdown, or -1 if no countdown is in progress. If the startup should occur during a utility failure, the startup shall not occur until the utility power is restored."
upsAutoRestart,1.3.6.1.2.1.33.1.8.5,integer,read-write,objecttype,scalar,"Setting this object to 'on' will cause the UPS system to restart after a shutdown if the shutdown occurred during a power loss as a result of either a upsShutdownAfterDelay or an internal battery depleted condition. Setting this object to 'off' will prevent the UPS system from restarting after a shutdown until an operator manually or remotely explicitly restarts it. If the UPS is in a startup or reboot countdown, then the UPS will not restart until that delay has been satisfied. Enumeration: 'on': 1, 'off': 2."
upsConfig,1.3.6.1.2.1.33.1.9,,,objectidentity,,""
upsConfigInputVoltage,1.3.6.1.2.1.33.1.9.1,nonnegativeinteger,read-write,objecttype,scalar,"The magnitude of the nominal input voltage. On those systems which support read-write access to this object, if there is an attempt to set this variable to a value that is not supported, the request must be rejected and the agent shall respond with an appropriate error message, i.e., badValue for SNMPv1, or inconsistentValue for SNMPv2."
upsConfigInputFreq,1.3.6.1.2.1.33.1.9.2,nonnegativeinteger,read-write,objecttype,scalar,"The nominal input frequency. On those systems which support read-write access to this object, if there is an attempt to set this variable to a value that is not supported, the request must be rejected and the agent shall respond with an appropriate error message, i.e., badValue for SNMPv1, or inconsistentValue for SNMPv2."
upsConfigOutputVoltage,1.3.6.1.2.1.33.1.9.3,nonnegativeinteger,read-write,objecttype,scalar,"The magnitude of the nominal output voltage. On those systems which support read-write access to this object, if there is an attempt to set this variable to a value that is not supported, the request must be rejected and the agent shall respond with an appropriate error message, i.e., badValue for SNMPv1, or inconsistentValue for SNMPv2."
upsConfigOutputFreq,1.3.6.1.2.1.33.1.9.4,nonnegativeinteger,read-write,objecttype,scalar,"The nominal output frequency. On those systems which support read-write access to this object, if there is an attempt to set this variable to a value that is not supported, the request must be rejected and the agent shall respond with an appropriate error message, i.e., badValue for SNMPv1, or inconsistentValue for SNMPv2."
upsConfigOutputVA,1.3.6.1.2.1.33.1.9.5,nonnegativeinteger,read-only,objecttype,scalar,"The magnitude of the nominal Volt-Amp rating."
upsConfigOutputPower,1.3.6.1.2.1.33.1.9.6,nonnegativeinteger,read-only,objecttype,scalar,"The magnitude of the nominal true power rating."
upsConfigLowBattTime,1.3.6.1.2.1.33.1.9.7,nonnegativeinteger,read-write,objecttype,scalar,"The value of upsEstimatedMinutesRemaining at which a lowBattery condition is declared. For agents which support only discrete (discontinuous) values, then the agent shall round up to the next supported value. If the requested value is larger than the largest supported value, then the largest supported value shall be selected."
upsConfigAudibleStatus,1.3.6.1.2.1.33.1.9.8,integer,read-write,objecttype,scalar,"The requested state of the audible alarm. When in the disabled state, the audible alarm should never sound. The enabled state is self-describing. Setting this object to muted(3) when the audible alarm is sounding shall temporarily silence the alarm. It will remain muted until it would normally stop sounding and the value returned for read operations during this period shall equal muted(3). At the end of this period, the value shall revert to enabled(2). Writes of the value muted(3) when the audible alarm is not sounding shall be accepted but otherwise shall have no effect. Enumeration: 'disabled': 1, 'muted': 3, 'enabled': 2."
upsConfigLowVoltageTransferPoint,1.3.6.1.2.1.33.1.9.9,nonnegativeinteger,read-write,objecttype,scalar,"The minimum input line voltage allowed before the UPS system transfers to battery backup."
upsConfigHighVoltageTransferPoint,1.3.6.1.2.1.33.1.9.10,nonnegativeinteger,read-write,objecttype,scalar,"The maximum line voltage allowed before the UPS system transfers to battery backup."
upsTraps,1.3.6.1.2.1.33.2,,,objectidentity,,""
upsTrapOnBattery,1.3.6.1.2.1.33.2.1,,,notificationtype,,"The UPS is operating on battery power. This trap is persistent and is resent at one minute intervals until the UPS either turns off or is no longer running on battery."
upsTrapTestCompleted,1.3.6.1.2.1.33.2.2,,,notificationtype,,"This trap is sent upon completion of a UPS diagnostic test."
upsTrapAlarmEntryAdded,1.3.6.1.2.1.33.2.3,,,notificationtype,,"This trap is sent each time an alarm is inserted into to the alarm table. It is sent on the insertion of all alarms except for upsAlarmOnBattery and upsAlarmTestInProgress."
upsTrapAlarmEntryRemoved,1.3.6.1.2.1.33.2.4,,,notificationtype,,"This trap is sent each time an alarm is removed from the alarm table. It is sent on the removal of all alarms except for upsAlarmTestInProgress."
upsConformance,1.3.6.1.2.1.33.3,,,objectidentity,,""
upsCompliances,1.3.6.1.2.1.33.3.1,,,objectidentity,,""
upsSubsetCompliance,1.3.6.1.2.1.33.3.1.1,,,modulecompliance,,"The compliance statement for UPSs that only support the two-contact communication protocol."
upsBasicCompliance,1.3.6.1.2.1.33.3.1.2,,,modulecompliance,,"The compliance statement for UPSs that support full-featured functions, such as control."
upsFullCompliance,1.3.6.1.2.1.33.3.1.3,,,modulecompliance,,"The compliance statement for UPSs that support advanced full-featured functions."
upsGroups,1.3.6.1.2.1.33.3.2,,,objectidentity,,""
upsSubsetGroups,1.3.6.1.2.1.33.3.2.1,,,objectidentity,,""
upsSubsetIdentGroup,1.3.6.1.2.1.33.3.2.1.1,,,objectgroup,,"The upsSubsetIdentGroup defines objects which are common across all UPSs which meet subset compliance. Most devices which conform to the upsSubsetIdentGroup will provide access to these objects via a proxy agent. If the proxy agent is compatible with multiple UPS types, configuration of the proxy agent will require specifying some of these values, either individually, or as a group (perhaps through a table lookup mechanism based on the UPS model number)."
upsSubsetBatteryGroup,1.3.6.1.2.1.33.3.2.1.2,,,objectgroup,,"The upsSubsetBatteryGroup defines the objects that are common to battery groups of two-contact UPSs."
upsSubsetInputGroup,1.3.6.1.2.1.33.3.2.1.3,,,objectgroup,,"The upsSubsetInputGroup defines the objects that are common to the Input groups of two-contact UPSs."
upsSubsetOutputGroup,1.3.6.1.2.1.33.3.2.1.4,,,objectgroup,,"The upsSubsetOutputGroup defines the objects that are common to the Output groups of two-contact UPSs."
upsSubsetAlarmGroup,1.3.6.1.2.1.33.3.2.1.6,,,objectgroup,,"The upsSubsetAlarmGroup defines the objects that are common to the Alarm groups of two-contact UPSs."
upsSubsetControlGroup,1.3.6.1.2.1.33.3.2.1.8,,,objectgroup,,"The upsSubsetControlGroup defines the objects that are common to the Control groups of two-contact UPSs."
upsSubsetConfigGroup,1.3.6.1.2.1.33.3.2.1.9,,,objectgroup,,"The upsSubsetConfigGroup defines the objects that are common to the Config groups of two-contact UPSs."
upsBasicGroups,1.3.6.1.2.1.33.3.2.2,,,objectidentity,,""
upsBasicIdentGroup,1.3.6.1.2.1.33.3.2.2.1,,,objectgroup,,"The upsBasicIdentGroup defines objects which are common to the Ident group of compliant UPSs which support basic functions."
upsBasicBatteryGroup,1.3.6.1.2.1.33.3.2.2.2,,,objectgroup,,"The upsBasicBatteryGroup defines the objects that are common to the battery groups of compliant UPSs which support basic functions."
upsBasicInputGroup,1.3.6.1.2.1.33.3.2.2.3,,,objectgroup,,"The upsBasicInputGroup defines the objects that are common to the Input groups of compliant UPSs which support basic functions."
upsBasicOutputGroup,1.3.6.1.2.1.33.3.2.2.4,,,objectgroup,,"The upsBasicOutputGroup defines the objects that are common to the Output groups of compliant UPSs which support basic functions."
upsBasicBypassGroup,1.3.6.1.2.1.33.3.2.2.5,,,objectgroup,,"The upsBasicBypassGroup defines the objects that are common to the Bypass groups of compliant UPSs which support basic functions."
upsBasicAlarmGroup,1.3.6.1.2.1.33.3.2.2.6,,,objectgroup,,"The upsBasicAlarmGroup defines the objects that are common to the Alarm groups of compliant UPSs which support basic functions."
upsBasicTestGroup,1.3.6.1.2.1.33.3.2.2.7,,,objectgroup,,"The upsBasicTestGroup defines the objects that are common to the Test groups of compliant UPSs which support basic functions."
upsBasicControlGroup,1.3.6.1.2.1.33.3.2.2.8,,,objectgroup,,"The upsBasicControlGroup defines the objects that are common to the Control groups of compliant UPSs which support basic functions."
upsBasicConfigGroup,1.3.6.1.2.1.33.3.2.2.9,,,objectgroup,,"The upsBasicConfigGroup defines the objects that are common to the Config groups of UPSs which support basic functions."
upsFullGroups,1.3.6.1.2.1.33.3.2.3,,,objectidentity,,""
upsFullIdentGroup,1.3.6.1.2.1.33.3.2.3.1,,,objectgroup,,"The upsFullIdentGroup defines objects which are common to the Ident group of fully compliant UPSs."
upsFullBatteryGroup,1.3.6.1.2.1.33.3.2.3.2,,,objectgroup,,"The upsFullBatteryGroup defines the objects that are common to the battery groups of fully compliant UPSs."
upsFullInputGroup,1.3.6.1.2.1.33.3.2.3.3,,,objectgroup,,"The upsFullInputGroup defines the objects that are common to the Input groups of fully compliant UPSs."
upsFullOutputGroup,1.3.6.1.2.1.33.3.2.3.4,,,objectgroup,,"The upsFullOutputGroup defines the objects that are common to the Output groups of fully compliant UPSs."
upsFullBypassGroup,1.3.6.1.2.1.33.3.2.3.5,,,objectgroup,,"The upsFullBypassGroup defines the objects that are common to the Bypass groups of fully compliant UPSs."
upsFullAlarmGroup,1.3.6.1.2.1.33.3.2.3.6,,,objectgroup,,"The upsFullAlarmGroup defines the objects that are common to the Alarm groups of fully compliant UPSs."
upsFullTestGroup,1.3.6.1.2.1.33.3.2.3.7,,,objectgroup,,"The upsFullTestGroup defines the objects that are common to the Test groups of fully compliant UPSs."
upsFullControlGroup,1.3.6.1.2.1.33.3.2.3.8,,,objectgroup,,"The upsFullControlGroup defines the objects that are common to the Control groups of fully compliant UPSs."
upsFullConfigGroup,1.3.6.1.2.1.33.3.2.3.9,,,objectgroup,,"The upsFullConfigGroup defines the objects that are common to the Config groups of fully compliant UPSs."
1 OBJECT_NAME OBJECT_IDENTIFIER OBJECT_DATA_TYPE OBJECT_PREMISSIONS OBJECT_CLASS OBJECT_NODE_TYPE OBJECT_DESCRIPTION
2 upsMIB 1.3.6.1.2.1.33 moduleidentity The MIB module to describe Uninterruptible Power Supplies.
3 upsObjects 1.3.6.1.2.1.33.1 objectidentity
4 upsIdent 1.3.6.1.2.1.33.1.1 objectidentity
5 upsIdentManufacturer 1.3.6.1.2.1.33.1.1.1 displaystring read-only objecttype scalar The name of the UPS manufacturer.
6 upsIdentModel 1.3.6.1.2.1.33.1.1.2 displaystring read-only objecttype scalar The UPS Model designation.
7 upsIdentUPSSoftwareVersion 1.3.6.1.2.1.33.1.1.3 displaystring read-only objecttype scalar The UPS firmware/software version(s). This variable may or may not have the same value as upsIdentAgentSoftwareVersion in some implementations.
8 upsIdentAgentSoftwareVersion 1.3.6.1.2.1.33.1.1.4 displaystring read-only objecttype scalar The UPS agent software version. This variable may or may not have the same value as upsIdentUPSSoftwareVersion in some implementations.
9 upsIdentName 1.3.6.1.2.1.33.1.1.5 displaystring read-write objecttype scalar A string identifying the UPS. This object should be set by the administrator.
10 upsIdentAttachedDevices 1.3.6.1.2.1.33.1.1.6 displaystring read-write objecttype scalar A string identifying the devices attached to the output(s) of the UPS. This object should be set by the administrator.
11 upsBattery 1.3.6.1.2.1.33.1.2 objectidentity
12 upsBatteryStatus 1.3.6.1.2.1.33.1.2.1 integer read-only objecttype scalar The indication of the capacity remaining in the UPS system's batteries. A value of batteryNormal indicates that the remaining run-time is greater than upsConfigLowBattTime. A value of batteryLow indicates that the remaining battery run-time is less than or equal to upsConfigLowBattTime. A value of batteryDepleted indicates that the UPS will be unable to sustain the present load when and if the utility power is lost (including the possibility that the utility power is currently absent and the UPS is unable to sustain the output). Enumeration: 'unknown': 1, 'batteryNormal': 2, 'batteryDepleted': 4, 'batteryLow': 3.
13 upsSecondsOnBattery 1.3.6.1.2.1.33.1.2.2 nonnegativeinteger read-only objecttype scalar If the unit is on battery power, the elapsed time since the UPS last switched to battery power, or the time since the network management subsystem was last restarted, whichever is less. Zero shall be returned if the unit is not on battery power.
14 upsEstimatedMinutesRemaining 1.3.6.1.2.1.33.1.2.3 positiveinteger read-only objecttype scalar An estimate of the time to battery charge depletion under the present load conditions if the utility power is off and remains off, or if it were to be lost and remain off.
15 upsEstimatedChargeRemaining 1.3.6.1.2.1.33.1.2.4 integer read-only objecttype scalar An estimate of the battery charge remaining expressed as a percent of full charge.
16 upsBatteryVoltage 1.3.6.1.2.1.33.1.2.5 nonnegativeinteger read-only objecttype scalar The magnitude of the present battery voltage.
17 upsBatteryCurrent 1.3.6.1.2.1.33.1.2.6 integer32 read-only objecttype scalar The present battery current.
18 upsBatteryTemperature 1.3.6.1.2.1.33.1.2.7 integer32 read-only objecttype scalar The ambient temperature at or near the UPS Battery casing.
19 upsInput 1.3.6.1.2.1.33.1.3 objectidentity
20 upsInputLineBads 1.3.6.1.2.1.33.1.3.1 counter32 read-only objecttype scalar A count of the number of times the input entered an out-of-tolerance condition as defined by the manufacturer. This count is incremented by one each time the input transitions from zero out-of-tolerance lines to one or more input lines out-of-tolerance.
21 upsInputNumLines 1.3.6.1.2.1.33.1.3.2 nonnegativeinteger read-only objecttype scalar The number of input lines utilized in this device. This variable indicates the number of rows in the input table.
22 upsInputTable 1.3.6.1.2.1.33.1.3.3 no-access objecttype table A list of input table entries. The number of entries is given by the value of upsInputNumLines.
23 upsInputEntry 1.3.6.1.2.1.33.1.3.3.1 no-access objecttype row An entry containing information applicable to a particular input line.
24 upsInputLineIndex 1.3.6.1.2.1.33.1.3.3.1.1 positiveinteger no-access objecttype column The input line identifier.
25 upsInputFrequency 1.3.6.1.2.1.33.1.3.3.1.2 nonnegativeinteger read-only objecttype column The present input frequency.
26 upsInputVoltage 1.3.6.1.2.1.33.1.3.3.1.3 nonnegativeinteger read-only objecttype column The magnitude of the present input voltage.
27 upsInputCurrent 1.3.6.1.2.1.33.1.3.3.1.4 nonnegativeinteger read-only objecttype column The magnitude of the present input current.
28 upsInputTruePower 1.3.6.1.2.1.33.1.3.3.1.5 nonnegativeinteger read-only objecttype column The magnitude of the present input true power.
29 upsOutput 1.3.6.1.2.1.33.1.4 objectidentity
30 upsOutputSource 1.3.6.1.2.1.33.1.4.1 integer read-only objecttype scalar The present source of output power. The enumeration none(2) indicates that there is no source of output power (and therefore no output power), for example, the system has opened the output breaker. Enumeration: 'none': 2, 'normal': 3, 'battery': 5, 'reducer': 7, 'other': 1, 'bypass': 4, 'booster': 6.
31 upsOutputFrequency 1.3.6.1.2.1.33.1.4.2 nonnegativeinteger read-only objecttype scalar The present output frequency.
32 upsOutputNumLines 1.3.6.1.2.1.33.1.4.3 nonnegativeinteger read-only objecttype scalar The number of output lines utilized in this device. This variable indicates the number of rows in the output table.
33 upsOutputTable 1.3.6.1.2.1.33.1.4.4 no-access objecttype table A list of output table entries. The number of entries is given by the value of upsOutputNumLines.
34 upsOutputEntry 1.3.6.1.2.1.33.1.4.4.1 no-access objecttype row An entry containing information applicable to a particular output line.
35 upsOutputLineIndex 1.3.6.1.2.1.33.1.4.4.1.1 positiveinteger no-access objecttype column The output line identifier.
36 upsOutputVoltage 1.3.6.1.2.1.33.1.4.4.1.2 nonnegativeinteger read-only objecttype column The present output voltage.
37 upsOutputCurrent 1.3.6.1.2.1.33.1.4.4.1.3 nonnegativeinteger read-only objecttype column The present output current.
38 upsOutputPower 1.3.6.1.2.1.33.1.4.4.1.4 nonnegativeinteger read-only objecttype column The present output true power.
39 upsOutputPercentLoad 1.3.6.1.2.1.33.1.4.4.1.5 integer read-only objecttype column The percentage of the UPS power capacity presently being used on this output line, i.e., the greater of the percent load of true power capacity and the percent load of VA.
40 upsBypass 1.3.6.1.2.1.33.1.5 objectidentity
41 upsBypassFrequency 1.3.6.1.2.1.33.1.5.1 nonnegativeinteger read-only objecttype scalar The present bypass frequency.
42 upsBypassNumLines 1.3.6.1.2.1.33.1.5.2 nonnegativeinteger read-only objecttype scalar The number of bypass lines utilized in this device. This entry indicates the number of rows in the bypass table.
43 upsBypassTable 1.3.6.1.2.1.33.1.5.3 no-access objecttype table A list of bypass table entries. The number of entries is given by the value of upsBypassNumLines.
44 upsBypassEntry 1.3.6.1.2.1.33.1.5.3.1 no-access objecttype row An entry containing information applicable to a particular bypass input.
45 upsBypassLineIndex 1.3.6.1.2.1.33.1.5.3.1.1 positiveinteger no-access objecttype column The bypass line identifier.
46 upsBypassVoltage 1.3.6.1.2.1.33.1.5.3.1.2 nonnegativeinteger read-only objecttype column The present bypass voltage.
47 upsBypassCurrent 1.3.6.1.2.1.33.1.5.3.1.3 nonnegativeinteger read-only objecttype column The present bypass current.
48 upsBypassPower 1.3.6.1.2.1.33.1.5.3.1.4 nonnegativeinteger read-only objecttype column The present true power conveyed by the bypass.
49 upsAlarm 1.3.6.1.2.1.33.1.6 objectidentity
50 upsAlarmsPresent 1.3.6.1.2.1.33.1.6.1 gauge32 read-only objecttype scalar The present number of active alarm conditions.
51 upsAlarmTable 1.3.6.1.2.1.33.1.6.2 no-access objecttype table A list of alarm table entries. The table contains zero, one, or many rows at any moment, depending upon the number of alarm conditions in effect. The table is initially empty at agent startup. The agent creates a row in the table each time a condition is detected and deletes that row when that condition no longer pertains. The agent creates the first row with upsAlarmId equal to 1, and increments the value of upsAlarmId each time a new row is created, wrapping to the first free value greater than or equal to 1 when the maximum value of upsAlarmId would otherwise be exceeded. Consequently, after multiple operations, the table may become sparse, e.g., containing entries for rows 95, 100, 101, and 203 and the entries should not be assumed to be in chronological order because upsAlarmId might have wrapped. Alarms are named by an AutonomousType (OBJECT IDENTIFIER), upsAlarmDescr, to allow a single table to reflect well known alarms plus alarms defined by a particular implementation, i.e., as documented in the private enterprise MIB definition for the device. No two rows will have the same value of upsAlarmDescr, since alarms define conditions. In order to meet this requirement, care should be taken in the definition of alarm conditions to insure that a system cannot enter the same condition multiple times simultaneously. The number of rows in the table at any given time is reflected by the value of upsAlarmsPresent.
52 upsAlarmEntry 1.3.6.1.2.1.33.1.6.2.1 no-access objecttype row An entry containing information applicable to a particular alarm.
53 upsAlarmId 1.3.6.1.2.1.33.1.6.2.1.1 positiveinteger no-access objecttype column A unique identifier for an alarm condition. This value must remain constant.
54 upsAlarmDescr 1.3.6.1.2.1.33.1.6.2.1.2 autonomoustype read-only objecttype column A reference to an alarm description object. The object referenced should not be accessible, but rather be used to provide a unique description of the alarm condition.
55 upsAlarmTime 1.3.6.1.2.1.33.1.6.2.1.3 timestamp read-only objecttype column The value of sysUpTime when the alarm condition was detected. If the alarm condition was detected at the time of agent startup and presumably existed before agent startup, the value of upsAlarmTime shall equal 0.
56 upsWellKnownAlarms 1.3.6.1.2.1.33.1.6.3 objectidentity
57 upsAlarmBatteryBad 1.3.6.1.2.1.33.1.6.3.1 objectidentity One or more batteries have been determined to require replacement.
58 upsAlarmOnBattery 1.3.6.1.2.1.33.1.6.3.2 objectidentity The UPS is drawing power from the batteries.
59 upsAlarmLowBattery 1.3.6.1.2.1.33.1.6.3.3 objectidentity The remaining battery run-time is less than or equal to upsConfigLowBattTime.
60 upsAlarmDepletedBattery 1.3.6.1.2.1.33.1.6.3.4 objectidentity The UPS will be unable to sustain the present load when and if the utility power is lost.
61 upsAlarmTempBad 1.3.6.1.2.1.33.1.6.3.5 objectidentity A temperature is out of tolerance.
62 upsAlarmInputBad 1.3.6.1.2.1.33.1.6.3.6 objectidentity An input condition is out of tolerance.
63 upsAlarmOutputBad 1.3.6.1.2.1.33.1.6.3.7 objectidentity An output condition (other than OutputOverload) is out of tolerance.
64 upsAlarmOutputOverload 1.3.6.1.2.1.33.1.6.3.8 objectidentity The output load exceeds the UPS output capacity.
65 upsAlarmOnBypass 1.3.6.1.2.1.33.1.6.3.9 objectidentity The Bypass is presently engaged on the UPS.
66 upsAlarmBypassBad 1.3.6.1.2.1.33.1.6.3.10 objectidentity The Bypass is out of tolerance.
67 upsAlarmOutputOffAsRequested 1.3.6.1.2.1.33.1.6.3.11 objectidentity The UPS has shutdown as requested, i.e., the output is off.
68 upsAlarmUpsOffAsRequested 1.3.6.1.2.1.33.1.6.3.12 objectidentity The entire UPS has shutdown as commanded.
69 upsAlarmChargerFailed 1.3.6.1.2.1.33.1.6.3.13 objectidentity An uncorrected problem has been detected within the UPS charger subsystem.
70 upsAlarmUpsOutputOff 1.3.6.1.2.1.33.1.6.3.14 objectidentity The output of the UPS is in the off state.
71 upsAlarmUpsSystemOff 1.3.6.1.2.1.33.1.6.3.15 objectidentity The UPS system is in the off state.
72 upsAlarmFanFailure 1.3.6.1.2.1.33.1.6.3.16 objectidentity The failure of one or more fans in the UPS has been detected.
73 upsAlarmFuseFailure 1.3.6.1.2.1.33.1.6.3.17 objectidentity The failure of one or more fuses has been detected.
74 upsAlarmGeneralFault 1.3.6.1.2.1.33.1.6.3.18 objectidentity A general fault in the UPS has been detected.
75 upsAlarmDiagnosticTestFailed 1.3.6.1.2.1.33.1.6.3.19 objectidentity The result of the last diagnostic test indicates a failure.
76 upsAlarmCommunicationsLost 1.3.6.1.2.1.33.1.6.3.20 objectidentity A problem has been encountered in the communications between the agent and the UPS.
77 upsAlarmAwaitingPower 1.3.6.1.2.1.33.1.6.3.21 objectidentity The UPS output is off and the UPS is awaiting the return of input power.
78 upsAlarmShutdownPending 1.3.6.1.2.1.33.1.6.3.22 objectidentity A upsShutdownAfterDelay countdown is underway.
79 upsAlarmShutdownImminent 1.3.6.1.2.1.33.1.6.3.23 objectidentity The UPS will turn off power to the load in less than 5 seconds; this may be either a timed shutdown or a low battery shutdown.
80 upsAlarmTestInProgress 1.3.6.1.2.1.33.1.6.3.24 objectidentity A test is in progress, as initiated and indicated by the Test Group. Tests initiated via other implementation-specific mechanisms can indicate the presence of the testing in the alarm table, if desired, via a OBJECT-IDENTITY macro in the MIB document specific to that implementation and are outside the scope of this OBJECT-IDENTITY.
81 upsTest 1.3.6.1.2.1.33.1.7 objectidentity
82 upsTestId 1.3.6.1.2.1.33.1.7.1 object identifier read-write objecttype scalar The test is named by an OBJECT IDENTIFIER which allows a standard mechanism for the initiation of tests, including the well known tests identified in this document as well as those introduced by a particular implementation, i.e., as documented in the private enterprise MIB definition for the device. Setting this variable initiates the named test. Sets to this variable require the presence of upsTestSpinLock in the same SNMP message. The set request will be rejected with an appropriate error message if the requested test cannot be performed, including attempts to start a test when another test is already in progress. The status of the current or last test is maintained in upsTestResultsSummary. Tests in progress may be aborted by setting the upsTestId variable to upsTestAbortTestInProgress. Read operations return the value of the name of the test in progress if a test is in progress or the name of the last test performed if no test is in progress, unless no test has been run, in which case the well known value upsTestNoTestsInitiated is returned.
83 upsTestSpinLock 1.3.6.1.2.1.33.1.7.2 testandincr read-write objecttype scalar A spin lock on the test subsystem. The spinlock is used as follows. Before starting a test, a manager-station should make sure that a test is not in progress as follows: try_again: get (upsTestSpinLock) while (upsTestResultsSummary == inProgress) { /* loop while a test is running for another manager */ short delay get (upsTestSpinLock) } lock_value = upsTestSpinLock /* no test in progress, start the test */ set (upsTestSpinLock = lock_value, upsTestId = requested_test) if (error_index == 1) { /* (upsTestSpinLock failed) */ /* if problem is not access control, then some other manager slipped in ahead of us */ goto try_again } if (error_index == 2) { /* (upsTestId) */ /* cannot perform the test */ give up } /* test started ok */ /* wait for test completion by polling upsTestResultsSummary */ get (upsTestSpinLock, upsTestResultsSummary, upsTestResultsDetail) while (upsTestResultsSummary == inProgress) { short delay get (upsTestSpinLock, upsTestResultsSummary, upsTestResultsDetail) } /* when test completes, retrieve any additional test results */ /* if upsTestSpinLock == lock_value + 1, then these are our test */ /* results (as opposed to another manager's */ The initial value of upsTestSpinLock at agent initialization shall be 1.
84 upsTestResultsSummary 1.3.6.1.2.1.33.1.7.3 integer read-only objecttype scalar The results of the current or last UPS diagnostics test performed. The values for donePass(1), doneWarning(2), and doneError(3) indicate that the test completed either successfully, with a warning, or with an error, respectively. The value aborted(4) is returned for tests which are aborted by setting the value of upsTestId to upsTestAbortTestInProgress. Tests which have not yet concluded are indicated by inProgress(5). The value noTestsInitiated(6) indicates that no previous test results are available, such as is the case when no tests have been run since the last reinitialization of the network management subsystem and the system has no provision for non- volatile storage of test results. Enumeration: 'doneError': 3, 'noTestsInitiated': 6, 'donePass': 1, 'doneWarning': 2, 'aborted': 4, 'inProgress': 5.
85 upsTestResultsDetail 1.3.6.1.2.1.33.1.7.4 displaystring read-only objecttype scalar Additional information about upsTestResultsSummary. If no additional information available, a zero length string is returned.
86 upsTestStartTime 1.3.6.1.2.1.33.1.7.5 timestamp read-only objecttype scalar The value of sysUpTime at the time the test in progress was initiated, or, if no test is in progress, the time the previous test was initiated. If the value of upsTestResultsSummary is noTestsInitiated(6), upsTestStartTime has the value 0.
87 upsTestElapsedTime 1.3.6.1.2.1.33.1.7.6 timeinterval read-only objecttype scalar The amount of time, in TimeTicks, since the test in progress was initiated, or, if no test is in progress, the previous test took to complete. If the value of upsTestResultsSummary is noTestsInitiated(6), upsTestElapsedTime has the value 0.
88 upsWellKnownTests 1.3.6.1.2.1.33.1.7.7 objectidentity
89 upsTestNoTestsInitiated 1.3.6.1.2.1.33.1.7.7.1 objectidentity No tests have been initiated and no test is in progress.
90 upsTestAbortTestInProgress 1.3.6.1.2.1.33.1.7.7.2 objectidentity The test in progress is to be aborted / the test in progress was aborted.
91 upsTestGeneralSystemsTest 1.3.6.1.2.1.33.1.7.7.3 objectidentity The manufacturer's standard test of UPS device systems.
92 upsTestQuickBatteryTest 1.3.6.1.2.1.33.1.7.7.4 objectidentity A test that is sufficient to determine if the battery needs replacement.
93 upsTestDeepBatteryCalibration 1.3.6.1.2.1.33.1.7.7.5 objectidentity The system is placed on battery to a discharge level, set by the manufacturer, sufficient to determine battery replacement and battery run-time with a high degree of confidence. WARNING: this test will leave the battery in a low charge state and will require time for recharging to a level sufficient to provide normal battery duration for the protected load.
94 upsControl 1.3.6.1.2.1.33.1.8 objectidentity
95 upsShutdownType 1.3.6.1.2.1.33.1.8.1 integer read-write objecttype scalar This object determines the nature of the action to be taken at the time when the countdown of the upsShutdownAfterDelay and upsRebootWithDuration objects reaches zero. Setting this object to output(1) indicates that shutdown requests should cause only the output of the UPS to turn off. Setting this object to system(2) indicates that shutdown requests will cause the entire UPS system to turn off. Enumeration: 'output': 1, 'system': 2.
96 upsShutdownAfterDelay 1.3.6.1.2.1.33.1.8.2 integer read-write objecttype scalar Setting this object will shutdown (i.e., turn off) either the UPS output or the UPS system (as determined by the value of upsShutdownType at the time of shutdown) after the indicated number of seconds, or less if the UPS batteries become depleted. Setting this object to 0 will cause the shutdown to occur immediately. Setting this object to -1 will abort the countdown. If the system is already in the desired state at the time the countdown reaches 0, then nothing will happen. That is, there is no additional action at that time if upsShutdownType = system and the system is already off. Similarly, there is no additional action at that time if upsShutdownType = output and the output is already off. When read, upsShutdownAfterDelay will return the number of seconds remaining until shutdown, or -1 if no shutdown countdown is in effect. On some systems, if the agent is restarted while a shutdown countdown is in effect, the countdown may be aborted. Sets to this object override any upsShutdownAfterDelay already in effect.
97 upsStartupAfterDelay 1.3.6.1.2.1.33.1.8.3 integer read-write objecttype scalar Setting this object will start the output after the indicated number of seconds, including starting the UPS, if necessary. Setting this object to 0 will cause the startup to occur immediately. Setting this object to -1 will abort the countdown. If the output is already on at the time the countdown reaches 0, then nothing will happen. Sets to this object override the effect of any upsStartupAfterDelay countdown or upsRebootWithDuration countdown in progress. When read, upsStartupAfterDelay will return the number of seconds until startup, or -1 if no startup countdown is in effect. If the countdown expires during a utility failure, the startup shall not occur until the utility power is restored. On some systems, if the agent is restarted while a startup countdown is in effect, the countdown is aborted.
98 upsRebootWithDuration 1.3.6.1.2.1.33.1.8.4 integer read-write objecttype scalar Setting this object will immediately shutdown (i.e., turn off) either the UPS output or the UPS system (as determined by the value of upsShutdownType at the time of shutdown) for a period equal to the indicated number of seconds, after which time the output will be started, including starting the UPS, if necessary. If the number of seconds required to perform the request is greater than the requested duration, then the requested shutdown and startup cycle shall be performed in the minimum time possible, but in no case shall this require more than the requested duration plus 60 seconds. When read, upsRebootWithDuration shall return the number of seconds remaining in the countdown, or -1 if no countdown is in progress. If the startup should occur during a utility failure, the startup shall not occur until the utility power is restored.
99 upsAutoRestart 1.3.6.1.2.1.33.1.8.5 integer read-write objecttype scalar Setting this object to 'on' will cause the UPS system to restart after a shutdown if the shutdown occurred during a power loss as a result of either a upsShutdownAfterDelay or an internal battery depleted condition. Setting this object to 'off' will prevent the UPS system from restarting after a shutdown until an operator manually or remotely explicitly restarts it. If the UPS is in a startup or reboot countdown, then the UPS will not restart until that delay has been satisfied. Enumeration: 'on': 1, 'off': 2.
100 upsConfig 1.3.6.1.2.1.33.1.9 objectidentity
101 upsConfigInputVoltage 1.3.6.1.2.1.33.1.9.1 nonnegativeinteger read-write objecttype scalar The magnitude of the nominal input voltage. On those systems which support read-write access to this object, if there is an attempt to set this variable to a value that is not supported, the request must be rejected and the agent shall respond with an appropriate error message, i.e., badValue for SNMPv1, or inconsistentValue for SNMPv2.
102 upsConfigInputFreq 1.3.6.1.2.1.33.1.9.2 nonnegativeinteger read-write objecttype scalar The nominal input frequency. On those systems which support read-write access to this object, if there is an attempt to set this variable to a value that is not supported, the request must be rejected and the agent shall respond with an appropriate error message, i.e., badValue for SNMPv1, or inconsistentValue for SNMPv2.
103 upsConfigOutputVoltage 1.3.6.1.2.1.33.1.9.3 nonnegativeinteger read-write objecttype scalar The magnitude of the nominal output voltage. On those systems which support read-write access to this object, if there is an attempt to set this variable to a value that is not supported, the request must be rejected and the agent shall respond with an appropriate error message, i.e., badValue for SNMPv1, or inconsistentValue for SNMPv2.
104 upsConfigOutputFreq 1.3.6.1.2.1.33.1.9.4 nonnegativeinteger read-write objecttype scalar The nominal output frequency. On those systems which support read-write access to this object, if there is an attempt to set this variable to a value that is not supported, the request must be rejected and the agent shall respond with an appropriate error message, i.e., badValue for SNMPv1, or inconsistentValue for SNMPv2.
105 upsConfigOutputVA 1.3.6.1.2.1.33.1.9.5 nonnegativeinteger read-only objecttype scalar The magnitude of the nominal Volt-Amp rating.
106 upsConfigOutputPower 1.3.6.1.2.1.33.1.9.6 nonnegativeinteger read-only objecttype scalar The magnitude of the nominal true power rating.
107 upsConfigLowBattTime 1.3.6.1.2.1.33.1.9.7 nonnegativeinteger read-write objecttype scalar The value of upsEstimatedMinutesRemaining at which a lowBattery condition is declared. For agents which support only discrete (discontinuous) values, then the agent shall round up to the next supported value. If the requested value is larger than the largest supported value, then the largest supported value shall be selected.
108 upsConfigAudibleStatus 1.3.6.1.2.1.33.1.9.8 integer read-write objecttype scalar The requested state of the audible alarm. When in the disabled state, the audible alarm should never sound. The enabled state is self-describing. Setting this object to muted(3) when the audible alarm is sounding shall temporarily silence the alarm. It will remain muted until it would normally stop sounding and the value returned for read operations during this period shall equal muted(3). At the end of this period, the value shall revert to enabled(2). Writes of the value muted(3) when the audible alarm is not sounding shall be accepted but otherwise shall have no effect. Enumeration: 'disabled': 1, 'muted': 3, 'enabled': 2.
109 upsConfigLowVoltageTransferPoint 1.3.6.1.2.1.33.1.9.9 nonnegativeinteger read-write objecttype scalar The minimum input line voltage allowed before the UPS system transfers to battery backup.
110 upsConfigHighVoltageTransferPoint 1.3.6.1.2.1.33.1.9.10 nonnegativeinteger read-write objecttype scalar The maximum line voltage allowed before the UPS system transfers to battery backup.
111 upsTraps 1.3.6.1.2.1.33.2 objectidentity
112 upsTrapOnBattery 1.3.6.1.2.1.33.2.1 notificationtype The UPS is operating on battery power. This trap is persistent and is resent at one minute intervals until the UPS either turns off or is no longer running on battery.
113 upsTrapTestCompleted 1.3.6.1.2.1.33.2.2 notificationtype This trap is sent upon completion of a UPS diagnostic test.
114 upsTrapAlarmEntryAdded 1.3.6.1.2.1.33.2.3 notificationtype This trap is sent each time an alarm is inserted into to the alarm table. It is sent on the insertion of all alarms except for upsAlarmOnBattery and upsAlarmTestInProgress.
115 upsTrapAlarmEntryRemoved 1.3.6.1.2.1.33.2.4 notificationtype This trap is sent each time an alarm is removed from the alarm table. It is sent on the removal of all alarms except for upsAlarmTestInProgress.
116 upsConformance 1.3.6.1.2.1.33.3 objectidentity
117 upsCompliances 1.3.6.1.2.1.33.3.1 objectidentity
118 upsSubsetCompliance 1.3.6.1.2.1.33.3.1.1 modulecompliance The compliance statement for UPSs that only support the two-contact communication protocol.
119 upsBasicCompliance 1.3.6.1.2.1.33.3.1.2 modulecompliance The compliance statement for UPSs that support full-featured functions, such as control.
120 upsFullCompliance 1.3.6.1.2.1.33.3.1.3 modulecompliance The compliance statement for UPSs that support advanced full-featured functions.
121 upsGroups 1.3.6.1.2.1.33.3.2 objectidentity
122 upsSubsetGroups 1.3.6.1.2.1.33.3.2.1 objectidentity
123 upsSubsetIdentGroup 1.3.6.1.2.1.33.3.2.1.1 objectgroup The upsSubsetIdentGroup defines objects which are common across all UPSs which meet subset compliance. Most devices which conform to the upsSubsetIdentGroup will provide access to these objects via a proxy agent. If the proxy agent is compatible with multiple UPS types, configuration of the proxy agent will require specifying some of these values, either individually, or as a group (perhaps through a table lookup mechanism based on the UPS model number).
124 upsSubsetBatteryGroup 1.3.6.1.2.1.33.3.2.1.2 objectgroup The upsSubsetBatteryGroup defines the objects that are common to battery groups of two-contact UPSs.
125 upsSubsetInputGroup 1.3.6.1.2.1.33.3.2.1.3 objectgroup The upsSubsetInputGroup defines the objects that are common to the Input groups of two-contact UPSs.
126 upsSubsetOutputGroup 1.3.6.1.2.1.33.3.2.1.4 objectgroup The upsSubsetOutputGroup defines the objects that are common to the Output groups of two-contact UPSs.
127 upsSubsetAlarmGroup 1.3.6.1.2.1.33.3.2.1.6 objectgroup The upsSubsetAlarmGroup defines the objects that are common to the Alarm groups of two-contact UPSs.
128 upsSubsetControlGroup 1.3.6.1.2.1.33.3.2.1.8 objectgroup The upsSubsetControlGroup defines the objects that are common to the Control groups of two-contact UPSs.
129 upsSubsetConfigGroup 1.3.6.1.2.1.33.3.2.1.9 objectgroup The upsSubsetConfigGroup defines the objects that are common to the Config groups of two-contact UPSs.
130 upsBasicGroups 1.3.6.1.2.1.33.3.2.2 objectidentity
131 upsBasicIdentGroup 1.3.6.1.2.1.33.3.2.2.1 objectgroup The upsBasicIdentGroup defines objects which are common to the Ident group of compliant UPSs which support basic functions.
132 upsBasicBatteryGroup 1.3.6.1.2.1.33.3.2.2.2 objectgroup The upsBasicBatteryGroup defines the objects that are common to the battery groups of compliant UPSs which support basic functions.
133 upsBasicInputGroup 1.3.6.1.2.1.33.3.2.2.3 objectgroup The upsBasicInputGroup defines the objects that are common to the Input groups of compliant UPSs which support basic functions.
134 upsBasicOutputGroup 1.3.6.1.2.1.33.3.2.2.4 objectgroup The upsBasicOutputGroup defines the objects that are common to the Output groups of compliant UPSs which support basic functions.
135 upsBasicBypassGroup 1.3.6.1.2.1.33.3.2.2.5 objectgroup The upsBasicBypassGroup defines the objects that are common to the Bypass groups of compliant UPSs which support basic functions.
136 upsBasicAlarmGroup 1.3.6.1.2.1.33.3.2.2.6 objectgroup The upsBasicAlarmGroup defines the objects that are common to the Alarm groups of compliant UPSs which support basic functions.
137 upsBasicTestGroup 1.3.6.1.2.1.33.3.2.2.7 objectgroup The upsBasicTestGroup defines the objects that are common to the Test groups of compliant UPSs which support basic functions.
138 upsBasicControlGroup 1.3.6.1.2.1.33.3.2.2.8 objectgroup The upsBasicControlGroup defines the objects that are common to the Control groups of compliant UPSs which support basic functions.
139 upsBasicConfigGroup 1.3.6.1.2.1.33.3.2.2.9 objectgroup The upsBasicConfigGroup defines the objects that are common to the Config groups of UPSs which support basic functions.
140 upsFullGroups 1.3.6.1.2.1.33.3.2.3 objectidentity
141 upsFullIdentGroup 1.3.6.1.2.1.33.3.2.3.1 objectgroup The upsFullIdentGroup defines objects which are common to the Ident group of fully compliant UPSs.
142 upsFullBatteryGroup 1.3.6.1.2.1.33.3.2.3.2 objectgroup The upsFullBatteryGroup defines the objects that are common to the battery groups of fully compliant UPSs.
143 upsFullInputGroup 1.3.6.1.2.1.33.3.2.3.3 objectgroup The upsFullInputGroup defines the objects that are common to the Input groups of fully compliant UPSs.
144 upsFullOutputGroup 1.3.6.1.2.1.33.3.2.3.4 objectgroup The upsFullOutputGroup defines the objects that are common to the Output groups of fully compliant UPSs.
145 upsFullBypassGroup 1.3.6.1.2.1.33.3.2.3.5 objectgroup The upsFullBypassGroup defines the objects that are common to the Bypass groups of fully compliant UPSs.
146 upsFullAlarmGroup 1.3.6.1.2.1.33.3.2.3.6 objectgroup The upsFullAlarmGroup defines the objects that are common to the Alarm groups of fully compliant UPSs.
147 upsFullTestGroup 1.3.6.1.2.1.33.3.2.3.7 objectgroup The upsFullTestGroup defines the objects that are common to the Test groups of fully compliant UPSs.
148 upsFullControlGroup 1.3.6.1.2.1.33.3.2.3.8 objectgroup The upsFullControlGroup defines the objects that are common to the Control groups of fully compliant UPSs.
149 upsFullConfigGroup 1.3.6.1.2.1.33.3.2.3.9 objectgroup The upsFullConfigGroup defines the objects that are common to the Config groups of fully compliant UPSs.

View File

@@ -41,7 +41,7 @@ static const val_t values[NAMOUNT] = {
static void *mainthread(void *s){
FNAME();
sensordata_t *sensor = (sensordata_t *)s;
while(1){
while(sensor->fdes > -1){
if(check_shm_block(&sdat)){
//DBG("Got next");
time_t tnow = time(NULL);
@@ -64,32 +64,24 @@ static void *mainthread(void *s){
return NULL;
}
sensordata_t *sensor_new(int N, time_t pollt, int _U_ fd){
int sensor_init(sensordata_t *s){
FNAME();
sensordata_t *s = common_new();
if(!s) return NULL;
s->PluginNo = N;
if(pollt) s->tpoll = pollt;
if(!s) return FALSE;
if(!get_shm_block(&sdat, ClientSide)){
WARNX("Can't get BTA shared memory block or create main thread");
s->kill(s);
return NULL;
return FALSE;
}
s->values = MALLOC(val_t, NAMOUNT);
for(int i = 0; i < NAMOUNT; ++i) s->values[i] = values[i];
s->Nvalues = NAMOUNT;
strncpy(s->name, SENSOR_NAME, NAME_LEN);
/*if(!(s->ringbuffer = sl_RB_new(BUFSIZ))){
WARNX("Can't init ringbuffer!");
common_kill(s);
return -1;
}*/
if(pthread_create(&s->thread, NULL, mainthread, (void*)s)){
WARN("Can't create main thread");
s->kill(s);
return NULL;
return FALSE;
}
s->fdes = 0;
return s;
return TRUE;
}

View File

@@ -31,14 +31,15 @@ static const val_t values[NS] = { // fields `name` and `comment` have no sense u
{.sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_PRESSURE},
{.sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_HUMIDITY},
{.sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_PRECIP},
//{.sense = VAL_FORCEDSHTDN, .type = VALT_FLOAT, .meaning = IS_LIGTDIST},
};
static void *mainthread(void *s){
FNAME();
double t0 = sl_dtime();
sensordata_t *sensor = (sensordata_t *)s;
while(1){
DBG("locked");
while(sensor->fdes > -1){
//DBG("locked");
pthread_mutex_lock(&sensor->valmutex);
float f = sensor->values[0].value.f + (drand48() - 0.5) / 2.;
if(f >= 0.) sensor->values[0].value.f = f;
@@ -48,13 +49,19 @@ static void *mainthread(void *s){
if(f > 13. && f < 21.) sensor->values[2].value.f = f;
f = sensor->values[3].value.f + (drand48() - 0.5) / 100.;
if(f > 585. && f < 615.) sensor->values[3].value.f = f;
f = sensor->values[4].value.f + (drand48() - 0.5)*10.;
f = sensor->values[4].value.f + (drand48() - 0.5) * 10.;
if(f > 60. && f <= 100.) sensor->values[4].value.f = f;
sensor->values[5].value.u = (f > 98.) ? 1 : 0;
//if(!sensor->values[5].value.u && drand48() > 0.7) sensor->values[5].value.u = 1;
time_t cur = time(NULL);
for(int i = 0; i < NS; ++i) sensor->values[i].time = cur;
for(int i = 0; i < NS-1; ++i) sensor->values[i].time = cur;
/*f = sensor->values[6].value.f - (drand48() - 0.52);
if(f > 0. && f < 60){
sensor->values[6].value.f = f;
sensor->values[6].time = cur;
}*/
pthread_mutex_unlock(&sensor->valmutex);
DBG("unlocked");
//DBG("unlocked");
if(sensor->freshdatahandler) sensor->freshdatahandler(sensor);
while(sl_dtime() - t0 < sensor->tpoll) usleep(500);
t0 = sl_dtime();
@@ -62,26 +69,24 @@ static void *mainthread(void *s){
return NULL;
}
sensordata_t *sensor_new(int N, time_t pollt, int _U_ fd){
int sensor_init(sensordata_t *s){
FNAME();
sensordata_t *s = common_new();
if(!s) return NULL;
if(!s) return FALSE;
s->Nvalues = NS;
strncpy(s->name, SENSOR_NAME, NAME_LEN);
if(pollt) s->tpoll = pollt;
s->values = MALLOC(val_t, NS);
for(int i = 0; i < NS; ++i) s->values[i] = values[i];
s->values[0].value.f = 1.;
s->values[1].value.f = 180.;
s->values[2].value.f = 17.;
s->values[3].value.f = 600.;
s->values[4].value.f = 80.;
s->values[4].value.f = 89.;
s->values[5].value.u = 0;
s->PluginNo = N;
//s->values[6].value.f = 4.5;
if(pthread_create(&s->thread, NULL, mainthread, (void*)s)){
s->kill(s);
return NULL;
return FALSE;
}
s->fdes = 0;
return s;
return TRUE;
}

View File

@@ -120,26 +120,24 @@ static void *mainthread(void *s){
return NULL;
}
sensordata_t *sensor_new(int N, time_t pollt, int fd){
int sensor_init(sensordata_t *s){
FNAME();
if(fd < 0) return NULL;
sensordata_t *s = common_new();
if(!s) return NULL;
if(!s) return FALSE;
int fd = getFD(s->path);
if(fd < 0) return FALSE;
s->fdes = fd;
s->PluginNo = N;
if(pollt) s->tpoll = pollt;
strncpy(s->name, SENSOR_NAME, NAME_LEN);
snprintf(s->name, NAME_LEN, "%s", SENSOR_NAME);
s->values = MALLOC(val_t, NS);
// don't use memcpy, as `values` could be aligned
for(int i = 0; i < NS; ++i) s->values[i] = values[i];
if(!(s->ringbuffer = sl_RB_new(BUFSIZ))){
WARNX("Can't init ringbuffer!");
s->kill(s);
return NULL;
return FALSE;
}
if(pthread_create(&s->thread, NULL, mainthread, (void*)s)){
s->kill(s);
return NULL;
return FALSE;
}
return s;
return TRUE;
}

View File

@@ -194,23 +194,21 @@ static void *mainthread(void *s){
return NULL;
}
sensordata_t *sensor_new(int N, time_t pollt, int fd){
int sensor_init(sensordata_t *s){
FNAME();
if(fd < 0) return NULL;
sensordata_t *s = common_new();
if(!s) return NULL;
strncpy(s->name, SENSOR_NAME, NAME_LEN);
if(!s) return FALSE;
int fd = getFD(s->path);
if(fd < 0) return FALSE;
snprintf(s->name, NAME_LEN, "%s", SENSOR_NAME);
s->fdes = fd;
s->PluginNo = N;
s->Nvalues = NAMOUNT;
if(pollt) s->tpoll = pollt;
s->values = MALLOC(val_t, NAMOUNT);
// don't use memcpy, as `values` could be aligned
for(int i = 0; i < NAMOUNT; ++i) s->values[i] = values[i];
if(!(s->ringbuffer = sl_RB_new(BUFSIZ)) ||
pthread_create(&s->thread, NULL, mainthread, (void*)s)){
s->kill(s);
return NULL;
return FALSE;
}
return s;
return TRUE;
}

View File

@@ -0,0 +1,201 @@
/*
* This file is part of the weatherdaemon project.
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Plugin for AS3935-based lightning sensor
* https://github.com/eddyem/stm32samples/tree/master/F1:F103/AS3935-lightning
**/
#include <string.h>
#include "weathlib.h"
#define SENSOR_NAME "AS3935 lightning sensor"
// minimal distance for forced shutdown
#define MINDIST (5.0)
// time to check wether sensor is alive, seconds
#define TCHECK (30)
// indexes for text commands and answers
enum{
CMD_INTERRUPT,
CMD_ENERGY,
CMD_DISTANCE,
ANS_LIGHTNING,
ANS_NOICE,
ANS_DISTURBER,
CMD_AMOUNT
};
static const char * commands[CMD_AMOUNT] = {
[CMD_INTERRUPT] = "INTERRUPT",
[CMD_ENERGY] = "energy",
[CMD_DISTANCE] = "distance",
[ANS_LIGHTNING] = "LIGHTNING",
[ANS_NOICE] = "NOICE",
[ANS_DISTURBER] = "DISTURBER",
};
// indexes of weather values
enum{
NINTERRUPT,
NENERGY,
NDISTANCE,
NLIGHTNING,
NSENSNO,
NAMOUNT
};
static const val_t values[NAMOUNT] = {
[NINTERRUPT] = {.sense = VAL_RECOMMENDED, .type = VALT_UINT, .meaning = IS_OTHER, .name = "LINTR", .comment = "Lightning int.: 3 - lightning, 4 - noice, 5 - disturber"},
[NENERGY] = {.sense = VAL_RECOMMENDED, .type = VALT_UINT, .meaning = IS_OTHER, .name = "LENERGY", .comment = "Last lightning energy"},
[NDISTANCE] = {.sense = VAL_OBLIGATORY, .type = VALT_UINT, .meaning = IS_OTHER, .name = "LIGTDIST", .comment = "Distance to last lightning, km"},
[NLIGHTNING] = {.sense = VAL_FORCEDSHTDN, .type = VALT_UINT, .meaning = IS_FORCEDSHTDN, .name = "LIGHTNIN", .comment = "Lightning event occured < 5km"},
[NSENSNO] = {.sense = VAL_RECOMMENDED, .type = VALT_UINT, .meaning = IS_OTHER, .name = "LSENSNO", .comment = "Last lightning event sensor number"},
};
/**
* @brief parse_string - parsing of sensor's answer
* @param str - text string with data
* @param val - value of key
* @return index of key in `values` or -1 if not found
*/
int parse_string(const char *str, uint32_t *val, uint32_t *nsens){
if(!str) return -1;
char key[SL_KEY_LEN], value[SL_VAL_LEN];
DBG("String: %s", str);
if(2 != sl_get_keyval(str, key, value)) return -1;
DBG("key=%s, val=%s", key, value);
int l = strlen(key);
int SensNo = key[--l] - '0';
DBG("SensNo=%d", SensNo);
if(SensNo < 0 || SensNo > 1) return -1;
key[l] = 0;
int idx = 0;
for(; idx < NLIGHTNING; ++idx){
if(0 == strcmp(key, commands[idx])) break;
}
if(idx == NLIGHTNING) return -1;
uint32_t u32;
if(idx == 0){ // check interrupt source
if(strstr(value, commands[ANS_LIGHTNING])) u32 = ANS_LIGHTNING;
else if(strstr(value, commands[ANS_DISTURBER])) u32 = ANS_DISTURBER;
else u32 = ANS_NOICE;
}else u32 = (uint32_t)atoi(value);
DBG("idx = %u, val=%u", idx, u32);
if(val) *val = u32;
if(nsens) *nsens = (uint32_t)SensNo;
return idx;
}
static void *mainthread(void *s){
FNAME();
char buf[BUFSIZ];
time_t tpoll = 0;
sensordata_t *sensor = (sensordata_t *)s;
while(sensor->fdes > -1){
time_t tnow = time(NULL);
if(tnow - tpoll > sensor->tpoll){
int dlen = sprintf(buf, "%s0\n%s1\n", commands[CMD_DISTANCE], commands[CMD_DISTANCE]);
if(dlen != write(sensor->fdes, buf, dlen)){
WARN("Can't ask new data from lightning monitor");
break;
}
DBG("poll @%zd, pollt=%zd", tnow, sensor->tpoll);
tpoll = tnow;
}
int canread = sl_canread(sensor->fdes);
if(canread < 0){
WARNX("Disconnected fd %d", sensor->fdes);
break;
}else if(canread == 1){
ssize_t got = read(sensor->fdes, buf, BUFSIZ);
if(got > 0){
sl_RB_write(sensor->ringbuffer, (uint8_t*)buf, got);
}else if(got < 0){
WARNX("Disconnected?");
break;
}
}
if(sl_RB_datalen(sensor->ringbuffer) > BUFSIZ-1){
WARNX("Overfull? Clear data from ring buffer");
sl_RB_clearbuf(sensor->ringbuffer);
}
int gotfresh = FALSE;
pthread_mutex_lock(&sensor->valmutex);
while(1){
if(sl_RB_readline(sensor->ringbuffer, buf, BUFSIZ-1) > 0){
tpoll = tnow;
uint32_t val, nsens;
int idx = parse_string(buf, &val, &nsens);
if(idx > -1){
DBG("Got index=%d", idx);
gotfresh = TRUE;
if(idx == NINTERRUPT && val == ANS_LIGHTNING){
DBG("Interrupt: lightning");
sensor->values[NSENSNO].value.u = nsens;
sensor->values[NSENSNO].time = tnow;
}
sensor->values[idx].value.u = val;
sensor->values[idx].time = tnow;
}
}else break;
}
// now check values
if(sensor->values[NINTERRUPT].time == tnow && sensor->values[NINTERRUPT].value.u == ANS_LIGHTNING){ // fresh strike
if(tnow - sensor->values[NDISTANCE].time < 3 && sensor->values[NDISTANCE].value.u <= MINDIST){ // ahtung!
if(sensor->values[NLIGHTNING].value.u == 0) DBG("Ahtung!");
sensor->values[NLIGHTNING].time = tnow;
sensor->values[NLIGHTNING].value.u = 1;
}
}else if(tnow - sensor->values[NINTERRUPT].time > TCHECK && sensor->values[NLIGHTNING].value.u){ // remove old lightning flag
DBG("Clear ahtung");
sensor->values[NLIGHTNING].value.u = 0;
sensor->values[NLIGHTNING].time = tnow;
}
pthread_mutex_unlock(&sensor->valmutex);
if(gotfresh) DBG("got fresh data");
if(gotfresh && sensor->freshdatahandler){
DBG("Run fresh data handler");
sensor->freshdatahandler(sensor);
}
usleep(1000);
}
DBG("suicide");
sensor->kill(sensor);
return NULL;
}
int sensor_init(sensordata_t *s){
FNAME();
if(!s) return FALSE;
int fd = getFD(s->path);
if(fd < 0) return FALSE;
snprintf(s->name, NAME_LEN, "%s", SENSOR_NAME);
s->fdes = fd;
s->Nvalues = NAMOUNT;
s->tpoll = TCHECK;
s->values = MALLOC(val_t, NAMOUNT);
for(int i = 0; i < NAMOUNT; ++i) s->values[i] = values[i];
if(!(s->ringbuffer = sl_RB_new(BUFSIZ)) ||
pthread_create(&s->thread, NULL, mainthread, (void*)s)){
s->kill(s);
return FALSE;
}
return TRUE;
}

View File

@@ -78,7 +78,7 @@ static void *mainthread(void *s){
while(sensor->fdes > -1){
time_t tnow = time(NULL);
if(tnow - tpoll > sensor->tpoll){
if(sl_tty_write(sensor->fdes, "?U\r\n", 4)){
if(4 != write(sensor->fdes, "?U\r\n", 4)){
WARN("Can't ask new data");
break;
}
@@ -166,22 +166,20 @@ static void *mainthread(void *s){
}
sensordata_t *sensor_new(int N, time_t pollt, int fd){
int sensor_init(sensordata_t *s){
FNAME();
if(fd < 0) return NULL;
sensordata_t *s = common_new();
if(!s) return NULL;
if(!s) return FALSE;
int fd = getFD(s->path);
if(fd < 0) return FALSE;
s->Nvalues = NAMOUNT;
s->PluginNo = N;
s->fdes = fd;
strncpy(s->name, SENSOR_NAME, NAME_LEN);
if(pollt) s->tpoll = pollt;
snprintf(s->name, NAME_LEN, "%s", SENSOR_NAME);
s->values = MALLOC(val_t, NAMOUNT);
for(int i = 0; i < NAMOUNT; ++i) s->values[i] = values[i];
if(!(s->ringbuffer = sl_RB_new(BUFSIZ)) ||
pthread_create(&s->thread, NULL, mainthread, (void*)s)){
s->kill(s);
return NULL;
return FALSE;
}
return s;
return TRUE;
}

View File

@@ -0,0 +1,216 @@
/*
* This file is part of the weatherdaemon project.
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <string.h>
#include <stdlib.h>
// set flag FORCE_OFF only within FORCEOFF_PAUSE seconds after power loss
#define FORCEOFF_PAUSE 30
#include "weathlib.h"
#define SENSOR_NAME "SNMP UPS monitor"
// https://mibs.observium.org/mib/XUPS-MIB/
// gcc $(net-snmp-config --cflags --libs) snmp.c -o snmptest
enum{
NBATSTAT,
NTONBAT,
NTREMAIN,
NBATCAP,
NSOURCE,
NONBAT,
NAMOUNT
};
enum{
BATT_STAT_UNKN = 1,
BATT_STAT_NORMAL,
BATT_STAT_LOW,
BATT_STAT_DEPLETED,
BATT_STAT_AMOUNT
};
const char* batt_stat[BATT_STAT_AMOUNT]= {
"--",
[BATT_STAT_UNKN] = "Unknown",
[BATT_STAT_NORMAL] = "Normal",
[BATT_STAT_LOW] = "Low",
[BATT_STAT_DEPLETED] = "Depleted"
};
enum{
SOURCE_OTHER = 1,
SOURCE_NONE,
SOURCE_NORMAL,
SOURCE_BYPASS,
SOURCE_BATTERY,
SOURCE_BOOSTER,
SOURCE_REDUCER,
SOURCE_AMOUNT
};
const char *sources[SOURCE_AMOUNT] = {
"--",
[SOURCE_OTHER] = "Other",
[SOURCE_NONE] = "None",
[SOURCE_NORMAL] = "Normal",
[SOURCE_BYPASS] = "Bypass",
[SOURCE_BATTERY] = "Battery",
[SOURCE_BOOSTER] = "Booster",
[SOURCE_REDUCER] = "Reducer"
};
enum{
OID_BATT_STATUS, // batt status: 1 - unkn, 2 - normal, 3 - low, 4 - depleted
OID_BATT_SECONDS_ONBAT, // seconds from ONBAT starts
OID_BATT_EST_MINUTES, // estimated minutes of work
OID_BATT_CAPACITY, // capacity of battery
OID_OUTPUT_SOURCE, // input source: 1 - other, 2 - none, 3 - normal, 4 - bypass, 5 - battery, 6 - booster, 7 - reducer
OID_AMOUNT
};
static netsnmp_session *snmp_session;
static oid anOID[OID_AMOUNT][MAX_OID_LEN];
static size_t anOID_len[OID_AMOUNT];
const char *oids[OID_AMOUNT] = {
[OID_BATT_STATUS] = ".1.3.6.1.2.1.33.1.2.1.0",
[OID_BATT_SECONDS_ONBAT] = ".1.3.6.1.2.1.33.1.2.2.0",
[OID_BATT_EST_MINUTES] = ".1.3.6.1.2.1.33.1.2.3.0",
[OID_BATT_CAPACITY] = ".1.3.6.1.2.1.33.1.2.4.0",
[OID_OUTPUT_SOURCE] = ".1.3.6.1.2.1.33.1.4.1.0",
};
static const val_t values[NAMOUNT] = {
[NBATSTAT] = {.sense = VAL_RECOMMENDED, .type = VALT_STRING, .meaning = IS_OTHER, .name = "UPSBTST", .comment = "UPS battery status"},
[NTONBAT] = {.sense = VAL_RECOMMENDED, .type = VALT_UINT, .meaning = IS_OTHER, .name = "UPSTONBT", .comment = "UPS worked on battery time (s)"},
[NTREMAIN] = {.sense = VAL_RECOMMENDED, .type = VALT_UINT, .meaning = IS_OTHER, .name = "UPSTREM", .comment = "UPS estimated time on battery (s)"},
[NBATCAP] = {.sense = VAL_RECOMMENDED, .type = VALT_UINT, .meaning = IS_OTHER, .name = "UPSBATCP", .comment = "UPS battery capacity, percents"},
[NSOURCE] = {.sense = VAL_RECOMMENDED, .type = VALT_STRING, .meaning = IS_OTHER, .name = "UPSSRC", .comment = "UPS power source"},
[NONBAT] = {.sense = VAL_FORCEDSHTDN, .type = VALT_UINT, .meaning = IS_FORCEDSHTDN, .name = "UPSONBAT", .comment = "AC power lost, works on battery"},
};
static void *mainthread(void *s){
double t0 = sl_dtime();
sensordata_t *sensor = (sensordata_t *)s;
netsnmp_pdu *pdu, *response;
while(sensor->fdes > -1){
//DBG("run");
pdu = snmp_pdu_create(SNMP_MSG_GET);
for(int i = 0; i < OID_AMOUNT; ++i)
snmp_add_null_var(pdu, anOID[i], anOID_len[i]);
int status = snmp_synch_response(snmp_session, pdu, &response);
//DBG("status = %d", status);
if(status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR){
time_t curt = time(NULL);
netsnmp_variable_list *vars = response->variables; // OID_BATT_STATUS
pthread_mutex_lock(&sensor->valmutex);
int ival = *vars->val.integer;
if(ival > 0 && ival < BATT_STAT_AMOUNT){
snprintf(sensor->values[NBATSTAT].value.str, STRT_LEN+1, "%s", batt_stat[ival]);
}
vars = vars->next_variable; // OID_BATT_SECONDS_ONBAT
uint32_t tonbat = (uint32_t) *vars->val.integer;
sensor->values[NTONBAT].value.u = tonbat;
vars = vars->next_variable; // OID_BATT_EST_MINUTES
sensor->values[NTREMAIN].value.u = 60 * (uint32_t) *vars->val.integer;
vars = vars->next_variable; // OID_BATT_CAPACITY
sensor->values[NBATCAP].value.u = (uint32_t) *vars->val.integer;
vars = vars->next_variable; // OID_OUTPUT_SOURCE
ival = *vars->val.integer;
if(ival > 0 && ival < SOURCE_AMOUNT)
snprintf(sensor->values[NSOURCE].value.str, STRT_LEN+1, "%s", sources[ival]);
if(ival == SOURCE_BATTERY && tonbat > FORCEOFF_PAUSE){
sensor->values[NONBAT].value.u = 1;
}else sensor->values[NONBAT].value.u = 0;
for(int i = 0; i < NAMOUNT; ++i)
sensor->values[i].time = curt;
//DBG("times updated to %zd", curt);
pthread_mutex_unlock(&sensor->valmutex);
if(sensor->freshdatahandler) sensor->freshdatahandler(sensor);
}else DBG("Error in packet");
if(response) snmp_free_pdu(response);
//DBG("sleep");
while(sl_dtime() - t0 < sensor->tpoll) usleep(500);
t0 = sl_dtime();
}
return NULL;
}
static void snmp_kill(sensordata_t *s){
s->fdes = -1;
pthread_join(s->thread, NULL);
snmp_close(snmp_session);
common_kill(s);
}
int sensor_init(sensordata_t *s){
FNAME();
if(!s || !s->path[0]) return FALSE;
netsnmp_session session;
init_snmp("snmpapp");
snmp_sess_init(&session);
session.version = SNMP_VERSION_1;
session.community = (u_char *)"public";
session.community_len = strlen((const char *)session.community);
DBG("PATH: %s", s->path);
const char *colon = strchr(s->path, ':');
if(colon) ++colon; // omit "N:" in field "N:host"
session.peername = strdup(colon);
snmp_session = snmp_open(&session);
if(!snmp_session){
snmp_sess_perror("snmp_open", &session);
FREE(session.peername);
return FALSE;
}
s->kill = snmp_kill;
snprintf(s->name, NAME_LEN, "%s", SENSOR_NAME);
s->fdes = 0;
s->Nvalues = NAMOUNT;
s->values = MALLOC(val_t, NAMOUNT);
for(int i = 0; i < NAMOUNT; ++i) s->values[i] = values[i];
DBG("init OIDs");
for(int i = 0; i < OID_AMOUNT; ++i){
anOID_len[i] = MAX_OID_LEN;
if(!read_objid(oids[i], anOID[i], &anOID_len[i])){
snmp_perror(oids[i]);
continue;
}
DBG("Got OID %s", oids[i]);
//snmp_add_null_var(snmp_pdu, anOID, anOID_len);
}
DBG("Start main thread");
if(!(s->ringbuffer = sl_RB_new(BUFSIZ)) ||
pthread_create(&s->thread, NULL, mainthread, (void*)s)){
s->kill(s);
return FALSE;
}
return TRUE;
}

View File

@@ -127,7 +127,7 @@ static void *mainthread(void *s){
while(sensor->fdes > -1){
time_t tnow = time(NULL);
if(tnow - tpoll > sensor->tpoll){
if(sl_tty_write(sensor->fdes, "!0R0\r\n", 6)){
if(6 != write(sensor->fdes, "!0R0\r\n", 6)){
WARN("Can't ask new data");
break;
}
@@ -194,23 +194,21 @@ static void *mainthread(void *s){
return NULL;
}
sensordata_t *sensor_new(int N, time_t pollt, int fd){
int sensor_init(sensordata_t *s){
FNAME();
if(fd < 0) return NULL;
sensordata_t *s = common_new();
if(!s) return NULL;
strncpy(s->name, SENSOR_NAME, NAME_LEN);
s->PluginNo = N;
if(!s) return FALSE;
int fd = getFD(s->path);
if(fd < 0) return FALSE;
snprintf(s->name, NAME_LEN, "%s", SENSOR_NAME);
s->fdes = fd;
s->Nvalues = NAMOUNT;
if(pollt) s->tpoll = pollt;
s->values = MALLOC(val_t, NAMOUNT);
for(int i = 0; i < NAMOUNT; ++i) s->values[i] = values[i];
if(!(s->ringbuffer = sl_RB_new(BUFSIZ)) ||
pthread_create(&s->thread, NULL, mainthread, (void*)s)){
s->kill(s);
return NULL;
return FALSE;
}
return s;
return TRUE;
}

View File

@@ -20,7 +20,6 @@
#include <string.h>
#include <usefull_macros.h>
#include "fd.h"
#include "mainweather.h"
#include "sensors.h"
#include "weathlib.h"
@@ -86,7 +85,7 @@ void *open_plugin(const char *name){
*/
static void dumpsensors(struct sensordata_t* station){
//FNAME();
if(!station || !station->get_value || station->Nvalues < 1) return;
if(!sensor_alive(station) || !station->get_value || station->Nvalues < 1 || station->IsMuted) return;
refresh_sensval(station);
#if 0
DBG("New values...");
@@ -137,16 +136,20 @@ int openplugins(char **paths, int N){
void* dlh = open_plugin(buf);
if(!dlh) continue;
DBG("OPENED");
sensor_new_t sensnew = (sensor_new_t) dlsym(dlh, "sensor_new");
if(sensnew){
int fd = -1;
if(colon) fd = getFD(colon);
sensordata_t *S = sensnew(nplugins, poll_interval, fd); // here nplugins is index in array
if(!S) WARNXL("Can't init plugin %s", paths[i]);
sensor_init_t sensinit = (sensor_init_t) dlsym(dlh, "sensor_init");
if(sensinit){
sensordata_t *S = sensor_new(nplugins, colon);
if(!S) WARNXL("Can't allocate memory for 'sensor' structure");
else{
if(!S->onrefresh || !S->onrefresh(S, dumpsensors)) WARNXL("Can't init refresh funtion");
LOGMSG("Plugin %s nave %d sensors", paths[i], S->Nvalues);
S->init = sensinit;
S->tpoll = poll_interval;
allplugins[nplugins++] = S;
int inited = sensinit(S); // here nplugins is index in array
if(!inited) WARNXL("Can't init plugin %s", paths[i]);
else{
if(!S->onrefresh || !S->onrefresh(S, dumpsensors)) WARNXL("Can't init refresh funtion");
LOGMSG("Plugin %s nave %d sensors", paths[i], S->Nvalues);
}
}
}else WARNXL("Can't find initing function in plugin %s: %s", paths[i], dlerror());
}
@@ -161,6 +164,7 @@ 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[i]);
}
FREE(allplugins);
nplugins = 0;
@@ -178,7 +182,8 @@ static const char* const NM[IS_OTHER] = { // names of standard fields
[IS_PRECIP_LEVEL]="PRECIPLV",
[IS_MIST] = "MIST",
[IS_CLOUDS] = "CLOUDS",
[IS_SKYTEMP] = "SKYTEMP"
[IS_SKYTEMP] = "SKYTEMP",
//[IS_LIGTDIST] = "LIGTDIST",
};
// format "sense" of sensor, like "[WIND][1]=2"
@@ -192,6 +197,19 @@ int format_senssense(const val_t *v, char *buf, int buflen, int Np){
return (got < buflen) ? got : buflen; // full or truncated
}
void get_fieldname(const val_t *v, char buf[KEY_LEN+1]){
if(!v || !buf) return;
int idx = v->meaning;
const char *name = NULL;
if(idx < IS_OTHER){
name = NM[idx];
}else{
name = v->name;
}
if(name) snprintf(buf, KEY_LEN+1, "%s", name);
else buf[0] = 0; // empty
}
/**
* @brief format_sensval - snprintf sensor's value into buffer
* @param v - value to get
@@ -202,16 +220,18 @@ int format_senssense(const val_t *v, char *buf, int buflen, int Np){
*/
int format_sensval(const val_t *v, char *buf, int buflen, int Np){
if(!v || !buf || buflen < 1) return -1;
char strval[VAL_LEN+1];
char strval[VAL_LEN];
int fieldlen = 20; // minimal distance between '=' and '/' is 22 bytes
switch(v->type){
case VALT_UINT: snprintf(strval, VAL_LEN, "%u", v->value.u); break;
case VALT_INT: snprintf(strval, VAL_LEN, "%d", v->value.i); break;
case VALT_FLOAT: snprintf(strval, VAL_LEN, "%.2f", v->value.f); break;
case VALT_STRING: sprintf(strval, "'%s'", v->value.str); fieldlen = -20; break;
default: sprintf(strval, "'ERROR'");
}
const char* const CMT[IS_OTHER] = { // comments for standard fields
[IS_WIND] = "Wind, m/s",
[IS_WINDDIR] = "Instant wind direction, degr (CW from north to FROM)",
[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",
@@ -221,7 +241,8 @@ int format_sensval(const val_t *v, char *buf, int buflen, int Np){
[IS_PRECIP_LEVEL]="Cumulative precipitation level (mm)",
[IS_MIST] = "Mist (1 - yes, 0 - no)",
[IS_CLOUDS] = "Integral sky quality value (bigger - better)",
[IS_SKYTEMP] = "Mean sky temperatyre"
[IS_SKYTEMP] = "Mean sky temperatyre",
//[IS_LIGTDIST] = "Distance to last lightning, km",
};
const char *name = NULL, *comment = NULL;
int idx = v->meaning;
@@ -232,19 +253,23 @@ int format_sensval(const val_t *v, char *buf, int buflen, int Np){
name = v->name;
comment = v->comment;
}
if(!name) return 0; // no name pointed - don't show this value
if(!comment) 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);
if(Np > -1) got = snprintf(buf, buflen, "%s[%d] = %s / %s", name, Np, strval, comment);
else got = snprintf(buf, buflen, "%-*s= %*s / %s", KEY_LEN, name, fieldlen, strval, comment);
return (got < buflen) ? got : buflen; // full or truncated
}
// the same for measurement time formatting
int format_msrmttm(time_t t, char *buf, int buflen){
int format_msrmttm(time_t t, char *buf, int buflen, int Np){
if(!buf || buflen < 1) return -1;
char cmt[COMMENT_LEN+1];
struct tm *T = localtime(&t);
strftime(cmt, COMMENT_LEN, "%F %T", T);
int got = snprintf(buf, buflen, "TMEAS=%zd / Last measurement time: %s", t, cmt);
int got;
if(Np > -1) got = snprintf(buf, buflen, "TWEATH[%d] = %zd / Last weather time: %s", Np, t, cmt);
else got = snprintf(buf, buflen, "TWEATH = %20zd / Last weather time: %s", t, cmt);
return (got < buflen) ? got : buflen;
}
@@ -279,3 +304,31 @@ int change_val_sense(sensordata_t *s, int idx, valsense_t sense){
s->values[idx].sense = sense;
return TRUE;
}
// convert val_t into double
double val2d(const val_t *value){
double curvalue;
switch(value->type){
case VALT_UINT: curvalue = (double) value->value.u; break;
case VALT_INT: curvalue = (double) value->value.i; break;
case VALT_FLOAT: curvalue = (double) value->value.f; break;
default: curvalue = 0.;
}
return curvalue;
}
// pause and continue sensors refresh
int station_mute(sensordata_t *s){
if(!s) return FALSE;
s->IsMuted = TRUE;
return TRUE;
}
int station_unmute(sensordata_t *s){
if(!s) return FALSE;
s->IsMuted = FALSE;
return TRUE;
}
int station_is_muted(sensordata_t *s){
if(s && s->IsMuted) return TRUE;
return FALSE;
}

View File

@@ -27,10 +27,21 @@ int openplugins(char **paths, int N);
void closeplugins();
sensordata_t *get_plugin(int N);
int get_nplugins();
int find_val_by_name(sensordata_t *s, const char *name);
int format_senssense(const val_t *v, char *buf, int buflen, int Np);
int format_sensval(const val_t *v, char *buf, int buflen, int Np);
int format_msrmttm(time_t t, char *buf, int buflen);
int format_msrmttm(time_t t, char *buf, int buflen, int Np);
int change_val_sense(sensordata_t *s, int idx, valsense_t sense);
int set_pollT(time_t t);
time_t get_pollT();
double val2d(const val_t *v);
void get_fieldname(const val_t *v, char buf[KEY_LEN+1]);
int station_mute(sensordata_t *s);
int station_unmute(sensordata_t *s);
int station_is_muted(sensordata_t *s);

View File

@@ -26,9 +26,6 @@
#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;
@@ -42,16 +39,18 @@ static sl_sock_hresult_e timehandler(sl_sock_t *client, _U_ sl_sock_hitem_t *ite
return RESULT_SILENCE;
}
#define SSZ_ (PATH_MAX + 256)
// 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[256];
char buf[SSZ_];
int N = get_nplugins();
if(N < 1) return RESULT_FAIL;
sensordata_t *d = NULL;
for(int i = 0; i < N; ++i){
if(!(d = get_plugin(i))) continue;
snprintf(buf, 255, "PLUGIN[%d]=%s\nNVALUES[%d]=%d\n", i, d->name, i, d->Nvalues);
if(d->path[0]) snprintf(buf, SSZ_, "PLUGIN[%d]=%s @ %s\nNVALUES[%d]=%d\n", i, d->name, d->path, i, d->Nvalues);
else snprintf(buf, SSZ_, "PLUGIN[%d]=%s\nNVALUES[%d]=%d\n", i, d->name, i, d->Nvalues);
sl_sock_sendstrmessage(client, buf);
}
return RESULT_SILENCE;
@@ -70,60 +69,42 @@ sensordata_t *get_plugin_w_message(sl_sock_t *client, int N){
}
/**
* @brief showdataN - send to user meteodata of Nth station
* @param client - client data
* @param N - index of station
*/
static void showdataN(sl_sock_t *client, int N){
char buf[FULL_LEN];
val_t v;
sensordata_t *s = get_plugin_w_message(client, N);
if(!s) return;
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(s, &v, i)) continue;
if(v.time < oldest) continue;
if(1 > format_sensval(&v, buf, FULL_LEN, N)) continue;
DBG("formatted: '%s'", buf);
sl_sock_sendstrmessage(client, buf);
sl_sock_sendbyte(client, '\n');
++nsum; Tsum += v.time;
}
if(nsum > 0){
oldest = (time_t)(Tsum / nsum);
if(0 < format_msrmttm(oldest, buf, FULL_LEN)){ // send mean measuring time
DBG("Formatted time: '%s'", buf);
sl_sock_sendstrmessage(client, buf);
sl_sock_sendbyte(client, '\n');
}
}
}
/**
* @brief showdata - send to client common gathered data
* @brief showdata - send to client sensor's data
* @param client - client data
* @param N - -1 for common data or station index for specific meteo
*/
static void showdata(sl_sock_t *client){
static void showdata(sl_sock_t *client, int N){
char buf[FULL_LEN];
val_t v;
int Ncoll = collected_amount();
time_t oldest = time(NULL) - oldest_interval;
uint64_t Tsum = 0; int nsum = 0;
int Ncoll = 0;
sensordata_t *s = NULL;
if(N < 0){
Ncoll = collected_amount();
}else{
s = get_plugin_w_message(client, N);
if(!s) return;
Ncoll = s->Nvalues;
}
time_t oldest = time(NULL) - 2 * WeatherConf.ahtung_delay, mstm = 0;
for(int i = 0; i < Ncoll; ++i){
if(!get_collected(&v, i)){ DBG("Can't get %dth value", i); continue; }
if(v.time < oldest){ DBG("%dth value is too old", i); continue; }
if(1 > format_sensval(&v, buf, FULL_LEN, -1)){ DBG("Can't format"); continue; }
DBG("formatted: '%s'", buf);
int ans = 0;
if(N < 0) ans = get_collected(&v, i);
else ans = s->get_value(s, &v, i);
if(!ans){ DBG("Can't get %dth value", i); continue; }
// hide old and broken sensors data
if(v.time < oldest || v.sense > VAL_UNNECESSARY){ /*DBG("%dth value is too old", i);*/ continue; }
if(1 > format_sensval(&v, buf, FULL_LEN, N)){ DBG("Can't format %d", i); continue; }
//DBG("formatted: '%s'", buf);
sl_sock_sendstrmessage(client, buf);
sl_sock_sendbyte(client, '\n');
++nsum; Tsum += v.time;
if(v.time > mstm) mstm = v.time;
}
DBG("nsum=%d", nsum);
if(nsum > 0){
oldest = (time_t)(Tsum / nsum);
if(0 < format_msrmttm(oldest, buf, FULL_LEN)){ // send mean measuring time
DBG("Formatted time: '%s'", buf);
// also send FORCE flag if have
if(N < 0 && is_forbidden()) sl_sock_sendstrmessage(client, "FORBID = 1 / Observations are forbidden by operator\n");
if(mstm){
if(0 < format_msrmttm(mstm, buf, FULL_LEN, N)){ // send mean measuring time
//DBG("Formatted time: '%s'", buf);
sl_sock_sendstrmessage(client, buf);
sl_sock_sendbyte(client, '\n');
}
@@ -136,18 +117,18 @@ static sl_sock_hresult_e gethandler(sl_sock_t *client, _U_ sl_sock_hitem_t *item
if(!client) return RESULT_FAIL;
int N = get_nplugins();
if(N < 1) return RESULT_FAIL;
if(!req) showdata(client);
if(!req) showdata(client, -1);
else{
int n;
if(!sl_str2i(&n, req) || n < 0 || n >= N) return RESULT_BADVAL;
showdataN(client, n);
showdata(client, n);
}
return RESULT_SILENCE;
}
// get parameters' level
static sl_sock_hresult_e getlvlhandler(sl_sock_t *client, _U_ sl_sock_hitem_t *item, const char *req){
if(!client) if(!client) return RESULT_FAIL;
if(!client)return RESULT_FAIL;
int N = get_nplugins();
if(N < 1) return RESULT_FAIL;
val_t v;
@@ -181,7 +162,7 @@ static sl_sock_hresult_e getlvlhandler(sl_sock_t *client, _U_ sl_sock_hitem_t *i
// set parameters' level; format: setlevel=N1:par=level,...,N1:par=level...
// Nx - "station" number, par - parameter name (like "HUMIDITY"), level - new level (0..3)
static sl_sock_hresult_e setlvlhandler(sl_sock_t *client, _U_ sl_sock_hitem_t *item, const char *req){
if(!client) if(!client) return RESULT_FAIL;
if(!client) return RESULT_FAIL;
int N = get_nplugins();
if(N < 1) return RESULT_FAIL;
if(!req) return RESULT_BADVAL;
@@ -242,11 +223,15 @@ static sl_sock_hresult_e setlvlhandler(sl_sock_t *client, _U_ sl_sock_hitem_t *i
break;
}
s = end;
val_t oldval;
int olds = -1;
if(sd->get_value && sd->get_value(sd, &oldval, validx)) olds = oldval.sense;
DBG("%ld\n", val);
if(!change_val_sense(sd, validx, (valsense_t)val)){
result = RESULT_BADVAL;
break;
}
LOGWARN("Change '%s' of '%s' sense from %d to %ld", sd->name, buf, olds, val);
while(isspace((unsigned char)*s)) s++;
if(*s == ','){ // omit delimeter
++s;
@@ -260,22 +245,84 @@ static sl_sock_hresult_e setlvlhandler(sl_sock_t *client, _U_ sl_sock_hitem_t *i
return result;
}
static sl_sock_hresult_e forbidhandler(sl_sock_t *client, sl_sock_hitem_t *item, const char *req){
char buf[256];
if(!client) return RESULT_FAIL;
if(req){ // setter
int l;
if(!sl_str2i(&l, req)) return RESULT_BADVAL;
forbid_observations(l);
LOGWARN("Manual set by socket command FORBID=%d", is_forbidden());
}
snprintf(buf, 256, "%s = %d\n", item->key, is_forbidden());
sl_sock_sendstrmessage(client, buf);
return RESULT_SILENCE;
}
static sl_sock_hresult_e forceoffhandler(sl_sock_t *client, sl_sock_hitem_t *item, const char *req){
char buf[256];
if(!client) return RESULT_FAIL;
int flag = -1;
if(req){ // setter
if(!sl_str2i(&flag, req) || flag < 0 || flag > 1) return RESULT_BADVAL;
}
snprintf(buf, 256, "%s = %d\n", item->key, force_off(flag));
sl_sock_sendstrmessage(client, buf);
return RESULT_SILENCE;
}
static sl_sock_hresult_e wlevhandler(sl_sock_t *client, sl_sock_hitem_t *item, const char *req){
char buf[256];
if(!client) return RESULT_FAIL;
int newlevel = -1;
if(req){ // setter
if(!sl_str2i(&newlevel, req) || newlevel < 0 || newlevel > WEATHER_PROHIBITED) return RESULT_BADVAL;
}
snprintf(buf, 256, "%s = %d\n", item->key, weather_level(newlevel));
sl_sock_sendstrmessage(client, buf);
return RESULT_SILENCE;
}
static sensordata_t *get_sd_by_num(const char *req, int *Num){
int N;
if(!req || !sl_str2i(&N, req) || N < 0) return NULL;
if(Num) *Num = N;
return get_plugin(N);
}
static sl_sock_hresult_e mutehandler(sl_sock_t *client, _U_ sl_sock_hitem_t *item, const char *req){
if(!client) return RESULT_FAIL;
sensordata_t *sd = get_sd_by_num(req, NULL);
if(!sd) return RESULT_BADVAL;
if(!station_mute(sd)) return RESULT_FAIL;
return RESULT_OK;
}
static sl_sock_hresult_e unmutehandler(sl_sock_t *client, _U_ sl_sock_hitem_t *item, const char *req){
if(!client) return RESULT_FAIL;
sensordata_t *sd = get_sd_by_num(req, NULL);
if(!sd) return RESULT_BADVAL;
if(!station_unmute(sd)) return RESULT_FAIL;
return RESULT_OK;
}
static sl_sock_hresult_e ismutedhandler(sl_sock_t *client, sl_sock_hitem_t *item, const char *req){
if(!client) return RESULT_FAIL;
int N;
sensordata_t *sd = get_sd_by_num(req, &N);
if(!sd) return RESULT_BADVAL;
char buf[256];
snprintf(buf, 256, "%s[%d] = %d\n", item->key, N, station_is_muted(sd));
sl_sock_sendstrmessage(client, buf);
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);
DBG("shutdown");
LOGWARN("Client fd=%d tried to connect after MAX reached", fd);
}
// new connections handler (return FALSE to reject client)
@@ -309,27 +356,17 @@ static sl_sock_hitem_t nethandlers[] = { // net - only getters and client-only s
{NULL, NULL, NULL, NULL}
};
static sl_sock_hitem_t localhandlers[] = { // local - full amount of setters/getters
{forbidhandler, "forbid", "get/set/clear MANUAL FORBID flag", NULL},
{forceoffhandler, "forceoff", "get/set/clear FORCE SHUTDOWN flag", NULL},
{setlvlhandler, "setlevel", "set 'sense level' (0..3) for given plugin parameters, e.g. setlevel=1:WIND=3,HUMIDITY=3 - disable fields for station 1", NULL},
{wlevhandler, "weathlevel", "set/get current weather level (0..3): goog/bad/terrible/prohibited", NULL},
{ismutedhandler, "ismuted", "==1 if station is muted", NULL},
{mutehandler, "mute", "pause station's data capture", NULL},
{unmutehandler, "unmute", "continue station's data capture", NULL},
COMMONHANDLERS
{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");
@@ -340,11 +377,13 @@ int start_servers(const char *netnode, const char *sockpath){
LOGERR("start_servers(): can't run network socket");
return FALSE;
}
DBG("Network server started");
localsocket = sl_sock_run_server(SOCKT_UNIX, sockpath, BUFSIZ, localhandlers);
if(!localsocket){
LOGERR("start_servers(): can't run local socket");
return FALSE;
}
DBG("Local server started");
sl_sock_changemaxclients(netsocket, MAX_CLIENTS);
sl_sock_changemaxclients(localsocket, 1);
sl_sock_maxclhandler(netsocket, toomuch);
@@ -355,35 +394,30 @@ int start_servers(const char *netnode, const char *sockpath){
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;
// now run checking cycle
int Nplugins = get_nplugins();
time_t tstart = time(NULL), tcur;
while(1){
for(int i = 0; i < Nplugins; ++i){
sensordata_t *s = get_plugin(i);
if(!s) continue;
if(sensor_alive(s)) continue;
// sensor isn't inited - try to do it
DBG("sensor with path %s isn't inited, try", s->path);
s->kill(s); // clear resources
if(s->init){
if(s->init(s)) LOGMSG("Sensor %s reinited @ %s", s->name, s->path);
else DBG("Can't reinit");
}
}
while((tcur = time(NULL)) - tstart < WeatherConf.reinit_delay) sleep(1);
tstart = tcur;
}
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
return TRUE; // should be never reached
}
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_join(locthread, NULL);
//pthread_join(netthread, NULL);
//LOGMSG("Server threads are dead");
}

View File

@@ -1,8 +1,8 @@
CMakeLists.txt
Readme.md
cmdlnopts.c
cmdlnopts.h
fd.c
fd.h
main.c
mainweather.c
mainweather.h
@@ -12,7 +12,9 @@ plugins/btameteo.c
plugins/dummy.c
plugins/fdexample.c
plugins/hydreon.c
plugins/lightning.c
plugins/reinhardt.c
plugins/snmp.c
plugins/wxa100.c
sensors.c
sensors.h

View File

@@ -23,21 +23,24 @@
#include "weathlib.h"
// private functions (for plugins usage only)
static int common_onrefresh(sensordata_t*, void (*handler)(sensordata_t*));
static void common_kill(sensordata_t *);
static int common_getval(sensordata_t*, val_t*, int);
//static int common_onrefresh(sensordata_t*, void (*handler)(sensordata_t*));
//static void common_kill(sensordata_t *);
//static int common_getval(sensordata_t*, val_t*, int);
//static int common_init(sensordata_t*, int, time_t, int);
/**
* @brief common_new - call this function from your plugin's `sensor_new`
* @return
* @brief sensor_new - call this function before calling `sensor_init`
* @param N - plugin number in array
* @return pointer to allocated sensor's structure
*/
sensordata_t *common_new(){
sensordata_t *sensor_new(int N, const char *descr){
sensordata_t *s = MALLOC(sensordata_t, 1);
s->fdes = -1; // not inited
s->onrefresh = common_onrefresh;
s->onrefresh = common_onrefresh; // `init` function can redefine basic stubs
s->get_value = common_getval;
s->kill = common_kill;
s->PluginNo = N; // `init` shouldn't change this value
snprintf(s->path, PATH_MAX, "%s", descr); // `init` shouldn't change this value
pthread_mutex_init(&s->valmutex, NULL);
return s;
}
@@ -66,19 +69,27 @@ int common_onrefresh(sensordata_t *s, void (*handler)(sensordata_t *)){
}
/**
* @brief common_kill - common `die` function
* @brief common_kill - common `die` function (close, but don't destroy sensor)
* @param s - sensor
*/
void common_kill(sensordata_t *s){
FNAME();
if(!s) return;
if(s->fdes > -1){ // inited and maybe have opened file/socket
if(0 == pthread_cancel(s->thread)){
DBG("%s main thread canceled, join", s->name);
pthread_join(s->thread, NULL);
DBG("Done");
}
close(s->fdes);
s->fdes = -1;
DBG("FD closed");
usleep(5000);
if(pthread_equal(pthread_self(), s->thread)){
DBG("Don't cancel myself");
}else{
DBG("Cancel sensor's thread");
if(0 == pthread_cancel(s->thread)){
DBG("%s main thread canceled, join", s->name);
pthread_join(s->thread, NULL);
DBG("Done");
}
}
}
DBG("Delete RB");
if(s->ringbuffer) sl_RB_delete(&s->ringbuffer);
@@ -86,9 +97,8 @@ void common_kill(sensordata_t *s){
if(s->privdatafree) s->privdatafree(s->privdata);
else FREE(s->privdata);
DBG("Sensor '%s' killed", s->name);
LOGERR("Sensor '%s' killed", s->name);
FREE(s);
DBG("There's no more this sensor");
if(s->path[0]) LOGERR("Sensor '%s' @ '%s' killed", s->name, s->path);
else LOGERR("Sensor '%s' killed", s->name);
}
/**

View File

@@ -27,6 +27,8 @@
// length (in symbols) of key, value and comment
#define KEY_LEN (8)
// length of STR type (without terminal zero)
#define STRT_LEN (11)
#define VAL_LEN (31)
#define COMMENT_LEN (63)
// maximal full length of "KEY=val / comment" (as for sfitsio)
@@ -36,6 +38,7 @@
// importance of values
typedef enum{
VAL_FORCEDSHTDN, // if this value is `terrible`, `forced sthudtown` flag will be set
VAL_OBLIGATORY, // can't be omitted
VAL_RECOMMENDED, // recommended to show
VAL_UNNECESSARY, // may be shown by user request
@@ -57,13 +60,19 @@ typedef enum{
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"
//IS_LIGTDIST, // distance to lightning
IS_OTHER, // something other - read "name" and "comment"
// values after `IS_OTHER` have no pre-defined names and comments!!!
IS_BADWEATH, // if meet this flag, set weather level to "BAD"
IS_TERRIBLEWEATH, // -//- "TERRIBLE"
IS_FORCEDSHTDN, // like "on battery" flag from UPS
} valmeaning_t;
typedef union{
uint32_t u;
int32_t i;
float f;
char str[STRT_LEN+1];// up to 8 symbols (with terminating zero)
} num_t;
// type of value
@@ -71,7 +80,7 @@ typedef enum{
VALT_UINT,
VALT_INT,
VALT_FLOAT,
//VALT_STRING,
VALT_STRING,
} valtype_t;
// value
@@ -88,9 +97,12 @@ typedef struct{
// all sensor's data
// all functions have `this` as first arg
typedef struct sensordata_t{
char name[NAME_LEN+1]; // max 31 symbol of sensor's name (e.g. "rain sensor")
char name[NAME_LEN+1]; // sensor's name (e.g. "rain sensor @ localhost")
char path[PATH_MAX]; // path to sensor's device or socket description in format D:/path, U:path, N:host:port
int Nvalues; // amount of values
int PluginNo; // plugin number in array (if several)
int IsMuted; // ==1 for "muted" station (don't refresh sensors' data)
int (*init)(struct sensordata_t*); // main init function
int (*onrefresh)(struct sensordata_t*, void (*handler)(struct sensordata_t*)); // handler of new data; return TRUE if OK
int (*get_value)(struct sensordata_t*, val_t*, int); // getter of Nth value
void (*kill)(struct sensordata_t*); // close everything and remove sensor
@@ -108,11 +120,16 @@ typedef struct sensordata_t{
} sensordata_t;
// type for function extraction
typedef sensordata_t* (*sensor_new_t)(int, time_t, int);
typedef int (*sensor_init_t)(sensordata_t *);
// init meteostation with given PluginNo, poll_interval and fd
sensordata_t *sensor_new(int PluginNo, time_t poll_interval, int fd); // external initial function for any plugin
// init meteostation with given PluginNo, poll_interval and descriptor
//sensordata_t *sensor_init(int PluginNo, time_t poll_interval, const char *descr); // external initial function for any plugin
int sensor_alive(sensordata_t *s);
sensordata_t *sensor_new(int N, const char *descr);
// private function (for plugins usage only)
sensordata_t *common_new();
void common_kill(sensordata_t *s);
int common_onrefresh(sensordata_t *s, void (*handler)(sensordata_t *));
int common_getval(struct sensordata_t *s, val_t *o, int N);
int getFD(const char *path);