mirror of
https://github.com/eddyem/small_tel.git
synced 2026-06-19 10:26:25 +03:00
weather check for astrosib dome daemon
This commit is contained in:
@@ -15,7 +15,7 @@ message("VER: ${VERSION}")
|
||||
option(DEBUG "Compile in debug mode" OFF)
|
||||
|
||||
# default flags
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wextra -std=gnu99")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wextra -std=c23")
|
||||
if(DEBUG)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Og -g3 -ggdb -fno-builtin-strlen -Werror")
|
||||
add_definitions(-DEBUG)
|
||||
@@ -53,11 +53,12 @@ target_include_directories(${PROJ} PUBLIC ${MODULES_INCLUDE_DIRS})
|
||||
# -L
|
||||
target_link_directories(${PROJ} PUBLIC ${MODULES_LIBRARY_DIRS})
|
||||
# -l
|
||||
target_link_libraries(${PROJ} ${MODULES_LIBRARIES})
|
||||
target_link_libraries(${PROJ} ${MODULES_LIBRARIES} -lweather)
|
||||
# -D
|
||||
add_definitions(
|
||||
-DPACKAGE_VERSION=\"${VERSION}\" -DMINOR_VERSION=\"${MINOR_VERSION}\"
|
||||
-DMID_VERSION=\"${MID_VERSION}\" -DMAJOR_VERSION=\"${MAJOR_VESION}\"
|
||||
-D_GNU_SOURCE
|
||||
)
|
||||
|
||||
# Installation of the program
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "astrosib_proto.h"
|
||||
#include "dome.h"
|
||||
#include "header.h"
|
||||
|
||||
// number of relay turning on/off motors power
|
||||
#define MOTRELAY_NO 1
|
||||
@@ -269,5 +270,6 @@ ret:
|
||||
if(state == DOME_S_IDLE) status_req_interval = STATUSREQ_IDLE;
|
||||
else status_req_interval = STATUSREQ_MOVE;
|
||||
pthread_mutex_unlock(&serialmutex);
|
||||
write_header();
|
||||
return st;
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
-std=c17
|
||||
-std=c23
|
||||
@@ -1,2 +1,3 @@
|
||||
// Add predefined macros for your project here. For example:
|
||||
// #define THE_ANSWER 42
|
||||
#define _XOPEN_SOURCE 666
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 16.0.2, 2025-06-04T11:18:43. -->
|
||||
<!-- Written by QtCreator 19.0.1, 2026-05-15T14:43:04. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
@@ -86,12 +86,14 @@
|
||||
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
|
||||
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
|
||||
</valuemap>
|
||||
<value type="int" key="RcSync">0</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Target.0</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="QString" key="DeviceType">Desktop</value>
|
||||
<value type="bool" key="HasPerBcDcs">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{91347f2c-5221-46a7-80b1-0a054ca02f79}</value>
|
||||
@@ -109,8 +111,8 @@
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
@@ -122,8 +124,8 @@
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
@@ -133,13 +135,49 @@
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">По умолчанию</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.SuppressionFiles"/>
|
||||
<value type="QList<int>" key="Analyzer.Valgrind.VisibleErrorKinds"></value>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
||||
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Развёртывание</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Развёртывание</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||
@@ -153,6 +191,7 @@
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.SuppressionFiles"/>
|
||||
<value type="QList<int>" key="Analyzer.Valgrind.VisibleErrorKinds"></value>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
@@ -163,6 +202,7 @@
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
||||
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
</valuemap>
|
||||
@@ -173,10 +213,6 @@
|
||||
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||
<value type="qlonglong">1</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>Version</variable>
|
||||
<value type="int">22</value>
|
||||
|
||||
@@ -1 +1 @@
|
||||
-std=c++17
|
||||
-std=c++23
|
||||
@@ -1,6 +1,8 @@
|
||||
astrosib_proto.h
|
||||
dome.c
|
||||
dome.h
|
||||
header.c
|
||||
header.h
|
||||
main.c
|
||||
server.c
|
||||
server.h
|
||||
|
||||
132
Daemons/domedaemon-astrosib/header.c
Normal file
132
Daemons/domedaemon-astrosib/header.c
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* This file is part of the baader_dome project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "header.h"
|
||||
#include "dome.h"
|
||||
#include "server.h"
|
||||
|
||||
#define VAL_LEN 22
|
||||
|
||||
static char *headername = NULL;
|
||||
static char *dome_name = NULL;
|
||||
|
||||
static int printhdr(int fd, const char *key, const char *val, const char *cmnt){
|
||||
char tmp[81];
|
||||
char tk[9];
|
||||
if(strlen(key) > 8){
|
||||
snprintf(tk, 8, "%s", key);
|
||||
key = tk;
|
||||
}
|
||||
if(cmnt){
|
||||
snprintf(tmp, 81, "%-8s= %-21s / %s", key, val, cmnt);
|
||||
}else{
|
||||
snprintf(tmp, 81, "%-8s= %s", key, val);
|
||||
}
|
||||
size_t l = strlen(tmp);
|
||||
tmp[l] = '\n';
|
||||
++l;
|
||||
if(write(fd, tmp, l) != (ssize_t)l){
|
||||
WARN("write()");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// return TRUE if can write to given header file
|
||||
int header_create(const char *file){
|
||||
if(!file) return FALSE;
|
||||
FILE *hf = fopen(file, "w");
|
||||
if(!hf) return FALSE;
|
||||
unlink(file);
|
||||
if(headername) FREE(headername);
|
||||
headername = strdup(file);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// set given `name` for telescope name
|
||||
void domename(const char *name){
|
||||
if(dome_name) FREE(dome_name);
|
||||
if(name){
|
||||
int l = strlen(name) + 3;
|
||||
dome_name = MALLOC(char, l);
|
||||
snprintf(dome_name, l, "'%s'", name);
|
||||
}
|
||||
}
|
||||
|
||||
const char* stringst[] = {
|
||||
[DOME_S_IDLE] = "idle",
|
||||
[DOME_S_MOVING] = "moving",
|
||||
[DOME_S_ERROR] = "error",
|
||||
};
|
||||
|
||||
void write_header(){
|
||||
if(!headername) return;
|
||||
char aname[PATH_MAX];
|
||||
char val[VAL_LEN];
|
||||
#define WRHDR(k, v, c) do{if(printhdr(fd, k, v, c)){goto returning;}}while(0)
|
||||
snprintf(aname, PATH_MAX-1, "%sXXXXXX", headername);
|
||||
int fd = mkstemp(aname);
|
||||
if(fd < 0){
|
||||
LOGWARN("Can't write header file: mkstemp()");
|
||||
return;
|
||||
}
|
||||
fchmod(fd, 0644);
|
||||
dome_status_t st;
|
||||
dome_state_t curstate = get_dome_state();
|
||||
double stattime = get_dome_status(&st);
|
||||
if(get_forbidden()) WRHDR("OPERATIO", "'FORBIDDEN'", "Observations are forbidden");
|
||||
if(dome_name) WRHDR("DOME", dome_name, "Dome manufacturer/name");
|
||||
int idx = (int)curstate;
|
||||
if(idx < 0 || idx > DOME_S_ERROR) idx = DOME_S_ERROR;
|
||||
snprintf(val, VAL_LEN, "'%s'", stringst[idx]);
|
||||
WRHDR("DOMESTAT", val, "Dome status");
|
||||
snprintf(val, VAL_LEN, "'%s'", textst(st.coverstate[0]));
|
||||
WRHDR("DOMECVR1", val, "Dome cover 1 status");
|
||||
snprintf(val, VAL_LEN, "'%s'", textst(st.coverstate[1]));
|
||||
WRHDR("DOMECVR2", val, "Dome cover 2 status");
|
||||
snprintf(val, VAL_LEN, "%d", st.encoder[0]);
|
||||
WRHDR("DOMEANG1", val, "Dome cover 1 angle");
|
||||
snprintf(val, VAL_LEN, "%d", st.encoder[1]);
|
||||
WRHDR("DOMEANG2", val, "Dome cover 2 angle");
|
||||
snprintf(val, VAL_LEN, "%d", st.relay[0]);
|
||||
WRHDR("DOMERLY1", val, "Dome relay1 state");
|
||||
snprintf(val, VAL_LEN, "%d", st.relay[1]);
|
||||
WRHDR("DOMERLY2", val, "Dome relay2 state");
|
||||
snprintf(val, VAL_LEN, "%d", st.relay[2]);
|
||||
WRHDR("DOMERLY3", val, "Dome relay3 state");
|
||||
if(st.israin) WRHDR("DOMERAIN", "1", "Dome rain sensor armed");
|
||||
snprintf(val, VAL_LEN, "%.3f", stattime);
|
||||
char timebuf[BUFSIZ];
|
||||
time_t t = (time_t) stattime;
|
||||
struct tm *tmp;
|
||||
tmp = localtime(&t);
|
||||
if(!tmp || 0 == strftime(timebuf, BUFSIZ, "Measurement time: %F %T", tmp)){
|
||||
LOGERR("localtime() returned NULL");
|
||||
goto returning;
|
||||
}
|
||||
WRHDR("TDOMMEAS", val, timebuf);
|
||||
#undef WRHDR
|
||||
returning:
|
||||
close(fd);
|
||||
rename(aname, headername);
|
||||
}
|
||||
23
Daemons/domedaemon-astrosib/header.h
Normal file
23
Daemons/domedaemon-astrosib/header.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* This file is part of the domedaemon-astrosib project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void write_header();
|
||||
int header_create(const char *file);
|
||||
void domename(const char *name);
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <sys/prctl.h> //prctl
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "header.h"
|
||||
#include "server.h"
|
||||
|
||||
// TCP socket port
|
||||
@@ -31,45 +32,101 @@
|
||||
#define DEFAULT_SERSPEED 9600
|
||||
// serial polling timeout - 100ms
|
||||
#define DEFAULT_SERTMOUT 100000
|
||||
#define DEFAULT_PIDFILE "/tmp/domedaemon.pid"
|
||||
#define DEFAULT_HEADERFILE "/tmp/dome.fits"
|
||||
#define DEFAULT_DOMENAME "Astrosib"
|
||||
|
||||
static pid_t childpid = 0;
|
||||
static sl_tty_t *serial = NULL;
|
||||
|
||||
typedef struct{
|
||||
int help;
|
||||
char *device; // serial device name
|
||||
char *node; // port to connect or UNIX socket name
|
||||
char *logfile; // logfile name
|
||||
char *pidfile; // PID-file path
|
||||
char *headerfile; // path to FITS-header with dome parameters
|
||||
char *dome_name; // name of dome
|
||||
int isunix; // open UNIX-socket instead of TCP
|
||||
int verbose; // verbose level
|
||||
} parameters;
|
||||
|
||||
|
||||
static parameters G = {
|
||||
.node = DEFAULT_PORT,
|
||||
.pidfile = DEFAULT_PIDFILE,
|
||||
.headerfile = DEFAULT_HEADERFILE,
|
||||
.dome_name = DEFAULT_DOMENAME,
|
||||
};
|
||||
static int help;
|
||||
|
||||
static sl_option_t cmdlnopts[] = {
|
||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), "show this help"},
|
||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.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)"},
|
||||
{"pidfile", NEED_ARG, NULL, 'p', arg_string, APTR(&G.pidfile), "full path to PID-file (default: " DEFAULT_PIDFILE ")"},
|
||||
{"headerfile",NEED_ARG, NULL, 'H', arg_string, APTR(&G.headerfile),"full path to output FITS-header (default: " DEFAULT_HEADERFILE ")"},
|
||||
{"domename",NEED_ARG, NULL, 'N', arg_string, APTR(&G.dome_name), "dome name in FITS-header (default: " DEFAULT_DOMENAME ")"},
|
||||
end_option
|
||||
};
|
||||
|
||||
void sl_iffound_deflt(pid_t pid){
|
||||
WARNX("Another copy of this process found, pid=%d. Exit.", pid);
|
||||
exit(1); // don't run `signals` to protect foreign PID-file from removal
|
||||
}
|
||||
|
||||
// SIGUSR1 - FORBID observations
|
||||
// SIGUSR2 - allow
|
||||
void signals(int sig){
|
||||
if(sig){
|
||||
signal(sig, SIG_IGN);
|
||||
DBG("Get signal %d, quit.\n", sig);
|
||||
LOGERR("Exit with status %d", sig);
|
||||
}else LOGERR("Exit");
|
||||
if(signals != signal(sig, SIG_IGN)) exit(sig); // function called "as is", before sig registration
|
||||
if(childpid == 0){ // child -> test USR1/USR2
|
||||
LOGDBG("Child gotta signal %d", sig);
|
||||
if(sig == SIGUSR1){
|
||||
forbid_observations(1);
|
||||
LOGMSG("Got signal `observations forbidden`");
|
||||
signal(sig, signals);
|
||||
return;
|
||||
}else if(sig == SIGUSR2){
|
||||
forbid_observations(0);
|
||||
LOGMSG("Got signal `observations permitted`");
|
||||
signal(sig, signals);
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOGDBG("Get signal %d, quit.\n", sig);
|
||||
}
|
||||
if(childpid == 0){
|
||||
DBG("Stop server");
|
||||
LOGMSG("Stop server");
|
||||
stopserver();
|
||||
DBG("Close terminal");
|
||||
LOGMSG("Close terminal");
|
||||
if(serial) sl_tty_close(&serial);
|
||||
}else{
|
||||
if(G.pidfile){
|
||||
LOGMSG("Unlink %s", G.pidfile);
|
||||
usleep(10000);
|
||||
unlink(G.pidfile);
|
||||
}
|
||||
}
|
||||
LOGERR("Exit with status %d", sig);
|
||||
exit(sig);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
sl_init();
|
||||
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||
if(help) sl_showhelp(-1, cmdlnopts);
|
||||
if(G.help) sl_showhelp(-1, cmdlnopts);
|
||||
if(!G.node) ERRX("Point node");
|
||||
if(!G.device) ERRX("Point path to serial device");
|
||||
if(!header_create(G.headerfile))
|
||||
ERRX("Cannot write into '%s'", G.headerfile);
|
||||
domename(G.dome_name);
|
||||
sl_check4running((char*)__progname, G.pidfile);
|
||||
if(sl_daemonize()) ERR("Can't daemonize!");
|
||||
sl_loglevel_e lvl = G.verbose + LOGLEVEL_ERR;
|
||||
if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1;
|
||||
if(G.logfile) OPENLOG(G.logfile, lvl, 1);
|
||||
@@ -79,6 +136,8 @@ int main(int argc, char **argv){
|
||||
signal(SIGQUIT, signals);
|
||||
signal(SIGTSTP, SIG_IGN);
|
||||
signal(SIGHUP, signals);
|
||||
signal(SIGUSR1, SIG_IGN);
|
||||
signal(SIGUSR2, SIG_IGN);
|
||||
#ifndef EBUG
|
||||
time_t lastd = 0;
|
||||
while(1){ // guard for dead processes
|
||||
@@ -101,7 +160,7 @@ int main(int argc, char **argv){
|
||||
}
|
||||
#endif
|
||||
sl_socktype_e type = (G.isunix) ? SOCKT_UNIX : SOCKT_NETLOCAL;
|
||||
sl_tty_t *serial = sl_tty_new(G.device, DEFAULT_SERSPEED, 4096);
|
||||
serial = sl_tty_new(G.device, DEFAULT_SERSPEED, 4096);
|
||||
if(serial) serial = sl_tty_open(serial, 1);
|
||||
if(!serial){
|
||||
LOGERR("Can't open serial device %s", G.device);
|
||||
|
||||
@@ -18,15 +18,22 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <usefull_macros.h>
|
||||
#include <weather_data.h>
|
||||
|
||||
#include "dome.h"
|
||||
|
||||
// max age time of last status - 30s
|
||||
#define STATUS_MAX_AGE (30.)
|
||||
#define STATUS_MAX_AGE (30.)
|
||||
|
||||
// if there's no signal from weather over `WEATHER_LOST` seconds, close the dome
|
||||
#define WEATHER_LOST (300.)
|
||||
// interval of weather polling
|
||||
#define WEATH_POLL (5.)
|
||||
|
||||
// commands
|
||||
#define CMD_UNIXT "unixt"
|
||||
@@ -41,6 +48,13 @@
|
||||
// main socket
|
||||
static sl_sock_t *s = NULL;
|
||||
|
||||
// external (manual) signal "deny/allow"
|
||||
static atomic_bool ForbidObservations = 0; // ==1 if all is forbidden -> close dome and not allow to open
|
||||
// weather don't allow to open
|
||||
static atomic_bool BadWeather = 0;
|
||||
|
||||
#define CHKALLOWED() do{if(ForbidObservations || BadWeather){return RESULT_FAIL;}}while(0)
|
||||
|
||||
/////// 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){
|
||||
@@ -60,7 +74,7 @@ static sl_sock_hresult_e statush(sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ co
|
||||
sl_sock_sendstrmessage(c, buf);
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
static const char *textst(int coverstate){
|
||||
const char *textst(int coverstate){
|
||||
switch(coverstate){
|
||||
case COVER_INTERMEDIATE: return "intermediate";
|
||||
case COVER_OPENED: return "opened";
|
||||
@@ -110,16 +124,19 @@ static sl_sock_hresult_e domecmd(dome_cmd_t cmd){
|
||||
return RESULT_OK;
|
||||
}
|
||||
static sl_sock_hresult_e opendome(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
||||
CHKALLOWED();
|
||||
return domecmd(DOME_OPEN);
|
||||
}
|
||||
static sl_sock_hresult_e closedome(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
||||
return domecmd(DOME_CLOSE);
|
||||
}
|
||||
static sl_sock_hresult_e stopdome(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
||||
CHKALLOWED(); // don't allow to stop closing on forbidden state
|
||||
return domecmd(DOME_STOP);
|
||||
}
|
||||
// half open/close
|
||||
static sl_sock_hresult_e halfmove(sl_sock_t *c, sl_sock_hitem_t *item, const char *req){
|
||||
CHKALLOWED();
|
||||
char buf[128];
|
||||
int N = item->key[sizeof(CMD_HALF) - 1] - '0';
|
||||
if(N < 1 || N > 2) return RESULT_BADKEY;
|
||||
@@ -196,6 +213,7 @@ void server_run(sl_socktype_e type, const char *node, sl_tty_t *serial){
|
||||
LOGERR("server_run(): wrong parameters");
|
||||
ERRX("server_run(): wrong parameters");
|
||||
}
|
||||
weather_data_t weather;
|
||||
dome_serialdev(serial);
|
||||
s = sl_sock_run_server(type, node, -1, handlers);
|
||||
if(!s) ERRX("Can't create socket and/or run threads");
|
||||
@@ -203,14 +221,59 @@ void server_run(sl_socktype_e type, const char *node, sl_tty_t *serial){
|
||||
sl_sock_maxclhandler(s, toomuch);
|
||||
sl_sock_connhandler(s, connected);
|
||||
sl_sock_dischandler(s, disconnected);
|
||||
double tnow = sl_dtime(), tweather = 0.;
|
||||
int cmdclosed = 0;
|
||||
while(s && s->connected){
|
||||
if(!s->rthread){
|
||||
LOGERR("Server handlers thread is dead");
|
||||
break;
|
||||
}
|
||||
if(tnow - tweather > WEATH_POLL){
|
||||
if(0 == get_weather_data(&weather)){ // got OK -> check if observations are forbidden
|
||||
tweather = tnow;
|
||||
int bad = 0;
|
||||
if((double)weather.last_update - tnow > WEATHER_LOST) bad = 1;
|
||||
if(weather.forceoff || weather.rain || weather.weather > WEATHER_BAD) bad = 1;
|
||||
if(bad) BadWeather = 1;
|
||||
else BadWeather = 0;
|
||||
}else{
|
||||
if(tnow - tweather > WEATHER_LOST) BadWeather = 1; // lost weather IPC
|
||||
}
|
||||
}
|
||||
// finite state machine polling
|
||||
dome_poll(DOME_POLL, 0);
|
||||
if(ForbidObservations || BadWeather){
|
||||
if(0 == cmdclosed){
|
||||
if(DOME_S_ERROR != dome_poll(DOME_CLOSE, 0)){
|
||||
LOGERR("Send command 'close' due to forbidden state");
|
||||
cmdclosed = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sl_sock_delete(&s);
|
||||
ERRX("Server handlers thread is dead");
|
||||
}
|
||||
|
||||
void stopserver(){
|
||||
if(s && s->connected){
|
||||
s->connected = 0;
|
||||
usleep(5000);
|
||||
if(s) sl_sock_delete(&s);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int get_forbidden(){
|
||||
return (ForbidObservations || BadWeather);
|
||||
}
|
||||
|
||||
@@ -20,4 +20,13 @@
|
||||
|
||||
#include <usefull_macros.h>
|
||||
|
||||
// size of weather/status buffers
|
||||
#define STATBUF_SZ 256
|
||||
// dome polling interval (clear watchdog & get status)
|
||||
#define T_INTERVAL (5.0)
|
||||
|
||||
void server_run(sl_socktype_e type, const char *node, sl_tty_t *serial);
|
||||
const char *textst(int coverstate);
|
||||
void stopserver();
|
||||
void forbid_observations(int forbid);
|
||||
int get_forbidden();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 18.0.0, 2026-04-01T11:06:44. -->
|
||||
<!-- Written by QtCreator 19.0.1, 2026-05-15T14:02:48. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
@@ -155,6 +155,7 @@
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.SuppressionFiles"/>
|
||||
<value type="QList<int>" key="Analyzer.Valgrind.VisibleErrorKinds"></value>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
@@ -191,6 +192,7 @@
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.SuppressionFiles"/>
|
||||
<value type="QList<int>" key="Analyzer.Valgrind.VisibleErrorKinds"></value>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
|
||||
@@ -136,10 +136,6 @@ 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(!term_open(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);
|
||||
@@ -163,6 +159,10 @@ int main(int argc, char **argv){
|
||||
// react for USRx only in child
|
||||
signal(SIGUSR1, signals);
|
||||
signal(SIGUSR2, signals);
|
||||
if(!term_open(G.termpath, G.serspeed, G.sertmout)){
|
||||
LOGERR("Can't open %s", G.termpath);
|
||||
ERRX("Fatal error");
|
||||
}
|
||||
runserver(G.isunix, G.node, G.maxclients);
|
||||
LOGERR("Server error -> exit");
|
||||
return 0;
|
||||
|
||||
@@ -309,7 +309,7 @@ void runserver(int isunix, const char *node, int maxclients){
|
||||
if(bad) BadWeather = 1;
|
||||
else BadWeather = 0;
|
||||
}else{
|
||||
if(tweather - tnow > WEATHER_LOST) BadWeather = 1; // lost weather IPC
|
||||
if(tnow - tweather > WEATHER_LOST) BadWeather = 1; // lost weather IPC
|
||||
}
|
||||
if(poll_device()){
|
||||
tgot = tnow;
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
badsky=1700
|
||||
[ $# = 1 ] && badsky=$1
|
||||
|
||||
export http_proxy=""
|
||||
|
||||
Q="192.168.70.33:12345"
|
||||
ANS=$(curl $Q 2>/dev/null)
|
||||
retval=$?
|
||||
[ $retval -ne "0" ] && exit $retval
|
||||
[ "$ANS" = "No data" ] && exit 2
|
||||
Rain=1
|
||||
Clouds=0
|
||||
Wind=100
|
||||
eval $ANS
|
||||
retval=0
|
||||
clouds=$(echo "$Clouds" | sed 's/\..*//g')
|
||||
wind=$(echo "$Wind" | sed 's/\..*//g')
|
||||
[ $Rain -ne "0" ] && retval=1
|
||||
[ $clouds -lt "$badsky" ] && retval=1
|
||||
[ $wind -gt "15" ] && retval=1
|
||||
echo "$ANS"
|
||||
exit $retval
|
||||
Reference in New Issue
Block a user