pre-alpha version of new dome daemon

This commit is contained in:
Edward Emelianov 2025-05-29 17:32:00 +03:00
parent e2c465dd30
commit ef2c86675e
34 changed files with 794 additions and 62 deletions

View File

@ -0,0 +1,12 @@
Astrosib all-sky dome control network daemon
==================
Open a socket at given port (default: 55555), works with http & direct requests.
**Protocol**
Send requests over socket (by curl or something else) or http requests (in browser).
* *open* - open dome;
* *close* - close dome;
* *status* - dome state (return "opened", "closed" or "intermediate");

View File

@ -0,0 +1,89 @@
/* geany_encoding=koi8-r
* main.c
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include "usefull_macros.h"
#include <signal.h>
#include <sys/wait.h> // wait
#include <sys/prctl.h> //prctl
#include <time.h>
#include "cmdlnopts.h"
#include "socket.h"
// dome @ /dev/ttyS2
glob_pars *GP;
static pid_t childpid = 0;
void signals(int signo){
if(childpid){ // parent process
restore_tty();
putlog("exit with status %d", signo);
}
exit(signo);
}
int main(int argc, char **argv){
initial_setup();
signal(SIGTERM, signals); // kill (-15) - quit
signal(SIGHUP, SIG_IGN); // hup - ignore
signal(SIGINT, signals); // ctrl+C - quit
signal(SIGQUIT, signals); // ctrl+\ - quit
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
GP = parse_args(argc, argv);
if(GP->terminal){
if(!GP->device) ERRX(_("Point serial device name"));
try_connect(GP->device);
run_terminal();
signals(0); // never reached!
}
if(GP->logfile)
openlogfile(GP->logfile);
#ifndef EBUG
if(daemon(1, 0)){
ERR("daemon()");
}
time_t lastd = 0;
while(1){ // guard for dead processes
childpid = fork();
if(childpid){
DBG("Created child with PID %d\n", childpid);
wait(NULL);
time_t t = time(NULL);
if(t - lastd > 600) // at least 10 minutes of work
putlog("child %d died\n", childpid);
lastd = t;
WARNX("Child %d died\n", childpid);
sleep(1);
}else{
prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies
break; // go out to normal functional
}
}
#endif
if(GP->device) try_connect(GP->device);
if(!poll_device()){
ERRX(_("No answer from device"));
}
putlog("Child %d connected to %s", getpid(), GP->device);
daemonize(GP->port);
signals(0); // newer reached
return 0;
}

View File

@ -0,0 +1,64 @@
cmake_minimum_required(VERSION 3.30)
set(PROJ domedaemon)
project(${PROJ})
set(MINOR_VERSION "0")
set(MID_VERSION "1")
set(MAJOR_VERSION "0")
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
enable_language(C)
message("VER: ${VERSION}")
# options
option(DEBUG "Compile in debug mode" OFF)
# default flags
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wextra -std=gnu99")
if(DEBUG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Og -g3 -ggdb -fno-builtin-strlen -Werror")
add_definitions(-DEBUG)
set(CMAKE_BUILD_TYPE DEBUG)
set(CMAKE_VERBOSE_MAKEFILE "ON")
else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -march=native -fdata-sections -ffunction-sections")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
set(CMAKE_BUILD_TYPE RELEASE)
endif()
message("Build type: ${CMAKE_BUILD_TYPE}")
set(CMAKE_COLOR_MAKEFILE ON)
# here is one of two variants: all .c in directory or .c files in list
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
###### pkgconfig ######
find_package(PkgConfig REQUIRED)
pkg_check_modules(MODULES REQUIRED usefull_macros>=0.3.2)
# change wrong behaviour with install prefix
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND CMAKE_INSTALL_PREFIX MATCHES "/usr/local")
else()
message("Change default install path to /usr/local")
set(CMAKE_INSTALL_PREFIX "/usr/local")
endif()
message("Install dir prefix: ${CMAKE_INSTALL_PREFIX}")
# executable file
add_executable(${PROJ} ${SOURCES})
# -I
target_include_directories(${PROJ} PUBLIC ${MODULES_INCLUDE_DIRS})
# -L
target_link_directories(${PROJ} PUBLIC ${MODULES_LIBRARY_DIRS})
# -l
target_link_libraries(${PROJ} ${MODULES_LIBRARIES})
# -D
add_definitions(
-DPACKAGE_VERSION=\"${VERSION}\" -DMINOR_VERSION=\"${MINOR_VERSION}\"
-DMID_VERSION=\"${MID_VERSION}\" -DMAJOR_VERSION=\"${MAJOR_VESION}\"
)
# Installation of the program
INSTALL(TARGETS ${PROJ} DESTINATION "bin")

View File

@ -1,12 +1,5 @@
Astrosib all-sky dome control network daemon New version of "Astrosib" dome daemon.
==================
Open a socket at given port (default: 55555), works with http & direct requests. Based on snippets_library ver. >= 0.3.2
Allow more functionality than old one.
**Protocol**
Send requests over socket (by curl or something else) or http requests (in browser).
* *open* - open dome;
* *close* - close dome;
* *status* - dome state (return "opened", "closed" or "intermediate");

View File

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

View File

@ -0,0 +1,257 @@
/*
* This file is part of the domedaemon-astrosib project.
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include "astrosib_proto.h"
#include "dome.h"
// number of relay turning on/off motors power
#define MOTRELAY_NO 1
// state of relay to turn power on/off
#define MOTRELAY_ON 1
#define MOTRELAY_OFF 0
// time interval for requiring current dome status in idle state (e.g. trigger watchdog)
#define STATUSREQ_IDLE 10.
// update interval in moving state
#define STATUSREQ_MOVE 0.5
// dome status by polling (each STATUSREQ_IDLE seconds)
static dome_status_t dome_status = {0};
// finite state machine state
static dome_state_t state = DOME_S_IDLE;
// last time dome_status was updated
static double last_status_time = 0.;
// current update interval
static double status_req_interval = STATUSREQ_MOVE;
// serial device
static sl_tty_t *serialdev = NULL;
static pthread_mutex_t serialmutex = PTHREAD_MUTEX_INITIALIZER;
void dome_serialdev(sl_tty_t *serial){
serialdev = serial;
}
/**
* @brief serial_write - write buffer without "\r" on end to port
* @param cmd - data to send
* @param answer - buffer for answer (or NULL if not need)
* @param anslen - length of `answer`
* @return error code
*/
static int serial_write(const char *cmd, char *answer, int anslen){
if(!serialdev || !cmd || !answer || anslen < 2) return FALSE;
static char *buf = NULL;
static size_t buflen = 0;
DBG("Write %s", cmd);
size_t cmdlen = strlen(cmd);
if(buflen < cmdlen + 2){
buflen = (cmdlen + 4096) / 4096;
buflen *= 4096;
if(!(buf = realloc(buf, buflen))){
LOGERR("serial_write(): realloc() failed!");
ERRX("serial_write(): realloc() failed!");
}
}
size_t _2write = snprintf(buf, buflen-1, "%s\r", cmd);
DBG("try to send %zd bytes", _2write);
if(sl_tty_write(serialdev->comfd, buf, _2write)) return FALSE;
int got = 0, totlen = 0;
--anslen; // for /0
do{
got = sl_tty_read(serialdev);
if(got > 0){
if(got > anslen) return FALSE; // buffer overflow
memcpy(answer, serialdev->buf, got);
totlen += got;
answer += got;
anslen -= got;
answer[0] = 0;
}
DBG("got = %d", got);
}while(got > 0 && anslen);
if(got < 0){
LOGERR("serial_write(): serial device disconnected!");
ERRX("serial_write(): serial device disconnected!");
}
if(totlen < 1) return FALSE; // no answer received
if(answer[-1] == '\r') answer[-1] = 0; // remove trailing trash
return TRUE;
}
// return TRUE if can parse status
static int parsestatus(const char *buf){
if(!buf) return FALSE;
DBG("buf=%s", buf);
int n = sscanf(buf, ASIB_CMD_STATUS "%d,%d,%d,%d,%f,%f,%f,%f,%f,%f,%d,%d,%d,%d,%d",
&dome_status.coverstate[0], &dome_status.coverstate[1],
&dome_status.encoder[0], &dome_status.encoder[1],
&dome_status.Tin, &dome_status.Tout,
&dome_status.Imot[0], &dome_status.Imot[1], &dome_status.Imot[2], &dome_status.Imot[3],
&dome_status.relay[0], &dome_status.relay[1], &dome_status.relay[2],
&dome_status.rainArmed, &dome_status.israin);
DBG("n=%d", n);
if(n != 15){
WARNX("Something wrong with STATUS answer");
LOGWARN("Something wrong with STATUS answer");
LOGWARNADD("%s", buf);
return FALSE;
}
return TRUE;
}
// check status; return FALSE if failed
static int check_status(){
char buf[BUFSIZ];
// clear input buffers
int got = sl_tty_read(serialdev);
if(got > 0) printf("Got from serial %zd bytes of trash: `%s`\n", serialdev->buflen, serialdev->buf);
else if(got < 0){
LOGERR("Serial device disconnected?");
ERRX("Serial device disconnected?");
}
DBG("Require status");
if(!serial_write(ASIB_CMD_STATUS, buf, BUFSIZ)) return FALSE;
int ret = FALSE;
if(parsestatus(buf)){
last_status_time = sl_dtime();
ret = TRUE;
}
return ret;
}
// run naked command or command with parameters
static int runcmd(const char *cmd, const char *par){
char buf[128];
if(!cmd) return FALSE;
DBG("Send command %s with par %s", cmd, par);
if(!par) snprintf(buf, 127, "%s", cmd);
else snprintf(buf, 127, "%s%s", cmd, par);
if(!serial_write(buf, buf, 128)) return FALSE;
if(strncmp(buf, "OK", 2)) return FALSE;
return TRUE;
}
// check current state and turn on/off relay if need
static void chkrelay(){
if(state != DOME_S_MOVING) return;
if(dome_status.coverstate[0] == COVER_INTERMEDIATE ||
dome_status.coverstate[1] == COVER_INTERMEDIATE) return; // still moving
// OK, we are on place - turn off motors' power
char buf[128];
snprintf(buf, 127, "%s%d,%d", ASIB_CMD_RELAY, MOTRELAY_NO, MOTRELAY_OFF);
if(serial_write(buf, buf, 128) && check_status()){
DBG("Check are motors really off");
if(dome_status.relay[MOTRELAY_NO-1] == MOTRELAY_OFF){
DBG("OK state->IDLE");
state = DOME_S_IDLE;
}
}
}
// turn ON motors' relay
static int motors_on(){
char buf[128];
snprintf(buf, 127, "%s%d,%d", ASIB_CMD_RELAY, MOTRELAY_NO, MOTRELAY_ON);
if(serial_write(buf, buf, 128) && check_status()){
DBG("Check are motors really on");
if(dome_status.relay[MOTRELAY_NO-1] == MOTRELAY_ON){
DBG("OK state->MOVING");
state = DOME_S_MOVING;
return TRUE;
}
}
return FALSE;
}
// just get current status
double get_dome_status(dome_status_t *s){
if(s){
pthread_mutex_lock(&serialmutex);
*s = dome_status;
pthread_mutex_unlock(&serialmutex);
}
return last_status_time;
}
dome_state_t get_dome_state(){return state;}
dome_state_t dome_poll(dome_cmd_t cmd, int par){
char buf[128];
int st = DOME_S_ERROR;
double curtime = sl_dtime();
//DBG("curtime-lasttime=%g", curtime - last_status_time);
// simple polling and there's a lot of time until next serial device poll
if(cmd == DOME_POLL && curtime - last_status_time < status_req_interval)
return state;
pthread_mutex_lock(&serialmutex);
// check if we need to turn ON motors' relay
switch(cmd){
case DOME_OPEN:
case DOME_CLOSE:
case DOME_OPEN_ONE:
case DOME_CLOSE_ONE:
if(!motors_on()) goto ret;
break;
default:
break;
}
switch(cmd){
case DOME_STOP:
if(!runcmd(ASIB_CMD_STOP, NULL)) goto ret;
break;
case DOME_OPEN:
if(!runcmd(ASIB_CMD_OPEN, NULL)) goto ret;
break;
case DOME_CLOSE:
if(!runcmd(ASIB_CMD_CLOSE, NULL)) goto ret;
break;
case DOME_OPEN_ONE:
if(par < 1 || par > 2) goto ret;
snprintf(buf, 127, "%d 90", par);
if(!runcmd(ASIB_CMD_MOVEONE, buf)) goto ret;
break;
case DOME_CLOSE_ONE:
if(par < 1 || par > 2) goto ret;
snprintf(buf, 127, "%d 0", par);
if(!runcmd(ASIB_CMD_MOVEONE, buf)) goto ret;
break;
case DOME_RELAY_ON:
if(par < 1 || par > 3) goto ret;
snprintf(buf, 127, "%d,1", par);
if(!runcmd(ASIB_CMD_RELAY, buf)) goto ret;
break;
case DOME_RELAY_OFF:
if(par < 1 || par > 3) goto ret;
snprintf(buf, 127, "%d,0", par);
if(!runcmd(ASIB_CMD_RELAY, buf)) goto ret;
break;
default:
break;
}
ret:
if(check_status()){
chkrelay();
st = state;
}
if(state == DOME_S_IDLE) status_req_interval = STATUSREQ_IDLE;
else status_req_interval = STATUSREQ_MOVE;
pthread_mutex_unlock(&serialmutex);
return st;
}

