mirror of
https://github.com/eddyem/small_tel.git
synced 2026-03-20 08:41:03 +03:00
Compare commits
7 Commits
50cbaea550
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| fa305a7cc0 | |||
| 205a190820 | |||
| 7c2aaf1cb0 | |||
| 54778dcf9a | |||
|
|
31752b58cc | ||
| 09642743a6 | |||
| a80347643f |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -23,3 +23,6 @@
|
||||
*.so
|
||||
*.so.*
|
||||
|
||||
# build dirs
|
||||
mk/
|
||||
build/
|
||||
|
||||
@@ -49,7 +49,7 @@ static glob_pars const Gdefault = {
|
||||
* Define command line options by filling structure:
|
||||
* name has_arg flag val type argptr help
|
||||
*/
|
||||
static myoption cmdlnopts[] = {
|
||||
static sl_option_t cmdlnopts[] = {
|
||||
// common options
|
||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
|
||||
{"device", NEED_ARG, NULL, 'd', arg_string, APTR(&G.device), _("serial device name (default: )" DEFAULT_DEV ")")},
|
||||
@@ -75,10 +75,10 @@ glob_pars *parse_args(int argc, char **argv){
|
||||
char helpstring[1024], *hptr = helpstring;
|
||||
snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n");
|
||||
// format of help: "Usage: progname [args]\n"
|
||||
change_helpstring(helpstring);
|
||||
sl_helpstring(helpstring);
|
||||
// parse arguments
|
||||
parseargs(&argc, &argv, cmdlnopts);
|
||||
if(help) showhelp(-1, cmdlnopts);
|
||||
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||
if(help) sl_showhelp(-1, cmdlnopts);
|
||||
if(argc > 0){
|
||||
WARNX("Ignore %d unknown parameters: ");
|
||||
for (i = 0; i < argc; i++)
|
||||
|
||||
@@ -57,7 +57,7 @@ static void dumpRchanges(rg11 *new, rg11 *old){
|
||||
int start = 1;
|
||||
for(int i = 0; i < RREGNUM; ++i){
|
||||
if(o[i] != n[i]){
|
||||
sl_putlogt(start, globlog, LOGLEVEL_MSG, "%s=%d", regname(i), n[i]);
|
||||
sl_putlogt(start, sl_globlog, LOGLEVEL_MSG, "%s=%d", regname(i), n[i]);
|
||||
DBG("%s=%d", regname(i), n[i]);
|
||||
if(start) start = 0;
|
||||
}
|
||||
@@ -68,7 +68,7 @@ static void dumpRchanges(rg11 *new, rg11 *old){
|
||||
uint8_t f = 1;
|
||||
for(int i = 0; i < RGBITNUM; ++i, f <<= 1){
|
||||
if(xOr & f){
|
||||
sl_putlogt(start, globlog, LOGLEVEL_MSG, "%s=%d", rgbitname(i), (new->RGBits & f) ? 1 : 0);
|
||||
sl_putlogt(start, sl_globlog, LOGLEVEL_MSG, "%s=%d", rgbitname(i), (new->RGBits & f) ? 1 : 0);
|
||||
DBG("%s=%d", rgbitname(i), (new->RGBits & f) ? 1 : 0);
|
||||
if(start) start = 0;
|
||||
}
|
||||
@@ -82,7 +82,7 @@ static void dumpSchanges(slowregs *new, slowregs *old){
|
||||
int start = 1;
|
||||
for(int i = 0; i < SREGNUM; ++i){
|
||||
if(o[i] != n[i]){
|
||||
sl_putlogt(start, globlog, LOGLEVEL_MSG, "%s=%d", slowname(i), n[i]);
|
||||
sl_putlogt(start, sl_globlog, LOGLEVEL_MSG, "%s=%d", slowname(i), n[i]);
|
||||
DBG("%s=%d", slowname(i), n[i]);
|
||||
if(start) start = 0;
|
||||
}
|
||||
@@ -147,12 +147,12 @@ static void puttotable(rg11 *R, slowregs *S){
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
initial_setup();
|
||||
sl_init();
|
||||
char *self = strdup(argv[0]);
|
||||
G = parse_args(argc, argv);
|
||||
if(G->timeout < 5) ERRX("Timeout should be not less than 5 seconds");
|
||||
if(!G->logfile && !G->outfile) ERRX("Point at least log or output file name");
|
||||
check4running(self, G->pidfile);
|
||||
sl_check4running(self, G->pidfile);
|
||||
if(!hydreon_open(G->device)) return 1;
|
||||
if(G->logfile) OPENLOG(G->logfile, LOGLEVEL_ANY, 0);
|
||||
if(G->outfile){
|
||||
@@ -166,9 +166,9 @@ int main(int argc, char **argv){
|
||||
signal(SIGINT, signals); // ctrl+C - quit
|
||||
signal(SIGQUIT, signals); // ctrl+\ - quit
|
||||
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
|
||||
double t0 = dtime();
|
||||
double t0 = sl_dtime();
|
||||
puttotable(NULL, NULL);
|
||||
while(dtime() - t0 < (double)G->timeout){ // dump only changes
|
||||
while(sl_dtime() - t0 < (double)G->timeout){ // dump only changes
|
||||
if(!hydreon_getpacket(&Rregs, &Sregs)) continue;
|
||||
int changes = FALSE;
|
||||
if(memcmp(&Rregs, &oRregs, RREGNUM + 1)){ // Rregs changed -> log changes
|
||||
@@ -182,7 +182,7 @@ int main(int argc, char **argv){
|
||||
changes = TRUE;
|
||||
}
|
||||
if(changes) puttotable(&Rregs, &Sregs);
|
||||
t0 = dtime();
|
||||
t0 = sl_dtime();
|
||||
}
|
||||
signals(-1); // never reached
|
||||
return 0;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "hydreon.h"
|
||||
|
||||
static TTY_descr *dev = NULL;
|
||||
static sl_tty_t *dev = NULL;
|
||||
|
||||
// regular registers names
|
||||
static const char* rregnames[RREGNUM] = {
|
||||
@@ -121,7 +121,7 @@ int hydreon_getpacket(rg11 *Rregs, slowregs *Sregs){
|
||||
if(!dev) return 0;
|
||||
static int buflen = 0;
|
||||
static char strbuf[BUFLEN];
|
||||
int l = read_tty(dev);
|
||||
int l = sl_tty_read(dev);
|
||||
if(l < 1) return FALSE;
|
||||
char s = dev->buf[0];
|
||||
if(s == 's'){ // start of new packet -> encode old
|
||||
@@ -146,13 +146,13 @@ int hydreon_getpacket(rg11 *Rregs, slowregs *Sregs){
|
||||
* @return TRUE or FALSE if failed
|
||||
*/
|
||||
int hydreon_open(const char *devname){
|
||||
dev = new_tty((char*)devname, 1200, 1);
|
||||
dev = sl_tty_new((char*)devname, 1200, 1);
|
||||
if(!dev) return FALSE;
|
||||
dev = tty_open(dev, 1);
|
||||
dev = sl_tty_open(dev, 1);
|
||||
if(!dev) return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void hydreon_close(){
|
||||
if(dev) close_tty(&dev);
|
||||
if(dev) sl_tty_close(&dev);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 18.0.1, 2026-01-22T21:17:18. -->
|
||||
<!-- Written by QtCreator 18.0.1, 2026-01-26T22:24:32. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
|
||||
@@ -22,6 +22,7 @@ examples/traectories.h
|
||||
main.h
|
||||
movingmodel.c
|
||||
movingmodel.h
|
||||
polltest/main.c
|
||||
ramp.c
|
||||
ramp.h
|
||||
serial.h
|
||||
|
||||
237
Auxiliary_utils/LibSidServo/polltest/main.c
Normal file
237
Auxiliary_utils/LibSidServo/polltest/main.c
Normal file
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* This file is part of the libsidservo 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 <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#define XYBUFSZ (2048)
|
||||
|
||||
struct{
|
||||
int help;
|
||||
char *Xpath;
|
||||
char *Ypath;
|
||||
double dt;
|
||||
} G = {
|
||||
.Xpath = "/dev/encoder_X0",
|
||||
.Ypath = "/dev/encoder_Y0",
|
||||
.dt = 0.001,
|
||||
};
|
||||
|
||||
sl_option_t options[] = {
|
||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), "show this help"},
|
||||
{"Xpath", NEED_ARG, NULL, 'X', arg_string, APTR(&G.Xpath), "path to X encoder"},
|
||||
{"Ypath", NEED_ARG, NULL, 'Y', arg_string, APTR(&G.Ypath), "path to Y encoder"},
|
||||
{"dt", NEED_ARG, NULL, 'd', arg_double, APTR(&G.dt), "request interval (1e-4..10s)"},
|
||||
};
|
||||
|
||||
typedef struct{
|
||||
char buf[XYBUFSZ+1];
|
||||
int len;
|
||||
} buf_t;
|
||||
|
||||
static int Xfd = -1, Yfd = -1;
|
||||
|
||||
void signals(int sig){
|
||||
if(sig){
|
||||
signal(sig, SIG_IGN);
|
||||
DBG("Get signal %d, quit.\n", sig);
|
||||
}
|
||||
DBG("close");
|
||||
if(Xfd > 0){ close(Xfd); Xfd = -1; }
|
||||
if(Yfd > 0){ close(Yfd); Yfd = -1; }
|
||||
exit(sig);
|
||||
}
|
||||
|
||||
static int op(const char *nm){
|
||||
int fd = open(nm, O_RDWR|O_NOCTTY|O_NONBLOCK);
|
||||
if(fd < 0) ERR("Can't open %s", nm);
|
||||
struct termios2 tty;
|
||||
if(ioctl(fd, TCGETS2, &tty)) ERR("Can't read TTY settings");
|
||||
tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG)
|
||||
tty.c_iflag = 0; // don't do any changes in input stream
|
||||
tty.c_oflag = 0; // don't do any changes in output stream
|
||||
tty.c_cflag = BOTHER | CS8 | CREAD | CLOCAL; // other speed, 8bit, RW, ignore line ctrl
|
||||
tty.c_ispeed = 1000000;
|
||||
tty.c_ospeed = 1000000;
|
||||
if(ioctl(fd, TCSETS2, &tty)) ERR("Can't set TTY settings");
|
||||
// try to set exclusive
|
||||
if(ioctl(fd, TIOCEXCL)){DBG("Can't make exclusive");}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int eolcnt(buf_t *buf){
|
||||
if(!buf) return -1;
|
||||
int cnt = 0;
|
||||
for(int i = 0; i < buf->len; ++i)
|
||||
if(buf->buf[i] == '\n') ++cnt;
|
||||
return cnt;
|
||||
}
|
||||
// move last record (if any) into head of buffer
|
||||
static void movelast(buf_t *buf){
|
||||
FNAME();
|
||||
if(!buf) return;
|
||||
DBG("buf was: %s", buf->buf);
|
||||
int cnt = eolcnt(buf);
|
||||
char *E = strrchr(buf->buf, '\n');
|
||||
int idx = -1;
|
||||
if(E){
|
||||
idx = ++E - buf->buf; // position of symbol after '\n'
|
||||
}else{
|
||||
buf->len = strlen(buf->buf);
|
||||
DBG("leave as is (%s)", buf->buf);
|
||||
return;
|
||||
}
|
||||
DBG("cnt=%d, idx=%d", cnt, idx);
|
||||
switch(cnt){
|
||||
case 0: // EOL not found - clear buf
|
||||
buf->len = 0;
|
||||
break;
|
||||
case 1: // only one record - move all after '\n'
|
||||
if(idx > 0 && idx < XYBUFSZ){
|
||||
buf->len = XYBUFSZ - idx;
|
||||
memmove(buf->buf, E, buf->len);
|
||||
}else buf->len = 0;
|
||||
break;
|
||||
default: // more than one record - move
|
||||
{
|
||||
int i = idx - 2;
|
||||
for(; i > -1; --i)
|
||||
if(buf->buf[i] == '\n') break;
|
||||
++i;
|
||||
buf->len = XYBUFSZ - i;
|
||||
memmove(buf->buf, &buf->buf[i], buf->len);
|
||||
}
|
||||
}
|
||||
buf->buf[buf->len] = 0;
|
||||
DBG("MOVED; now buf[%d]=%s", buf->len, buf->buf);
|
||||
}
|
||||
// write to buffer next data portion; return FALSE in case of error
|
||||
static int readstrings(buf_t *buf, int fd){
|
||||
if(!buf){WARNX("Empty buffer"); return FALSE;}
|
||||
int L = XYBUFSZ - buf->len;
|
||||
if(L == 0){
|
||||
DBG("len: %d", buf->len);
|
||||
movelast(buf);
|
||||
L = XYBUFSZ - buf->len;
|
||||
}
|
||||
int got = read(fd, &buf->buf[buf->len], L);
|
||||
if(got < 0){
|
||||
WARN("read()");
|
||||
return FALSE;
|
||||
}else if(got == 0) return TRUE;
|
||||
buf->len += got;
|
||||
buf->buf[buf->len] = 0;
|
||||
DBG("buf[%d]: %s", buf->len, buf->buf);
|
||||
return TRUE;
|
||||
}
|
||||
// return TRUE if got, FALSE if no data found
|
||||
static int getdata(buf_t *buf, long *out){
|
||||
if(!buf) return -1;
|
||||
// read record between last '\n' and previous (or start of string)
|
||||
int cnt = eolcnt(buf);
|
||||
if(cnt < 1) return FALSE;
|
||||
char *last = strrchr(buf->buf, '\n');
|
||||
if(!last) return FALSE; // WTF?
|
||||
*last = 0;
|
||||
char *prev = buf->buf;
|
||||
if(cnt > 1) prev = strrchr(buf->buf, '\n') + 1;
|
||||
if(!prev) prev = buf->buf; // ??
|
||||
if(out) *out = atol(prev);
|
||||
int l = strlen(++last);
|
||||
if(l < XYBUFSZ){
|
||||
buf->len = l;
|
||||
if(l){
|
||||
memmove(buf->buf, last, l);
|
||||
DBG("moved: %s", buf->buf);
|
||||
}else DBG("empty line");
|
||||
}else{
|
||||
buf->len = 0;
|
||||
DBG("buffer clear");
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
// try to write '\n' asking new data portion; return FALSE if failed
|
||||
static int asknext(int fd){
|
||||
FNAME();
|
||||
if(fd < 0) return FALSE;
|
||||
int i = 0;
|
||||
for(; i < 5; ++i){
|
||||
int l = write(fd, "\n", 1);
|
||||
DBG("l=%d", l);
|
||||
if(1 == l) return TRUE;
|
||||
usleep(100);
|
||||
}
|
||||
DBG("5 tries... failed!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
buf_t xbuf, ybuf;
|
||||
long xlast, ylast;
|
||||
double xtlast, ytlast;
|
||||
sl_init();
|
||||
sl_parseargs(&argc, &argv, options);
|
||||
if(G.help) sl_showhelp(-1, options);
|
||||
if(G.dt < 1e-4) ERRX("dx too small");
|
||||
if(G.dt > 10.) ERRX("dx too big");
|
||||
Xfd = op(G.Xpath);
|
||||
Yfd = op(G.Ypath);
|
||||
struct pollfd pfds[2];
|
||||
pfds[0].fd = Xfd; pfds[0].events = POLLIN;
|
||||
pfds[1].fd = Yfd; pfds[1].events = POLLIN;
|
||||
double t0x, t0y, tstart;
|
||||
asknext(Xfd); asknext(Yfd);
|
||||
t0x = t0y = tstart = sl_dtime();
|
||||
DBG("Start");
|
||||
do{ // main cycle
|
||||
if(poll(pfds, 2, 1) < 0){
|
||||
WARN("poll()");
|
||||
break;
|
||||
}
|
||||
if(pfds[0].revents && POLLIN){
|
||||
if(!readstrings(&xbuf, Xfd)) break;
|
||||
}
|
||||
if(pfds[1].revents && POLLIN){
|
||||
if(!readstrings(&ybuf, Yfd)) break;
|
||||
}
|
||||
double curt = sl_dtime();
|
||||
if(getdata(&xbuf, &xlast)) xtlast = curt;
|
||||
if(curt - t0x >= G.dt){ // get last records
|
||||
if(curt - xtlast < 1.5*G.dt)
|
||||
printf("%-14.4fX=%ld\n", xtlast-tstart, xlast);
|
||||
if(!asknext(Xfd)) break;
|
||||
t0x = (curt - t0x < 2.*G.dt) ? t0x + G.dt : curt;
|
||||
}
|
||||
curt = sl_dtime();
|
||||
if(getdata(&ybuf, &ylast)) ytlast = curt;
|
||||
if(curt - t0y >= G.dt){ // get last records
|
||||
if(curt - ytlast < 1.5*G.dt)
|
||||
printf("%-14.4fY=%ld\n", ytlast-tstart, ylast);
|
||||
if(!asknext(Yfd)) break;
|
||||
t0y = (curt - t0y < 2.*G.dt) ? t0y + G.dt : curt;
|
||||
}
|
||||
}while(Xfd > 0 && Yfd > 0);
|
||||
DBG("OOps: disconnected");
|
||||
signals(0);
|
||||
return 0;
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <math.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
@@ -64,14 +65,12 @@ void getXspeed(){
|
||||
ls = LS_init(Conf.EncoderSpeedInterval / Conf.EncoderReqInterval);
|
||||
if(!ls) return;
|
||||
}
|
||||
pthread_mutex_lock(&datamutex);
|
||||
double dt = timediff0(&mountdata.encXposition.t);
|
||||
double speed = LS_calc_slope(ls, mountdata.encXposition.val, dt);
|
||||
if(fabs(speed) < 1.5 * Xlimits.max.speed){
|
||||
mountdata.encXspeed.val = speed;
|
||||
mountdata.encXspeed.t = mountdata.encXposition.t;
|
||||
}
|
||||
pthread_mutex_unlock(&datamutex);
|
||||
//DBG("Xspeed=%g", mountdata.encXspeed.val);
|
||||
}
|
||||
void getYspeed(){
|
||||
@@ -80,14 +79,12 @@ void getYspeed(){
|
||||
ls = LS_init(Conf.EncoderSpeedInterval / Conf.EncoderReqInterval);
|
||||
if(!ls) return;
|
||||
}
|
||||
pthread_mutex_lock(&datamutex);
|
||||
double dt = timediff0(&mountdata.encYposition.t);
|
||||
double speed = LS_calc_slope(ls, mountdata.encYposition.val, dt);
|
||||
if(fabs(speed) < 1.5 * Ylimits.max.speed){
|
||||
mountdata.encYspeed.val = speed;
|
||||
mountdata.encYspeed.t = mountdata.encYposition.t;
|
||||
}
|
||||
pthread_mutex_unlock(&datamutex);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,13 +133,12 @@ static void parse_encbuf(uint8_t databuf[ENC_DATALEN], struct timespec *t){
|
||||
DBG("Got positions X/Y= %.6g / %.6g", mountdata.encXposition.val, mountdata.encYposition.val);
|
||||
mountdata.encXposition.t = *t;
|
||||
mountdata.encYposition.t = *t;
|
||||
pthread_mutex_unlock(&datamutex);
|
||||
//if(t - lastXenc.t > Conf.EncoderSpeedInterval) getXspeed();
|
||||
//if(t - lastYenc.t > Conf.EncoderSpeedInterval) getYspeed();
|
||||
getXspeed(); getYspeed();
|
||||
pthread_mutex_unlock(&datamutex);
|
||||
//DBG("time = %zd+%zd/1e6, X=%g deg, Y=%g deg", tv->tv_sec, tv->tv_usec, mountdata.encposition.X*180./M_PI, mountdata.encposition.Y*180./M_PI);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* @brief getencval - get uint64_t data from encoder
|
||||
* @param fd - encoder fd
|
||||
@@ -211,6 +207,8 @@ static int getencval(int fd, double *val, struct timespec *t){
|
||||
if(t){ if(!curtime(t)){ DBG("Can't get time"); return 0; }}
|
||||
return got;
|
||||
}
|
||||
#endif
|
||||
|
||||
// try to read 1 byte from encoder; return -1 if nothing to read or -2 if device seems to be disconnected
|
||||
static int getencbyte(){
|
||||
if(encfd[0] < 0) return -1;
|
||||
@@ -322,65 +320,130 @@ static void *encoderthread1(void _U_ *u){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define XYBUFSZ (128)
|
||||
typedef struct{
|
||||
char buf[XYBUFSZ+1];
|
||||
int len;
|
||||
} buf_t;
|
||||
|
||||
// write to buffer next data portion; return FALSE in case of error
|
||||
static int readstrings(buf_t *buf, int fd){
|
||||
FNAME();
|
||||
if(!buf){DBG("Empty buffer"); return FALSE;}
|
||||
int L = XYBUFSZ - buf->len;
|
||||
if(L == 0){
|
||||
DBG("buffer overfull: %d!", buf->len);
|
||||
char *lastn = strrchr(buf->buf, '\n');
|
||||
if(lastn){
|
||||
fprintf(stderr, "BUFOVR: _%s_", buf->buf);
|
||||
++lastn;
|
||||
buf->len = XYBUFSZ - (lastn - buf->buf);
|
||||
DBG("Memmove %d", buf->len);
|
||||
memmove(lastn, buf->buf, buf->len);
|
||||
buf->buf[buf->len] = 0;
|
||||
}else buf->len = 0;
|
||||
L = XYBUFSZ - buf->len;
|
||||
}
|
||||
int got = read(fd, &buf->buf[buf->len], L);
|
||||
if(got < 0){
|
||||
DBG("read()");
|
||||
return FALSE;
|
||||
}else if(got == 0){ DBG("NO data"); return TRUE; }
|
||||
buf->len += got;
|
||||
buf->buf[buf->len] = 0;
|
||||
DBG("buf[%d]: %s", buf->len, buf->buf);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// return TRUE if got, FALSE if no data found
|
||||
static int getdata(buf_t *buf, long *out){
|
||||
if(!buf || buf->len < 1) return FALSE;
|
||||
// read record between last '\n' and previous (or start of string)
|
||||
char *last = &buf->buf[buf->len - 1];
|
||||
//DBG("buf: _%s_", buf->buf);
|
||||
if(*last != '\n') return FALSE;
|
||||
*last = 0;
|
||||
//DBG("buf: _%s_", buf->buf);
|
||||
char *prev = strrchr(buf->buf, '\n');
|
||||
if(!prev) prev = buf->buf;
|
||||
else{
|
||||
fprintf(stderr, "MORETHANONE: _%s_", buf->buf);
|
||||
++prev; // after last '\n'
|
||||
}
|
||||
if(out) *out = atol(prev);
|
||||
// clear buffer
|
||||
buf->len = 0;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// try to write '\n' asking new data portion; return FALSE if failed
|
||||
static int asknext(int fd){
|
||||
FNAME();
|
||||
if(fd < 0) return FALSE;
|
||||
int i = 0;
|
||||
for(; i < 5; ++i){
|
||||
int l = write(fd, "\n", 1);
|
||||
//DBG("l=%d", l);
|
||||
if(1 == l) return TRUE;
|
||||
usleep(100);
|
||||
}
|
||||
DBG("5 tries... failed!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// main encoder thread for separate encoders as USB devices /dev/encoder_X0 and /dev/encoder_Y0
|
||||
static void *encoderthread2(void _U_ *u){
|
||||
if(Conf.SepEncoder != 2) return NULL;
|
||||
DBG("Thread started");
|
||||
struct pollfd pfds[2];
|
||||
pfds[0].fd = encfd[0]; pfds[0].events = POLLIN;
|
||||
pfds[1].fd = encfd[1]; pfds[1].events = POLLIN;
|
||||
double t0[2], tstart;
|
||||
buf_t strbuf[2];
|
||||
long msrlast[2]; // last encoder data
|
||||
double mtlast[2]; // last measurement time
|
||||
asknext(encfd[0]); asknext(encfd[1]);
|
||||
t0[0] = t0[1] = tstart = timefromstart();
|
||||
int errctr = 0;
|
||||
double t0 = timefromstart();
|
||||
const char *req = "\n";
|
||||
int need2ask = 0; // need or not to ask encoder for new data
|
||||
while(encfd[0] > -1 && encfd[1] > -1 && errctr < MAX_ERR_CTR){
|
||||
struct timespec t;
|
||||
if(need2ask){
|
||||
//DBG("ASK for new data, tfs=%.6g", timefromstart());
|
||||
if(1 != write(encfd[0], req, 1)) { ++errctr; continue; }
|
||||
else if(1 != write(encfd[1], req, 1)) { ++errctr; continue; }
|
||||
//DBG("OK");
|
||||
need2ask = 0;
|
||||
usleep(100);
|
||||
do{ // main cycle
|
||||
if(poll(pfds, 2, 0) < 0){
|
||||
DBG("poll()");
|
||||
break;
|
||||
}
|
||||
if(!curtime(&t)){
|
||||
DBG("Where is time?");
|
||||
continue;
|
||||
}
|
||||
double v;
|
||||
if(getencval(encfd[0], &v, &t)){
|
||||
pthread_mutex_lock(&datamutex);
|
||||
mountdata.encXposition.val = Xenc2rad(v);
|
||||
mountdata.encXposition.t = t;
|
||||
pthread_mutex_unlock(&datamutex);
|
||||
//DBG("MDXpos: %.10g/%g (xez=%d, xesr=%.10g), tfs=%.6g", v, mountdata.encXposition.val, X_ENC_ZERO, X_ENC_STEPSPERREV, timefromstart());
|
||||
getXspeed();
|
||||
if(getencval(encfd[1], &v, &t)){
|
||||
pthread_mutex_lock(&datamutex);
|
||||
mountdata.encYposition.val = Yenc2rad(v);
|
||||
mountdata.encYposition.t = t;
|
||||
pthread_mutex_unlock(&datamutex);
|
||||
//DBG("MDYpos: %.10g/%g (yez=%d, yesr=%.10g)", v, mountdata.encYposition.val, Y_ENC_ZERO, Y_ENC_STEPSPERREV);
|
||||
getYspeed();
|
||||
errctr = 0;
|
||||
need2ask = 0;
|
||||
//DBG("tgot=%.6g", timefromstart());
|
||||
while(timefromstart() - t0 < Conf.EncoderReqInterval){ usleep(50); }
|
||||
t0 = timefromstart();
|
||||
}else{
|
||||
DBG("NO Y DATA!!!");
|
||||
/*if(need2ask) ++errctr;
|
||||
else need2ask = 1;
|
||||
continue;*/
|
||||
++errctr;
|
||||
need2ask = 1;
|
||||
int got = 0;
|
||||
for(int i = 0; i < 2; ++i){
|
||||
if(pfds[i].revents && POLLIN){
|
||||
if(!readstrings(&strbuf[i], encfd[i])){
|
||||
++errctr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
double curt = timefromstart();
|
||||
if(getdata(&strbuf[i], &msrlast[i])) mtlast[i] = curt;
|
||||
if(curt - t0[i] >= Conf.EncoderReqInterval){ // get last records
|
||||
if(curt - mtlast[i] < 1.5*Conf.EncoderReqInterval){
|
||||
pthread_mutex_lock(&datamutex);
|
||||
if(i == 0){
|
||||
mountdata.encXposition.val = Xenc2rad((double)msrlast[i]);
|
||||
curtime(&mountdata.encXposition.t);
|
||||
getXspeed();
|
||||
}else{
|
||||
mountdata.encYposition.val = Yenc2rad((double)msrlast[i]);
|
||||
curtime(&mountdata.encYposition.t);
|
||||
getYspeed();
|
||||
}
|
||||
pthread_mutex_unlock(&datamutex);
|
||||
}
|
||||
if(!asknext(encfd[i])){
|
||||
++errctr;
|
||||
break;
|
||||
}
|
||||
t0[i] = (curt - t0[i] < 2.*Conf.EncoderReqInterval) ? t0[i] + Conf.EncoderReqInterval : curt;
|
||||
++got;
|
||||
}
|
||||
}else{ // no data - ask for new
|
||||
//DBG("Need new, tfs=%.6g", timefromstart());
|
||||
/*if(need2ask) ++errctr;
|
||||
else need2ask = 1;
|
||||
continue;*/
|
||||
++errctr;
|
||||
need2ask = 1;
|
||||
}
|
||||
}
|
||||
if(got == 2) errctr = 0;
|
||||
}while(encfd[0] > -1 && encfd[1] > -1 && errctr < MAX_ERR_CTR);
|
||||
DBG("\n\nEXIT: ERRCTR=%d", errctr);
|
||||
for(int i = 0; i < 2; ++i){
|
||||
if(encfd[i] > -1){
|
||||
@@ -463,8 +526,8 @@ static void *mountthread(void _U_ *u){
|
||||
}else mountdata.millis = oldmillis;
|
||||
chkModStopped(&Xprev, c.X, &xcnt, &mountdata.Xstate);
|
||||
chkModStopped(&Yprev, c.Y, &ycnt, &mountdata.Ystate);
|
||||
pthread_mutex_unlock(&datamutex);
|
||||
getXspeed(); getYspeed();
|
||||
pthread_mutex_unlock(&datamutex);
|
||||
while(timefromstart() - t0 < Conf.EncoderReqInterval) usleep(50);
|
||||
t0 = timefromstart();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
cmake_minimum_required(VERSION 3.30)
|
||||
set(PROJ PCS_create)
|
||||
set(MINOR_VERSION "0")
|
||||
set(MID_VERSION "1")
|
||||
|
||||
@@ -321,7 +321,7 @@ static void printheader(){
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
initial_setup();
|
||||
sl_init();
|
||||
G = parse_args(argc, argv);
|
||||
if(G->pressure < 0.) ERRX("Pressure should be greater than zero");
|
||||
if(G->temperature < -100. || G->temperature > 100.) ERRX("Temperature over the range -100..+100");
|
||||
|
||||
@@ -40,7 +40,7 @@ glob_pars const Gdefault = {
|
||||
* Define command line options by filling structure:
|
||||
* name has_arg flag val type argptr help
|
||||
*/
|
||||
static myoption cmdlnopts[] = {
|
||||
static sl_option_t cmdlnopts[] = {
|
||||
// set 1 to param despite of its repeating number:
|
||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
|
||||
{"10m", NO_ARGS, NULL, 't', arg_int, APTR(&G.for10m), _("make output suitable for 10-micron mount")},
|
||||
@@ -68,10 +68,10 @@ static myoption cmdlnopts[] = {
|
||||
glob_pars *parse_args(int argc, char **argv){
|
||||
void *ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr);
|
||||
// format of help: "Usage: progname [args]\n"
|
||||
change_helpstring(_("Version: " PACKAGE_VERSION "\nUsage: %s [args] FITS_files\nMake PCS list for equatorial mount\n\tWhere args are:\n"));
|
||||
sl_helpstring(_("Version: " PACKAGE_VERSION "\nUsage: %s [args] FITS_files\nMake PCS list for equatorial mount\n\tWhere args are:\n"));
|
||||
// parse arguments
|
||||
parseargs(&argc, &argv, cmdlnopts);
|
||||
if(help) showhelp(-1, cmdlnopts);
|
||||
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||
if(help) sl_showhelp(-1, cmdlnopts);
|
||||
G.nfiles = argc;
|
||||
G.infiles = MALLOC(char*, argc);
|
||||
for(int i = 0; i < argc; i++){
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
cmake_minimum_required(VERSION 3.30)
|
||||
set(PROJ chkweather)
|
||||
set(MINOR_VERSION "1")
|
||||
set(MID_VERSION "0")
|
||||
|
||||
@@ -40,7 +40,7 @@ glob_pars const Gdefault = {
|
||||
* Define command line options by filling structure:
|
||||
* name has_arg flag val type argptr help
|
||||
*/
|
||||
static myoption cmdlnopts[] = {
|
||||
static sl_option_t cmdlnopts[] = {
|
||||
// set 1 to param despite of its repeating number:
|
||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
|
||||
{"speed", NEED_ARG, NULL, 's', arg_int, APTR(&G.speed), _("baudrate (default: 9600)")},
|
||||
@@ -59,10 +59,10 @@ static myoption cmdlnopts[] = {
|
||||
glob_pars *parse_args(int argc, char **argv){
|
||||
void *ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr);
|
||||
// format of help: "Usage: progname [args]\n"
|
||||
change_helpstring(_("Usage: %s [args]\n\n\tWhere args are:\n"));
|
||||
sl_helpstring(_("Usage: %s [args]\n\n\tWhere args are:\n"));
|
||||
// parse arguments
|
||||
parseargs(&argc, &argv, cmdlnopts);
|
||||
if(help) showhelp(-1, cmdlnopts);
|
||||
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||
if(help) sl_showhelp(-1, cmdlnopts);
|
||||
if(argc > 0){
|
||||
WARNX("Wrong arguments:\n");
|
||||
for(int i = 0; i < argc; i++)
|
||||
|
||||
@@ -51,28 +51,28 @@ static int getpar(char *string, double *Val, char *Name){
|
||||
|
||||
int main(int argc, char **argv){
|
||||
glob_pars *G = NULL; // default parameters see in cmdlnopts.c
|
||||
initial_setup();
|
||||
sl_init();
|
||||
G = parse_args(argc, argv);
|
||||
TTY_descr *dev = new_tty(G->ttyname, G->speed, 64);
|
||||
sl_tty_t *dev = sl_tty_new(G->ttyname, G->speed, 64);
|
||||
if(!dev) return 1;
|
||||
size_t got, L = 0;
|
||||
char buff[BUFLEN], *ptr = buff;
|
||||
int errctr = 0;
|
||||
for(; errctr < ERRCTR_MAX; ++errctr){
|
||||
if(!tty_open(dev, 1)){
|
||||
if(!sl_tty_open(dev, 1)){
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
while(read_tty(dev)); // clear buffer
|
||||
if(write_tty(dev->comfd, "?U\r\n", 3)){
|
||||
while(sl_tty_read(dev)); // clear buffer
|
||||
if(sl_tty_write(dev->comfd, "?U\r\n", 3)){
|
||||
WARNX("write_tty()");
|
||||
continue;
|
||||
}
|
||||
double t0 = dtime();
|
||||
while(dtime() - t0 < 10.){ // timeout - 10s
|
||||
got = read_tty(dev);
|
||||
double t0 = sl_dtime();
|
||||
while(sl_dtime() - t0 < 10.){ // timeout - 10s
|
||||
got = sl_tty_read(dev);
|
||||
if(got == 0) continue;
|
||||
t0 = dtime();
|
||||
t0 = sl_dtime();
|
||||
if(L + got > BUFLEN - 1) break;
|
||||
L += got;
|
||||
buff[L] = 0;
|
||||
@@ -92,8 +92,8 @@ int main(int argc, char **argv){
|
||||
continue;
|
||||
}else break;
|
||||
}
|
||||
while(read_tty(dev));
|
||||
close_tty(&dev);
|
||||
while(sl_tty_read(dev));
|
||||
sl_tty_close(&dev);
|
||||
if(errctr == ERRCTR_MAX){
|
||||
ERRX("No connection to meteostation");
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ A="90:00:00"
|
||||
H="45:00:00"
|
||||
|
||||
function sendcmd(){
|
||||
echo $1 | nc 192.168.70.33 10001 -q10
|
||||
echo $1 | nc localhost 10001 -q10
|
||||
}
|
||||
|
||||
sendcmd ":Sz${A}#"
|
||||
|
||||
20
Auxiliary_utils/bash_scripts/allsky_sixel.sh
Executable file
20
Auxiliary_utils/bash_scripts/allsky_sixel.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# $1 - scaling factor (in percents) of the image
|
||||
#
|
||||
|
||||
scale=50
|
||||
if [ $# -gt 0 ]; then
|
||||
scale=$1
|
||||
fi
|
||||
|
||||
unset http_proxy
|
||||
|
||||
clear
|
||||
while [[ 1 ]]; do
|
||||
tput cup 0 0
|
||||
curl -s http://zarch.sao.ru/webcam/mirat_allsky.cgi | magick - -colors 256 +dither -normalize -resize $scale% sixel:-
|
||||
# sleep 30s
|
||||
sleep 5s
|
||||
done
|
||||
114
Auxiliary_utils/bash_scripts/allsky_sixel_weather.sh
Executable file
114
Auxiliary_utils/bash_scripts/allsky_sixel_weather.sh
Executable file
@@ -0,0 +1,114 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# $1 - scaling factor (in percents) of the image
|
||||
#
|
||||
|
||||
function get_val {
|
||||
echo $(echo $1 | cut -d "=" -f 2 | cut -d "." -f 1)
|
||||
}
|
||||
|
||||
white_col="\e[97m"
|
||||
red_col="\e[1m\e[31m"
|
||||
end_col="\e[0m"
|
||||
last_row=0
|
||||
|
||||
scale=50
|
||||
if [ $# -gt 0 ]; then
|
||||
scale=$1
|
||||
fi
|
||||
|
||||
unset http_proxy
|
||||
|
||||
im_sleep=20 # in secs
|
||||
info_sleep=180 # in secs
|
||||
|
||||
n_info=$((info_sleep/im_sleep))
|
||||
|
||||
|
||||
|
||||
clear
|
||||
while [[ 1 ]]; do
|
||||
# weather info
|
||||
m_old=($(curl 192.168.70.33:12345 2>/dev/null)// / )
|
||||
rain=${m_old[0]}
|
||||
clouds=${m_old[1]}
|
||||
temp=${m_old[2]}
|
||||
|
||||
m_new=($(curl localhost:3333/stat3600 2>/dev/null | sed 's/[\x01-\x1F\x7F]//g')// / )
|
||||
windmax=${m_new[0]}
|
||||
m_new=($(curl localhost:3333 2>/dev/null | sed 's/[\x01-\x1F\x7F]//g')// / )
|
||||
wind=${m_new[0]}
|
||||
humi=${m_new[4]}
|
||||
|
||||
rain_col=$white_col
|
||||
clouds_col=$white_col
|
||||
humi_col=$white_col
|
||||
wind_col=$white_col
|
||||
wind_max_col=$white_col
|
||||
|
||||
let rain_flag=$(get_val $rain)
|
||||
if [[ $rain_flag -eq 1 ]]; then
|
||||
rain_col=$red_col
|
||||
fi
|
||||
|
||||
let clouds_val=$(get_val $clouds)
|
||||
if [[ $clouds_val -le 1500 ]]; then
|
||||
clouds_col=$red_col
|
||||
|
||||
fi
|
||||
|
||||
let humi_val=$(get_val $humi)
|
||||
if [[ $humi_val -ge 90 ]]; then
|
||||
humi_col=$red_col
|
||||
fi
|
||||
|
||||
let wind_val=$(get_val $wind)
|
||||
if [[ $wind_val -ge 10 ]]; then
|
||||
wind_col=$red_col
|
||||
fi
|
||||
|
||||
let wind_max_val=$(get_val $windmax)
|
||||
if [[ $wind_max_val -ge 10 ]]; then
|
||||
wind_max_col=$red_col
|
||||
fi
|
||||
|
||||
ncols=`tput cols`
|
||||
start_col=$((ncols-25))
|
||||
|
||||
tput cup $last_row $start_col
|
||||
# echo -e "\e[4m`date`:\e[0m"
|
||||
echo -e "\e[4m`date +'%F %T'`:\e[0m"
|
||||
|
||||
((++last_row))
|
||||
|
||||
tput cup $last_row $start_col
|
||||
echo -e "$clouds_col$clouds $rain_col($rain)$end_col"
|
||||
|
||||
((++last_row))
|
||||
|
||||
tput cup $last_row $start_col
|
||||
# echo -e "$temp $humi_col($humi)$end_col"
|
||||
temp_val=`printf "%.1f" ${temp#*=}`
|
||||
echo -e "Temp=$temp_val, ${humi_col}Hum=$humi_val%$end_col"
|
||||
|
||||
((++last_row))
|
||||
|
||||
tput cup $last_row $start_col
|
||||
# echo -e "$wind_col$wind $wind_max_col($windmax @hour)$end_col\n"
|
||||
wind_max_val=`printf "%.1f" ${windmax#*=}`
|
||||
echo -e "$wind_col$wind $wind_max_col(Max=$wind_max_val/hour)$end_col\n"
|
||||
|
||||
IFS='[;' read -p $'\e[6n' -d R -rs _ last_row col _
|
||||
|
||||
# allsky image
|
||||
|
||||
for i in `seq $n_info`; do
|
||||
tput cup 0 0
|
||||
curl -s http://zarch.sao.ru/webcam/omea_allsky.cgi | magick - -colors 256 -normalize +dither -resize $scale% sixel:-
|
||||
# curl -s http://zarch.sao.ru/webcam/mirat_allsky.cgi | magick - -colors 256 -resize $scale% sixel:-
|
||||
sleep ${im_sleep}s
|
||||
done
|
||||
|
||||
|
||||
done
|
||||
18
Auxiliary_utils/bash_scripts/copy_and_xz.sh
Executable file
18
Auxiliary_utils/bash_scripts/copy_and_xz.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# $1 - directory to be copied
|
||||
#
|
||||
|
||||
RDIR="/home/obs/robotel1_2025"
|
||||
|
||||
# copy non-FITS files
|
||||
tar c --exclude='*.fit' --exclude='*shit*' $1 | ssh obs@roboserv "tar x -C $RDIR"
|
||||
#tar c --exclude='*.fit' --exclude='*shit*' $1 | ssh obs@roboserv "tar x -C /home/obs/robotel1_2023"
|
||||
|
||||
# copy FITS files and XZ-ing it on remote server
|
||||
CMD='sh -c "xz -6e -T0 - > $TAR_FILENAME.xz"'
|
||||
tar c $1/*.fit | ssh obs@roboserv "cd $RDIR; tar x --to-command='$CMD'"
|
||||
#tar c $1/*.fit | ssh obs@roboserv "cd /home/obs/robotel1_2023; tar x --to-command='$CMD'"
|
||||
|
||||
ssh obs@roboserv "cd $RDIR; tar c $1 | ssh data@robostorage 'tar x -C /mnt/ARCHIVE/ROBOTEL1/'"
|
||||
165
Auxiliary_utils/bash_scripts/run_full.new_foc
Executable file
165
Auxiliary_utils/bash_scripts/run_full.new_foc
Executable file
@@ -0,0 +1,165 @@
|
||||
#!/bin/bash
|
||||
RA="17:17:08.86"
|
||||
DEC="+67:57:11.4"
|
||||
OBJ="GALEX171708.5"
|
||||
EXPTIME=31000
|
||||
FLATTIME=40000
|
||||
OBS="Fatkhullin T.A."
|
||||
BADWEATHER=1300
|
||||
DATEEND=$(sunrise 14)
|
||||
# focus each N seconds
|
||||
FOCUST=7200
|
||||
|
||||
FNO=1
|
||||
FDATE=0
|
||||
|
||||
#
|
||||
# NOTE: THIS IS A NEW VERSION OF THE OBSERVATION SCRIPT!
|
||||
# CHAGLELOG:
|
||||
# Oct 2024: replace focussing algorithm (T. Fatkhullin)
|
||||
# At the night start the first focussing run
|
||||
# uses of two-step algorithm:
|
||||
# 1) rough focus astimation along full season-to-season
|
||||
# focus range (as it was implemented in the old obs. script)
|
||||
# 2) precise focussing along narrow range computed from
|
||||
# previous rough estimation
|
||||
# Next focussing runs compute range from the current focus value.
|
||||
# (new function 'focustel_new')
|
||||
#
|
||||
# May 2025: rewrite detection of running this script process (T. Fatkhullin)
|
||||
# (use of '-c' commandline option for 'pgrep' command)
|
||||
|
||||
function focustel_new(){
|
||||
rm focus*.fit 2>/dev/null
|
||||
echo "Focussing..."
|
||||
|
||||
let fno=$1
|
||||
if [[ $fno -gt 1 ]]; then
|
||||
let curr_foc=`fli_control | tail -n 5 | head -n 1 | cut -d "=" -f 2`
|
||||
let lowf=$curr_foc-1500
|
||||
let highf=$curr_foc+1500
|
||||
focus_seq_FLI.py -v -c focus$(printf "%02d" $1).jpg $lowf $highf 500
|
||||
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo -e "\nFOCUSSING SCRIPT RETURNED: $?"
|
||||
echo -e "SOMETHING WAS WRONG IN FOCUSSING SEQUENCE!!! SET FOCUS TO PREVIOUS VALUE!!\n"
|
||||
fli_control -g $curr_foc
|
||||
fi
|
||||
else
|
||||
focus_seq_FLI.py --guess -v -N 7 -c focus$(printf "%02d" $1).jpg 47000 60000 500
|
||||
fi
|
||||
|
||||
FDATE=$(date +%s)
|
||||
}
|
||||
|
||||
function sendcmd(){
|
||||
echo $1 | nc 192.168.70.33 10001 -q10
|
||||
}
|
||||
|
||||
function point_tel(){
|
||||
touch lastpointing
|
||||
send_coords -r $1 -d $2
|
||||
}
|
||||
|
||||
#c=$(pgrep run_full | wc -l)
|
||||
# $ will run another run_full, so c=2 for single run
|
||||
#if [[ $c -gt 1 ]]; then
|
||||
# echo "Another process is running; exiting"
|
||||
# exit 1
|
||||
#fi
|
||||
|
||||
if [[ $(pgrep -c run_full) -gt 1 ]]; then
|
||||
echo "Another process is running! Exit!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export http_proxy=""
|
||||
|
||||
# set lower limit to 5degr
|
||||
send_command2mount ":So5#"
|
||||
|
||||
echo "Time diff: $(($DATEEND-$(date +%s)))"
|
||||
if [ $(($DATEEND-$(date +%s))) -lt 3600 ]; then
|
||||
echo "There's less an hour for observation!"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [ $(($DATEEND-$(date +%s))) -gt 53200 ]; then
|
||||
echo "There's more than 12 hours till closing, check script data!"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
#STARTobs
|
||||
|
||||
#send_coords
|
||||
#echo "Wait a little"
|
||||
#sleep 10
|
||||
|
||||
echo "GoTo object: ${RA} ${DEC}"
|
||||
send_coords -r${RA} -d${DEC}
|
||||
|
||||
echo "Start taking object"
|
||||
|
||||
badweather=0
|
||||
while true; do
|
||||
now=$(date +%s)
|
||||
ANS=$(curl localhost:55555/status 2>/dev/null)
|
||||
echo "Dome status: $ANS"
|
||||
if [ $ANS != "opened" ]; then
|
||||
echo "Closed"
|
||||
curl localhost:55555/weather 2>/dev/null > DomeClosed
|
||||
break;
|
||||
fi
|
||||
chkweather ${BADWEATHER} > lastweather && badweather=0 || badweather=$((badweather+1))
|
||||
[ $badweather -gt 5 ] && break
|
||||
[ -f stopobs ] && break
|
||||
[ -f exitjob ] && exit 0
|
||||
if [ "$now" -lt "$DATEEND" ]; then
|
||||
est=$(sendcmd ":Gmte#"|sed -e 's/^0*//' -e 's/#//')
|
||||
echo -e "\n\n\n\n\nEstimated time to flip: $est minutes"
|
||||
if [[ ("x$est" == "x") || ($est -lt 3) ]]; then
|
||||
point_tel "${RA}" "${DEC}"
|
||||
continue
|
||||
fi
|
||||
ST=$(send_coords | awk '{print $4}')
|
||||
[ "x$ST" == "x" ] && break
|
||||
if [ $ST -ne "0" ]; then
|
||||
point_tel "${RA}" "${DEC}"
|
||||
continue
|
||||
else
|
||||
[ $(($(date +%s) - $FDATE)) -gt $FOCUST ] && focustel_new $((FNO++))
|
||||
# [ $(($(date +%s) - $FDATE)) -gt $FOCUST ] && focustel $((FNO++))
|
||||
preflash
|
||||
fli_control -r /tmp/10micron.fitsheader -x $EXPTIME -N "${OBS}" -O "${OBJ}" "$OBJ"
|
||||
fi
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Dome closed @ $(date)" >> closed
|
||||
curl localhost:55555/close
|
||||
|
||||
relay_manage -s1
|
||||
park_telescope
|
||||
|
||||
for x in $(seq 1 10); do
|
||||
preflash
|
||||
fli_control -r /tmp/10micron.fitsheader -x1 -N "${OBS}" -O "bias" -d bias
|
||||
preflash
|
||||
fli_control -r /tmp/10micron.fitsheader -x $EXPTIME -N "${OBS}" -O "dark" -d dark
|
||||
preflash
|
||||
fli_control -r /tmp/10micron.fitsheader -n3 -x $FLATTIME -N "${OBS}" -O "flat" flat
|
||||
done
|
||||
|
||||
relay_manage -r1
|
||||
echo "Closed @ $(date)" >> closed
|
||||
|
||||
STOPobs || true
|
||||
|
||||
DIR=$(basename $PWD)
|
||||
echo "TAR: $DIR"
|
||||
cd ..
|
||||
./copy_and_xz.sh $DIR
|
||||
echo "Archived, end"
|
||||
echo "$DIR archived @ $(date)" >> archived
|
||||
44
Auxiliary_utils/bash_scripts/setup_obs.sh
Executable file
44
Auxiliary_utils/bash_scripts/setup_obs.sh
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# The script creates working directory,
|
||||
# copies 'run' script into it and
|
||||
# edits observer name according to
|
||||
# the its argument
|
||||
#
|
||||
# Working directory name is formed from
|
||||
# date of the script running and is computed
|
||||
# as follows:
|
||||
# now - 12hours
|
||||
# i.e. the day starts from 12h not from 0h!
|
||||
#
|
||||
# $1 - observer name
|
||||
#
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
obs_name="Fatkhullin T.A."
|
||||
else
|
||||
obs_name=$1
|
||||
fi
|
||||
|
||||
# now - 12h
|
||||
let now12=`date +%s`-12*3600
|
||||
|
||||
# working directory
|
||||
wdir=/DATA/FITS/`date -d @$now12 +%y.%m.%d`
|
||||
|
||||
echo -n "Creating working directory: $wdir ..."
|
||||
if [[ -d $wdir ]]; then
|
||||
echo -e "\tFAILED! The directory already exists! Exit!"
|
||||
exit 1
|
||||
else
|
||||
echo -e "\tOK!"
|
||||
mkdir $wdir
|
||||
cd $wdir
|
||||
fi
|
||||
|
||||
cp ../run_full.new_foc .
|
||||
|
||||
# replace observer name
|
||||
sed -i "0,/^OBS=\".*\"/ s//OBS=\"${obs_name}\"/" run_full.new_foc
|
||||
#sed -i "0,/^OBS=\"[a-zA-Z \.]*\"/ s//OBS=\"${obs_name}\"/" run_full.new_foc
|
||||
3
Auxiliary_utils/bash_scripts/video2dome1-2.sh
Executable file
3
Auxiliary_utils/bash_scripts/video2dome1-2.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
mpv -vo sixel --really-quiet=yes "rtsp://viewer:view25@192.168.70.25:554/axis-media/media.amp?videocodec=h264&resolution=640x480"
|
||||
@@ -41,7 +41,7 @@ static glob_pars G = {
|
||||
* Define command line options by filling structure:
|
||||
* name has_arg flag val type argptr help
|
||||
*/
|
||||
static myoption cmdlnopts[] = {
|
||||
static sl_option_t cmdlnopts[] = {
|
||||
// common options
|
||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), "show this help"},
|
||||
{"delimeter",NEED_ARG, NULL, 'd', arg_string, APTR(&G.delimeter), "coordinates delimeter string (default: ':')"},
|
||||
@@ -71,10 +71,10 @@ glob_pars *parse_args(int argc, char **argv){
|
||||
char helpstring[1024], *hptr = helpstring;
|
||||
snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n");
|
||||
// format of help: "Usage: progname [args]\n"
|
||||
change_helpstring(helpstring);
|
||||
sl_helpstring(helpstring);
|
||||
// parse arguments
|
||||
parseargs(&argc, &argv, cmdlnopts);
|
||||
if(help) showhelp(-1, cmdlnopts);
|
||||
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||
if(help) sl_showhelp(-1, cmdlnopts);
|
||||
if(argc > 0){
|
||||
G.rest_pars_num = argc;
|
||||
G.rest_pars = MALLOC(char *, argc);
|
||||
|
||||
@@ -100,7 +100,7 @@ static void savepoints(FILE *f, point *pts, int N, char *delim, int mask){
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
initial_setup();
|
||||
sl_init();
|
||||
glob_pars *G = parse_args(argc, argv);
|
||||
FILE *f = NULL;
|
||||
if(G->outfile){
|
||||
|
||||
@@ -32,7 +32,7 @@ typedef struct{
|
||||
|
||||
static glob_pars G = {.tolerance = 10.};
|
||||
|
||||
static myoption cmdlnopts[] = {
|
||||
static sl_option_t cmdlnopts[] = {
|
||||
// common options
|
||||
{"help", NO_ARGS, NULL, 'h', arg_none, APTR(&G.help), _("show this help")},
|
||||
{"infile", NEED_ARG, NULL, 'i', arg_string, APTR(&G.input), _("input file name")},
|
||||
@@ -158,11 +158,11 @@ static double calcfocus(double coeffs[3]){
|
||||
|
||||
int main(int argc, char **argv){
|
||||
char helpstring[256];
|
||||
initial_setup();
|
||||
sl_init();
|
||||
snprintf(helpstring, 255, "Usage: `cat file | %%s` or with args; file format \"x y\\n..\"\n\tArgs:\n");
|
||||
change_helpstring(helpstring);
|
||||
parseargs(&argc, &argv, cmdlnopts);
|
||||
if(G.help) showhelp(-1, cmdlnopts);
|
||||
sl_helpstring(helpstring);
|
||||
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||
if(G.help) sl_showhelp(-1, cmdlnopts);
|
||||
if(G.tolerance <= 0.) ERRX("Tolerance should be > 0");
|
||||
FILE *f = stdin;
|
||||
if(G.input){
|
||||
|
||||
@@ -36,7 +36,7 @@ static const char *radtodeg(double r){
|
||||
|
||||
|
||||
int main(){
|
||||
initial_setup();
|
||||
sl_init();
|
||||
at_MJD_t mjd;
|
||||
if(!at_get_MJDu(time(NULL), &mjd)) ERRX("at_get_MJDu");
|
||||
printf("MJD=%g; TAI=%g/%g, TT=%g/%g, UTC=%g/%g\n", mjd.MJD, mjd.tai1, mjd.tai2, mjd.tt1, mjd.tt2, mjd.utc1, mjd.utc2);
|
||||
|
||||
@@ -56,7 +56,7 @@ static parameters G = {
|
||||
.py = -10000.
|
||||
};
|
||||
|
||||
static myoption cmdlnopts[] = {
|
||||
static sl_option_t cmdlnopts[] = {
|
||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), "show this help"},
|
||||
{"obsplace", NO_ARGS, NULL, 'O', arg_int, APTR(&G.obsplace), "input RA/Dec is observed place"},
|
||||
{"JD", NEED_ARG, NULL, 'J', arg_double, APTR(&G.JD), "Julian date"},
|
||||
@@ -77,9 +77,9 @@ static myoption cmdlnopts[] = {
|
||||
|
||||
|
||||
int main(int argc, char **argv){
|
||||
initial_setup();
|
||||
parseargs(&argc, &argv, cmdlnopts);
|
||||
if(G.help) showhelp(-1, cmdlnopts);
|
||||
sl_init();
|
||||
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||
if(G.help) sl_showhelp(-1, cmdlnopts);
|
||||
at_MJD_t MJD;
|
||||
G.ra *= ERFA_DD2R;
|
||||
G.dec *= ERFA_DD2R;
|
||||
|
||||
@@ -180,9 +180,10 @@ static void toomuch(int fd){
|
||||
LOGWARN("Client fd=%d tried to connect after MAX reached", fd);
|
||||
}
|
||||
// new connections handler
|
||||
static void connected(sl_sock_t *c){
|
||||
static int connected(sl_sock_t *c){
|
||||
if(c->type == SOCKT_UNIX) LOGMSG("New client fd=%d connected", c->fd);
|
||||
else LOGMSG("New client fd=%d, IP=%s connected", c->fd, c->IP);
|
||||
return TRUE;
|
||||
}
|
||||
// disconnected handler
|
||||
static void disconnected(sl_sock_t *c){
|
||||
@@ -196,12 +197,12 @@ void server_run(sl_socktype_e type, const char *node, sl_tty_t *serial){
|
||||
ERRX("server_run(): wrong parameters");
|
||||
}
|
||||
dome_serialdev(serial);
|
||||
sl_sock_changemaxclients(5);
|
||||
sl_sock_maxclhandler(toomuch);
|
||||
sl_sock_connhandler(connected);
|
||||
sl_sock_dischandler(disconnected);
|
||||
s = sl_sock_run_server(type, node, -1, handlers);
|
||||
if(!s) ERRX("Can't create socket and/or run threads");
|
||||
sl_sock_changemaxclients(s, 5);
|
||||
sl_sock_maxclhandler(s, toomuch);
|
||||
sl_sock_connhandler(s, connected);
|
||||
sl_sock_dischandler(s, disconnected);
|
||||
while(s && s->connected){
|
||||
if(!s->rthread){
|
||||
LOGERR("Server handlers thread is dead");
|
||||
|
||||
@@ -54,7 +54,7 @@ glob_pars const Gdefault = {
|
||||
* Define command line options by filling structure:
|
||||
* name has_arg flag val type argptr help
|
||||
*/
|
||||
myoption cmdlnopts[] = {
|
||||
sl_option_t cmdlnopts[] = {
|
||||
// common options
|
||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
|
||||
{"device", NEED_ARG, NULL, 'i', arg_string, APTR(&G.device), _("serial device name (default: none)")},
|
||||
@@ -79,10 +79,10 @@ glob_pars *parse_args(int argc, char **argv){
|
||||
void *ptr;
|
||||
ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr);
|
||||
// format of help: "Usage: progname [args]\n"
|
||||
change_helpstring("Usage: %s [args]\n\n\tWhere args are:\n");
|
||||
sl_helpstring("Usage: %s [args]\n\n\tWhere args are:\n");
|
||||
// parse arguments
|
||||
parseargs(&argc, &argv, cmdlnopts);
|
||||
if(help) showhelp(-1, cmdlnopts);
|
||||
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||
if(help) sl_showhelp(-1, cmdlnopts);
|
||||
if(argc > 0){
|
||||
G.rest_pars_num = argc;
|
||||
G.rest_pars = calloc(argc, sizeof(char*));
|
||||
|
||||
@@ -30,14 +30,14 @@
|
||||
glob_pars *GP;
|
||||
|
||||
void signals(int signo){
|
||||
restore_console();
|
||||
if(ttydescr) close_tty(&ttydescr);
|
||||
sl_restore_con();
|
||||
if(ttydescr) sl_tty_close(&ttydescr);
|
||||
LOGERR("exit with status %d", signo);
|
||||
exit(signo);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
initial_setup();
|
||||
sl_init();
|
||||
signal(SIGTERM, signals); // kill (-15) - quit
|
||||
signal(SIGHUP, SIG_IGN); // hup - ignore
|
||||
signal(SIGINT, signals); // ctrl+C - quit
|
||||
@@ -52,7 +52,7 @@ int main(int argc, char **argv){
|
||||
signals(0); // never reached!
|
||||
}
|
||||
if(GP->logfile){
|
||||
sl_loglevel lvl = LOGLEVEL_ERR;
|
||||
sl_loglevel_e lvl = LOGLEVEL_ERR;
|
||||
for(; GP->verb && lvl < LOGLEVEL_ANY; --GP->verb) ++lvl;
|
||||
DBG("Loglevel: %d", lvl);
|
||||
if(!OPENLOG(GP->logfile, lvl, 1)) ERRX("Can't open log file");
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
#define BUFLEN 1024
|
||||
|
||||
TTY_descr *ttydescr = NULL;
|
||||
sl_tty_t *ttydescr = NULL;
|
||||
|
||||
static char buf[BUFLEN];
|
||||
|
||||
@@ -50,14 +50,14 @@ static char *read_string(){
|
||||
return ptr;
|
||||
}
|
||||
ptr = buf;
|
||||
double d0 = dtime();
|
||||
double d0 = sl_dtime();
|
||||
do{
|
||||
if((l = read_tty(ttydescr))){
|
||||
if((l = sl_tty_read(ttydescr))){
|
||||
r += l; LL -= l; ptr += l;
|
||||
if(ptr[-1] == '\n') break;
|
||||
d0 = dtime();
|
||||
d0 = sl_dtime();
|
||||
}
|
||||
}while(dtime() - d0 < WAIT_TMOUT && LL);
|
||||
}while(sl_dtime() - d0 < WAIT_TMOUT && LL);
|
||||
if(r){
|
||||
buf[r] = 0;
|
||||
//DBG("r=%zd, got string: %s", r, buf);
|
||||
@@ -75,10 +75,10 @@ static char *read_string(){
|
||||
int try_connect(char *device, int baudrate){
|
||||
if(!device) return 0;
|
||||
fflush(stdout);
|
||||
ttydescr = new_tty(device, baudrate, 1024);
|
||||
if(ttydescr) ttydescr = tty_open(ttydescr, 1); // exclusive open
|
||||
ttydescr = sl_tty_new(device, baudrate, 1024);
|
||||
if(ttydescr) ttydescr = sl_tty_open(ttydescr, 1); // exclusive open
|
||||
if(!ttydescr) return 0;
|
||||
while(read_tty(ttydescr)); // clear rbuf
|
||||
while(sl_tty_read(ttydescr)); // clear rbuf
|
||||
LOGMSG("Connected to %s", device);
|
||||
return 1;
|
||||
}
|
||||
@@ -91,14 +91,14 @@ void run_terminal(){
|
||||
green(_("Work in terminal mode without echo\n"));
|
||||
int rb;
|
||||
size_t l;
|
||||
setup_con();
|
||||
sl_setup_con();
|
||||
while(1){
|
||||
if((l = read_tty(ttydescr))){
|
||||
if((l = sl_tty_read(ttydescr))){
|
||||
printf("%s", ttydescr->buf);
|
||||
}
|
||||
if((rb = read_console())){
|
||||
if((rb = sl_read_con())){
|
||||
char c = (char) rb;
|
||||
write_tty(ttydescr->comfd, &c, 1);
|
||||
sl_tty_write(ttydescr->comfd, &c, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,8 +109,8 @@ void run_terminal(){
|
||||
*/
|
||||
char *poll_device(){
|
||||
char *ans;
|
||||
double t0 = dtime();
|
||||
while(dtime() - t0 < T_POLLING_TMOUT){
|
||||
double t0 = sl_dtime();
|
||||
while(sl_dtime() - t0 < T_POLLING_TMOUT){
|
||||
if((ans = read_string())){ // parse new data
|
||||
DBG("got %s", ans);
|
||||
/*
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
// Terminal polling timeout - 1 second
|
||||
#define T_POLLING_TMOUT (1.0)
|
||||
|
||||
extern TTY_descr *ttydescr;
|
||||
extern sl_tty_t *ttydescr;
|
||||
void run_terminal();
|
||||
int try_connect(char *device, int baudrate);
|
||||
char *poll_device();
|
||||
|
||||
@@ -46,7 +46,7 @@ static glob_pars const Gdefault = {
|
||||
* Define command line options by filling structure:
|
||||
* name has_arg flag val type argptr help
|
||||
*/
|
||||
static myoption cmdlnopts[] = {
|
||||
static sl_option_t cmdlnopts[] = {
|
||||
// common options
|
||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
|
||||
{"port", NEED_ARG, NULL, 'P', arg_string, APTR(&G.port), _("port to connect (default: " DEFAULT_PORT ")")},
|
||||
@@ -73,13 +73,13 @@ glob_pars *parse_args(int argc, char **argv){
|
||||
char helpstring[1024], *hptr = helpstring;
|
||||
snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n");
|
||||
// format of help: "Usage: progname [args]\n"
|
||||
change_helpstring(helpstring);
|
||||
sl_helpstring(helpstring);
|
||||
// parse arguments
|
||||
parseargs(&argc, &argv, cmdlnopts);
|
||||
if(help) showhelp(-1, cmdlnopts);
|
||||
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||
if(help) sl_showhelp(-1, cmdlnopts);
|
||||
if(argc > 0){
|
||||
fprintf(stderr, "Undefined extra parameters!\n");
|
||||
showhelp(-1, cmdlnopts);
|
||||
sl_showhelp(-1, cmdlnopts);
|
||||
G.rest_pars_num = argc;
|
||||
G.rest_pars = MALLOC(char *, argc);
|
||||
for (i = 0; i < argc; i++)
|
||||
|
||||
@@ -36,7 +36,7 @@ void signals(int sig){
|
||||
signal(sig, SIG_IGN);
|
||||
DBG("Get signal %d, quit.\n", sig);
|
||||
}
|
||||
restore_console();
|
||||
sl_restore_con();
|
||||
exit(sig);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ void iffound_default(pid_t pid){
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]){
|
||||
initial_setup();
|
||||
sl_init();
|
||||
char *self = strdup(argv[0]);
|
||||
GP = parse_args(argc, argv);
|
||||
DBG("here");
|
||||
@@ -66,7 +66,7 @@ int main(int argc, char *argv[]){
|
||||
signal(SIGQUIT, signals); // ctrl+\ - quit
|
||||
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
|
||||
DBG("here");
|
||||
setup_con();
|
||||
sl_setup_con();
|
||||
/*
|
||||
if(GP->rest_pars_num){
|
||||
for(int i = 0; i < GP->rest_pars_num; ++i)
|
||||
|
||||
@@ -56,7 +56,7 @@ void signals(int signo){
|
||||
exit(signo);
|
||||
}
|
||||
|
||||
static myoption cmdlnopts[] = {
|
||||
static sl_option_t cmdlnopts[] = {
|
||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), "show this help"},
|
||||
{"address", NEED_ARG, NULL, 'a', arg_string, APTR(&G.server), "server name or IP"},
|
||||
{"port", NEED_ARG, NULL, 'p', arg_string, APTR(&G.port), "server port"},
|
||||
@@ -68,19 +68,19 @@ static myoption cmdlnopts[] = {
|
||||
|
||||
int main(int argc, char **argv){
|
||||
char *self = strdup(argv[0]);
|
||||
initial_setup();
|
||||
parseargs(&argc, &argv, cmdlnopts);
|
||||
if(G.help) showhelp(-1, cmdlnopts);
|
||||
sl_init();
|
||||
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||
if(G.help) sl_showhelp(-1, cmdlnopts);
|
||||
if(argc > 0) WARNX("Got %d unused keys", argc);
|
||||
if(!G.dbname) ERRX("Point database file name");
|
||||
if(!G.server) ERRX("Point server IP or name");
|
||||
if(!G.port) ERRX("Point server port");
|
||||
sl_loglevel lvl = LOGLEVEL_ERR + G.v;
|
||||
sl_loglevel_e lvl = LOGLEVEL_ERR + G.v;
|
||||
if(lvl > LOGLEVEL_ANY) lvl = LOGLEVEL_ANY;
|
||||
if(G.logfile) OPENLOG(G.logfile, lvl, 1);
|
||||
LOGMSG("hello, start");
|
||||
LOGDBG("SQLite version: %s", sqlite3_libversion());
|
||||
check4running(self, G.pidfile);
|
||||
sl_check4running(self, G.pidfile);
|
||||
// signal reactions:
|
||||
signal(SIGTERM, signals); // kill (-15) - quit
|
||||
signal(SIGHUP, SIG_IGN); // hup - ignore
|
||||
@@ -93,11 +93,11 @@ int main(int argc, char **argv){
|
||||
while(1){
|
||||
childpid = fork();
|
||||
if(childpid){ // master
|
||||
double t0 = dtime();
|
||||
double t0 = sl_dtime();
|
||||
LOGMSG("Created child with pid %d", childpid);
|
||||
wait(NULL);
|
||||
LOGWARN("Child %d died", childpid);
|
||||
if(dtime() - t0 < 1.) pause += 5;
|
||||
if(sl_dtime() - t0 < 1.) pause += 5;
|
||||
else pause = 1;
|
||||
if(pause > 900) pause = 900;
|
||||
sleep(pause); // wait a little before respawn
|
||||
|
||||
@@ -78,7 +78,7 @@ static void sendmessage(int fd, const char *msg, int l){
|
||||
LOGWARN("write()");
|
||||
WARN("write()");
|
||||
}else{
|
||||
if(globlog){ // logging turned ON
|
||||
if(sl_globlog){ // logging turned ON
|
||||
tmpbuf[l-1] = 0; // remove trailing '\n' for logging
|
||||
LOGMSG("SEND to fd %d: %s", fd, tmpbuf);
|
||||
}
|
||||
@@ -125,10 +125,10 @@ static int canberead(int fd){
|
||||
// collect data and write into database
|
||||
// @return FALSE if can't get full data string
|
||||
static int getdata(int fd){
|
||||
double t0 = dtime();
|
||||
double t0 = sl_dtime();
|
||||
char buf[BUFSIZ];
|
||||
int len = 0, leave = BUFSIZ, got = 0;
|
||||
while(dtime() - t0 < ANS_TIMEOUT){
|
||||
while(sl_dtime() - t0 < ANS_TIMEOUT){
|
||||
int r = canberead(fd);
|
||||
if(r == 0) continue;
|
||||
r = read(fd, buf + len, leave);
|
||||
@@ -169,7 +169,7 @@ static int getdata(int fd){
|
||||
void run_socket(int fd){
|
||||
double t0 = 0.;
|
||||
while(1){
|
||||
double tlast = dtime();
|
||||
double tlast = sl_dtime();
|
||||
if(tlast - t0 >= POLLING_INTERVAL){
|
||||
sendstrmessage(fd, SERVER_COMMAND);
|
||||
if(getdata(fd)) t0 = tlast;
|
||||
|
||||
69
Daemons/weatherdaemon_multimeteo/CMakeLists.txt
Normal file
69
Daemons/weatherdaemon_multimeteo/CMakeLists.txt
Normal file
@@ -0,0 +1,69 @@
|
||||
cmake_minimum_required(VERSION 4.0)
|
||||
set(PROJ weatherdaemon)
|
||||
set(PROJLIB senslib)
|
||||
set(MAJOR_VERSION "0")
|
||||
set(MID_VERSION "0")
|
||||
set(MINOR_VERSION "1")
|
||||
|
||||
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
|
||||
|
||||
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
|
||||
project(${PROJ} VERSION ${VERSION} LANGUAGES C)
|
||||
message("VER: ${VERSION}")
|
||||
|
||||
# list of options
|
||||
option(DEBUG "Compile in debug mode" OFF)
|
||||
option(DUMMY "Dummy device plugin" ON)
|
||||
option(FDEXAMPLE "Example of file descriptor plugin" ON)
|
||||
|
||||
# default flags
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wextra -pedantic-errors -fPIC")
|
||||
|
||||
message("Install dir prefix: ${CMAKE_INSTALL_PREFIX}")
|
||||
|
||||
add_definitions(-D_XOPEN_SOURCE=1234 -D_DEFAULT_SOURCE -D_GNU_SOURCE
|
||||
-DPACKAGE_VERSION=\"${VERSION}\" -DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\"
|
||||
-DMAJOR_VERSION=\"${MAJOR_VESION}\")
|
||||
|
||||
set(CMAKE_COLOR_MAKEFILE ON)
|
||||
|
||||
# cmake -DDEBUG=on -> debugging
|
||||
if(DEBUG)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Og -g3 -ggdb -Werror")
|
||||
add_definitions(-DEBUG)
|
||||
set(CMAKE_BUILD_TYPE DEBUG)
|
||||
set(CMAKE_VERBOSE_MAKEFILE "ON")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -march=native -fdata-sections -ffunction-sections -flto=auto")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections -flto=auto")
|
||||
set(CMAKE_BUILD_TYPE RELEASE)
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(${PROJ} REQUIRED usefull_macros>=0.3.5)
|
||||
|
||||
#include(FindOpenMP)
|
||||
#if(OPENMP_FOUND)
|
||||
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
|
||||
# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
|
||||
# add_definitions(-DOMP_FOUND)
|
||||
#endif()
|
||||
|
||||
# static lib for sensors
|
||||
set(LIBSRC "weathlib.c")
|
||||
set(LIBHEADER "weathlib.h")
|
||||
add_library(${PROJLIB} STATIC ${LIBSRC})
|
||||
set_target_properties(${PROJLIB} PROPERTIES VERSION ${VERSION})
|
||||
|
||||
# exe & deps files
|
||||
add_executable(${PROJ} ${SOURCES})
|
||||
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} ${PROJLIB} -lm ${CMAKE_DL_LIBS})
|
||||
target_include_directories(${PROJ} PUBLIC ${${PROJ}_INCLUDE_DIRS} .)
|
||||
target_link_directories(${PROJ} PUBLIC ${${PROJ}_LIBRARY_DIRS} )
|
||||
|
||||
include(GNUInstallDirs)
|
||||
# Installation of the program
|
||||
install(TARGETS ${PROJ} DESTINATION "bin")
|
||||
|
||||
add_subdirectory("plugins")
|
||||
22
Daemons/weatherdaemon_multimeteo/Readme.md
Normal file
22
Daemons/weatherdaemon_multimeteo/Readme.md
Normal file
@@ -0,0 +1,22 @@
|
||||
Weather daemon for several different weather stations
|
||||
=====================================================
|
||||
|
||||
## Usage:
|
||||
|
||||
```
|
||||
Usage: weatherdaemon [args]
|
||||
Be careful: command line options have priority over config
|
||||
Where args are:
|
||||
|
||||
-P, --pidfile=arg pidfile name (default: /tmp/weatherdaemon.pid)
|
||||
-c, --conffile=arg configuration file name (consists all or a part of long-named parameters and their values (e.g. plugin=liboldweather.so)
|
||||
-h, --help show this help
|
||||
-l, --logfile=arg save logs to file (default: none)
|
||||
-p, --plugin=arg add this weather plugin (may be a lot of) (can occur multiple times)
|
||||
-v, --verb logfile verbocity level (each -v increased)
|
||||
--port=arg network port to connect (default: 12345); hint: use "localhost:port" to make local net socket
|
||||
--sockpath=arg UNIX socket path (starting from '\0' for anonimous) of command socket
|
||||
```
|
||||
|
||||
|
||||
TODO: brief documentation will be here
|
||||
164
Daemons/weatherdaemon_multimeteo/cmdlnopts.c
Normal file
164
Daemons/weatherdaemon_multimeteo/cmdlnopts.c
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* This file is part of the weatherdaemon project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <usefull_macros.h>
|
||||
#include "cmdlnopts.h"
|
||||
|
||||
/*
|
||||
* here are global parameters initialisation
|
||||
*/
|
||||
int help;
|
||||
|
||||
// default values for Gdefault & help
|
||||
#define DEFAULT_PORT "12345"
|
||||
#define DEFAULT_PID "/tmp/weatherdaemon.pid"
|
||||
|
||||
// DEFAULTS
|
||||
// default global parameters
|
||||
static glob_pars defpars = {
|
||||
.port = DEFAULT_PORT,
|
||||
.logfile = NULL,
|
||||
.verb = 0,
|
||||
.pidfile = DEFAULT_PID
|
||||
};
|
||||
// default config: all values should be wrong or empty to understand than user change them
|
||||
static glob_pars defconf = {
|
||||
.verb = -1,
|
||||
};
|
||||
|
||||
static glob_pars G;
|
||||
|
||||
/*
|
||||
* Define command line options by filling structure:
|
||||
* name has_arg flag val type argptr help
|
||||
*/
|
||||
#define COMMON_OPTS \
|
||||
{"port", NEED_ARG, NULL, 0, arg_string, APTR(&G.port), "network port to connect (default: " DEFAULT_PORT "); hint: use \"localhost:port\" to make local net socket"}, \
|
||||
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), "save logs to file (default: none)"}, \
|
||||
{"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfile), "pidfile name (default: " DEFAULT_PID ")"}, \
|
||||
{"sockpath",NEED_ARG, NULL, 0, arg_string, APTR(&G.sockname), "UNIX socket path (starting from '\\0' for anonimous) of command socket"}, \
|
||||
{"plugin", MULT_PAR, NULL, 'p', arg_string, APTR(&G.plugins), "add this weather plugin (may be a lot of); 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)"},
|
||||
|
||||
sl_option_t cmdlnopts[] = {
|
||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), "show this help"},
|
||||
{"conffile",NEED_ARG, NULL, 'c', arg_string, APTR(&G.conffile), "configuration file name (consists all or a part of long-named parameters and their values (e.g. plugin=liboldweather.so:D:/dev/ttyS0:115200)"},
|
||||
{"verb", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verb), "logfile verbocity level (each -v increased)"}, \
|
||||
COMMON_OPTS
|
||||
end_option
|
||||
};
|
||||
|
||||
sl_option_t confopts[] = {
|
||||
{"verbose", NEED_ARG, NULL, 'v', arg_int, APTR(&G.verb), "logfile verbocity level"}, \
|
||||
COMMON_OPTS
|
||||
end_option
|
||||
};
|
||||
|
||||
static int sortstrings(const void *v1, const void *v2){
|
||||
const char **s1 = (const char **)v1, **s2 = (const char **)v2;
|
||||
return strcmp(*s1, *s2);
|
||||
}
|
||||
|
||||
// compare plugins from configuration and command line; add to command line plugins all new
|
||||
static void compplugins(glob_pars *cmdline, glob_pars *conf){
|
||||
if(!cmdline) return;
|
||||
char **p;
|
||||
int nconf = 0;
|
||||
if(conf){
|
||||
p = conf->plugins;
|
||||
if(p && *p) while(*p++) ++nconf;
|
||||
}
|
||||
int ncmd = 0;
|
||||
p = cmdline->plugins;
|
||||
if(p && *p) while(*p++) ++ncmd;
|
||||
DBG("Got %d plugins in conf and %d in cmdline", nconf, ncmd);
|
||||
// compare plugins and rebuild new list
|
||||
int newsize = ncmd + nconf;
|
||||
if(newsize == 0) return; // no plugins in both
|
||||
char **newarray = MALLOC(char*, newsize + 1); // +1 for ending NULL
|
||||
for(int i = 0; i < ncmd; ++i){ newarray[i] = cmdline->plugins[i]; }
|
||||
FREE(cmdline->plugins);
|
||||
if(conf){
|
||||
for(int i = 0; i < nconf; ++i){ newarray[i+ncmd] = conf->plugins[i]; }
|
||||
FREE(conf->plugins);
|
||||
}
|
||||
qsort(newarray, newsize, sizeof(char*), sortstrings);
|
||||
DBG("NOW together:"); p = newarray; while(*p) printf("\t%s\n", *p++);
|
||||
p = newarray;
|
||||
int nondobuleidx = 0;
|
||||
for(int i = 0; i < newsize;){
|
||||
int j = i + 1;
|
||||
for(; j < newsize; ++j){
|
||||
if(strcmp(newarray[i], newarray[j])) break;
|
||||
FREE(newarray[j]);
|
||||
}
|
||||
if(nondobuleidx != i){
|
||||
newarray[nondobuleidx] = newarray[i];
|
||||
newarray[i] = NULL;
|
||||
}
|
||||
++nondobuleidx;
|
||||
i = j;
|
||||
}
|
||||
DBG("Result:"); p = newarray; while(*p) printf("\t%s\n", *p++);
|
||||
cmdline->plugins = newarray;
|
||||
cmdline->nplugins = nondobuleidx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse command line options and return dynamically allocated structure
|
||||
* to global parameters
|
||||
* @param argc - copy of argc from main
|
||||
* @param argv - copy of argv from main
|
||||
* @return allocated structure with global parameters
|
||||
*/
|
||||
glob_pars *parse_args(int argc, char **argv){
|
||||
G = defpars; // copy defaults
|
||||
// format of help: "Usage: progname [args]\n"
|
||||
sl_helpstring("Usage: %s [args]\n\t" COLOR_RED "Be careful: command line options have priority over config" COLOR_OLD "\n\tWhere args are:\n");
|
||||
// parse arguments
|
||||
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||
DBG("verb: %d", G.verb);
|
||||
if(help) sl_showhelp(-1, cmdlnopts);
|
||||
if(argc > 0){
|
||||
WARNX("You give %d unused parameters:", argc);
|
||||
while(argc) printf("\t%s\n", argv[--argc]);
|
||||
}
|
||||
DBG("PARS: \n-------------\n%s-------------\n\n", sl_print_opts(cmdlnopts, 1));
|
||||
if(G.conffile){ // read conffile and fix parameters (cmdline args are in advantage)
|
||||
glob_pars oldpars = G; // save cmdline opts
|
||||
G = defconf;
|
||||
if(!sl_conf_readopts(oldpars.conffile, confopts)) ERRX("Can't get options from %s", G.conffile);
|
||||
DBG("CONF: \n-------------\n%s-------------\n\n", sl_print_opts(confopts, 1));
|
||||
if((0 == strcmp(oldpars.port, DEFAULT_PORT)) && G.port) oldpars.port = G.port;
|
||||
if(!oldpars.logfile && G.logfile) oldpars.logfile = G.logfile;
|
||||
if(!oldpars.verb && G.verb > -1) oldpars.verb = G.verb;
|
||||
if((0 == strcmp(oldpars.pidfile, DEFAULT_PID)) && G.pidfile) oldpars.pidfile = G.pidfile;
|
||||
if(!oldpars.sockname && G.sockname) oldpars.sockname = G.sockname;
|
||||
// now check plugins
|
||||
compplugins(&oldpars, &G);
|
||||
G = oldpars;
|
||||
}else compplugins(&G, NULL);
|
||||
DBG("RESULT: \n-------------\n%s-------------\n\n", sl_print_opts(cmdlnopts, 1));
|
||||
DBG("Nplugins = %d", G.nplugins);
|
||||
return &G;
|
||||
}
|
||||
|
||||
37
Daemons/weatherdaemon_multimeteo/cmdlnopts.h
Normal file
37
Daemons/weatherdaemon_multimeteo/cmdlnopts.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* This file is part of the weatherdaemon project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* here are some typedef's for global data
|
||||
*/
|
||||
typedef struct{
|
||||
char *sockname; // UNIX socket name for internal connections (commands etc)
|
||||
char *port; // port for external clients
|
||||
char *logfile; // logfile name
|
||||
int verb; // verbocity level
|
||||
char *pidfile; // pidfile name
|
||||
char **plugins; // all plugins connected
|
||||
int nplugins; // amount of plugins
|
||||
char *conffile; // configuration file used instead of long command line
|
||||
int pollt; // sensors maximal polling interval
|
||||
} glob_pars;
|
||||
|
||||
|
||||
glob_pars *parse_args(int argc, char **argv);
|
||||
152
Daemons/weatherdaemon_multimeteo/fd.c
Normal file
152
Daemons/weatherdaemon_multimeteo/fd.c
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// try to open device or socket that user pointed for plugin
|
||||
// WARNING!!! The `getFD` function intended for single use for each plugin!
|
||||
// WARNING!!! If you will try to close some plugins in running mode and open others, it
|
||||
// WARNING!!! would cause to a memory leak!
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#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){
|
||||
FNAME();
|
||||
int speed = 9600; // default speed
|
||||
char *colon = strchr(path, ':');
|
||||
if(colon){
|
||||
*colon++ = 0;
|
||||
if(!sl_str2i(&speed, colon)){
|
||||
WARNX("Wrong speed settings: '%s'", colon);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
sl_tty_t *serial = sl_tty_new(path, speed, BUFSIZ);
|
||||
if(!serial || !sl_tty_open(serial, TRUE)){
|
||||
WARN("Can't open %s @ speed %d", path, speed);
|
||||
return -1;
|
||||
}
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief getFD - try to open given device/socket
|
||||
* @param path - rest of string for --plugin= (e.g. "N:host.com:12345")
|
||||
* WARNING!!! Contents of `path` would be modified in this function!
|
||||
* @return opened file descriptor or -1 in case of error
|
||||
*/
|
||||
int getFD(char *path){
|
||||
if(!path || !*path) return -1;
|
||||
char type = *path;
|
||||
path += 2;
|
||||
switch(type){
|
||||
case 'D': // serial device
|
||||
return openserial(path);
|
||||
case 'N': // INET socket
|
||||
return opensocket(path, SOCKT_NET);
|
||||
case 'U': // UNIX socket
|
||||
return opensocket(path, SOCKT_UNIX);
|
||||
}
|
||||
WARNX("Wrong plugin format: '%c', should be 'D', 'N' or 'U'", type);
|
||||
return -1;
|
||||
}
|
||||
21
Daemons/weatherdaemon_multimeteo/fd.h
Normal file
21
Daemons/weatherdaemon_multimeteo/fd.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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);
|
||||
102
Daemons/weatherdaemon_multimeteo/main.c
Normal file
102
Daemons/weatherdaemon_multimeteo/main.c
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* This file is part of the weatherdaemon project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h> // wait
|
||||
#include <sys/prctl.h> //prctl
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "cmdlnopts.h"
|
||||
#include "sensors.h"
|
||||
#include "server.h"
|
||||
|
||||
static pid_t childpid = 0;
|
||||
|
||||
void signals(int signo){
|
||||
if(childpid){
|
||||
LOGERR("Killed with status %d", signo);
|
||||
closeplugins();
|
||||
kill_servers();
|
||||
usleep(1000); // let child close everything before dead
|
||||
}else{
|
||||
LOGERR("Main process exits with status %d", signo);
|
||||
}
|
||||
exit(signo);
|
||||
}
|
||||
|
||||
static void getpipe(int _U_ signo){
|
||||
WARNX("Get sigpipe!");
|
||||
// TODO: check all sensors for disconnected one
|
||||
signal(SIGPIPE, getpipe);
|
||||
}
|
||||
|
||||
extern const char *__progname;
|
||||
|
||||
int main(int argc, char **argv){
|
||||
glob_pars *GP = NULL;
|
||||
sl_init();
|
||||
signal(SIGTERM, signals); // kill (-15) - quit
|
||||
signal(SIGHUP, SIG_IGN); // hup - ignore
|
||||
signal(SIGINT, signals); // ctrl+C - quit
|
||||
signal(SIGQUIT, signals); // ctrl+\ - quit
|
||||
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
|
||||
signal(SIGPIPE, getpipe); // socket disconnected
|
||||
GP = parse_args(argc, argv);
|
||||
if(!GP) ERRX("Error parsing args");
|
||||
if(!GP->sockname) ERRX("Point command socket name");
|
||||
if(GP->logfile){
|
||||
sl_loglevel_e lvl = LOGLEVEL_ERR + GP->verb;
|
||||
if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1;
|
||||
DBG("Loglevel: %d", lvl);
|
||||
if(!OPENLOG(GP->logfile, lvl, 1)) ERRX("Can't open log file");
|
||||
LOGMSG("Started");
|
||||
}
|
||||
if(GP->pollt > 0){
|
||||
if(!set_pollT((time_t)GP->pollt)) ERRX("Can't set polling time to %d seconds", GP->pollt);
|
||||
}
|
||||
int nopened = openplugins(GP->plugins, GP->nplugins);
|
||||
if(nopened < 1){
|
||||
LOGERR("No plugins found; exit!");
|
||||
ERRX("Can't find any sensor plugin");
|
||||
}
|
||||
if(GP->nplugins && GP->nplugins != nopened) LOGWARN("Work without some plugins");
|
||||
#ifndef EBUG
|
||||
sl_check4running((char*)__progname, GP->pidfile);
|
||||
while(1){ // guard for dead processes
|
||||
childpid = fork();
|
||||
if(childpid){
|
||||
LOGDBG("create child with PID %d\n", childpid);
|
||||
DBG("Created child with PID %d\n", childpid);
|
||||
wait(NULL);
|
||||
WARNX("Child %d died\n", childpid);
|
||||
LOGWARN("Child %d died\n", childpid);
|
||||
sleep(1);
|
||||
}else{
|
||||
prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies
|
||||
break; // go out to normal functional
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if(!start_servers(GP->port, GP->sockname)) ERRX("Can't run server's threads");
|
||||
while(1);
|
||||
//WARNX("TEST ends");
|
||||
//signals(0);
|
||||
return 0; // never reached
|
||||
}
|
||||
24
Daemons/weatherdaemon_multimeteo/plugins/CMakeLists.txt
Normal file
24
Daemons/weatherdaemon_multimeteo/plugins/CMakeLists.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(PLUGINS REQUIRED usefull_macros)
|
||||
|
||||
include_directories(${PLUGINS_INCLUDE_DIRS} ..)
|
||||
link_directories(${PLUGINS_LIBRARY_DIRS} ..)
|
||||
link_libraries(${$PLUGINS_LIBRARIES} ${PROJLIB} -fPIC)
|
||||
|
||||
set(LIBS "")
|
||||
|
||||
if(DUMMY)
|
||||
add_library(wsdummy SHARED dummy.c)
|
||||
list(APPEND LIBS wsdummy)
|
||||
endif()
|
||||
|
||||
if(FDEXAMPLE)
|
||||
add_library(fdex SHARED fdexample.c)
|
||||
list(APPEND LIBS fdex)
|
||||
endif()
|
||||
|
||||
install(TARGETS ${LIBS} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
95
Daemons/weatherdaemon_multimeteo/plugins/dummy.c
Normal file
95
Daemons/weatherdaemon_multimeteo/plugins/dummy.c
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* This file is part of the weatherdaemon project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// dummy meteostation sending data each `tpoll` seconds
|
||||
|
||||
#include "weathlib.h"
|
||||
|
||||
#define NS (6)
|
||||
|
||||
extern sensordata_t sensor;
|
||||
|
||||
static const val_t values[NS] = { // fields `name` and `comment` have no sense until value meaning is `IS_OTHER`
|
||||
{.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WIND},
|
||||
{.sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_WINDDIR},
|
||||
{.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_AMB_TEMP},
|
||||
{.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},
|
||||
};
|
||||
|
||||
static void *mainthread(void _U_ *U){
|
||||
FNAME();
|
||||
double t0 = sl_dtime();
|
||||
while(1){
|
||||
float f = sensor.values[0].value.f + (drand48() - 0.5) / 2.;
|
||||
if(f >= 0.) sensor.values[0].value.f = f;
|
||||
f = sensor.values[1].value.f + (drand48() - 0.5) * 4.;
|
||||
if(f > 160. && f < 200.) sensor.values[1].value.f = f;
|
||||
f = sensor.values[2].value.f + (drand48() - 0.5) / 20.;
|
||||
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.;
|
||||
if(f > 60. && f <= 100.) sensor.values[4].value.f = f;
|
||||
sensor.values[5].value.u = (f > 98.) ? 1 : 0;
|
||||
time_t cur = time(NULL);
|
||||
for(int i = 0; i < NS; ++i) sensor.values[i].time = cur;
|
||||
if(sensor.freshdatahandler) sensor.freshdatahandler(&sensor);
|
||||
while(sl_dtime() - t0 < sensor.tpoll) usleep(500);
|
||||
t0 = sl_dtime();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int init(struct sensordata_t* s, int N, time_t pollt, int _U_ fd){
|
||||
FNAME();
|
||||
if(pthread_create(&s->thread, NULL, mainthread, NULL)) return 0;
|
||||
if(pollt) s->tpoll = pollt;
|
||||
s->values = MALLOC(val_t, NS);
|
||||
for(int i = 0; i < NS; ++i) s->values[i] = values[i];
|
||||
sensor.values[0].value.f = 1.;
|
||||
sensor.values[1].value.f = 180.;
|
||||
sensor.values[2].value.f = 17.;
|
||||
sensor.values[3].value.f = 600.;
|
||||
sensor.values[4].value.f = 80.;
|
||||
sensor.values[5].value.u = 0;
|
||||
sensor.PluginNo = N;
|
||||
return NS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief getval - value's getter
|
||||
* @param o (o) - value
|
||||
* @param N - it's index
|
||||
* @return FALSE if failed
|
||||
*/
|
||||
static int getval(struct sensordata_t* s, val_t *o, int N){
|
||||
if(N < 0 || N >= NS) return FALSE;
|
||||
if(o) *o = s->values[N];
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
sensordata_t sensor = {
|
||||
.name = "Dummy weatherstation",
|
||||
.Nvalues = NS,
|
||||
.init = init,
|
||||
.onrefresh = common_onrefresh,
|
||||
.get_value = getval,
|
||||
.kill = common_kill,
|
||||
};
|
||||
151
Daemons/weatherdaemon_multimeteo/plugins/fdexample.c
Normal file
151
Daemons/weatherdaemon_multimeteo/plugins/fdexample.c
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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 <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "weathlib.h"
|
||||
|
||||
// dummy example of file descriptors usage
|
||||
|
||||
#define NS (4)
|
||||
|
||||
extern sensordata_t sensor;
|
||||
|
||||
static const val_t values[NS] = { // fields `name` and `comment` have no sense until value meaning is `IS_OTHER`
|
||||
{.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WIND},
|
||||
{.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_AMB_TEMP},
|
||||
{.sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_PRESSURE},
|
||||
{.sense = VAL_RECOMMENDED, .type = VALT_FLOAT, .meaning = IS_HUMIDITY},
|
||||
};
|
||||
|
||||
static int format_values(char *buf){
|
||||
int gotvals = 0;
|
||||
char *token = strtok(buf, ",");
|
||||
time_t tnow = time(NULL);
|
||||
while(token && gotvals < NS){
|
||||
double v;
|
||||
DBG("TOKEN: %s", token);
|
||||
if(sl_str2d(&v, token)){
|
||||
DBG("next value: %g", v);
|
||||
sensor.values[gotvals].value.f = (float) v;
|
||||
sensor.values[gotvals].time = tnow;
|
||||
++gotvals;
|
||||
}
|
||||
token = strtok(NULL, ",");
|
||||
}
|
||||
DBG("GOT: %d", gotvals);
|
||||
return gotvals;
|
||||
}
|
||||
|
||||
static ssize_t writedata(int fd, const char *str, size_t size){
|
||||
ssize_t sent = 0;
|
||||
do{
|
||||
DBG("try to write %zd bytes", size);
|
||||
int canwrite = sl_canwrite(fd);
|
||||
if(canwrite < 0){
|
||||
WARNX("Disconnected?!");
|
||||
return -1;
|
||||
}else if(canwrite){
|
||||
ssize_t r = write(fd, str+sent, size);
|
||||
if(r < 0){
|
||||
sent = -1;
|
||||
WARNX("Disconnected??");
|
||||
break;
|
||||
}else{
|
||||
sent += r;
|
||||
size -= r;
|
||||
}
|
||||
DBG("sent %zd bytes; total send %zd, leave %zd", r, sent, size);
|
||||
}
|
||||
}while(size);
|
||||
return sent;
|
||||
}
|
||||
|
||||
static void *mainthread(void _U_ *U){
|
||||
FNAME();
|
||||
time_t task = 0;
|
||||
const char begging[] = "Enter comma-separated data: wind, exttemp, pressure, humidity\n";
|
||||
char buf[128];
|
||||
while(sensor.fdes > -1){
|
||||
time_t tnow = time(NULL);
|
||||
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, 128);
|
||||
if(got > 0){
|
||||
sl_RB_write(sensor.ringbuffer, (uint8_t*)buf, got);
|
||||
}else if(got < 0){
|
||||
DBG("Disconnected?");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(sl_RB_readline(sensor.ringbuffer, buf, 127) > 0){
|
||||
if(NS == format_values(buf) && sensor.freshdatahandler)
|
||||
sensor.freshdatahandler(&sensor);
|
||||
}
|
||||
if(sensor.tpoll){
|
||||
if(tnow >= task){
|
||||
DBG("write %s", begging);
|
||||
ssize_t got = writedata(sensor.fdes, begging, sizeof(begging)-1);
|
||||
if(got > 0) task = tnow + sensor.tpoll;
|
||||
else if(got < 0){
|
||||
close(sensor.fdes);
|
||||
sensor.fdes = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DBG("OOOOps!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int init(struct sensordata_t *s, int N, time_t pollt, int fd){
|
||||
FNAME();
|
||||
if(!s) return -1;
|
||||
s->fdes = fd;
|
||||
if(s->fdes < 0) return -1;
|
||||
sensor.PluginNo = N;
|
||||
if(pollt) s->tpoll = pollt;
|
||||
if(pthread_create(&s->thread, NULL, mainthread, NULL)) return -1;
|
||||
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!");
|
||||
return -1;
|
||||
}
|
||||
return NS;
|
||||
}
|
||||
|
||||
static int getval(struct sensordata_t *s, val_t *o, int N){
|
||||
if(!s || N < 0 || N >= NS) return FALSE;
|
||||
if(o) *o = s->values[N];
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
sensordata_t sensor = {
|
||||
.name = "Dummy socket or serial device weatherstation",
|
||||
.Nvalues = NS,
|
||||
.init = init,
|
||||
.onrefresh = common_onrefresh,
|
||||
.get_value = getval,
|
||||
.kill = common_kill,
|
||||
};
|
||||
226
Daemons/weatherdaemon_multimeteo/sensors.c
Normal file
226
Daemons/weatherdaemon_multimeteo/sensors.c
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* This file is part of the weatherdaemon project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <string.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "fd.h"
|
||||
#include "sensors.h"
|
||||
|
||||
#define WARNXL(...) do{ LOGWARN(__VA_ARGS__); WARNX(__VA_ARGS__); } while(0)
|
||||
#define WARNL(...) do{ LOGWARN(__VA_ARGS__); WARN(__VA_ARGS__); } while(0)
|
||||
#define ERRXL(...) do{ LOGERR(__VA_ARGS__); ERRX(__VA_ARGS__); } while(0)
|
||||
#define ERRL(...) do{ LOGERR(__VA_ARGS__); ERR(__VA_ARGS__); } while(0)
|
||||
|
||||
// poll each `poll_interval` seconds
|
||||
static time_t poll_interval = 15;
|
||||
|
||||
static int nplugins = 0;
|
||||
static sensordata_t **allplugins = NULL;
|
||||
|
||||
int get_nplugins(){
|
||||
return nplugins;
|
||||
}
|
||||
|
||||
// set polling interval
|
||||
int set_pollT(time_t t){
|
||||
if(t == 0 || t > MAX_POLLT) return FALSE;
|
||||
poll_interval = t;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get_plugin - get link to opened plugin
|
||||
* @param o (o) - plugin with given index
|
||||
* @param N - index in `allplugins`
|
||||
* @return NULL if failed or pointer
|
||||
*/
|
||||
sensordata_t *get_plugin(int N){
|
||||
if(N < 0 || N >= nplugins) return NULL;
|
||||
return allplugins[N];
|
||||
}
|
||||
|
||||
void *open_plugin(const char *name){
|
||||
DBG("try to open lib %s", name);
|
||||
void* dlh = dlopen(name, RTLD_NOLOAD); // library may be already opened
|
||||
if(!dlh){
|
||||
DBG("Not loaded - load");
|
||||
dlh = dlopen(name, RTLD_NOW);
|
||||
}
|
||||
if(!dlh){
|
||||
char *e = dlerror();
|
||||
WARNXL("Can't find plugin! %s", (e) ? e : "");
|
||||
return NULL;
|
||||
}
|
||||
return dlh;
|
||||
}
|
||||
|
||||
#ifdef EBUG
|
||||
// in release this function can be used for meteo logging
|
||||
static void dumpsensors(struct sensordata_t* station){
|
||||
FNAME();
|
||||
if(!station || !station->get_value || station->Nvalues < 1) return;
|
||||
char buf[FULL_LEN+1];
|
||||
uint64_t Tsum = 0; int nsum = 0;
|
||||
int N = (nplugins > 1) ? station->PluginNo : -1;
|
||||
for(int i = 0; i < station->Nvalues; ++i){
|
||||
val_t v;
|
||||
if(!station->get_value(station, &v, i)) continue;
|
||||
if(0 < format_sensval(&v, buf, FULL_LEN+1, N)){
|
||||
printf("%s\n", buf);
|
||||
++nsum; Tsum += v.time;
|
||||
}
|
||||
}
|
||||
time_t last = (time_t)(Tsum / nsum);
|
||||
if(0 < format_msrmttm(last, buf, FULL_LEN+1)){
|
||||
printf("%s\n\n", buf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief openplugins - open sensors' plugin and init it
|
||||
* @param paths - paths to plugins
|
||||
* @param N - amount of plugins
|
||||
* @return amount of opened and inited plugins
|
||||
* This function should be runned only once at start
|
||||
*/
|
||||
int openplugins(char **paths, int N){
|
||||
char buf[PATH_MAX+1];
|
||||
if(!paths || !*paths || N < 1) return 0;
|
||||
if(allplugins || nplugins){
|
||||
WARNXL("Plugins already opened"); return 0;
|
||||
}
|
||||
allplugins = MALLOC(sensordata_t*, N);
|
||||
green("Try to open plugins:\n");
|
||||
for(int i = 0; i < N; ++i){
|
||||
printf("\tplugin[%d]=%s\n", i, paths[i]);
|
||||
snprintf(buf, PATH_MAX, "%s", paths[i]);
|
||||
char *colon = strchr(buf, ':');
|
||||
if(colon) *colon++ = 0;
|
||||
void* dlh = open_plugin(buf);
|
||||
if(!dlh) continue;
|
||||
DBG("OPENED");
|
||||
void *s = dlsym(dlh, "sensor");
|
||||
if(s){
|
||||
sensordata_t *S = (sensordata_t*) s;
|
||||
if(!S->get_value) WARNXL("Sensor library %s have no values' getter!", paths[i]);
|
||||
if(!S->init){
|
||||
WARNXL("Sensor library %s have no init funtion");
|
||||
continue;
|
||||
}
|
||||
int fd = -1;
|
||||
if(colon) fd = getFD(colon);
|
||||
int ns = S->init(S, nplugins, poll_interval, fd); // here nplugins is index in array
|
||||
if(ns < 1) WARNXL("Can't init plugin %s", paths[i]);
|
||||
else{
|
||||
#ifdef EBUG
|
||||
if(!S->onrefresh(S, dumpsensors)) WARNXL("Can't init refresh funtion");
|
||||
#endif
|
||||
LOGMSG("Plugin %s nave %d sensors", paths[i], ns);
|
||||
allplugins[nplugins++] = S;
|
||||
}
|
||||
}else WARNXL("Can't find field `sensor` in plugin %s: %s", paths[i], dlerror());
|
||||
}
|
||||
return nplugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief closeplugins - call `die` function for all sensors
|
||||
* This function should be runned at exit
|
||||
*/
|
||||
void closeplugins(){
|
||||
if(!allplugins || nplugins < 1) return;
|
||||
for(int i = 0; i < nplugins; ++i){
|
||||
if(allplugins[i]->kill) allplugins[i]->kill(allplugins[i]);
|
||||
}
|
||||
FREE(allplugins);
|
||||
nplugins = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief format_sensval - snprintf sensor's value into buffer
|
||||
* @param v - value to get
|
||||
* @param buf - buffer
|
||||
* @param buflen - full length of `buf`
|
||||
* @param Np - if Np>-1, show it as plugin number (added to field name in square brackets, like WIND[1]);
|
||||
* @return amount of symbols printed or -1 if error
|
||||
*/
|
||||
int format_sensval(const val_t *v, char *buf, int buflen, int Np){
|
||||
--buflen; // for trailing zero
|
||||
if(!v || !buf || buflen < FULL_LEN) return -1;
|
||||
char strval[VAL_LEN+1];
|
||||
switch(v->type){
|
||||
case VALT_UINT: snprintf(strval, VAL_LEN, "%u", v->value.u); break;
|
||||
case VALT_INT: snprintf(strval, VAL_LEN, "%d", v->value.i); break;
|
||||
case VALT_FLOAT: snprintf(strval, VAL_LEN, "%g", v->value.f); break;
|
||||
default: sprintf(strval, "'ERROR'");
|
||||
}
|
||||
const char* const NM[] = { // names of standard fields
|
||||
[IS_WIND] = "WIND",
|
||||
[IS_WINDDIR] = "WINDDIR",
|
||||
[IS_HUMIDITY] = "HUMIDITY",
|
||||
[IS_AMB_TEMP] = "EXTTEMP",
|
||||
[IS_INNER_TEMP] = "INTTEMP",
|
||||
[IS_HW_TEMP] = "HWTEMP", // mirror?
|
||||
[IS_PRESSURE] = "PRESSURE",
|
||||
[IS_PRECIP] = "PRECIP",
|
||||
[IS_PRECIP_LEVEL]="PRECIPLV",
|
||||
[IS_MIST] = "MIST",
|
||||
[IS_CLOUDS] = "CLOUDS",
|
||||
[IS_SKYTEMP] = "SKYTEMP"
|
||||
};
|
||||
const char* const CMT[] = { // comments for standard fields
|
||||
[IS_WIND] = "Wind, m/s",
|
||||
[IS_WINDDIR] = "Wind direction, degr (CW from north to FROM)",
|
||||
[IS_HUMIDITY] = "Humidity, percent",
|
||||
[IS_AMB_TEMP] = "Ambient temperature, degC",
|
||||
[IS_INNER_TEMP] = "In-dome temperature, degC",
|
||||
[IS_HW_TEMP] = "Hardware (mirror?) termperature, degC",
|
||||
[IS_PRESSURE] = "Atmospheric pressure, mmHg",
|
||||
[IS_PRECIP] = "Precipitation (1 - yes, 0 - no)",
|
||||
[IS_PRECIP_LEVEL]="Precipitation level (mm)",
|
||||
[IS_MIST] = "Mist (1 - yes, 0 - no)",
|
||||
[IS_CLOUDS] = "Integral clouds value (bigger - better)",
|
||||
[IS_SKYTEMP] = "Mean sky temperatyre"
|
||||
};
|
||||
const char *name = NULL, *comment = NULL;
|
||||
int idx = v->meaning;
|
||||
if(idx < IS_OTHER){
|
||||
name = NM[idx];
|
||||
comment = CMT[idx];
|
||||
}else{
|
||||
name = v->name;
|
||||
comment = v->comment;
|
||||
}
|
||||
int got;
|
||||
if(Np > -1) got = snprintf(buf, buflen, "%s[%d]=%s / %s", name, Np, strval, comment);
|
||||
else got = snprintf(buf, buflen, "%s=%s / %s", name, strval, comment);
|
||||
return got;
|
||||
}
|
||||
|
||||
// the same for measurement time formatting
|
||||
int format_msrmttm(time_t t, char *buf, int buflen){
|
||||
--buflen; // for trailing zero
|
||||
if(!buf || buflen < FULL_LEN) return -1;
|
||||
char cmt[COMMENT_LEN+1];
|
||||
struct tm *T = localtime(&t);
|
||||
strftime(cmt, COMMENT_LEN, "%F %T", T);
|
||||
return snprintf(buf, buflen, "TMEAS=%zd / Last measurement time: %s", t, cmt);
|
||||
}
|
||||
32
Daemons/weatherdaemon_multimeteo/sensors.h
Normal file
32
Daemons/weatherdaemon_multimeteo/sensors.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* This file is part of the weatherdaemon project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "weathlib.h"
|
||||
|
||||
// don't allow to set polling time more than 10 minutes
|
||||
#define MAX_POLLT (600)
|
||||
|
||||
int openplugins(char **paths, int N);
|
||||
void closeplugins();
|
||||
sensordata_t *get_plugin(int N);
|
||||
int get_nplugins();
|
||||
int format_sensval(const val_t *v, char *buf, int buflen, int Np);
|
||||
int format_msrmttm(time_t t, char *buf, int buflen);
|
||||
int set_pollT(time_t t);
|
||||
229
Daemons/weatherdaemon_multimeteo/server.c
Normal file
229
Daemons/weatherdaemon_multimeteo/server.c
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* This file is part of the weatherdaemon project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "sensors.h"
|
||||
#include "server.h"
|
||||
|
||||
// if measurement time oldest than now minus `oldest_interval`, we think measurement are too old
|
||||
static time_t oldest_interval = 60;
|
||||
|
||||
// server's sockets: net and local (UNIX)
|
||||
static sl_sock_t *netsocket = NULL, *localsocket;
|
||||
//static pthread_t netthread, locthread;
|
||||
|
||||
// show user current time
|
||||
static sl_sock_hresult_e timehandler(sl_sock_t *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
||||
if(!client) return RESULT_FAIL;
|
||||
char buf[32];
|
||||
snprintf(buf, 31, "UNIXT=%.3f\n", sl_dtime());
|
||||
sl_sock_sendstrmessage(client, buf);
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
|
||||
// show all connected libraries
|
||||
static sl_sock_hresult_e listhandler(sl_sock_t *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
||||
if(!client) return RESULT_FAIL;
|
||||
char buf[256];
|
||||
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);
|
||||
sl_sock_sendstrmessage(client, buf);
|
||||
}
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief showdata - send to user meteodata
|
||||
* @param client - client data
|
||||
* @param N - index of station
|
||||
* @param showidx - == TRUE to show index in square brackets
|
||||
*/
|
||||
static void showdata(sl_sock_t *client, int N, int showidx){
|
||||
char buf[FULL_LEN+1];
|
||||
val_t v;
|
||||
sensordata_t *s = NULL;
|
||||
if(!(s = get_plugin(N)) || (s->Nvalues < 1)){
|
||||
snprintf(buf, FULL_LEN, "Can't get plugin[%d]\n", N);
|
||||
sl_sock_sendstrmessage(client, buf);
|
||||
return;
|
||||
}
|
||||
if(!showidx || get_nplugins() == 1) N = -1; // only one -> don't show indexes
|
||||
time_t oldest = time(NULL) - oldest_interval;
|
||||
uint64_t Tsum = 0; int nsum = 0;
|
||||
for(int i = 0; i < s->Nvalues; ++i){
|
||||
if(!s->get_value(s, &v, i)) continue;
|
||||
if(v.time < oldest) continue;
|
||||
if(1 > format_sensval(&v, buf, FULL_LEN+1, N)) continue;
|
||||
DBG("formatted: '%s'", buf);
|
||||
sl_sock_sendstrmessage(client, buf);
|
||||
sl_sock_sendbyte(client, '\n');
|
||||
++nsum; Tsum += v.time;
|
||||
}
|
||||
oldest = (time_t)(Tsum / nsum);
|
||||
if(0 < format_msrmttm(oldest, buf, FULL_LEN+1)){ // send mean measuring time
|
||||
DBG("Formatted time: '%s'", buf);
|
||||
sl_sock_sendstrmessage(client, buf);
|
||||
sl_sock_sendbyte(client, '\n');
|
||||
}
|
||||
}
|
||||
|
||||
// get meteo data
|
||||
static sl_sock_hresult_e gethandler(sl_sock_t *client, _U_ sl_sock_hitem_t *item, const char *req){
|
||||
if(!client) return RESULT_FAIL;
|
||||
int N = get_nplugins();
|
||||
if(N < 1) return RESULT_FAIL;
|
||||
if(!req) for(int i = 0; i < N; ++i) showdata(client, i, TRUE);
|
||||
else{
|
||||
int n;
|
||||
if(!sl_str2i(&n, req) || n < 0 || n >= N) return RESULT_BADVAL;
|
||||
showdata(client, n, FALSE);
|
||||
}
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
|
||||
// graceful closing socket: let client know that he's told to fuck off
|
||||
static void toomuch(int fd){
|
||||
const char *m = "Try later: too much clients connected\n";
|
||||
send(fd, m, sizeof(m)-1, MSG_NOSIGNAL);
|
||||
shutdown(fd, SHUT_WR);
|
||||
DBG("shutdown, wait");
|
||||
double t0 = sl_dtime();
|
||||
uint8_t buf[8];
|
||||
while(sl_dtime() - t0 < 90.){ // change this value to smaller for real work
|
||||
if(sl_canread(fd)){
|
||||
ssize_t got = read(fd, buf, 8);
|
||||
DBG("Got=%zd", got);
|
||||
if(got < 1) break;
|
||||
}
|
||||
}
|
||||
DBG("Disc after %gs", sl_dtime() - t0);
|
||||
LOGWARN("Client fd=%d tried to connect after MAX reached", fd);
|
||||
}
|
||||
// new connections handler (return FALSE to reject client)
|
||||
static int connected(sl_sock_t *c){
|
||||
if(c->type == SOCKT_UNIX) LOGMSG("New local client fd=%d connected", c->fd);
|
||||
else LOGMSG("New client fd=%d, IP=%s connected", c->fd, c->IP);
|
||||
return TRUE;
|
||||
}
|
||||
// disconnected handler
|
||||
static void disconnected(sl_sock_t *c){
|
||||
if(c->type == SOCKT_UNIX) LOGMSG("Disconnected local client fd=%d", c->fd);
|
||||
else LOGMSG("Disconnected client fd=%d, IP=%s", c->fd, c->IP);
|
||||
}
|
||||
static sl_sock_hresult_e defhandler(struct sl_sock *s, const char *str){
|
||||
if(!s || !str) return RESULT_FAIL;
|
||||
sl_sock_sendstrmessage(s, "You entered wrong command:\n```\n");
|
||||
sl_sock_sendstrmessage(s, str);
|
||||
sl_sock_sendstrmessage(s, "\n```\nTry \"help\"\n");
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
|
||||
// handlers for network and local (UNIX) sockets
|
||||
static sl_sock_hitem_t nethandlers[] = { // net - only getters and client-only setters
|
||||
{gethandler, "get", "get all meteo or only for given plugin number", NULL},
|
||||
{listhandler, "list", "show all opened plugins", NULL},
|
||||
{timehandler, "time", "get server's UNIX time", NULL},
|
||||
{NULL, NULL, NULL, NULL}
|
||||
};
|
||||
static sl_sock_hitem_t localhandlers[] = { // local - full amount of setters/getters
|
||||
{gethandler, "get", "get all meteo or only for given plugin number", NULL},
|
||||
{listhandler, "list", "show all opened plugins", NULL},
|
||||
{timehandler, "time", "get server's UNIX time", NULL},
|
||||
{NULL, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
#if 0
|
||||
// common parsers for both net and local sockets
|
||||
static void *cmdparser(void *U){
|
||||
if(!U) return NULL;
|
||||
sl_sock_t *s = (sl_sock_t*) U;
|
||||
while(s && s->connected){
|
||||
if(!s->rthread){
|
||||
LOGERR("Server's handlers' thread is dead");
|
||||
break;
|
||||
}
|
||||
}
|
||||
LOGDBG("cmdparser(): exit");
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
int start_servers(const char *netnode, const char *sockpath){
|
||||
if(!netnode || !sockpath){
|
||||
LOGERR("start_servers(): need arguments");
|
||||
return FALSE;
|
||||
}
|
||||
netsocket = sl_sock_run_server(SOCKT_NET, netnode, BUFSIZ, nethandlers);
|
||||
if(!netsocket){
|
||||
LOGERR("start_servers(): can't run network socket");
|
||||
return FALSE;
|
||||
}
|
||||
localsocket = sl_sock_run_server(SOCKT_UNIX, sockpath, BUFSIZ, localhandlers);
|
||||
if(!localsocket){
|
||||
LOGERR("start_servers(): can't run local socket");
|
||||
return FALSE;
|
||||
}
|
||||
sl_sock_changemaxclients(netsocket, MAX_CLIENTS);
|
||||
sl_sock_changemaxclients(localsocket, 1);
|
||||
sl_sock_maxclhandler(netsocket, toomuch);
|
||||
sl_sock_maxclhandler(localsocket, toomuch);
|
||||
sl_sock_connhandler(netsocket, connected);
|
||||
sl_sock_connhandler(localsocket, connected);
|
||||
sl_sock_dischandler(netsocket, disconnected);
|
||||
sl_sock_dischandler(localsocket, disconnected);
|
||||
sl_sock_defmsghandler(netsocket, defhandler);
|
||||
sl_sock_defmsghandler(localsocket, defhandler);
|
||||
#if 0
|
||||
if(pthread_create(&netthread, NULL, cmdparser, (void*)netsocket)){
|
||||
LOGERR("Can't run server's net thread");
|
||||
goto errs;
|
||||
}
|
||||
if(pthread_create(&locthread, NULL, cmdparser, (void*)localsocket)){
|
||||
LOGERR("Can't run server's local thread");
|
||||
goto errs;
|
||||
}
|
||||
#endif
|
||||
return TRUE;
|
||||
#if 0
|
||||
errs:
|
||||
sl_sock_delete(&localsocket);
|
||||
sl_sock_delete(&netsocket);
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
void kill_servers(){
|
||||
//pthread_cancel(locthread);
|
||||
//pthread_cancel(netthread);
|
||||
//LOGMSG("Server threads canceled");
|
||||
//usleep(500);
|
||||
sl_sock_delete(&localsocket);
|
||||
sl_sock_delete(&netsocket);
|
||||
LOGMSG("Server sockets destroyed");
|
||||
//usleep(500);
|
||||
//pthread_kill(locthread, 9);
|
||||
//pthread_kill(netthread, 9);
|
||||
//LOGMSG("Server threads killed");
|
||||
}
|
||||
25
Daemons/weatherdaemon_multimeteo/server.h
Normal file
25
Daemons/weatherdaemon_multimeteo/server.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* This file is part of the weatherdaemon project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// maximal amount of simultaneous clients connected
|
||||
#define MAX_CLIENTS (30)
|
||||
|
||||
int start_servers(const char *netnode, const char *sockpath);
|
||||
void kill_servers();
|
||||
1
Daemons/weatherdaemon_multimeteo/weatherdaemon.cflags
Normal file
1
Daemons/weatherdaemon_multimeteo/weatherdaemon.cflags
Normal file
@@ -0,0 +1 @@
|
||||
-std=c17
|
||||
4
Daemons/weatherdaemon_multimeteo/weatherdaemon.config
Normal file
4
Daemons/weatherdaemon_multimeteo/weatherdaemon.config
Normal file
@@ -0,0 +1,4 @@
|
||||
#define EBUG 1
|
||||
#define _GNU_SOURCE
|
||||
#define _XOPEN_SOURCE 1111
|
||||
#define _POSIX_C_SOURCE 200000
|
||||
1
Daemons/weatherdaemon_multimeteo/weatherdaemon.creator
Normal file
1
Daemons/weatherdaemon_multimeteo/weatherdaemon.creator
Normal file
@@ -0,0 +1 @@
|
||||
[General]
|
||||
1
Daemons/weatherdaemon_multimeteo/weatherdaemon.cxxflags
Normal file
1
Daemons/weatherdaemon_multimeteo/weatherdaemon.cxxflags
Normal file
@@ -0,0 +1 @@
|
||||
-std=c++17
|
||||
14
Daemons/weatherdaemon_multimeteo/weatherdaemon.files
Normal file
14
Daemons/weatherdaemon_multimeteo/weatherdaemon.files
Normal file
@@ -0,0 +1,14 @@
|
||||
CMakeLists.txt
|
||||
cmdlnopts.c
|
||||
cmdlnopts.h
|
||||
fd.c
|
||||
fd.h
|
||||
main.c
|
||||
plugins/dummy.c
|
||||
plugins/fdexample.c
|
||||
sensors.c
|
||||
sensors.h
|
||||
server.c
|
||||
server.h
|
||||
weathlib.c
|
||||
weathlib.h
|
||||
1
Daemons/weatherdaemon_multimeteo/weatherdaemon.includes
Normal file
1
Daemons/weatherdaemon_multimeteo/weatherdaemon.includes
Normal file
@@ -0,0 +1 @@
|
||||
.
|
||||
66
Daemons/weatherdaemon_multimeteo/weathlib.c
Normal file
66
Daemons/weatherdaemon_multimeteo/weathlib.c
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// Some common functions and handlers for sensors
|
||||
|
||||
#include "weathlib.h"
|
||||
|
||||
/**
|
||||
* @brief sensor_alive - test if sensor's thread isn't dead
|
||||
* @param s - sensor
|
||||
* @return FALSE if thread is dead
|
||||
*/
|
||||
int sensor_alive(sensordata_t *s){
|
||||
if(!s) return FALSE;
|
||||
if(pthread_kill(s->thread, 0)) return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief common_onrefresh - common `change onrefresh handler`
|
||||
* @param s - sensor
|
||||
* @return FALSE if failed
|
||||
*/
|
||||
int common_onrefresh(sensordata_t *s, void (*handler)(sensordata_t *)){
|
||||
FNAME();
|
||||
if(!s || !handler) return FALSE;
|
||||
s->freshdatahandler = handler;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief common_kill - common `die` function
|
||||
* @param s - sensor
|
||||
*/
|
||||
void common_kill(sensordata_t *s){
|
||||
FNAME();
|
||||
if(!s) return;
|
||||
if(0 == pthread_kill(s->thread, -9)){
|
||||
DBG("%s main thread killed, join", s->name);
|
||||
pthread_join(s->thread, NULL);
|
||||
DBG("Done");
|
||||
}
|
||||
DBG("Delete RB");
|
||||
sl_RB_delete(&s->ringbuffer);
|
||||
if(s->fdes > -1){
|
||||
close(s->fdes);
|
||||
DBG("FD closed");
|
||||
}
|
||||
FREE(s->values);
|
||||
DBG("Sensor %s killed", s->name);
|
||||
}
|
||||
108
Daemons/weatherdaemon_multimeteo/weathlib.h
Normal file
108
Daemons/weatherdaemon_multimeteo/weathlib.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* This file is part of the weatherdaemon project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pthread.h>
|
||||
#include <signal.h> // pthread_kill
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
// length (in symbols) of key, value and comment
|
||||
#define KEY_LEN (8)
|
||||
#define VAL_LEN (31)
|
||||
#define COMMENT_LEN (63)
|
||||
// maximal full length of "KEY=val / comment" (as for sfitsio)
|
||||
#define FULL_LEN (81)
|
||||
// name of meteo-plugin
|
||||
#define NAME_LEN (127)
|
||||
|
||||
// importance of values
|
||||
typedef enum{
|
||||
VAL_OBLIGATORY, // can't be omitted
|
||||
VAL_RECOMMENDED, // recommended to show
|
||||
VAL_UNNECESSARY, // may be shown by user request
|
||||
VAL_BROKEN // sensor is broken, omit it
|
||||
} valsense_t;
|
||||
|
||||
// meaning of values
|
||||
typedef enum{
|
||||
IS_WIND, // wind, m/s
|
||||
IS_WINDDIR, // wind direction, degr
|
||||
IS_HUMIDITY, // humidity, percent
|
||||
IS_AMB_TEMP, // ambient temperature, degC
|
||||
IS_INNER_TEMP, // in-dome temperature, degC
|
||||
IS_HW_TEMP, // hardware (?) termperature, degC
|
||||
IS_PRESSURE, // atmospheric pressure, mmHg
|
||||
IS_PRECIP, // precipitation (1 - yes, 0 - no)
|
||||
IS_PRECIP_LEVEL, // precipitation level (mm)
|
||||
IS_MIST, // mist (1 - yes, 0 - no)
|
||||
IS_CLOUDS, // integral clouds value (bigger - better)
|
||||
IS_SKYTEMP, // mean sky temperatyre
|
||||
IS_OTHER // something other - read "name" and "comment"
|
||||
} valmeaning_t;
|
||||
|
||||
typedef union{
|
||||
uint32_t u;
|
||||
int32_t i;
|
||||
float f;
|
||||
} num_t;
|
||||
|
||||
// type of value
|
||||
typedef enum{
|
||||
VALT_UINT,
|
||||
VALT_INT,
|
||||
VALT_FLOAT,
|
||||
//VALT_STRING,
|
||||
} valtype_t;
|
||||
|
||||
// value
|
||||
typedef struct{
|
||||
char name[KEY_LEN+1]; // max VAL_LEN symbols FITS header keyword name
|
||||
char comment[COMMENT_LEN+1];// max COMMENT_LEN symbols of comment to FITS header
|
||||
valsense_t sense; // importance
|
||||
valtype_t type; // type of given value
|
||||
valmeaning_t meaning; // what type of sensor is it
|
||||
num_t value; // value itself
|
||||
time_t time; // last changing time
|
||||
} val_t;
|
||||
|
||||
// all sensor's data
|
||||
// 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")
|
||||
int Nvalues; // amount of values
|
||||
int PluginNo; // plugin number in array (if several)
|
||||
int (*init)(struct sensordata_t*, int, time_t, int); // init meteostation with given PluginNo, poll_interval and fd; return amount of parameters found or -1 if error
|
||||
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
|
||||
// private members:
|
||||
val_t *values; // array of values
|
||||
pthread_t thread; // main thread
|
||||
void (*freshdatahandler)(struct sensordata_t*); // handler of fresh data
|
||||
int fdes; // file descriptor of device/socket
|
||||
sl_ringbuffer_t *ringbuffer; // ringbuffer for device reading
|
||||
time_t tpoll; // forced polling time for sensor
|
||||
} sensordata_t;
|
||||
|
||||
// library functions and other
|
||||
int common_onrefresh(sensordata_t*, void (*handler)(sensordata_t*));
|
||||
void common_kill(sensordata_t *s);
|
||||
int sensor_alive(sensordata_t *s);
|
||||
Reference in New Issue
Block a user