diff --git a/Daemons/domedaemon_baader/.qtcreator/baader_dome.creator.user b/Daemons/domedaemon_baader/.qtcreator/baader_dome.creator.user index f55d7c1..9aa141d 100644 --- a/Daemons/domedaemon_baader/.qtcreator/baader_dome.creator.user +++ b/Daemons/domedaemon_baader/.qtcreator/baader_dome.creator.user @@ -1,10 +1,10 @@ - + EnvironmentId - {7bd84e39-ca37-46d3-be9d-99ebea85bc0d} + {cf63021e-ef53-49b0-b03b-2f2570cdf3b6} ProjectExplorer.Project.ActiveTarget @@ -32,7 +32,7 @@ KOI8-R false 4 - false + true 0 80 true @@ -40,9 +40,9 @@ 1 0 false - true + false false - 0 + 1 true true 0 @@ -51,10 +51,10 @@ false 1 true - false + true true *.md, *.MD, Makefile - false + true true true @@ -79,7 +79,7 @@ true true Builtin.DefaultTidyAndClazy - 8 + 4 true @@ -96,12 +96,12 @@ true Desktop Desktop - {65a14f9e-e008-4c1b-89df-4eaa4774b6e3} + {91347f2c-5221-46a7-80b1-0a054ca02f79} 0 0 0 - /Big/Data/00__Small_tel/C-sources/domedaemon_baader + /tmp/p/git/small_tel/Daemons/domedaemon_baader @@ -155,7 +155,6 @@ true 0 true - 2 @@ -192,7 +191,6 @@ true 0 true - 2 diff --git a/Daemons/domedaemon_baader/Makefile b/Daemons/domedaemon_baader/Makefile index 01255f7..099cd48 100644 --- a/Daemons/domedaemon_baader/Makefile +++ b/Daemons/domedaemon_baader/Makefile @@ -5,7 +5,7 @@ LDFLAGS += -lusefull_macros SRCS := $(wildcard *.c) DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111 OBJDIR := mk -CFLAGS += -O2 -Wall -Wextra -Wno-trampolines -std=gnu23 +CFLAGS += -O2 -Wall -Wextra -Wno-trampolines -Wno-format-truncation -std=gnu23 OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o)) DEPS := $(OBJS:.o=.d) TARGFILE := $(OBJDIR)/TARGET diff --git a/Daemons/domedaemon_baader/baader_dome.files b/Daemons/domedaemon_baader/baader_dome.files index 27793b3..fcaddd3 100644 --- a/Daemons/domedaemon_baader/baader_dome.files +++ b/Daemons/domedaemon_baader/baader_dome.files @@ -1,4 +1,6 @@ commands.h +header.c +header.h main.c socket.c socket.h diff --git a/Daemons/domedaemon_baader/header.c b/Daemons/domedaemon_baader/header.c new file mode 100644 index 0000000..d9421a5 --- /dev/null +++ b/Daemons/domedaemon_baader/header.c @@ -0,0 +1,107 @@ +/* + * This file is part of the baader_dome project. + * Copyright 2026 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "header.h" +#include "socket.h" + +static char *headername = NULL; +static char *dome_name = NULL; + +static int printhdr(int fd, const char *key, const char *val, const char *cmnt){ + char tmp[81]; + char tk[9]; + if(strlen(key) > 8){ + snprintf(tk, 8, "%s", key); + key = tk; + } + if(cmnt){ + snprintf(tmp, 81, "%-8s= %-21s / %s", key, val, cmnt); + }else{ + snprintf(tmp, 81, "%-8s= %s", key, val); + } + size_t l = strlen(tmp); + tmp[l] = '\n'; + ++l; + if(write(fd, tmp, l) != (ssize_t)l){ + WARN("write()"); + return 1; + } + return 0; +} + +// return TRUE if can write to given header file +int header_create(const char *file){ + if(!file) return FALSE; + FILE *hf = fopen(file, "w"); + if(!hf) return FALSE; + unlink(file); + if(headername) FREE(headername); + headername = strdup(file); + return TRUE; +} + +// set given `name` for telescope name +void domename(const char *name){ + if(dome_name) FREE(dome_name); + if(name){ + int l = strlen(name) + 3; + dome_name = MALLOC(char, l); + snprintf(dome_name, l-1, "'%s'", name); + } +} + +void write_header(){ + if(!headername) return; + char aname[PATH_MAX]; + char val[22]; +#define WRHDR(k, v, c) do{if(printhdr(fd, k, v, c)){goto returning;}}while(0) + snprintf(aname, PATH_MAX-1, "%sXXXXXX", headername); + int fd = mkstemp(aname); + if(fd < 0){ + LOGWARN("Can't write header file: mkstemp()"); + return; + } + fchmod(fd, 0644); + + dome_data_t st; + if(get_dome_data(&st)) WRHDR("OPERATIO", "'FORBIDDEN'", "Observations are forbidden"); + if(dome_name) WRHDR("DOME", dome_name, "Dome manufacturer/name"); + WRHDR("DOMESTAT", st.status, "Dome status"); + WRHDR("DOMEWEAT", st.weather, "Dome weather sensor status"); + snprintf(val, 21, "%d", st.errcode); + WRHDR("DOMEECOD", val, "Dome error code: Rain|Watchdog|Power"); + snprintf(val, 21, "%.3f", st.stattime); + char timebuf[BUFSIZ]; + time_t t = (time_t) st.stattime; + struct tm *tmp; + tmp = localtime(&t); + if(!tmp || 0 == strftime(timebuf, BUFSIZ, "Measurement time: %F %T", tmp)){ + LOGERR("localtime() returned NULL"); + goto returning; + } + WRHDR("TDOMMEAS", val, timebuf); +#undef WRHDR +returning: + close(fd); + rename(aname, headername); +} diff --git a/Daemons/domedaemon_baader/header.h b/Daemons/domedaemon_baader/header.h new file mode 100644 index 0000000..0606046 --- /dev/null +++ b/Daemons/domedaemon_baader/header.h @@ -0,0 +1,23 @@ +/* + * This file is part of the baader_dome project. + * Copyright 2026 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +void write_header(); +int header_create(const char *file); +void domename(const char *name); diff --git a/Daemons/domedaemon_baader/main.c b/Daemons/domedaemon_baader/main.c index f1fca3c..64b3f69 100644 --- a/Daemons/domedaemon_baader/main.c +++ b/Daemons/domedaemon_baader/main.c @@ -20,12 +20,22 @@ #include #include #include +#include +#include +#include #include #include +#include "header.h" #include "socket.h" #include "term.h" +#define DEFAULT_PIDFILE "/tmp/domedaemon.pid" +#define DEFAULT_HEADERFILE "/tmp/dome.fits" +#define DEFAULT_DOMENAME "Baader" + +static pid_t childpid = 0; + typedef struct{ int help; int verbose; @@ -37,12 +47,17 @@ typedef struct{ char *node; char *termpath; char *pidfile; + char *headerfile; + char *dome_name; } parameters; static parameters G = { .maxclients = 2, .serspeed = 9600, - .sertmout = 5000 + .sertmout = 5000, + .pidfile = DEFAULT_PIDFILE, + .headerfile = DEFAULT_HEADERFILE, + .dome_name = DEFAULT_DOMENAME, }; static sl_option_t cmdlnopts[] = { @@ -52,34 +67,56 @@ static sl_option_t cmdlnopts[] = { {"node", NEED_ARG, NULL, 'n', arg_string, APTR(&G.node), "node \"IP\", \"name:IP\" or path (could be \"\\0path\" for anonymous UNIX-socket)"}, {"unixsock", NO_ARGS, NULL, 'u', arg_int, APTR(&G.isunix), "UNIX socket instead of INET"}, {"maxclients", NEED_ARG, NULL, 'm', arg_int, APTR(&G.maxclients),"max amount of clients connected to server (default: 2)"}, - {"pidfile", NEED_ARG, NULL, 'p', arg_string, APTR(&G.pidfile), "PID-file"}, + {"pidfile", NEED_ARG, NULL, 'p', arg_string, APTR(&G.pidfile), "full path to PID-file (default: " DEFAULT_PIDFILE ")"}, {"serialdev", NEED_ARG, NULL, 'd', arg_string, APTR(&G.termpath), "full path to serial device"}, - {"baudrate", NEED_ARG, NULL, 'b', arg_int, APTR(&G.serspeed), "serial device speed (baud)"}, - {"sertmout", NEED_ARG, NULL, 'T', arg_double, APTR(&G.sertmout), "serial device timeout (us)"}, + {"baudrate", NEED_ARG, NULL, 'b', arg_int, APTR(&G.serspeed), "serial device speed (baud, default: 9600)"}, + {"sertmout", NEED_ARG, NULL, 'T', arg_double, APTR(&G.sertmout), "serial device timeout (us, default: 5000)"}, + {"headerfile", NEED_ARG, NULL, 'H', arg_string, APTR(&G.headerfile),"full path to output FITS-header (default: " DEFAULT_HEADERFILE ")"}, + {"domename", NEED_ARG, NULL, 'N', arg_string, APTR(&G.dome_name), "dome name in FITS-header (default: " DEFAULT_DOMENAME ")"}, end_option }; +void sl_iffound_deflt(pid_t pid){ + WARNX("Another copy of this process found, pid=%d. Exit.", pid); + exit(1); // don't run `signals` to protect foreign PID-file from removal +} // SIGUSR1 - FORBID observations // SIGUSR2 - allow void signals(int sig){ if(sig){ - if(sig == SIGUSR1){ - forbid_observations(1); - return; - }else if(sig == SIGUSR2){ - forbid_observations(0); - return; + if(signals != signal(sig, SIG_IGN)) exit(sig); // function called "as is", before sig registration + if(childpid == 0){ // child -> test USR1/USR2 + LOGDBG("Child gotta signal %d", sig); + if(sig == SIGUSR1){ + forbid_observations(1); + LOGMSG("Got signal `observations forbidden`"); + signal(sig, signals); + return; + }else if(sig == SIGUSR2){ + forbid_observations(0); + LOGMSG("Got signal `observations permitted`"); + signal(sig, signals); + return; + } } - signal(sig, SIG_IGN); - DBG("Get signal %d, quit.\n", sig); - LOGERR("Exit with status %d", sig); - }else LOGERR("Exit"); - DBG("Stop server"); - stopserver(); - DBG("Close terminal"); - term_close(); - DBG("Exit"); + LOGDBG("Get signal %d, quit.\n", sig); + } + if(childpid == 0){ + DBG("Stop server"); + LOGMSG("Stop server"); + stopserver(); + DBG("Close terminal"); + LOGMSG("Close terminal"); + term_close(); + }else{ + if(G.pidfile){ + LOGMSG("Unlink %s", G.pidfile); + usleep(10000); + unlink(G.pidfile); + } + } + LOGERR("Exit with status %d", sig); exit(sig); } @@ -88,9 +125,13 @@ int main(int argc, char **argv){ sl_init(); sl_parseargs(&argc, &argv, cmdlnopts); if(G.help) sl_showhelp(-1, cmdlnopts); - if(!G.node) ERRX("Point node"); + if(!G.node) ERRX("Point communication node"); if(!G.termpath) ERRX("Point serial device path"); + if(!header_create(G.headerfile)) + ERRX("Cannot write into '%s'", G.headerfile); + domename(G.dome_name); sl_check4running((char*)__progname, G.pidfile); + if(sl_daemonize()) ERR("Can't daemonize!"); sl_loglevel_e lvl = G.verbose + LOGLEVEL_ERR; if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1; if(G.logfile) OPENLOG(G.logfile, lvl, 1); @@ -103,11 +144,26 @@ int main(int argc, char **argv){ signal(SIGINT, signals); signal(SIGQUIT, signals); signal(SIGTSTP, SIG_IGN); - signal(SIGHUP, signals); + signal(SIGUSR1, SIG_IGN); + signal(SIGUSR2, SIG_IGN); + while(1){ // guard for dead processes + childpid = fork(); + if(childpid){ + LOGMSG("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 + } + } + // react for USRx only in child signal(SIGUSR1, signals); signal(SIGUSR2, signals); runserver(G.isunix, G.node, G.maxclients); - LOGMSG("Ended"); - DBG("Close"); + LOGERR("Server error -> exit"); return 0; } diff --git a/Daemons/domedaemon_baader/socket.c b/Daemons/domedaemon_baader/socket.c index eff6fb0..f3901c1 100644 --- a/Daemons/domedaemon_baader/socket.c +++ b/Daemons/domedaemon_baader/socket.c @@ -22,6 +22,7 @@ #include #include "commands.h" +#include "header.h" #include "socket.h" #include "term.h" @@ -33,15 +34,12 @@ typedef enum{ } dome_commands_t; typedef struct{ + int errcode; // error code + char status[STATBUF_SZ]; // device status + double stattime;// time of last status + char weather[STATBUF_SZ]; // data from weather sensor dome_commands_t cmd; dome_commands_t erroredcmd; // command didn't run - waiting or stalled - int errcode; // error code - char *status; // device status - int statlen; // size of `status` buffer - double stattime;// time of last status - char *weather; // data from weather sensor - int weathlen; // length of `weather` buffer - double weathtime;// time of last weather pthread_mutex_t mutex; } dome_t; @@ -53,6 +51,14 @@ static atomic_bool ForbidObservations = 0; // ==1 if all is forbidden -> close d static sl_sock_t *locksock = NULL; // local server socket static sl_ringbuffer_t *rb = NULL; // incoming serial data +int get_dome_data(dome_data_t *d){ + if(!d) return FALSE; + pthread_mutex_lock(&Dome.mutex); + *d = *((dome_data_t*)&Dome); + pthread_mutex_unlock(&Dome.mutex); + return ForbidObservations; +} + void stopserver(){ if(locksock) sl_sock_delete(&locksock); if(rb) sl_RB_delete(&rb); @@ -150,10 +156,10 @@ static sl_sock_hresult_e weathh(sl_sock_t *client, sl_sock_hitem_t *item, _U_ co char buf[256]; double t = NAN; pthread_mutex_lock(&Dome.mutex); - if(!*Dome.weather || sl_dtime() - Dome.weathtime > 3.*T_INTERVAL) snprintf(buf, 255, "%s=unknown\n", item->key); + if(sl_dtime() - Dome.stattime > 3.*T_INTERVAL) snprintf(buf, 255, "%s=unknown\n", item->key); else{ snprintf(buf, 255, "%s=%s\n", item->key, Dome.weather); - t = Dome.weathtime; + t = Dome.stattime; } pthread_mutex_unlock(&Dome.mutex); sl_sock_sendstrmessage(client, buf); @@ -227,11 +233,11 @@ static int poll_device(){ if(sscanf(data, "%d", &I) == 1){ pthread_mutex_lock(&Dome.mutex); if(I == 1111) - snprintf(Dome.status, Dome.statlen, "opened"); + snprintf(Dome.status, STATBUF_SZ-1, "opened"); else if(I == 2222) - snprintf(Dome.status, Dome.statlen, "closed"); + snprintf(Dome.status, STATBUF_SZ-1, "closed"); else - snprintf(Dome.status, Dome.statlen, "intermediate"); + snprintf(Dome.status, STATBUF_SZ-1, "intermediate"); Dome.stattime = sl_dtime(); pthread_mutex_unlock(&Dome.mutex); } @@ -240,12 +246,11 @@ static int poll_device(){ if(sscanf(data, "%d", &I) == 1){ pthread_mutex_lock(&Dome.mutex); if(I == 0) - snprintf(Dome.weather, Dome.weathlen, "good"); + snprintf(Dome.weather, STATBUF_SZ-1, "good"); else if (I == 1) - snprintf(Dome.weather, Dome.weathlen, "rain/clouds"); + snprintf(Dome.weather, STATBUF_SZ-1, "rain/clouds"); else - snprintf(Dome.weather, Dome.weathlen, "unknown"); - Dome.weathtime = sl_dtime(); + snprintf(Dome.weather, STATBUF_SZ-1, "unknown"); pthread_mutex_unlock(&Dome.mutex); } return TRUE; @@ -258,12 +263,6 @@ void runserver(int isunix, const char *node, int maxclients){ if(rb) sl_RB_delete(&rb); rb = sl_RB_new(BUFSIZ); Dome.cmd = CMD_NONE; - FREE(Dome.status); - Dome.statlen = STATBUF_SZ; - Dome.status = MALLOC(char, STATBUF_SZ); - FREE(Dome.weather); - Dome.weathlen = STATBUF_SZ; - Dome.weather = MALLOC(char, STATBUF_SZ); pthread_mutex_init(&Dome.mutex, NULL); sl_socktype_e type = (isunix) ? SOCKT_UNIX : SOCKT_NET; locksock = sl_sock_run_server(type, node, -1, handlers); @@ -293,7 +292,10 @@ void runserver(int isunix, const char *node, int maxclients){ } double tnow = sl_dtime(); if(tnow - tgot > T_INTERVAL){ - if(poll_device()) tgot = tnow; + if(poll_device()){ + tgot = tnow; + write_header(); + } } if(ForbidObservations) continue; pthread_mutex_lock(&Dome.mutex); diff --git a/Daemons/domedaemon_baader/socket.h b/Daemons/domedaemon_baader/socket.h index ccace72..d5fa84d 100644 --- a/Daemons/domedaemon_baader/socket.h +++ b/Daemons/domedaemon_baader/socket.h @@ -25,6 +25,14 @@ // dome polling interval (clear watchdog & get status) #define T_INTERVAL (5.0) +typedef struct{ + int errcode; // error code + char status[STATBUF_SZ]; // device status + double stattime;// time of last status + char weather[STATBUF_SZ]; // data from weather sensor +} dome_data_t; + +int get_dome_data(dome_data_t *d); void runserver(int isunix, const char *node, int maxclients); void stopserver(); void forbid_observations(int forbid); diff --git a/Daemons/teldaemon_astrosib/header.c b/Daemons/teldaemon_astrosib/header.c index 0d54125..8e42636 100644 --- a/Daemons/teldaemon_astrosib/header.c +++ b/Daemons/teldaemon_astrosib/header.c @@ -101,18 +101,18 @@ void write_header(){ if(get_telescope_data(&st)) WRHDR("OPERATIO", "'FORBIDDEN'", "Observations are forbidden"); if(header_mask.telname && telescope_name) WRHDR("TELESCOP", telescope_name, "Telescope name"); - WRHDR("STATUS", st.status, "Telescope shutters' status"); + WRHDR("TELSTAT", st.status, "Telescope shutters' status"); if(header_mask.fosuser){ snprintf(val, 21, "%d", st.focuserpos); WRHDR("FOCUS", val, "Current focuser position"); } if(header_mask.cooler){ snprintf(val, 21, "%d", st.cooler); - WRHDR("COOLER", val, "Primary mirror cooler status: 0/1 (off/on)"); + WRHDR("TELCOOLR", val, "Primary mirror cooler status: 0/1 (off/on)"); } if(header_mask.heater){ snprintf(val, 21, "%d", st.heater); - WRHDR("HEATER", val, "Secondary mirror heater status: 0/1 (off/on)"); + WRHDR("TELHEATR", val, "Secondary mirror heater status: 0/1 (off/on)"); } if(header_mask.exttemp){ snprintf(val, 21, "%.1f", st.ambienttemp); @@ -128,11 +128,11 @@ void write_header(){ time_t t = (time_t) st.stattime; struct tm *tmp; tmp = localtime(&t); - if(!tmp || 0 == strftime(timebuf, BUFSIZ, "Measurement time: %F %T", tmp)){ + if(!tmp || 0 == strftime(timebuf, BUFSIZ, "Measurement time (telescope): %F %T", tmp)){ LOGERR("localtime() returned NULL"); goto returning; } - WRHDR("TMEAS", val, timebuf); + WRHDR("TTELMEAS", val, timebuf); } #undef WRHDR diff --git a/Daemons/teldaemon_astrosib/main.c b/Daemons/teldaemon_astrosib/main.c index 8c31067..a276cd3 100644 --- a/Daemons/teldaemon_astrosib/main.c +++ b/Daemons/teldaemon_astrosib/main.c @@ -31,7 +31,7 @@ #include "term.h" #define DEFAULT_PIDFILE "/tmp/teldaemon.pid" -#define DEFAULT_HEADERFILE "/tmp/telescope.pid" +#define DEFAULT_HEADERFILE "/tmp/telescope.fits" #define DEFAULT_TELNAME "Astro-M (1)" static pid_t childpid = 0;