From 6ea6bad008d4f239bfb78dacc92ba5edab483fa3 Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Mon, 23 Mar 2026 00:05:51 +0300 Subject: [PATCH] add astrosib telescope daemon --- Daemons/{ => deprecated}/teldaemon/Makefile | 0 Daemons/{ => deprecated}/teldaemon/Readme.md | 0 .../teldaemon/astrosib_readme.koi8-r | 0 .../{ => deprecated}/teldaemon/cmdlnopts.c | 0 .../{ => deprecated}/teldaemon/cmdlnopts.h | 0 Daemons/{ => deprecated}/teldaemon/main.c | 0 .../{ => deprecated}/teldaemon/parseargs.c | 0 .../{ => deprecated}/teldaemon/parseargs.h | 0 Daemons/{ => deprecated}/teldaemon/socket.c | 0 Daemons/{ => deprecated}/teldaemon/socket.h | 0 Daemons/{ => deprecated}/teldaemon/term.c | 0 Daemons/{ => deprecated}/teldaemon/term.h | 0 .../teldaemon/usefull_macros.c | 0 .../teldaemon/usefull_macros.h | 0 Daemons/teldaemon_astrosib/Makefile | 57 +++ Daemons/teldaemon_astrosib/main.c | 112 +++++ Daemons/teldaemon_astrosib/socket.c | 383 ++++++++++++++++++ Daemons/teldaemon_astrosib/socket.h | 30 ++ Daemons/teldaemon_astrosib/teldaemon.cflags | 1 + Daemons/teldaemon_astrosib/teldaemon.config | 3 + Daemons/teldaemon_astrosib/teldaemon.creator | 1 + Daemons/teldaemon_astrosib/teldaemon.cxxflags | 1 + Daemons/teldaemon_astrosib/teldaemon.files | 5 + Daemons/teldaemon_astrosib/teldaemon.includes | 0 Daemons/teldaemon_astrosib/term.c | 115 ++++++ Daemons/teldaemon_astrosib/term.h | 48 +++ 26 files changed, 756 insertions(+) rename Daemons/{ => deprecated}/teldaemon/Makefile (100%) rename Daemons/{ => deprecated}/teldaemon/Readme.md (100%) rename Daemons/{ => deprecated}/teldaemon/astrosib_readme.koi8-r (100%) rename Daemons/{ => deprecated}/teldaemon/cmdlnopts.c (100%) rename Daemons/{ => deprecated}/teldaemon/cmdlnopts.h (100%) rename Daemons/{ => deprecated}/teldaemon/main.c (100%) rename Daemons/{ => deprecated}/teldaemon/parseargs.c (100%) rename Daemons/{ => deprecated}/teldaemon/parseargs.h (100%) rename Daemons/{ => deprecated}/teldaemon/socket.c (100%) rename Daemons/{ => deprecated}/teldaemon/socket.h (100%) rename Daemons/{ => deprecated}/teldaemon/term.c (100%) rename Daemons/{ => deprecated}/teldaemon/term.h (100%) rename Daemons/{ => deprecated}/teldaemon/usefull_macros.c (100%) rename Daemons/{ => deprecated}/teldaemon/usefull_macros.h (100%) create mode 100644 Daemons/teldaemon_astrosib/Makefile create mode 100644 Daemons/teldaemon_astrosib/main.c create mode 100644 Daemons/teldaemon_astrosib/socket.c create mode 100644 Daemons/teldaemon_astrosib/socket.h create mode 100644 Daemons/teldaemon_astrosib/teldaemon.cflags create mode 100644 Daemons/teldaemon_astrosib/teldaemon.config create mode 100644 Daemons/teldaemon_astrosib/teldaemon.creator create mode 100644 Daemons/teldaemon_astrosib/teldaemon.cxxflags create mode 100644 Daemons/teldaemon_astrosib/teldaemon.files create mode 100644 Daemons/teldaemon_astrosib/teldaemon.includes create mode 100644 Daemons/teldaemon_astrosib/term.c create mode 100644 Daemons/teldaemon_astrosib/term.h diff --git a/Daemons/teldaemon/Makefile b/Daemons/deprecated/teldaemon/Makefile similarity index 100% rename from Daemons/teldaemon/Makefile rename to Daemons/deprecated/teldaemon/Makefile diff --git a/Daemons/teldaemon/Readme.md b/Daemons/deprecated/teldaemon/Readme.md similarity index 100% rename from Daemons/teldaemon/Readme.md rename to Daemons/deprecated/teldaemon/Readme.md diff --git a/Daemons/teldaemon/astrosib_readme.koi8-r b/Daemons/deprecated/teldaemon/astrosib_readme.koi8-r similarity index 100% rename from Daemons/teldaemon/astrosib_readme.koi8-r rename to Daemons/deprecated/teldaemon/astrosib_readme.koi8-r diff --git a/Daemons/teldaemon/cmdlnopts.c b/Daemons/deprecated/teldaemon/cmdlnopts.c similarity index 100% rename from Daemons/teldaemon/cmdlnopts.c rename to Daemons/deprecated/teldaemon/cmdlnopts.c diff --git a/Daemons/teldaemon/cmdlnopts.h b/Daemons/deprecated/teldaemon/cmdlnopts.h similarity index 100% rename from Daemons/teldaemon/cmdlnopts.h rename to Daemons/deprecated/teldaemon/cmdlnopts.h diff --git a/Daemons/teldaemon/main.c b/Daemons/deprecated/teldaemon/main.c similarity index 100% rename from Daemons/teldaemon/main.c rename to Daemons/deprecated/teldaemon/main.c diff --git a/Daemons/teldaemon/parseargs.c b/Daemons/deprecated/teldaemon/parseargs.c similarity index 100% rename from Daemons/teldaemon/parseargs.c rename to Daemons/deprecated/teldaemon/parseargs.c diff --git a/Daemons/teldaemon/parseargs.h b/Daemons/deprecated/teldaemon/parseargs.h similarity index 100% rename from Daemons/teldaemon/parseargs.h rename to Daemons/deprecated/teldaemon/parseargs.h diff --git a/Daemons/teldaemon/socket.c b/Daemons/deprecated/teldaemon/socket.c similarity index 100% rename from Daemons/teldaemon/socket.c rename to Daemons/deprecated/teldaemon/socket.c diff --git a/Daemons/teldaemon/socket.h b/Daemons/deprecated/teldaemon/socket.h similarity index 100% rename from Daemons/teldaemon/socket.h rename to Daemons/deprecated/teldaemon/socket.h diff --git a/Daemons/teldaemon/term.c b/Daemons/deprecated/teldaemon/term.c similarity index 100% rename from Daemons/teldaemon/term.c rename to Daemons/deprecated/teldaemon/term.c diff --git a/Daemons/teldaemon/term.h b/Daemons/deprecated/teldaemon/term.h similarity index 100% rename from Daemons/teldaemon/term.h rename to Daemons/deprecated/teldaemon/term.h diff --git a/Daemons/teldaemon/usefull_macros.c b/Daemons/deprecated/teldaemon/usefull_macros.c similarity index 100% rename from Daemons/teldaemon/usefull_macros.c rename to Daemons/deprecated/teldaemon/usefull_macros.c diff --git a/Daemons/teldaemon/usefull_macros.h b/Daemons/deprecated/teldaemon/usefull_macros.h similarity index 100% rename from Daemons/teldaemon/usefull_macros.h rename to Daemons/deprecated/teldaemon/usefull_macros.h diff --git a/Daemons/teldaemon_astrosib/Makefile b/Daemons/teldaemon_astrosib/Makefile new file mode 100644 index 0000000..90d80a1 --- /dev/null +++ b/Daemons/teldaemon_astrosib/Makefile @@ -0,0 +1,57 @@ +# run `make DEF=...` to add extra defines +PROGRAM := teldaemon +LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all +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 +OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o)) +DEPS := $(OBJS:.o=.d) +TARGFILE := $(OBJDIR)/TARGET +CC = gcc +#TARGET := RELEASE + +ifeq ($(shell test -e $(TARGFILE) && echo -n yes),yes) + TARGET := $(file < $(TARGFILE)) +else + TARGET := RELEASE +endif + +ifeq ($(TARGET), DEBUG) + .DEFAULT_GOAL := debug +endif + +release: $(PROGRAM) + +debug: CFLAGS += -DEBUG -Werror +debug: TARGET := DEBUG +debug: $(PROGRAM) + +$(TARGFILE): $(OBJDIR) + @echo -e "\t\tTARGET: $(TARGET)" + @echo "$(TARGET)" > $(TARGFILE) + +$(PROGRAM) : $(TARGFILE) $(OBJS) + @echo -e "\t\tLD $(PROGRAM)" + $(CC) $(OBJS) $(LDFLAGS) -o $(PROGRAM) + +$(OBJDIR): + @mkdir $(OBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +-include $(DEPS) +endif + +$(OBJDIR)/%.o: %.c + @echo -e "\t\tCC $<" + $(CC) $< -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ + +clean: + @echo -e "\t\tCLEAN" + @rm -rf $(OBJDIR) 2>/dev/null || true + +xclean: clean + @rm -f $(PROGRAM) + +.PHONY: clean xclean diff --git a/Daemons/teldaemon_astrosib/main.c b/Daemons/teldaemon_astrosib/main.c new file mode 100644 index 0000000..3305ce7 --- /dev/null +++ b/Daemons/teldaemon_astrosib/main.c @@ -0,0 +1,112 @@ +/* + * This file is part of the teldaemon 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 +#include + +#include "socket.h" +#include "term.h" + +typedef struct{ + int help; + int verbose; + int isunix; + int maxclients; + int serspeed; + double sertmout; + char *logfile; + char *node; + char *termpath; + char *pidfile; +} parameters; + +static parameters G = { + .maxclients = 2, + .serspeed = 9600 +}; + +static sl_option_t cmdlnopts[] = { + {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), "show this help"}, + {"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verbose), "verbose level (each -v adds 1)"}, + {"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), "log file name"}, + {"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"}, + {"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)"}, + end_option +}; + + +// 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; + } + 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"); + close_term(); + DBG("Exit"); + exit(sig); +} + + +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.termpath) ERRX("Point serial device path"); + sl_check4running((char*)__progname, G.pidfile); + sl_loglevel_e lvl = G.verbose + LOGLEVEL_ERR; + if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1; + if(G.logfile) OPENLOG(G.logfile, lvl, 1); + LOGMSG("Started"); + if(!open_term(G.termpath, G.serspeed, G.sertmout)){ + LOGERR("Can't open %s", G.termpath); + ERRX("Fatal error"); + } + signal(SIGTERM, signals); + signal(SIGINT, signals); + signal(SIGQUIT, signals); + signal(SIGTSTP, SIG_IGN); + signal(SIGHUP, signals); + signal(SIGUSR1, signals); + signal(SIGUSR2, signals); + runserver(G.isunix, G.node, G.maxclients); + LOGMSG("Ended"); + DBG("Close"); + return 0; +} diff --git a/Daemons/teldaemon_astrosib/socket.c b/Daemons/teldaemon_astrosib/socket.c new file mode 100644 index 0000000..70b9dec --- /dev/null +++ b/Daemons/teldaemon_astrosib/socket.c @@ -0,0 +1,383 @@ +/** + * This file is part of the teldaemon 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 "socket.h" +#include "term.h" + +typedef enum{ + CMD_OPEN, + CMD_CLOSE, + CMD_FOCSTOP, + CMD_COOLERON, + CMD_COOLEROFF, + CMD_NONE +} tel_commands_t; + + + +typedef struct{ + tel_commands_t cmd; // deferred command + int focuserpos; // focuser position + char *status; // device status + int statlen; // size of `status` buffer + double stattime; // time of last status + int cooler; // cooler's status + double mirrortemp; // T mirror, degC + double ambienttemp; // T ambient, degC + double temptime; // measurement time + pthread_mutex_t mutex; +} tel_t; + +static tel_t telescope = {0}; + +// external signal "deny/allow" +static atomic_bool ForbidObservations = 0; // ==1 if all is forbidden -> close dome and not allow to open + +static sl_sock_t *locksock = NULL; // local server socket +static sl_ringbuffer_t *rb = NULL; // incoming serial data + +// args of absolute/relative focus move commands +static sl_sock_int_t Absmove = {0}; +static sl_sock_int_t Relmove = {0}; + +void stopserver(){ + if(locksock) sl_sock_delete(&locksock); + if(rb) sl_RB_delete(&rb); +} + +// send "measuret=..." +static void sendtmeasured(sl_sock_t *client, double t){ + char buf[256]; + snprintf(buf, 255, "measuret=%.3f\n", t); + sl_sock_sendstrmessage(client, buf); +} + +static sl_sock_hresult_e dtimeh(sl_sock_t *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){ + char buf[32]; + snprintf(buf, 31, "UNIXT=%.3f\n", sl_dtime()); + sl_sock_sendstrmessage(client, buf); + return RESULT_SILENCE; +} + +#define CHKALLOWED() do{if(ForbidObservations){pthread_mutex_unlock(&telescope.mutex); return RESULT_FAIL;}}while(0) + +static sl_sock_hresult_e openh(_U_ sl_sock_t *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){ + pthread_mutex_lock(&telescope.mutex); + CHKALLOWED(); + telescope.cmd = CMD_OPEN; + pthread_mutex_unlock(&telescope.mutex); + return RESULT_OK; +} + +static sl_sock_hresult_e closeh(_U_ sl_sock_t *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){ + pthread_mutex_lock(&telescope.mutex); + telescope.cmd = CMD_CLOSE; + pthread_mutex_unlock(&telescope.mutex); + return RESULT_OK; +} + +static sl_sock_hresult_e fstoph(_U_ sl_sock_t *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){ + pthread_mutex_lock(&telescope.mutex); + CHKALLOWED(); + telescope.cmd = CMD_FOCSTOP; + pthread_mutex_unlock(&telescope.mutex); + return RESULT_OK; +} + +static sl_sock_hresult_e statush(sl_sock_t *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){ + char buf[256]; + double t = NAN; + pthread_mutex_lock(&telescope.mutex); + if(!*telescope.status || sl_dtime() - telescope.stattime > 3.*T_INTERVAL) snprintf(buf, 255, "%s=unknown\n", item->key); + else{ + snprintf(buf, 255, "%s=%s\n", item->key, telescope.status); + t = telescope.stattime; + } + sl_sock_sendstrmessage(client, buf); + if(!isnan(t)) sendtmeasured(client, t); + snprintf(buf, 255, "mirrortemp=%.1f\n", telescope.mirrortemp); + sl_sock_sendstrmessage(client, buf); + snprintf(buf, 255, "ambienttemp=%.1f\n", telescope.ambienttemp); + sl_sock_sendstrmessage(client, buf); + pthread_mutex_unlock(&telescope.mutex); + if(ForbidObservations) sl_sock_sendstrmessage(client, "FORBIDDEN\n"); + return RESULT_SILENCE; +} + +static sl_sock_hresult_e coolerh(sl_sock_t *client, sl_sock_hitem_t *item, const char *req){ + char buf[256]; + if(req){ // getter + int onoff; + if(!sl_str2i(&onoff, req)) return RESULT_BADVAL; + pthread_mutex_lock(&telescope.mutex); + if(onoff) telescope.cmd = CMD_COOLERON; + else telescope.cmd = CMD_COOLEROFF; + pthread_mutex_unlock(&telescope.mutex); + return RESULT_OK; + } + // getter + pthread_mutex_lock(&telescope.mutex); + snprintf(buf, 255, "%s=%d\n", item->key, telescope.cooler); + pthread_mutex_unlock(&telescope.mutex); + sl_sock_sendstrmessage(client, buf); + return RESULT_SILENCE; +} + +// focuser getter +static sl_sock_hresult_e foch(sl_sock_t *client, sl_sock_hitem_t *item, _U_ const char *req){ + char buf[256]; + pthread_mutex_lock(&telescope.mutex); + snprintf(buf, 255, "%s=%d\n", item->key, telescope.focuserpos); + pthread_mutex_unlock(&telescope.mutex); + sl_sock_sendstrmessage(client, buf); + return RESULT_SILENCE; +} + +// Too much clients handler +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 < 11.){ + 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 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 client fd=%d", c->fd); + else LOGMSG("Disconnected client fd=%d, IP=%s", c->fd, c->IP); +} +// default (unknown key) handler +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; +} + +static sl_sock_hitem_t handlers[] = { +#if 0 + {sl_sock_inthandler, "int", "set/get integer flag", (void*)&iflag}, + {sl_sock_dblhandler, "dbl", "set/get double flag", (void*)&dflag}, + {sl_sock_strhandler, "str", "set/get string variable", (void*)&sflag}, + {keyparhandler, "flags", "set/get bit flags as whole (flags=val) or by bits (flags[bit]=val)", (void*)&kph_number}, + {show, "show", "show current flags @ server console", NULL}, +#endif + {sl_sock_inthandler, "focrel", "relative focus move", (void*)&Relmove}, + {sl_sock_inthandler, "focabs", "absolute focus move", (void*)&Absmove}, + {foch, "focpos", "current focuser position", NULL}, + {openh, "open", "open shutters", NULL}, + {closeh, "close", "close shutters", NULL}, + {statush, "status", "get shutters' status and temperatures", NULL}, + {fstoph, "focstop", "stop focuser moving", NULL}, + {coolerh, "cooler", "get/set cooler status", NULL}, + {dtimeh, "dtime", "get server's UNIX time for all clients connected", NULL}, + {NULL, NULL, NULL, NULL} +}; + +static void serial_parser(){ + char line[256]; + int l = 0; + do{ + l = read_term(line, 256); + if(l > 0){ + if(l != (int)sl_RB_write(rb, (uint8_t*) line, l)) break; + } + }while(l > 0); + + pthread_mutex_lock(&telescope.mutex); // prepare user buffers + // read ringbuffer, run parser and change buffers in `telescope` + while((l = sl_RB_readto(rb, '\r', (uint8_t*)line, sizeof(line))) > 0){ + line[l-1] = 0; // substitute '\r' with 0 + DBG("IN: %s", line); + if(strncmp(line, TXT_ANS_STATUS, strlen(TXT_ANS_STATUS)) == 0){ + DBG("Got status ans"); + telescope.stattime = sl_dtime(); + char *s = line + strlen(TXT_ANS_STATUS); + if(strncmp(s, "0,0,0,0,0", 9) == 0) snprintf(telescope.status, telescope.statlen, "closed"); + else if(strncmp(s, "1,1,1,1,1", 9) == 0) snprintf(telescope.status, telescope.statlen, "opened"); + else snprintf(telescope.status, telescope.statlen, "intermediate"); + }else if(strncmp(line, TXT_ANS_COOLERSTAT, strlen(TXT_ANS_COOLERSTAT)) == 0){ + DBG("Got cooler status"); + int s; + if(sscanf(line + strlen(TXT_ANS_COOLERSTAT), "%d", &s) == 1){ + telescope.cooler = s; + } + }else if(strncmp(line, TXT_ANS_COOLERT, strlen(TXT_ANS_COOLERT)) == 0){ + DBG("Got weather ans"); + float m, a; + if(sscanf(line + strlen(TXT_ANS_COOLERT), "%f,%f", &m, &a) == 2){ + telescope.ambienttemp = a; + telescope.mirrortemp = m; + } + }else if(strncmp(line, TXT_ANS_FOCPOS, strlen(TXT_ANS_FOCPOS)) == 0){ + DBG("Got focuser position"); + int p; + if(sscanf(line + strlen(TXT_ANS_FOCPOS), "%d", &p) == 1){ + telescope.focuserpos = p; + } + }else{ + DBG("Unknown answer: %s", line); + } + } + pthread_mutex_unlock(&telescope.mutex); +} + +// dome polling; @return TRUE if all OK +static int poll_device(){ + static const char *reqcmds[] = {TXT_FOCGET, TXT_STATUS, TXT_COOLERT, TXT_COOLERSTAT, NULL}; + for(const char **cmd = reqcmds; *cmd; ++cmd){ + if(write_cmd(*cmd)){ + LOGWARN("Can't write command %s", *cmd); + DBG("Can't write command %s", *cmd); + return FALSE; + } + serial_parser(); + } + return TRUE; +} + +void runserver(int isunix, const char *node, int maxclients){ + int forbidden = 0; + if(locksock) sl_sock_delete(&locksock); + if(rb) sl_RB_delete(&rb); + rb = sl_RB_new(BUFSIZ); + telescope.cmd = CMD_NONE; + FREE(telescope.status); + telescope.statlen = STATBUF_SZ; + telescope.status = MALLOC(char, STATBUF_SZ); + pthread_mutex_init(&telescope.mutex, NULL); + sl_socktype_e type = (isunix) ? SOCKT_UNIX : SOCKT_NET; + locksock = sl_sock_run_server(type, node, -1, handlers); + sl_sock_changemaxclients(locksock, maxclients); + sl_sock_maxclhandler(locksock, toomuch); + sl_sock_connhandler(locksock, connected); + sl_sock_dischandler(locksock, disconnected); + sl_sock_defmsghandler(locksock, defhandler); + double tgot = 0.; + while(locksock && locksock->connected){ + if(ForbidObservations){ + if(!forbidden){ + if(0 == write_cmd(TXT_CLOSE)) forbidden = 1; + pthread_mutex_lock(&telescope.mutex); + telescope.cmd = CMD_NONE; + pthread_mutex_unlock(&telescope.mutex); + } + }else forbidden = 0; + usleep(1000); + if(!locksock->rthread){ + WARNX("Server handlers thread is dead"); + LOGERR("Server handlers thread is dead"); + break; + } + double tnow = sl_dtime(); + if(tnow - tgot > T_INTERVAL){ + if(poll_device()) tgot = tnow; + } + pthread_mutex_lock(&telescope.mutex); + if(telescope.cmd != CMD_NONE){ + switch(telescope.cmd){ + case CMD_OPEN: + DBG("received command: open"); + if(0 == write_cmd(TXT_OPEN)) telescope.cmd = CMD_NONE; + break; + case CMD_CLOSE: + DBG("received command: close"); + if(0 == write_cmd(TXT_CLOSE)) telescope.cmd = CMD_NONE; + break; + case CMD_FOCSTOP: + DBG("received command: stop focus"); + if(0 == write_cmd(TXT_FOCSTOP)) telescope.cmd = CMD_NONE; + break; + case CMD_COOLEROFF: + DBG("received command: cooler off"); + if(0 == write_cmd(TXT_COOLEROFF)) telescope.cmd = CMD_NONE; + break; + case CMD_COOLERON: + DBG("received command: cooler on"); + if(0 == write_cmd(TXT_COOLERON)) telescope.cmd = CMD_NONE; + break; + default: + DBG("WTF?"); + } + } + pthread_mutex_unlock(&telescope.mutex); + // check abs/rel move + char buf[256]; + double dt = tnow - Absmove.timestamp; + if(dt < 3. * T_INTERVAL){ + if(Absmove.val < FOC_MINPOS || Absmove.val > FOC_MAXPOS) Absmove.timestamp = 0.; // reset wrong data + else{ + snprintf(buf, 255, "%s%d\r", TXT_FOCUSABS, (int)Absmove.val); + if(0 == write_cmd(buf)){ + DBG("STARTED absmove"); + Absmove.timestamp = 0.; + } + } + } + dt = tnow - Relmove.timestamp; + if(dt < 3. * T_INTERVAL){ + if(Relmove.val < -FOC_MAXPOS || Relmove.val > FOC_MAXPOS) Relmove.timestamp = 0.; + else{ // "in" < 0, "out" > 0 + char *cmd = TXT_FOCUSOUT; + int pos = (int)Relmove.val; + DBG("pos=%d", pos); + if(pos < 0){ cmd = TXT_FOCUSIN; pos = -pos; } + snprintf(buf, 255, "%s%d\r", cmd, pos); + if(0 == write_cmd(buf)){ + DBG("STARTED relmove"); + Relmove.timestamp = 0.; + } + } + } + serial_parser(); + } + stopserver(); +} + +void forbid_observations(int forbid){ + if(forbid){ + ForbidObservations = true; + LOGWARN("Got forbidden signal"); + }else{ + ForbidObservations = false; + LOGWARN("Got allowed signal"); + } + DBG("Change ForbidObservations=%d", forbid); +} diff --git a/Daemons/teldaemon_astrosib/socket.h b/Daemons/teldaemon_astrosib/socket.h new file mode 100644 index 0000000..a045d76 --- /dev/null +++ b/Daemons/teldaemon_astrosib/socket.h @@ -0,0 +1,30 @@ +/* + * This file is part of the teldaemon 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 + +#include + +// size of weather/status buffers +#define STATBUF_SZ 256 +// dome polling interval (clear watchdog & get status) +#define T_INTERVAL (1.0) + +void runserver(int isunix, const char *node, int maxclients); +void stopserver(); +void forbid_observations(int forbid); diff --git a/Daemons/teldaemon_astrosib/teldaemon.cflags b/Daemons/teldaemon_astrosib/teldaemon.cflags new file mode 100644 index 0000000..68d5165 --- /dev/null +++ b/Daemons/teldaemon_astrosib/teldaemon.cflags @@ -0,0 +1 @@ +-std=c17 \ No newline at end of file diff --git a/Daemons/teldaemon_astrosib/teldaemon.config b/Daemons/teldaemon_astrosib/teldaemon.config new file mode 100644 index 0000000..baa1bf1 --- /dev/null +++ b/Daemons/teldaemon_astrosib/teldaemon.config @@ -0,0 +1,3 @@ +// Add predefined macros for your project here. For example: +// #define THE_ANSWER 42 +#define EBUG diff --git a/Daemons/teldaemon_astrosib/teldaemon.creator b/Daemons/teldaemon_astrosib/teldaemon.creator new file mode 100644 index 0000000..e94cbbd --- /dev/null +++ b/Daemons/teldaemon_astrosib/teldaemon.creator @@ -0,0 +1 @@ +[General] diff --git a/Daemons/teldaemon_astrosib/teldaemon.cxxflags b/Daemons/teldaemon_astrosib/teldaemon.cxxflags new file mode 100644 index 0000000..6435dfc --- /dev/null +++ b/Daemons/teldaemon_astrosib/teldaemon.cxxflags @@ -0,0 +1 @@ +-std=c++17 \ No newline at end of file diff --git a/Daemons/teldaemon_astrosib/teldaemon.files b/Daemons/teldaemon_astrosib/teldaemon.files new file mode 100644 index 0000000..9b3fd75 --- /dev/null +++ b/Daemons/teldaemon_astrosib/teldaemon.files @@ -0,0 +1,5 @@ +main.c +socket.c +socket.h +term.c +term.h diff --git a/Daemons/teldaemon_astrosib/teldaemon.includes b/Daemons/teldaemon_astrosib/teldaemon.includes new file mode 100644 index 0000000..e69de29 diff --git a/Daemons/teldaemon_astrosib/term.c b/Daemons/teldaemon_astrosib/term.c new file mode 100644 index 0000000..21875b5 --- /dev/null +++ b/Daemons/teldaemon_astrosib/term.c @@ -0,0 +1,115 @@ +/* + * This file is part of the teldaemon 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 "term.h" + +static sl_tty_t *dev = NULL; // shoul be global to restore if die + +// close serial device +void close_term(){ + if(dev) sl_tty_close(&dev); +} + +/** + * @brief open_term - open serial device + * @param path - path to device + * @param speed - its speed + * @param usec - timeout (us), if < 1e-6 - leave default + * @return FALSE if failed + */ +int open_term(char *path, int speed, double usec){ + if(dev) sl_tty_close(&dev); + LOGMSG("Try to open serial %s at speed %d", path, speed); + DBG("Open serial"); + dev = sl_tty_new(path, speed, 4096); + if(dev) dev = sl_tty_open(dev, 1); + if(!dev){ + LOGERR("Can't open %s with speed %d. Exit.", path, speed); + return FALSE; + } + if(usec >= 1e-6){ + DBG("set timeout to %gus", usec); + if(!sl_tty_tmout(usec)){ + LOGWARN("Can't set timeout to %gus", usec); + WARNX("Can't set timeout to %gus", usec); + } + } + if(speed != dev->speed){ + LOGWARN("Can't set exact speed! Opened %s at speed %d", dev->portname, dev->speed); + WARNX("Can't set speed %d (try %d)", speed, dev->speed); + } + if(dev) return TRUE; + return FALSE; +} + +/** + * @brief read_term - read data from serial terminal + * @param buf - buffer for data + * @param length - size of `buf` + * @return amount of data read + */ +int read_term(char *buf, int length){ + static char *bufptr = NULL; // last message, if it was longer than `length` + static int lastL = 0; + if(!dev || !buf || length < 1) return 0; + if(bufptr && lastL){ + if(length > lastL) length = lastL; + DBG("got %d bytes from old record", length); + memcpy(buf, bufptr, length); + if((lastL -= length) < 1){ + lastL = 0; bufptr = NULL; + } + return length; + } + if(!sl_tty_read(dev)) return 0; + DBG("Got from serial %zd bytes", dev->buflen); + LOGDBG("Got from serial: %zd bytes", dev->buflen); + if(length >= (int)dev->buflen){ + DBG("Full buffer can be copied"); + length = (int)dev->buflen; + bufptr = NULL; + lastL = 0; + }else{ // store part of data in buffer + lastL = dev->buflen - length; + DBG("Store %d bytes for next read", lastL); + bufptr = dev->buf + length; + } + memcpy(buf, dev->buf, length); + return length; +} + +/** + * @brief write_term - write data + * @param buf - buffer + * @param length - its length + * @return 0 if OK and 1 if failed + */ +int write_term(const char *buf, int length){ + if(!dev || !buf || length < 1) return 0; + return sl_tty_write(dev->comfd, buf, length); +} + +// write string command +int write_cmd(const char *buf){ + if(!buf || !*buf) return 0; + DBG("Ask to write %s", buf); + return write_term(buf, strlen(buf)); +} diff --git a/Daemons/teldaemon_astrosib/term.h b/Daemons/teldaemon_astrosib/term.h new file mode 100644 index 0000000..80f610a --- /dev/null +++ b/Daemons/teldaemon_astrosib/term.h @@ -0,0 +1,48 @@ +/* + * This file is part of the teldaemon 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 + +// text commands and answers +#define TXT_FOCUSABS "FOCUSERGO?" +#define TXT_FOCUSIN "FOCUSERI?" +#define TXT_FOCUSOUT "FOCUSERO?" +#define TXT_FOCSTOP "FOCUSERSTOP?\r" +#define TXT_FOCGET "FOCUSERGPOS?\r" +#define TXT_OPEN "SHUTTEROPEN?1,1,1,1,1\r" +#define TXT_CLOSE "SHUTTERCLOSE?1,1,1,1,1\r" +#define TXT_STATUS "SHUTTERSTATUS?\r" +#define TXT_COOLERON "COOLERON?100\r" +#define TXT_COOLEROFF "COOLEROFF?\r" +#define TXT_COOLERT "COOLERT?\r" +#define TXT_COOLERSTAT "COOLERSTATUS?\r" +#define TXT_PING "PING?\r" +#define TXT_ANS_OK "OK" +#define TXT_ANS_COOLERSTAT "COOLERPWM?" +#define TXT_ANS_COOLERT "COOLERT?" +#define TXT_ANS_STATUS "SHUTTERSTATUS?" +#define TXT_ANS_FOCPOS "FOCUSERPOS?" + +#define FOC_MINPOS 0 +#define FOC_MAXPOS 65000 + +int open_term(char *path, int speed, double usec); +void close_term(); +int read_term(char *buf, int length); +int write_term(const char *buf, int length); +int write_cmd(const char *buf);