View File

@ -0,0 +1,65 @@
/*
* This file is part of the domedaemon-astrosib project.
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <usefull_macros.h>
#define NRELAY_MIN 1
#define NRELAY_MAX 3
// dome finite state machine state
typedef enum{
DOME_S_IDLE, // idle, motors disabled
DOME_S_MOVING, // moving, motors enabled
DOME_S_ERROR // some kind of error
} dome_state_t;
// commands through dome_poll interface
typedef enum{
DOME_POLL, // just poll state maching
DOME_STOP, // stop any moving
DOME_OPEN, // fully open dome
DOME_CLOSE, // fully close dome
DOME_OPEN_ONE, // open only one part # `par` (1-2)
DOME_CLOSE_ONE, // close only one part # `par`
DOME_RELAY_ON, // turn on relay # `par` (1-3)
DOME_RELAY_OFF, // turn off relay # `par`
} dome_cmd_t;
// cover states
enum{
COVER_INTERMEDIATE = 1,
COVER_OPENED = 2,
COVER_CLOSED = 3
};
typedef struct{
int coverstate[2]; // north/south covers state (3 - closed, 2 - opened, 1 - intermediate)
int encoder[2]; // encoders values
float Tin; // temperatures (unavailable)
float Tout;
float Imot[4]; // motors' currents
int relay[3]; // relays' state
int rainArmed; // rain sensor closes the dome
int israin; // arm sensor signal
} dome_status_t;
double get_dome_status(dome_status_t *s);
dome_state_t get_dome_state();
dome_state_t dome_poll(dome_cmd_t cmd, int par);
void dome_serialdev(sl_tty_t *serial);

View File

@ -0,0 +1 @@
-std=c17

View File

@ -0,0 +1,2 @@
// Add predefined macros for your project here. For example:
// #define THE_ANSWER 42

View File

@ -0,0 +1 @@
[General]

View File

@ -0,0 +1 @@
-std=c++17

View File

@ -0,0 +1,6 @@
astrosib_proto.h
dome.c
dome.h
main.c
server.c
server.h

View File

@ -1,11 +1,10 @@
/* geany_encoding=koi8-r /*
* main.c * This file is part of the Snippets project.
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
* *
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com> * This program is free software: you can redistribute it and/or modify
*
* 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
@ -14,60 +13,84 @@
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program. If not, see <http://www.gnu.org/licenses/>.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/ */
#include "usefull_macros.h"
#include <signal.h> #include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h> // wait #include <sys/wait.h> // wait
#include <sys/prctl.h> //prctl #include <sys/prctl.h> //prctl
#include <time.h> #include <usefull_macros.h>
#include "cmdlnopts.h"
#include "socket.h"
// dome @ /dev/ttyS2 #include "server.h"
glob_pars *GP; // TCP socket port
static pid_t childpid = 0; #define DEFAULT_PORT "55555"
// baudrate - 9600
#define DEFAULT_SERSPEED 9600
// serial polling timeout - 100ms
#define DEFAULT_SERTMOUT 100000
void signals(int signo){ typedef struct{
if(childpid){ // parent process char *device; // serial device name
restore_tty(); char *node; // port to connect or UNIX socket name
putlog("exit with status %d", signo); char *logfile; // logfile name
} int isunix; // open UNIX-socket instead of TCP
exit(signo); int verbose; // verbose level
} parameters;
static parameters G = {
.node = DEFAULT_PORT,
};
static int help;
static sl_option_t cmdlnopts[] = {
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), "show this help"},
{"device", NEED_ARG, NULL, 'd', arg_string, APTR(&G.device), "serial device name"},
{"node", NEED_ARG, NULL, 'n', arg_string, APTR(&G.node), "UNIX socket name or network port to connect (default: " DEFAULT_PORT ")"},
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), "save logs to file"},
{"unix", NO_ARGS, NULL, 'u', arg_int, APTR(&G.isunix), "open UNIX-socket instead of TCP"},
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verbose), "logging verbose level (each -v adds one)"},
end_option
};
void signals(int sig){
if(sig){
signal(sig, SIG_IGN);
DBG("Get signal %d, quit.\n", sig);
LOGERR("Exit with status %d", sig);
}else LOGERR("Exit");
exit(sig);
} }
int main(int argc, char **argv){ int main(int argc, char **argv){
initial_setup(); sl_init();
signal(SIGTERM, signals); // kill (-15) - quit sl_parseargs(&argc, &argv, cmdlnopts);
signal(SIGHUP, SIG_IGN); // hup - ignore if(help) sl_showhelp(-1, cmdlnopts);
signal(SIGINT, signals); // ctrl+C - quit if(!G.node) ERRX("Point node");
signal(SIGQUIT, signals); // ctrl+\ - quit if(!G.device) ERRX("Point path to serial device");
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z sl_loglevel_e lvl = G.verbose + LOGLEVEL_ERR;
GP = parse_args(argc, argv); if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1;
if(GP->terminal){ if(G.logfile) OPENLOG(G.logfile, lvl, 1);
if(!GP->device) ERRX(_("Point serial device name")); LOGMSG("Started");
try_connect(GP->device); signal(SIGTERM, signals);
run_terminal(); signal(SIGINT, signals);
signals(0); // never reached! signal(SIGQUIT, signals);
} signal(SIGTSTP, SIG_IGN);
if(GP->logfile) signal(SIGHUP, signals);
openlogfile(GP->logfile);
#ifndef EBUG #ifndef EBUG
if(daemon(1, 0)){
ERR("daemon()");
}
time_t lastd = 0; time_t lastd = 0;
while(1){ // guard for dead processes while(1){ // guard for dead processes
childpid = fork(); pid_t childpid = fork();
if(childpid){ if(childpid){
DBG("Created child with PID %d\n", childpid); DBG("Created child with PID %d\n", childpid);
LOGMSG("Created child with PID %d\n", childpid);
wait(NULL); wait(NULL);
time_t t = time(NULL); time_t t = time(NULL);
if(t - lastd > 600) // at least 10 minutes of work if(t - lastd > 600){ // at least 10 minutes of work
putlog("child %d died\n", childpid); LOGERR("Child %d died\n", childpid);
}
lastd = t; lastd = t;
WARNX("Child %d died\n", childpid); WARNX("Child %d died\n", childpid);
sleep(1); sleep(1);
@ -77,13 +100,15 @@ int main(int argc, char **argv){
} }
} }
#endif #endif
sl_socktype_e type = (G.isunix) ? SOCKT_UNIX : SOCKT_NETLOCAL;
if(GP->device) try_connect(GP->device); sl_tty_t *serial = sl_tty_new(G.device, DEFAULT_SERSPEED, 4096);
if(!poll_device()){ if(serial) serial = sl_tty_open(serial, 1);
ERRX(_("No answer from device")); if(!serial){
LOGERR("Can't open serial device %s", G.device);
ERRX("Can't open serial device %s", G.device);
} }
putlog("Child %d connected to %s", getpid(), GP->device); sl_tty_tmout(DEFAULT_SERTMOUT);
daemonize(GP->port); server_run(type, G.node, serial);
signals(0); // newer reached LOGERR("Unreacheable code reached!");
return 0; return 0;
} }

