diff --git a/Makefile b/Makefile index 9d5491b..61ab2cf 100644 --- a/Makefile +++ b/Makefile @@ -1,33 +1,56 @@ -# run `make DEF=...` to add extra defines +# run `make DEF=...` to add extra defines, +# conditional compile flags: +# NOLIBRAW=1 - not use libraw & libgd +# NOTIFF - not use libtiff +# NOCFITSIO - not use cfitsio +# LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all LDFLAGS += -lm -pthread -LDIMG := $(shell pkg-config --libs libraw) -lgd $(shell pkg-config --libs cfitsio) -ltiff -SRCS := $(wildcard *.c) +LDIMG := +SRCS := $(wildcard *.c) DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111 -#DEFINES += -DEBUG +DEFINES += -DEBUG CFLAGS += -Wall -Wextra -O2 -CFLAGS += $(shell pkg-config --cflags libraw) OBJS := $(SRCS:%.c=%.o) CC = gcc CPP = g++ -all : sbig340_standalone sbig340_daemon sbig340_client +TARGETS := sbig340_daemon + +ifndef NOLIBRAW + LDIMG += $(shell pkg-config --libs libraw) -lgd + CFLAGS += $(shell pkg-config --cflags libraw) + DEBAYER := debayer.o + DEFINES += -DLIBRAW +endif + +ifndef NOTIFF + LDIMG += -ltiff + DEFINES += -DLIBTIFF +endif + +ifndef NOCFITSIO + LDIMG += $(shell pkg-config --libs cfitsio) + DEFINES += -DLIBCFITSIO +endif + +all : sbig340_daemon sbig340_standalone sbig340_client debayer.o : debayer.cpp @echo -e "\t\tG++ debayer" $(CPP) $(CFLAGS) $(DEFINES) debayer.cpp -c -sbig340_standalone : $(SRCS) debayer.o +sbig340_standalone : $(SRCS) $(DEBAYER) @echo -e "\t\tBuild standalone" - $(CC) $(CFLAGS) -std=gnu99 $(DEFINES) $(LDFLAGS) $(LDIMG) $(SRCS) debayer.o -o $@ + $(CC) $(CFLAGS) -std=gnu99 $(DEFINES) $(LDFLAGS) $(LDIMG) $(SRCS) $(DEBAYER) -o $@ sbig340_daemon : $(SRCS) @echo -e "\t\tBuild daemon" $(CC) -DDAEMON $(CFLAGS) -std=gnu99 $(DEFINES) $(LDFLAGS) $(SRCS) -o $@ -sbig340_client : $(SRCS) debayer.o +sbig340_client : $(SRCS) $(DEBAYER) @echo -e "\t\tBuild client" - $(CC) -DCLIENT $(CFLAGS) -std=gnu99 $(DEFINES) $(LDFLAGS) $(LDIMG) $(SRCS) debayer.o -o $@ + $(CC) -DCLIENT $(CFLAGS) -std=gnu99 $(DEFINES) $(LDFLAGS) $(LDIMG) $(SRCS) $(DEBAYER) -o $@ clean: @echo -e "\t\tCLEAN" diff --git a/cmdlnopts.c b/cmdlnopts.c index 0a7f8b6..73f759d 100644 --- a/cmdlnopts.c +++ b/cmdlnopts.c @@ -59,6 +59,8 @@ glob_pars const Gdefault = { .timestamp = 0, .dark_interval = 1800, .min_dark_exp = 30, + .max_exptime = -1, + .htrperiod = 0 }; /* @@ -82,6 +84,8 @@ myoption cmdlnopts[] = { #endif // not client options #ifndef CLIENT + {"exptime-max",NEED_ARG,NULL, 0, arg_double, APTR(&G.max_exptime),_("maximal exposition time (seconds)")}, + {"heaterperiod", NEED_ARG,NULL, 0, arg_int, APTR(&G.htrperiod), _("change heater ON time (0..3599 seconds)")}, {"device", NEED_ARG, NULL, 'i', arg_string, APTR(&G.device), _("serial device name (default: " DEFAULT_COMDEV ")")}, {"heater-on",NO_ARGS, APTR(&G.heater),HEATER_ON, arg_none, NULL, _("turn heater on")}, {"heater-off",NO_ARGS, APTR(&G.heater),HEATER_OFF, arg_none, NULL, _("turn heater off")}, diff --git a/cmdlnopts.h b/cmdlnopts.h index 6eabeb1..649903f 100644 --- a/cmdlnopts.h +++ b/cmdlnopts.h @@ -52,6 +52,8 @@ typedef struct{ char *port; // port to connect double dark_interval; // time interval (in seconds) between dark images taken double min_dark_exp; // minimal exposition (in seconds) @ which darks would be taken + double max_exptime; // maximal exposition time + int htrperiod; // new value for heater ON time (0..3599 seconds) char** rest_pars; // the rest parameters: array of char* } glob_pars; diff --git a/imfunctions.c b/imfunctions.c index fd60cad..0a7b021 100644 --- a/imfunctions.c +++ b/imfunctions.c @@ -401,6 +401,11 @@ uint16_t *get_imdata(imstorage *img){ } #endif // CLIENT +static double max_exptime = 180.; +void set_max_exptime(double t){ + if(t > 30. && t < 300.) max_exptime = t; +} + /** * save truncated to 256 levels histogram of `img` into file `f` * @return 0 if all OK @@ -447,7 +452,7 @@ int save_histo(FILE *f, imstorage *img){ if(mul > mulmax) mul = mulmax; double E = img->exptime * mul; if(E < 5e-5) E = 5e-5; // too short exposition - else if(E > 180.) E = 180.; // no need to do expositions larger than 3 minutes + else if(E > max_exptime) E = max_exptime; // no need to do expositions larger than max_exptime green("Recommended exposition time: %g seconds\n", E); exp_calculated = E; return 0; @@ -523,6 +528,7 @@ int store_image(imstorage *img){ if(write_debayer(img, (uint16_t)lowval)) status |= 8; // and save colour image } #endif + putlog("Save image, status=%d", status); return status; } #endif // !DAEMON diff --git a/imfunctions.h b/imfunctions.h index 0c164b2..7ecb68b 100644 --- a/imfunctions.h +++ b/imfunctions.h @@ -78,6 +78,8 @@ extern double exp_calculated; #define SUFFIX_TIFF "tiff" #define SUFFIX_JPEG "jpg" + +void set_max_exptime(double t); char *make_filename(imstorage *img, const char *suff); imstorage *chk_storeimg(imstorage *img, char* store, char *format); int store_image(imstorage *filename); diff --git a/main.c b/main.c index 3808493..db3474e 100644 --- a/main.c +++ b/main.c @@ -41,6 +41,7 @@ void signals(int signo){ restore_console(); restore_tty(); #endif + putlog("Exit with status %d", signo); exit(signo); } @@ -52,10 +53,13 @@ int main(int argc, char **argv){ signal(SIGQUIT, signals); // ctrl+\ - quit signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z glob_pars *G = parse_args(argc, argv); - + if(G->rest_pars_num) + openlogfile(G->rest_pars[0]); imstorage *img = NULL; imsubframe *F = NULL; #ifndef CLIENT + if(G->htrperiod) set_heater_period(G->htrperiod); + if(G->max_exptime) set_max_exptime(G->max_exptime); if(G->splist){ list_speeds(); return 0; @@ -65,18 +69,20 @@ int main(int argc, char **argv){ #if defined DAEMON || defined CLIENT if(!G->once){ #ifndef EBUG - green("Daemonize"); - printf("\n"); + putlog("Daemonize"); fflush(stdout); if(daemon(1, 0)){ + putlog("Can't daemonize"); ERR("daemon()"); } #endif // EBUG while(1){ // guard for dead processes pid_t childpid = fork(); if(childpid){ - DBG("Created child with PID %d\n", childpid); + putlog("Create child with PID %d\n", childpid); + DBG("Create child with PID %d\n", childpid); wait(NULL); + putlog("Child %d died\n", childpid); WARNX("Child %d died\n", childpid); sleep(1); }else{ @@ -88,23 +94,31 @@ int main(int argc, char **argv){ #endif // DAEMON || CLIENT #ifndef CLIENT if(!try_connect(G->device, G->speed)){ + putlog("device not answer"); WARNX(_("Check power and connection: device not answer!")); return 1; } char *fw = get_firmvare_version(); if(fw) printf(_("Firmware version: %s\n"), fw); - if(G->newspeed && term_setspeed(G->newspeed)) + if(G->newspeed && term_setspeed(G->newspeed)){ + putlog("Can't change speed to %d", G->newspeed); ERRX(_("Can't change speed to %d"), G->newspeed); - if(G->shutter_cmd && shutter_command(G->shutter_cmd)) + } + if(G->shutter_cmd && shutter_command(G->shutter_cmd)){ + putlog("Can't send shutter command: %s", G->shutter_cmd); WARNX(_("Can't send shutter command: %s"), G->shutter_cmd); - if(G->heater != HEATER_LEAVE) + } + if(G->heater != HEATER_LEAVE){ heater(G->heater); // turn on/off heater + } #ifndef DAEMON if(G->takeimg){ #endif // DAEMON if(G->subframe){ - if(!(F = define_subframe(G->subframe))) + if(!(F = define_subframe(G->subframe))){ + putlog("Error defining subframe"); ERRX(_("Error defining subframe")); + } G->binning = 0xff; // take subframe } #endif // !CLIENT @@ -126,14 +140,18 @@ int main(int argc, char **argv){ img->binning = G->binning; if(start_exposition(img, G->imtype)){ // start test exposition even in daemon + putlog("Error starting exposition"); ERRX(_("Error starting exposition")); }else{ if(!get_imdata(img)){ + putlog("Error image transfer"); WARNX(_("Error image transfer")); }else{ #ifndef DAEMON - if(store_image(img)) + if(store_image(img)){ + putlog("Error storing image"); WARNX(_("Error storing image")); + } #endif // !DAEMON } } diff --git a/socket.c b/socket.c index a53c234..03582ad 100644 --- a/socket.c +++ b/socket.c @@ -79,7 +79,7 @@ static uint8_t *findpar(uint8_t *str, char *par){ * get integer & double `parameter` value from string `str`, put value to `ret` * @return 1 if all OK */ -static long getintpar(uint8_t *str, char *parameter, long *ret){ +static int getintpar(uint8_t *str, char *parameter, long *ret){ long tmp; char *endptr; if(!(str = findpar(str, parameter))) return 0; @@ -90,6 +90,7 @@ static long getintpar(uint8_t *str, char *parameter, long *ret){ DBG("get par: %s = %ld", parameter, tmp); return 1; } +#ifdef CLIENT static int getdpar(uint8_t *str, char *parameter, double *ret){ double tmp; char *endptr; @@ -101,6 +102,7 @@ static int getdpar(uint8_t *str, char *parameter, double *ret){ DBG("get par: %s = %g", parameter, tmp); return 1; } +#endif // CLIENT /**************** CLIENT/SERVER FUNCTIONS ****************/ #ifdef DAEMON @@ -201,6 +203,7 @@ int send_ima(int sock, int webquery){ WARN("write()"); return 0; } + putlog("send image to client"); return 1; } @@ -243,6 +246,7 @@ void *handle_socket(void *asock){ } _read = read(sock, buff, BUFLEN); if(_read < 1){ // error or disconnect + putlog("Client disconnected"); DBG("Nothing to read from fd %d (ret: %zd)", sock, _read); break; } @@ -259,9 +263,15 @@ void *handle_socket(void *asock){ } // here we can process user data printf("user send: %s\n", found); - long ii; double dd; - if(getdpar((uint8_t*)found, "exptime", &dd)) printf("exptime: %g\n", dd); - if(getintpar((uint8_t*)found, "somepar", &ii)) printf("somepar: %ld\n", ii); + long htr; + // double dd; + //if(getdpar((uint8_t*)found, "exptime", &dd)) printf("exptime: %g\n", dd); + if(getintpar((uint8_t*)found, "heater", &htr)){ + putlog("got command: heater=%ld", htr); + if(htr == 0) heater_off(); + else heater_on(); + break; // disconnect after command receiving + } } close(sock); //DBG("closed"); @@ -286,6 +296,7 @@ void *server(void *asock){ WARN("accept()"); continue; } + putlog("Got connection from %s\n", inet_ntoa(their_addr.sin_addr)); pthread_t handler_thread; if(pthread_create(&handler_thread, NULL, handle_socket, (void*) &newsock)) WARN("pthread_create()"); @@ -307,6 +318,7 @@ static void daemon_(imstorage *img, int sock){ do{ if(pthread_kill(sock_thread, 0) == ESRCH){ // died WARNX("Sockets thread died"); + putlog("Sockets thread died"); pthread_join(sock_thread, NULL); if(pthread_create(&sock_thread, NULL, server, (void*) &sock)) ERR("pthread_create()"); @@ -322,12 +334,14 @@ static void daemon_(imstorage *img, int sock){ } } if(start_exposition(img, NULL)){ + putlog("Error starting exposition, try later"); WARNX(_("Error starting exposition, try later")); ++errcntr; }else{ FREE(img->imdata); if(!get_imdata(img)){ ++errcntr; + putlog("Error image transfer"); WARNX(_("Error image transfer")); }else{ errcntr = 0; @@ -341,6 +355,7 @@ static void daemon_(imstorage *img, int sock){ } } if(errcntr >= 33){ + putlog("Unrecoverable error, errcntr=%d. Exit", errcntr); ERRX(_("Unrecoverable error")); } }while(1); @@ -383,8 +398,6 @@ static void client_(imstorage *img, int sock){ size_t Bufsiz = BUFLEN10; uint8_t *recvBuff = MALLOC(uint8_t, Bufsiz); while(1){ - //size_t L = strlen(msg); - //if(send(sock, msg, L, 0) != L){ WARN("send"); continue;} if(!waittoread(sock)) continue; size_t offset = 0; do{ @@ -398,21 +411,30 @@ static void client_(imstorage *img, int sock){ DBG("Buffer reallocated, new size: %zd\n", Bufsiz); } ssize_t n = read(sock, &recvBuff[offset], Bufsiz - offset); - if(!n) break; + if(!n){ + putlog("Socket closed"); + break; + } if(n < 0){ + putlog("error in read()"); WARN("read"); break; } offset += n; }while(waittoread(sock)); if(!offset){ + putlog("Socket closed"); WARN("Socket closed\n"); return; } DBG("read %zd bytes\n", offset); if(get_imstorage(img, recvBuff, offset)){ - if(store_image(img)) + if(store_image(img)){ + putlog("Error storing image"); WARNX(_("Error storing image")); + }else{ + putlog("Image saved"); + } } if(img->once) break; } @@ -438,6 +460,7 @@ void daemonize(imstorage *img, char *hostname, char *port){ char str[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(ia->sin_addr), str, INET_ADDRSTRLEN); DBG("canonname: %s, port: %u, addr: %s\n", res->ai_canonname, ntohs(ia->sin_port), str); + putlog("connect to %s:%s", hostname, port); // loop through all the results and bind to the first we can for(p = res; p != NULL; p = p->ai_next){ if((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){ @@ -465,6 +488,7 @@ void daemonize(imstorage *img, char *hostname, char *port){ } if(p == NULL){ // looped off the end of the list with no successful bind + putlog("failed to bind socket"); ERRX("failed to bind socket"); } freeaddrinfo(res); @@ -473,6 +497,7 @@ void daemonize(imstorage *img, char *hostname, char *port){ #else daemon_(img, sock); #endif + putlog("Close socket"); close(sock); signals(0); } diff --git a/term.c b/term.c index 1076099..8a5b8ac 100644 --- a/term.c +++ b/term.c @@ -48,6 +48,22 @@ static int speeds[] = { 460800 }; +// values changed by heater_on() and heater_off() for indirect heater commands +static int set_heater_on = 0, set_heater_off = 0; +static time_t heater_period = 600; // default value for heater ON - 10 minutes +// change value of heater_period +void set_heater_period(int p){ + if(p > 0 && p < 3600) heater_period = (time_t) p; +} +void heater_on(){ + set_heater_on = 1; + set_heater_off = 0; +} +void heater_off(){ + set_heater_off = 1; + set_heater_on = 0; +} + void list_speeds(){ green(_("Speeds available:\n")); for(int i = 0; i < speedssize; ++i) @@ -126,6 +142,7 @@ static int download_in_progress = 0; // == 1 when image downloading runs * Used only on exit, so don't check commands status */ void abort_image(){ + putlog("Abort image exposition"); uint8_t tmpbuf[4096]; if(download_in_progress){ read_tty(tmpbuf, 4096); @@ -235,9 +252,11 @@ int try_connect(char *device, int speed){ } if(TRANS_SUCCEED != st || l != 1 || *rd != ANS_COMM_TEST) continue; DBG("Got it!"); + putlog("Connection established at B%d", speeds[curspd]); green(_("Connection established at B%d.\n"), speeds[curspd]); return speeds[curspd]; } + putlog("No connection!"); red(_("No connection!\n")); return 0; } @@ -338,6 +357,7 @@ void heater(heater_cmd cmd){ trans_status st = TRANS_TIMEOUT; if(i < 10) st = wait_checksum(); if(i == 10 || st != TRANS_SUCCEED){ + putlog("Can't send heater command"); WARNX(_("Can't send heater command: %s"), (st==TRANS_TIMEOUT) ? _("no answer") : _("bad checksum")); } } @@ -464,7 +484,7 @@ imsubframe *define_subframe(char *parm){ } /** - * Send command to start exposition + * Send command to start exposition & turn heater on/off if got command to do it * @param binning - binning to expose * @param exptime - exposition time * @param imtype - autodark, light or dark @@ -472,6 +492,23 @@ imsubframe *define_subframe(char *parm){ */ int start_exposition(imstorage *im, char *imtype){ FNAME(); + // check heater commands + static time_t htr_on_time = 0; + if(htr_on_time && time(NULL) - htr_on_time > heater_period){ + set_heater_off = 0; + set_heater_on = 0; + heater(HEATER_OFF); + htr_on_time = 0; + } + if(set_heater_off){ + set_heater_off = 0; + heater(HEATER_OFF); + htr_on_time = 0; + }else if(set_heater_on){ + set_heater_on = 0; + heater(HEATER_ON); + htr_on_time = time(NULL); + } double exptime = im->exptime; uint64_t exp100us = exptime * 10000.; static uint8_t cmd[6] = {CMD_TAKE_IMAGE}; // `static` to save all data after first call @@ -545,6 +582,7 @@ int start_exposition(imstorage *im, char *imtype){ WARNX(_("Didn't get the respond")); return 8; } + putlog("start exposition, exptime=%gs", exptime); im->imtype = it; size_t W, H; switch(im->binning){ // set image size @@ -699,6 +737,7 @@ uint16_t *get_image(imstorage *img){ bptr = ptr; }while(rest); printf("\b Done!\n"); + putlog("got image data"); DBG("Got full data packet, capture time: %.1f seconds", dtime() - tstart); download_in_progress = 0; return buff; diff --git a/term.h b/term.h index 0627d4c..d4346f0 100644 --- a/term.h +++ b/term.h @@ -85,6 +85,11 @@ int open_serial(char *dev); int get_curspeed(); int try_connect(char *device, int speed); void heater(heater_cmd cmd); +// setters for indirect heater using +void heater_on(); +void heater_off(); +void set_heater_period(int p); + void list_speeds(); void abort_image(); int term_setspeed(int speed); @@ -94,5 +99,6 @@ imsubframe *define_subframe(char *parm); int start_exposition(imstorage *im, char *imtype); int wait4image(); uint16_t *get_image(imstorage *img); +void set_heater_period(int p); #endif // __TERM_H__ diff --git a/usefull_macros.c b/usefull_macros.c index 0e3d7a4..de3e03e 100644 --- a/usefull_macros.c +++ b/usefull_macros.c @@ -20,6 +20,7 @@ */ #include "usefull_macros.h" +#include // PATH_MAX /** * function for different purposes that need to know time intervals @@ -383,3 +384,49 @@ int str2double(double *num, const char *str){ if(num) *num = res; // you may run it like myatod(NULL, str) to test wether str is double number return TRUE; } + +FILE *Flog = NULL; // log file descriptor +char *logname = NULL; +time_t log_open_time = 0; +/** + * Try to open log file + * if failed show warning message + */ +void openlogfile(char *name){ + if(!name){ + WARNX(_("Need filename")); + return; + } + green(_("Try to open log file %s in append mode\n"), name); + if(!(Flog = fopen(name, "a"))){ + WARN(_("Can't open log file")); + return; + } + log_open_time = time(NULL); + logname = name; +} + +/** + * Save message to log file, rotate logs every 24 hours + */ +int putlog(const char *fmt, ...){ + if(!Flog) return 0; + time_t t_now = time(NULL); + if(t_now - log_open_time > 86400){ // rotate log + fprintf(Flog, "\n\t\t%sRotate log\n", ctime(&t_now)); + fclose(Flog); + char newname[PATH_MAX]; + snprintf(newname, PATH_MAX, "%s.old", logname); + if(rename(logname, newname)) WARN("rename()"); + openlogfile(logname); + if(!Flog) return 0; + } + int i = fprintf(Flog, "\n\t\t%s", ctime(&t_now)); + va_list ar; + va_start(ar, fmt); + i = vfprintf(Flog, fmt, ar); + va_end(ar); + fprintf(Flog, "\n"); + fflush(Flog); + return i; +} diff --git a/usefull_macros.h b/usefull_macros.h index 1e2f325..6a94ae0 100644 --- a/usefull_macros.h +++ b/usefull_macros.h @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -137,4 +138,7 @@ int write_tty(const uint8_t *buff, size_t length); int str2double(double *num, const char *str); +void openlogfile(char *name); +int putlog(const char *fmt, ...); + #endif // __USEFULL_MACROS_H__