From 734e36a85dfa6293f6e2302c6383dd5275a45d5f Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Tue, 24 Mar 2026 23:52:07 +0300 Subject: [PATCH] new baader-dome & astrosib-telescope daemons --- .gitignore | 12 ++ .../10micron_readme.koi8-r | 58 ------ Daemons/10micron_stellarium/telescope.h | 2 +- Daemons/10micron_stellarium/usefull_macro.c | 2 +- Daemons/10micron_stellarium/usefull_macro.h | 3 +- .../.qtcreator/baader_dome.creator.user | 2 +- Daemons/domedaemon_baader/baader_dome.files | 1 + Daemons/domedaemon_baader/commands.h | 31 +++ Daemons/domedaemon_baader/main.c | 7 +- Daemons/domedaemon_baader/socket.c | 187 +++++++----------- Daemons/domedaemon_baader/socket.h | 2 +- Daemons/domedaemon_baader/term.c | 155 +++++++++++---- Daemons/domedaemon_baader/term.h | 27 ++- Daemons/teldaemon_astrosib/commands.h | 42 ++++ Daemons/teldaemon_astrosib/main.c | 7 +- Daemons/teldaemon_astrosib/socket.c | 160 ++++++++------- Daemons/teldaemon_astrosib/socket.h | 2 +- Daemons/teldaemon_astrosib/teldaemon.files | 1 + Daemons/teldaemon_astrosib/term.c | 157 +++++++++++---- Daemons/teldaemon_astrosib/term.h | 39 ++-- 20 files changed, 497 insertions(+), 400 deletions(-) delete mode 100644 Daemons/10micron_stellarium/10micron_readme.koi8-r create mode 100644 Daemons/domedaemon_baader/commands.h create mode 100644 Daemons/teldaemon_astrosib/commands.h diff --git a/.gitignore b/.gitignore index df80c97..f61c398 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +# executables +* +!*.* +!*/ + # Prerequisites *.d @@ -26,3 +31,10 @@ # build dirs mk/ build/ + +# diff +*.log +*.tgz +*.tar.gz +*.7z +*.zip diff --git a/Daemons/10micron_stellarium/10micron_readme.koi8-r b/Daemons/10micron_stellarium/10micron_readme.koi8-r deleted file mode 100644 index d6bf479..0000000 --- a/Daemons/10micron_stellarium/10micron_readme.koi8-r +++ /dev/null @@ -1,58 +0,0 @@ -# - очистить принимающий буфер - -:shutdown# - выключить монтировку - -:U2# - установить наивысшую точность - -Состояние гидирования (ACK): команда 0x06, ответ L/P - вкл/выкл -:AL# - выключить ведение (без ответа) -:AP# - включить - -GPS: -:gtg# - возвращает 1, если часы синхронизированы с GPS - -наведение на цель -:MS# - двигаться на цель, возврат: 0, если все в порядке, либо текст с ошибкой -:SrHH:MM:SS.SS# - установить RA цели (возврат 1 если ОК) -:SdsDD*MM:SS.S# - установить DECL цели (возврат 1 если ОК) -:D# - статус наведения, возврат: 0x7f, если в процессе, либо # -:NUDGEsXXX,sYYY# - скорректировать положение по RA/DEC на N угловых секунд -:STOP# - останов (:AP# - для возобновления гидирования) - -:Gstat# - состояние монтировки -:GT# - частота для гидирования (60.1643377745 по звездам) -:AL# - выкл. гидирование -:AP# - вкл -:Gpgc# - состояние гидирования -:GSC# - коррекция скорости гидирования на косинус h -:GTTRK# - возможно ли наведение на текущий объект -:pS# - с какой стороны монтировки телескоп (для перекладывания?) -:FLIP# - переложить монтировку - -:GVP# - название монтировки -:GTMP@# (@ - номер датчика) - температура датчика -:GREF# - состояние коррекции на рефракцию - -:GA# - высота телескопа над горизонтом -:GZ# - азимут -:GC# - дата -:GD# - склонение -:GR# - восхождение - -:GJD# - юлианская дата (GJD1 - с наивысшей точностью) -:GLDT# - местные дата и время -:GS# - звездное время - -парковка: стр. 20 - -ехать: -:Q# - прекратить наведение (а еще :Qe# - east и пр.) -:D# - состояние наведения -:NUDGEsXXXX,sYYYY# - коррекция положения на XXX/YYY секунд (RA/Dec) -:MA# - по введенным горизонтальным координатам цели -:MS# - ехать на цель -:Me$=#, :Mw#, :Mn#, :Ms# - движение в данную сторону -:MeXXX#, :MnXXX# и др. стороны - коррекция на XXX миллисекунд скорости гида - -модель наведения - стр. 44 -после определения центра изображения ввести его командами :Sr# и :Sd# и добавить точку командой :CMS# diff --git a/Daemons/10micron_stellarium/telescope.h b/Daemons/10micron_stellarium/telescope.h index fdb5230..0d560ae 100644 --- a/Daemons/10micron_stellarium/telescope.h +++ b/Daemons/10micron_stellarium/telescope.h @@ -29,7 +29,7 @@ // make datetime/pressure/temperature corrections each CORRECTIONS_TIMEDIFF seconds #define CORRECTIONS_TIMEDIFF (3600) -#define TELESCOPE_NAME "'Astrosib-500 (1)'" +#define TELESCOPE_NAME "'Astrosib-500 (2)'" // telescope statuses typedef enum{ diff --git a/Daemons/10micron_stellarium/usefull_macro.c b/Daemons/10micron_stellarium/usefull_macro.c index 622db63..2513e87 100644 --- a/Daemons/10micron_stellarium/usefull_macro.c +++ b/Daemons/10micron_stellarium/usefull_macro.c @@ -274,7 +274,7 @@ int mygetchar(){ // getchar() without need of pressing ENTER /******************************************************************************\ * TTY with select() \******************************************************************************/ -static struct termio oldtty, tty; // TTY flags +static struct termios oldtty, tty; // TTY flags static int comfd = -1; // TTY fd // run on exit: diff --git a/Daemons/10micron_stellarium/usefull_macro.h b/Daemons/10micron_stellarium/usefull_macro.h index a937ec1..ea95476 100644 --- a/Daemons/10micron_stellarium/usefull_macro.h +++ b/Daemons/10micron_stellarium/usefull_macro.h @@ -33,6 +33,8 @@ #include #include #include +#include + #if defined GETTEXT_PACKAGE && defined LOCALEDIR /* * GETTEXT @@ -47,7 +49,6 @@ #endif #include #include -#include #include #include #include diff --git a/Daemons/domedaemon_baader/.qtcreator/baader_dome.creator.user b/Daemons/domedaemon_baader/.qtcreator/baader_dome.creator.user index 3a93b01..f55d7c1 100644 --- a/Daemons/domedaemon_baader/.qtcreator/baader_dome.creator.user +++ b/Daemons/domedaemon_baader/.qtcreator/baader_dome.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/Daemons/domedaemon_baader/baader_dome.files b/Daemons/domedaemon_baader/baader_dome.files index 9b3fd75..27793b3 100644 --- a/Daemons/domedaemon_baader/baader_dome.files +++ b/Daemons/domedaemon_baader/baader_dome.files @@ -1,3 +1,4 @@ +commands.h main.c socket.c socket.h diff --git a/Daemons/domedaemon_baader/commands.h b/Daemons/domedaemon_baader/commands.h new file mode 100644 index 0000000..41e2c2d --- /dev/null +++ b/Daemons/domedaemon_baader/commands.h @@ -0,0 +1,31 @@ +/* + * 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 + +// text commands and answers +#define TXT_GETWARN "d#warning" +#define TXT_OPENDOME "d#opendom" +#define TXT_CLOSEDOME "d#closdom" +#define TXT_STOPDOME "d#stopdom" +#define TXT_GETSTAT "d#get_dom" +#define TXT_GETWEAT "d#ask_wea" +#define TXT_ANS_MSGOK "d#gotmess" +#define TXT_ANS_WEAT "d#wea" +#define TXT_ANS_STAT "d#pos" +#define TXT_ANS_ERR "d#erro" diff --git a/Daemons/domedaemon_baader/main.c b/Daemons/domedaemon_baader/main.c index 482222c..f1fca3c 100644 --- a/Daemons/domedaemon_baader/main.c +++ b/Daemons/domedaemon_baader/main.c @@ -41,7 +41,8 @@ typedef struct{ static parameters G = { .maxclients = 2, - .serspeed = 9600 + .serspeed = 9600, + .sertmout = 5000 }; static sl_option_t cmdlnopts[] = { @@ -77,7 +78,7 @@ void signals(int sig){ DBG("Stop server"); stopserver(); DBG("Close terminal"); - close_term(); + term_close(); DBG("Exit"); exit(sig); } @@ -94,7 +95,7 @@ int main(int argc, char **argv){ 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)){ + if(!term_open(G.termpath, G.serspeed, G.sertmout)){ LOGERR("Can't open %s", G.termpath); ERRX("Fatal error"); } diff --git a/Daemons/domedaemon_baader/socket.c b/Daemons/domedaemon_baader/socket.c index ee9094c..eff6fb0 100644 --- a/Daemons/domedaemon_baader/socket.c +++ b/Daemons/domedaemon_baader/socket.c @@ -21,6 +21,7 @@ #include #include +#include "commands.h" #include "socket.h" #include "term.h" @@ -33,6 +34,7 @@ typedef enum{ typedef struct{ 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 @@ -56,24 +58,6 @@ void stopserver(){ if(rb) sl_RB_delete(&rb); } -#if 0 -// flags for standard handlers -static sl_sock_int_t iflag = {0}; -static sl_sock_double_t dflag = {0}; -static sl_sock_string_t sflag = {0}; -static uint32_t bitflags = 0; - -static sl_sock_hresult_e show(sl_sock_t *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){ - if(locksock && locksock->type != SOCKT_UNIX){ - if(*client->IP){ - printf("Client \"%s\" (fd=%d) ask for flags:\n", client->IP, client->fd); - }else printf("Can't get client's IP, flags:\n"); - }else printf("Socket fd=%d asks for flags:\n", client->fd); - printf("\tiflag=%" PRId64 ", dflag=%g\n", iflag.val, dflag.val); - return RESULT_OK; -} -#endif - // send "measuret=..." static void sendtmeasured(sl_sock_t *client, double t){ char buf[256]; @@ -116,17 +100,18 @@ static sl_sock_hresult_e stoph(_U_ sl_sock_t *client, _U_ sl_sock_hitem_t *item, 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; - int ecode; + int ecode, lastecmd; pthread_mutex_lock(&Dome.mutex); if(!*Dome.status || 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.status); t = Dome.stattime; } + ecode = Dome.errcode; + lastecmd = Dome.erroredcmd; + pthread_mutex_unlock(&Dome.mutex); sl_sock_sendstrmessage(client, buf); if(!isnan(t)) sendtmeasured(client, t); - ecode = Dome.errcode; - pthread_mutex_unlock(&Dome.mutex); if(ecode){ int l = snprintf(buf, 255, "error=closed"); if(ecode > 99){ @@ -147,6 +132,17 @@ static sl_sock_hresult_e statush(sl_sock_t *client, _U_ sl_sock_hitem_t *item, _ sl_sock_sendstrmessage(client, buf); } if(ForbidObservations) sl_sock_sendstrmessage(client, "FORBIDDEN\n"); + if(lastecmd != CMD_NONE){ + const char *tcmd; + switch(lastecmd){ + case CMD_OPEN: tcmd = "open"; break; + case CMD_CLOSE: tcmd = "close"; break; + case CMD_STOP: tcmd = "stop"; break; + default: tcmd = "unknown"; + } + snprintf(buf, 255, "errored_command=%s\n", tcmd); + sl_sock_sendstrmessage(client, buf); + } return RESULT_SILENCE; } @@ -202,44 +198,8 @@ static sl_sock_hresult_e defhandler(struct sl_sock *s, const char *str){ sl_sock_sendstrmessage(s, "\n```\nTry \"help\"\n"); return RESULT_SILENCE; } -#if 0 -// if we use this macro, there's no need to run `sl_sock_keyno_init` later -static sl_sock_keyno_t kph_number = SL_SOCK_KEYNO_DEFAULT; -// handler for key with optional parameter number -static sl_sock_hresult_e keyparhandler(struct sl_sock *s, sl_sock_hitem_t *item, const char *req){ - if(!item->data) return RESULT_FAIL; - char buf[1024]; - int no = sl_sock_keyno_check((sl_sock_keyno_t*)item->data); - long long newval = -1; - if(req){ - if(!sl_str2ll(&newval, req) || newval < 0 || newval > 0xffffffff) return RESULT_BADVAL; - } - printf("no = %d\n", no); - if(no < 0){ // flags as a whole - if(req) bitflags = (uint32_t)newval; - snprintf(buf, 1023, "flags = 0x%08X\n", bitflags); - sl_sock_sendstrmessage(s, buf); - }else if(no < 32){ // bit access - int bitmask = 1 << no; - if(req){ - if(newval) bitflags |= bitmask; - else bitflags &= ~bitmask; - } - snprintf(buf, 1023, "flags[%d] = %d\n", no, bitflags & bitmask ? 1 : 0); - sl_sock_sendstrmessage(s, buf); - }else return RESULT_BADKEY; - return RESULT_SILENCE; -} -#endif 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 {openh, "open", "open dome", NULL}, {closeh, "close", "close dome", NULL}, {statush, "status", "get dome status", NULL}, @@ -251,68 +211,49 @@ static sl_sock_hitem_t handlers[] = { // dome polling; @return TRUE if all OK static int poll_device(){ - char line[256]; - if(write_cmd(TXT_GETWARN)){ - DBG("Can't write command warning"); - return FALSE; + char ans[ANSLEN]; + char *data; + if(!(data = term_cmdwans(TXT_GETWARN, TXT_ANS_ERR, ans))) return FALSE; + DBG("Got status errno"); + int I; + if(sscanf(data, "%d", &I) == 1){ + pthread_mutex_lock(&Dome.mutex); + Dome.errcode = I; + pthread_mutex_unlock(&Dome.mutex); + DBG("errcode: %d", I); } - if(write_cmd(TXT_GETSTAT)){ - DBG("Can't write command getstat"); - return FALSE; + if(!(data = term_cmdwans(TXT_GETSTAT, TXT_ANS_STAT, ans))) return FALSE; + DBG("Got status ans"); + if(sscanf(data, "%d", &I) == 1){ + pthread_mutex_lock(&Dome.mutex); + if(I == 1111) + snprintf(Dome.status, Dome.statlen, "opened"); + else if(I == 2222) + snprintf(Dome.status, Dome.statlen, "closed"); + else + snprintf(Dome.status, Dome.statlen, "intermediate"); + Dome.stattime = sl_dtime(); + pthread_mutex_unlock(&Dome.mutex); } - if(write_cmd(TXT_GETWEAT)){ - DBG("Can't write command getweath"); - return FALSE; + if(!(data = term_cmdwans(TXT_GETWEAT, TXT_ANS_WEAT, ans))) return FALSE; + DBG("Got weather ans"); + if(sscanf(data, "%d", &I) == 1){ + pthread_mutex_lock(&Dome.mutex); + if(I == 0) + snprintf(Dome.weather, Dome.weathlen, "good"); + else if (I == 1) + snprintf(Dome.weather, Dome.weathlen, "rain/clouds"); + else + snprintf(Dome.weather, Dome.weathlen, "unknown"); + Dome.weathtime = sl_dtime(); + pthread_mutex_unlock(&Dome.mutex); } - int l = 0; - do{ - l = read_term(line, 256); - if(l > 0) sl_RB_write(rb, (uint8_t*) line, l); - }while(l > 0); - pthread_mutex_lock(&Dome.mutex); // prepare user buffers - // read ringbuffer, run parser and change buffers in `Dome` - while(sl_RB_readline(rb, line, sizeof(line)) > 0){ - if(strncmp(line, TXT_ANS_STAT, strlen(TXT_ANS_STAT)) == 0){ - DBG("Got status ans"); - int stat; - Dome.stattime = sl_dtime(); - if(sscanf(line + strlen(TXT_ANS_STAT), "%d", &stat) == 1){ - if(stat == 1111) - snprintf(Dome.status, Dome.statlen, "opened"); - else if(stat == 2222) - snprintf(Dome.status, Dome.statlen, "closed"); - else - snprintf(Dome.status, Dome.statlen, "intermediate"); - } - }else if(strncmp(line, TXT_ANS_ERR, strlen(TXT_ANS_ERR)) == 0){ - DBG("Got status errno"); - int ecode; - if(sscanf(line + strlen(TXT_ANS_ERR), "%d", &ecode) == 1){ - Dome.errcode = ecode; - DBG("errcode: %d", ecode); - } - }else if(strncmp(line, TXT_ANS_WEAT, strlen(TXT_ANS_WEAT)) == 0){ - DBG("Got weather ans"); - int weather; - Dome.weathtime = sl_dtime(); - if(sscanf(line + strlen(TXT_ANS_WEAT), "%d", &weather) == 1){ - if(weather == 0) - snprintf(Dome.weather, Dome.weathlen, "good weather"); - else if (weather == 1) - snprintf(Dome.weather, Dome.weathlen, "rain or clouds"); - else - snprintf(Dome.weather, Dome.weathlen, "unknown"); - } - }else{ - DBG("Unknown answer: %s", line); - } - } - pthread_mutex_unlock(&Dome.mutex); return TRUE; } void runserver(int isunix, const char *node, int maxclients){ int forbidden = 0; + char ans[ANSLEN]; if(locksock) sl_sock_delete(&locksock); if(rb) sl_RB_delete(&rb); rb = sl_RB_new(BUFSIZ); @@ -335,11 +276,14 @@ void runserver(int isunix, const char *node, int maxclients){ while(locksock && locksock->connected){ if(ForbidObservations){ if(!forbidden){ - if(0 == write_cmd(TXT_CLOSEDOME)) forbidden = 1; - pthread_mutex_lock(&Dome.mutex); - Dome.cmd = CMD_NONE; - pthread_mutex_unlock(&Dome.mutex); + if(term_cmdwans(TXT_CLOSEDOME, TXT_ANS_MSGOK, ans)) forbidden = 1; } + pthread_mutex_lock(&Dome.mutex); + if(Dome.cmd != CMD_NONE){ + Dome.erroredcmd = Dome.cmd; + Dome.cmd = CMD_NONE; + } + pthread_mutex_unlock(&Dome.mutex); }else forbidden = 0; usleep(1000); if(!locksock->rthread){ @@ -347,28 +291,31 @@ void runserver(int isunix, const char *node, int maxclients){ LOGERR("Server handlers thread is dead"); break; } - if(sl_dtime() - tgot > T_INTERVAL){ - if(poll_device()) tgot = sl_dtime(); + double tnow = sl_dtime(); + if(tnow - tgot > T_INTERVAL){ + if(poll_device()) tgot = tnow; } + if(ForbidObservations) continue; pthread_mutex_lock(&Dome.mutex); if(Dome.cmd != CMD_NONE){ switch(Dome.cmd){ case CMD_OPEN: DBG("received command: open"); - if(0 == write_cmd(TXT_OPENDOME)) Dome.cmd = CMD_NONE; + if(term_cmdwans(TXT_OPENDOME, TXT_ANS_MSGOK, ans)) Dome.cmd = CMD_NONE; break; case CMD_CLOSE: DBG("received command: close"); - if(0 == write_cmd(TXT_CLOSEDOME)) Dome.cmd = CMD_NONE; + if(term_cmdwans(TXT_CLOSEDOME, TXT_ANS_MSGOK, ans)) Dome.cmd = CMD_NONE; break; case CMD_STOP: DBG("received command: stop"); - if(0 == write_cmd(TXT_STOPDOME)) Dome.cmd = CMD_NONE; + if(term_cmdwans(TXT_STOPDOME, TXT_ANS_MSGOK, ans)) Dome.cmd = CMD_NONE; break; default: DBG("WTF?"); } } + Dome.erroredcmd = Dome.cmd; // if command didn't run last time, it will store here pthread_mutex_unlock(&Dome.mutex); } stopserver(); diff --git a/Daemons/domedaemon_baader/socket.h b/Daemons/domedaemon_baader/socket.h index 39e2521..ccace72 100644 --- a/Daemons/domedaemon_baader/socket.h +++ b/Daemons/domedaemon_baader/socket.h @@ -23,7 +23,7 @@ // size of weather/status buffers #define STATBUF_SZ 256 // dome polling interval (clear watchdog & get status) -#define T_INTERVAL (1.0) +#define T_INTERVAL (5.0) void runserver(int isunix, const char *node, int maxclients); void stopserver(); diff --git a/Daemons/domedaemon_baader/term.c b/Daemons/domedaemon_baader/term.c index 3fbda9c..ab1e9e3 100644 --- a/Daemons/domedaemon_baader/term.c +++ b/Daemons/domedaemon_baader/term.c @@ -16,15 +16,17 @@ * along with this program. If not, see . */ +#include #include #include #include "term.h" static sl_tty_t *dev = NULL; // shoul be global to restore if die +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // close serial device -void close_term(){ +void term_close(){ if(dev) sl_tty_close(&dev); } @@ -35,19 +37,19 @@ void close_term(){ * @param usec - timeout (us), if < 1e-6 - leave default * @return FALSE if failed */ -int open_term(char *path, int speed, double usec){ +int term_open(char *path, int speed, double usec){ + pthread_mutex_lock(&mutex); if(dev) sl_tty_close(&dev); LOGMSG("Try to open serial %s at speed %d", path, speed); - DBG("Open serial"); + DBG("Init serial"); dev = sl_tty_new(path, speed, 4096); + DBG("Open serial"); if(dev) dev = sl_tty_open(dev, 1); - if(!dev){ - LOGERR("Can't open %s with speed %d. Exit.", path, speed); - return FALSE; - } + if(!dev) goto rtn; + DBG("Opened"); if(usec >= 1e-6){ DBG("set timeout to %gus", usec); - if(!sl_tty_tmout(usec)){ + if(sl_tty_tmout(usec) < 0){ LOGWARN("Can't set timeout to %gus", usec); WARNX("Can't set timeout to %gus", usec); } @@ -56,60 +58,129 @@ int open_term(char *path, int speed, double usec){ 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); } + pthread_mutex_unlock(&mutex); +rtn: if(dev) return TRUE; + LOGERR("Can't open %s with speed %d. Exit.", path, speed); 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 + * @brief nonblk_read - internal read + * @param ans (o) - buffer for data + * @return NULL if failed or `ans` */ -int read_term(char *buf, int length){ +static char *nonblk_read(char ans[ANSLEN]){ static char *bufptr = NULL; // last message, if it was longer than `length` static int lastL = 0; - if(!dev || !buf || length < 1) return 0; + FNAME(); + if(!dev) return NULL; + int length = ANSLEN - 1; + DBG("ok"); + if(!ans){ // clear + DBG("clr"); + while(sl_tty_read(dev) > 0); + bufptr = NULL; + lastL = 0; + return NULL; + } if(bufptr && lastL){ if(length > lastL) length = lastL; - DBG("got %d bytes from old record", length); - memcpy(buf, bufptr, length); + memcpy(ans, bufptr, length); if((lastL -= length) < 1){ lastL = 0; bufptr = NULL; } - return length; + ans[length] = 0; + DBG("got %d bytes from old record: '%s'", length, ans); + return ans; } - 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; + int totlen = 0; + while(length > 0 && sl_tty_read(dev) > 0){ + int lmax = length; + if(lmax >= (int)dev->buflen){ + DBG("Full buffer can be copied"); + lmax = (int)dev->buflen; + }else{ // store part of data in buffer + lastL = dev->buflen - lmax; + DBG("Store %d bytes for next read", lastL); + bufptr = dev->buf + lmax; + } + memcpy(ans + totlen, dev->buf, lmax); + length -= lmax; + totlen += lmax; + ans[totlen] = 0; } - memcpy(buf, dev->buf, length); - return length; + DBG("totlen: %d", totlen); + if(totlen == 0) return NULL; + DBG("copied %d: '%s'", totlen, ans); + return ans; } /** - * @brief write_term - write data - * @param buf - buffer + * @brief nonblk_write - internal write + * @param buf (i) - data to write * @param length - its length - * @return 0 if OK and 1 if failed + * @return 0 if failed or `length` */ -int write_term(const char *buf, int length){ - if(!dev || !buf || length < 1) return 0; - return sl_tty_write(dev->comfd, buf, length); +static int nonblk_write(const char *buf, int length){ + if(!buf || length < 1) return 0; + if(0 == sl_tty_write(dev->comfd, buf, length)) return length; + return 0; } -// 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)); +/** + * @brief term_read - read string from terminal + * @param ans (o) - buffer or NULL to clear last data + * @return NULL or pointer to zero-terminated `ans` + */ +char *term_read(char ans[ANSLEN]){ + pthread_mutex_lock(&mutex); + char *ret = nonblk_read(ans); + pthread_mutex_unlock(&mutex); + DBG("read: '%s'", ret); + return ret; +} + +/** + * @brief term_write - write data and got answer + * @param str (i) - zero-terminated string to write + * @param ans (o) - NULL to clear incoming data or buffer to read + * @return + */ +char *term_write(const char *str, char ans[ANSLEN]){ + if(!str || !*str) return NULL; + DBG("Send cmd '%s'", str); + char *ret = NULL; + pthread_mutex_lock(&mutex); + if(nonblk_write(str, strlen(str))){ + usleep(USLEEP_BEFORE_READ); + ret = nonblk_read(ans); + } + pthread_mutex_unlock(&mutex); + DBG("read: '%s'", ret); + return ret; +} + +/** + * @brief term_cmdwans - write command and get answer + * @param str (i) - command to write + * @param prefix (i) - prefix of answer (string should begin with this text) + * @param ans (o) - buffer for answer (non-NULL!!!) + * @return pointer to data just after `prefix` in `ans` or NULL if no `prefix` found + */ +char *term_cmdwans(const char *str, const char *prefix, char ans[ANSLEN]){ + if(!str || !prefix || !ans) return NULL; + DBG("Send cmd '%s'", str); + char *ret = NULL; + pthread_mutex_lock(&mutex); + if(nonblk_write(str, strlen(str))){ + usleep(USLEEP_BEFORE_READ); + ret = nonblk_read(ans); + } + pthread_mutex_unlock(&mutex); + int l = strlen(prefix); + DBG("compare %s with %s", ret ? (ret) : "null", prefix); + if(!ret || strncmp(ans, prefix, l)) return NULL; // no answer or not found + DBG("found"); + return ans + l; } diff --git a/Daemons/domedaemon_baader/term.h b/Daemons/domedaemon_baader/term.h index f59c7b0..196f24c 100644 --- a/Daemons/domedaemon_baader/term.h +++ b/Daemons/domedaemon_baader/term.h @@ -18,19 +18,16 @@ #pragma once -// text commands and answers -#define TXT_GETWARN "d#warning\n" -#define TXT_OPENDOME "d#opendom\n" -#define TXT_CLOSEDOME "d#closdom\n" -#define TXT_STOPDOME "d#stopdom\n" -#define TXT_GETSTAT "d#get_dom\n" -#define TXT_GETWEAT "d#ask_wea\n" -#define TXT_ANS_WEAT "d#wea" -#define TXT_ANS_STAT "d#pos" -#define TXT_ANS_ERR "d#erro" +#include -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); +// pause before reading answer: for stupid baader = 50ms +#define USLEEP_BEFORE_READ 50000 + +// length of answer (including terminating zero) +#define ANSLEN 128 + +int term_open(char *path, int speed, double usec); +void term_close(); +char *term_read(char ans[ANSLEN]); +char *term_write(const char *str, char ans[ANSLEN]); +char *term_cmdwans(const char *str, const char *prefix, char ans[ANSLEN]); diff --git a/Daemons/teldaemon_astrosib/commands.h b/Daemons/teldaemon_astrosib/commands.h new file mode 100644 index 0000000..8c24728 --- /dev/null +++ b/Daemons/teldaemon_astrosib/commands.h @@ -0,0 +1,42 @@ +/* + * 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 "OK\rCOOLERPWM?" +#define TXT_ANS_COOLERT "OK\rCOOLERT?" +#define TXT_ANS_STATUS "OK\rSHUTTERS?" +#define TXT_ANS_FOCPOS "OK\rFOCUSERPOS?" + +#define FOC_MINPOS 0 +#define FOC_MAXPOS 65000 diff --git a/Daemons/teldaemon_astrosib/main.c b/Daemons/teldaemon_astrosib/main.c index 3305ce7..cc3c7be 100644 --- a/Daemons/teldaemon_astrosib/main.c +++ b/Daemons/teldaemon_astrosib/main.c @@ -41,7 +41,8 @@ typedef struct{ static parameters G = { .maxclients = 2, - .serspeed = 9600 + .serspeed = 9600, + .sertmout = 1000., }; static sl_option_t cmdlnopts[] = { @@ -77,7 +78,7 @@ void signals(int sig){ DBG("Stop server"); stopserver(); DBG("Close terminal"); - close_term(); + term_close(); DBG("Exit"); exit(sig); } @@ -94,7 +95,7 @@ int main(int argc, char **argv){ 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)){ + if(!term_open(G.termpath, G.serspeed, G.sertmout)){ LOGERR("Can't open %s", G.termpath); ERRX("Fatal error"); } diff --git a/Daemons/teldaemon_astrosib/socket.c b/Daemons/teldaemon_astrosib/socket.c index 70b9dec..79f34ee 100644 --- a/Daemons/teldaemon_astrosib/socket.c +++ b/Daemons/teldaemon_astrosib/socket.c @@ -21,6 +21,7 @@ #include #include +#include "commands.h" #include "socket.h" #include "term.h" @@ -37,6 +38,7 @@ typedef enum{ typedef struct{ tel_commands_t cmd; // deferred command + tel_commands_t errcmd; // command ended with error int focuserpos; // focuser position char *status; // device status int statlen; // size of `status` buffer @@ -54,7 +56,6 @@ static tel_t telescope = {0}; 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}; @@ -62,7 +63,6 @@ static sl_sock_int_t Relmove = {0}; void stopserver(){ if(locksock) sl_sock_delete(&locksock); - if(rb) sl_RB_delete(&rb); } // send "measuret=..." @@ -106,20 +106,37 @@ static sl_sock_hresult_e fstoph(_U_ sl_sock_t *client, _U_ sl_sock_hitem_t *item 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; + double t = NAN, mirt, ambt; + tel_commands_t lastecmd; 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; } + mirt = telescope.mirrortemp; + ambt = telescope.ambienttemp; + lastecmd = telescope.errcmd; + pthread_mutex_unlock(&telescope.mutex); sl_sock_sendstrmessage(client, buf); if(!isnan(t)) sendtmeasured(client, t); - snprintf(buf, 255, "mirrortemp=%.1f\n", telescope.mirrortemp); + snprintf(buf, 255, "mirrortemp=%.1f\n", mirt); sl_sock_sendstrmessage(client, buf); - snprintf(buf, 255, "ambienttemp=%.1f\n", telescope.ambienttemp); + snprintf(buf, 255, "ambienttemp=%.1f\n", ambt); sl_sock_sendstrmessage(client, buf); - pthread_mutex_unlock(&telescope.mutex); + if(lastecmd != CMD_NONE){ + const char *tcmd; + switch(lastecmd){ + case CMD_OPEN: tcmd = "open"; break; + case CMD_CLOSE: tcmd = "close"; break; + case CMD_FOCSTOP: tcmd = "focstop"; break; + case CMD_COOLERON: case CMD_COOLEROFF: tcmd = "cooler"; break; + default: tcmd = "unknown"; + } + snprintf(buf, 255, "errored_command=%s\n", tcmd); + sl_sock_sendstrmessage(client, buf); + + } if(ForbidObservations) sl_sock_sendstrmessage(client, "FORBIDDEN\n"); return RESULT_SILENCE; } @@ -192,13 +209,6 @@ static sl_sock_hresult_e defhandler(struct sl_sock *s, const char *str){ } 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}, @@ -211,74 +221,54 @@ static sl_sock_hitem_t handlers[] = { {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(); + char ans[ANSLEN]; + char *data; + int I; + + if(!(data = term_cmdwans(TXT_FOCGET, TXT_ANS_FOCPOS, ans))) return FALSE; + DBG("\nGot focuser position"); + if(sscanf(data, "%d", &I) == 1){ + pthread_mutex_lock(&telescope.mutex); + telescope.focuserpos = I; + pthread_mutex_unlock(&telescope.mutex); + } + + if(!(data = term_cmdwans(TXT_STATUS, TXT_ANS_STATUS, ans))) return FALSE; + DBG("\nGot status"); + pthread_mutex_lock(&telescope.mutex); + telescope.stattime = sl_dtime(); + if(strncmp(data, "0,0,0,0,0", 9) == 0) snprintf(telescope.status, telescope.statlen, "closed"); + else if(strncmp(data, "1,1,1,1,1", 9) == 0) snprintf(telescope.status, telescope.statlen, "opened"); + else snprintf(telescope.status, telescope.statlen, "intermediate"); + pthread_mutex_unlock(&telescope.mutex); + + if(!(data = term_cmdwans(TXT_COOLERT, TXT_ANS_COOLERT, ans))) return FALSE; + DBG("\nGot weather ans"); + float m, a; + if(sscanf(data, "%f,%f", &m, &a) == 2){ + pthread_mutex_lock(&telescope.mutex); + telescope.ambienttemp = a; + telescope.mirrortemp = m; + pthread_mutex_unlock(&telescope.mutex); + } + + if(!(data = term_cmdwans(TXT_COOLERSTAT, TXT_ANS_COOLERSTAT, ans))) return FALSE; + DBG("\nGot cooler status"); + if(sscanf(data, "%d", &I) == 1){ + pthread_mutex_lock(&telescope.mutex); + telescope.cooler = I; + pthread_mutex_unlock(&telescope.mutex); } return TRUE; } void runserver(int isunix, const char *node, int maxclients){ + char ans[ANSLEN]; int forbidden = 0; if(locksock) sl_sock_delete(&locksock); - if(rb) sl_RB_delete(&rb); - rb = sl_RB_new(BUFSIZ); - telescope.cmd = CMD_NONE; + telescope.errcmd = telescope.cmd = CMD_NONE; FREE(telescope.status); telescope.statlen = STATBUF_SZ; telescope.status = MALLOC(char, STATBUF_SZ); @@ -294,7 +284,7 @@ void runserver(int isunix, const char *node, int maxclients){ while(locksock && locksock->connected){ if(ForbidObservations){ if(!forbidden){ - if(0 == write_cmd(TXT_CLOSE)) forbidden = 1; + if(term_cmdwans(TXT_CLOSE, TXT_ANS_OK, ans)) forbidden = 1; pthread_mutex_lock(&telescope.mutex); telescope.cmd = CMD_NONE; pthread_mutex_unlock(&telescope.mutex); @@ -310,33 +300,38 @@ void runserver(int isunix, const char *node, int maxclients){ if(tnow - tgot > T_INTERVAL){ if(poll_device()) tgot = tnow; } + if(ForbidObservations) continue; pthread_mutex_lock(&telescope.mutex); - if(telescope.cmd != CMD_NONE){ - switch(telescope.cmd){ + tel_commands_t tcmd = telescope.cmd; + pthread_mutex_unlock(&telescope.mutex); + if(tcmd != CMD_NONE){ + switch(tcmd){ case CMD_OPEN: DBG("received command: open"); - if(0 == write_cmd(TXT_OPEN)) telescope.cmd = CMD_NONE; + if(term_cmdwans(TXT_OPEN, TXT_ANS_OK, ans)) tcmd = CMD_NONE; break; case CMD_CLOSE: DBG("received command: close"); - if(0 == write_cmd(TXT_CLOSE)) telescope.cmd = CMD_NONE; + if(term_cmdwans(TXT_CLOSE, TXT_ANS_OK, ans)) tcmd = CMD_NONE; break; case CMD_FOCSTOP: DBG("received command: stop focus"); - if(0 == write_cmd(TXT_FOCSTOP)) telescope.cmd = CMD_NONE; + term_write(TXT_FOCSTOP, ans); tcmd = CMD_NONE; // erroreous thing: no answer for this command break; case CMD_COOLEROFF: DBG("received command: cooler off"); - if(0 == write_cmd(TXT_COOLEROFF)) telescope.cmd = CMD_NONE; + if(term_cmdwans(TXT_COOLEROFF, TXT_ANS_OK, ans)) tcmd = CMD_NONE; break; case CMD_COOLERON: DBG("received command: cooler on"); - if(0 == write_cmd(TXT_COOLERON)) telescope.cmd = CMD_NONE; + if(term_cmdwans(TXT_COOLERON, TXT_ANS_OK, ans)) tcmd = CMD_NONE; break; default: DBG("WTF?"); } } + pthread_mutex_lock(&telescope.mutex); + telescope.cmd = telescope.errcmd = tcmd; pthread_mutex_unlock(&telescope.mutex); // check abs/rel move char buf[256]; @@ -345,7 +340,7 @@ void runserver(int isunix, const char *node, int maxclients){ 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)){ + if(term_cmdwans(buf, TXT_ANS_OK, ans)){ DBG("STARTED absmove"); Absmove.timestamp = 0.; } @@ -360,13 +355,12 @@ void runserver(int isunix, const char *node, int maxclients){ 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)){ + if(term_cmdwans(buf, TXT_ANS_OK, ans)){ DBG("STARTED relmove"); Relmove.timestamp = 0.; } } } - serial_parser(); } stopserver(); } diff --git a/Daemons/teldaemon_astrosib/socket.h b/Daemons/teldaemon_astrosib/socket.h index a045d76..7e8e403 100644 --- a/Daemons/teldaemon_astrosib/socket.h +++ b/Daemons/teldaemon_astrosib/socket.h @@ -23,7 +23,7 @@ // size of weather/status buffers #define STATBUF_SZ 256 // dome polling interval (clear watchdog & get status) -#define T_INTERVAL (1.0) +#define T_INTERVAL (2.0) void runserver(int isunix, const char *node, int maxclients); void stopserver(); diff --git a/Daemons/teldaemon_astrosib/teldaemon.files b/Daemons/teldaemon_astrosib/teldaemon.files index 9b3fd75..27793b3 100644 --- a/Daemons/teldaemon_astrosib/teldaemon.files +++ b/Daemons/teldaemon_astrosib/teldaemon.files @@ -1,3 +1,4 @@ +commands.h main.c socket.c socket.h diff --git a/Daemons/teldaemon_astrosib/term.c b/Daemons/teldaemon_astrosib/term.c index 21875b5..ab1e9e3 100644 --- a/Daemons/teldaemon_astrosib/term.c +++ b/Daemons/teldaemon_astrosib/term.c @@ -1,5 +1,5 @@ /* - * This file is part of the teldaemon project. + * 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 @@ -16,15 +16,17 @@ * along with this program. If not, see . */ +#include #include #include #include "term.h" static sl_tty_t *dev = NULL; // shoul be global to restore if die +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // close serial device -void close_term(){ +void term_close(){ if(dev) sl_tty_close(&dev); } @@ -35,19 +37,19 @@ void close_term(){ * @param usec - timeout (us), if < 1e-6 - leave default * @return FALSE if failed */ -int open_term(char *path, int speed, double usec){ +int term_open(char *path, int speed, double usec){ + pthread_mutex_lock(&mutex); if(dev) sl_tty_close(&dev); LOGMSG("Try to open serial %s at speed %d", path, speed); - DBG("Open serial"); + DBG("Init serial"); dev = sl_tty_new(path, speed, 4096); + DBG("Open serial"); if(dev) dev = sl_tty_open(dev, 1); - if(!dev){ - LOGERR("Can't open %s with speed %d. Exit.", path, speed); - return FALSE; - } + if(!dev) goto rtn; + DBG("Opened"); if(usec >= 1e-6){ DBG("set timeout to %gus", usec); - if(!sl_tty_tmout(usec)){ + if(sl_tty_tmout(usec) < 0){ LOGWARN("Can't set timeout to %gus", usec); WARNX("Can't set timeout to %gus", usec); } @@ -56,60 +58,129 @@ int open_term(char *path, int speed, double usec){ 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); } + pthread_mutex_unlock(&mutex); +rtn: if(dev) return TRUE; + LOGERR("Can't open %s with speed %d. Exit.", path, speed); 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 + * @brief nonblk_read - internal read + * @param ans (o) - buffer for data + * @return NULL if failed or `ans` */ -int read_term(char *buf, int length){ +static char *nonblk_read(char ans[ANSLEN]){ static char *bufptr = NULL; // last message, if it was longer than `length` static int lastL = 0; - if(!dev || !buf || length < 1) return 0; + FNAME(); + if(!dev) return NULL; + int length = ANSLEN - 1; + DBG("ok"); + if(!ans){ // clear + DBG("clr"); + while(sl_tty_read(dev) > 0); + bufptr = NULL; + lastL = 0; + return NULL; + } if(bufptr && lastL){ if(length > lastL) length = lastL; - DBG("got %d bytes from old record", length); - memcpy(buf, bufptr, length); + memcpy(ans, bufptr, length); if((lastL -= length) < 1){ lastL = 0; bufptr = NULL; } - return length; + ans[length] = 0; + DBG("got %d bytes from old record: '%s'", length, ans); + return ans; } - 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; + int totlen = 0; + while(length > 0 && sl_tty_read(dev) > 0){ + int lmax = length; + if(lmax >= (int)dev->buflen){ + DBG("Full buffer can be copied"); + lmax = (int)dev->buflen; + }else{ // store part of data in buffer + lastL = dev->buflen - lmax; + DBG("Store %d bytes for next read", lastL); + bufptr = dev->buf + lmax; + } + memcpy(ans + totlen, dev->buf, lmax); + length -= lmax; + totlen += lmax; + ans[totlen] = 0; } - memcpy(buf, dev->buf, length); - return length; + DBG("totlen: %d", totlen); + if(totlen == 0) return NULL; + DBG("copied %d: '%s'", totlen, ans); + return ans; } /** - * @brief write_term - write data - * @param buf - buffer + * @brief nonblk_write - internal write + * @param buf (i) - data to write * @param length - its length - * @return 0 if OK and 1 if failed + * @return 0 if failed or `length` */ -int write_term(const char *buf, int length){ - if(!dev || !buf || length < 1) return 0; - return sl_tty_write(dev->comfd, buf, length); +static int nonblk_write(const char *buf, int length){ + if(!buf || length < 1) return 0; + if(0 == sl_tty_write(dev->comfd, buf, length)) return length; + return 0; } -// 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)); +/** + * @brief term_read - read string from terminal + * @param ans (o) - buffer or NULL to clear last data + * @return NULL or pointer to zero-terminated `ans` + */ +char *term_read(char ans[ANSLEN]){ + pthread_mutex_lock(&mutex); + char *ret = nonblk_read(ans); + pthread_mutex_unlock(&mutex); + DBG("read: '%s'", ret); + return ret; +} + +/** + * @brief term_write - write data and got answer + * @param str (i) - zero-terminated string to write + * @param ans (o) - NULL to clear incoming data or buffer to read + * @return + */ +char *term_write(const char *str, char ans[ANSLEN]){ + if(!str || !*str) return NULL; + DBG("Send cmd '%s'", str); + char *ret = NULL; + pthread_mutex_lock(&mutex); + if(nonblk_write(str, strlen(str))){ + usleep(USLEEP_BEFORE_READ); + ret = nonblk_read(ans); + } + pthread_mutex_unlock(&mutex); + DBG("read: '%s'", ret); + return ret; +} + +/** + * @brief term_cmdwans - write command and get answer + * @param str (i) - command to write + * @param prefix (i) - prefix of answer (string should begin with this text) + * @param ans (o) - buffer for answer (non-NULL!!!) + * @return pointer to data just after `prefix` in `ans` or NULL if no `prefix` found + */ +char *term_cmdwans(const char *str, const char *prefix, char ans[ANSLEN]){ + if(!str || !prefix || !ans) return NULL; + DBG("Send cmd '%s'", str); + char *ret = NULL; + pthread_mutex_lock(&mutex); + if(nonblk_write(str, strlen(str))){ + usleep(USLEEP_BEFORE_READ); + ret = nonblk_read(ans); + } + pthread_mutex_unlock(&mutex); + int l = strlen(prefix); + DBG("compare %s with %s", ret ? (ret) : "null", prefix); + if(!ret || strncmp(ans, prefix, l)) return NULL; // no answer or not found + DBG("found"); + return ans + l; } diff --git a/Daemons/teldaemon_astrosib/term.h b/Daemons/teldaemon_astrosib/term.h index 80f610a..196f24c 100644 --- a/Daemons/teldaemon_astrosib/term.h +++ b/Daemons/teldaemon_astrosib/term.h @@ -1,5 +1,5 @@ /* - * This file is part of the teldaemon project. + * 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 @@ -18,31 +18,16 @@ #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?" +#include -#define FOC_MINPOS 0 -#define FOC_MAXPOS 65000 +// pause before reading answer: for stupid baader = 50ms +#define USLEEP_BEFORE_READ 50000 -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); +// length of answer (including terminating zero) +#define ANSLEN 128 + +int term_open(char *path, int speed, double usec); +void term_close(); +char *term_read(char ans[ANSLEN]); +char *term_write(const char *str, char ans[ANSLEN]); +char *term_cmdwans(const char *str, const char *prefix, char ans[ANSLEN]);