View File

@ -0,0 +1,166 @@
/*
* This file is part of the Snippets project.
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <inttypes.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <usefull_macros.h>
#include "dome.h"
// max age time of last status - 30s
#define STATUS_MAX_AGE (30.)
// commands
#define CMD_UNIXT "unixt"
#define CMD_STATUS "status"
#define CMD_STATUST "statust"
#define CMD_RELAY "relay"
// main socket
static sl_sock_t *s = NULL;
/////// handlers
// unixt - send to ALL clients
static sl_sock_hresult_e dtimeh(sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){
char buf[32];
snprintf(buf, 31, "%s=%.2f\n", item->key, sl_dtime());
sl_sock_sendstrmessage(c, buf);
return RESULT_SILENCE;
}
// status - get current dome status in old format (first four numbers)
static sl_sock_hresult_e statush(sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){
char buf[128];
dome_status_t dome_status;
double lastt = get_dome_status(&dome_status);
if(sl_dtime() - lastt > STATUS_MAX_AGE) return RESULT_FAIL;
snprintf(buf, 127, "%s=%d,%d,%d,%d\n", item->key, dome_status.coverstate[0],
dome_status.coverstate[1], dome_status.encoder[0], dome_status.encoder[1]);
sl_sock_sendstrmessage(c, buf);
return RESULT_SILENCE;
}
static const char *textst(int coverstate){
switch(coverstate){
case COVER_INTERMEDIATE: return "intermediate";
case COVER_OPENED: return "opened";
case COVER_CLOSED: return "closed";
default: return "undefined";
}
return NULL;
}
// statust - text format status
static sl_sock_hresult_e statusth(sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){
char buf[BUFSIZ];
dome_status_t dome_status;
double lastt = get_dome_status(&dome_status);
if(sl_dtime() - lastt > STATUS_MAX_AGE) return RESULT_FAIL;
snprintf(buf, 127, "cover1=%s\ncover2=%s\nangle1=%d\nangle2=%d\nrelay1=%d\nrelay2=%d\nrelay3=%d\nreqtime=%.9f\n",
textst(dome_status.coverstate[0]), textst(dome_status.coverstate[1]),
dome_status.encoder[0], dome_status.encoder[1],
dome_status.relay[0], dome_status.relay[1], dome_status.relay[2],
lastt);
sl_sock_sendstrmessage(c, buf);
return RESULT_SILENCE;
}
// relay on/off
static sl_sock_hresult_e relays(int Nrelay, int Stat){
if(Nrelay < NRELAY_MIN|| Nrelay > NRELAY_MAX) return RESULT_BADKEY;
dome_cmd_t cmd = Stat ? DOME_RELAY_ON : DOME_RELAY_OFF;
if(DOME_S_ERROR == dome_poll(cmd, Nrelay)) return RESULT_FAIL;
return RESULT_OK;
}
static sl_sock_hresult_e relay(sl_sock_t *c, sl_sock_hitem_t *item, const char *req){
char buf[128];
int N = item->key[sizeof(CMD_RELAY) - 1] - '0';
if(!req || !*req){ // getter
dome_status_t dome_status;
double lastt = get_dome_status(&dome_status);
if(sl_dtime() - lastt > STATUS_MAX_AGE) return RESULT_FAIL;
snprintf(buf, 127, "%s=%d\n", item->key, dome_status.relay[N-1]);
sl_sock_sendstrmessage(c, buf);
return RESULT_SILENCE;
}
int Stat = *req - '0';
return relays(N, Stat);
}
// and all handlers collection
static sl_sock_hitem_t handlers[] = {
{dtimeh, CMD_UNIXT, "get server's UNIX time", NULL},
{statush, CMD_STATUS, "get dome's status in old format", NULL},
{statusth, CMD_STATUST, "get dome's status in full text format", NULL},
{relay, CMD_RELAY "1", "turn on/off (=1/0) relay 1", NULL},
{relay, CMD_RELAY "2", "turn on/off (=1/0) relay 2", NULL},
{relay, CMD_RELAY "3", "turn on/off (=1/0) relay 3", NULL},
{NULL, NULL, NULL, NULL}
};
// 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
static void 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);
}
// 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);
}
void server_run(sl_socktype_e type, const char *node, sl_tty_t *serial){
if(!node || !serial){
LOGERR("server_run(): wrong parameters");
ERRX("server_run(): wrong parameters");
}
dome_serialdev(serial);
sl_sock_changemaxclients(5);
sl_sock_maxclhandler(toomuch);
sl_sock_connhandler(connected);
sl_sock_dischandler(disconnected);
s = sl_sock_run_server(type, node, -1, handlers);
if(!s) ERRX("Can't create socket and/or run threads");
while(s && s->connected){
if(!s->rthread){
LOGERR("Server handlers thread is dead");
break;
}
// finite state machine polling
dome_poll(DOME_POLL, 0);
}
sl_sock_delete(&s);
ERRX("Server handlers thread is dead");
}

View File

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