diff --git a/Dummy_cameras/dummyfunc.c b/Dummy_cameras/dummyfunc.c index fd1dd1e..7b50825 100644 --- a/Dummy_cameras/dummyfunc.c +++ b/Dummy_cameras/dummyfunc.c @@ -49,7 +49,7 @@ static int campoll(cc_capture_status *st, float *remain){ if(capstat != CAPTURE_PROCESS){ if(st) *st = capstat; if(remain) *remain = 0.; - return TRUE; + return FALSE; } if(sl_dtime() - texpstart > exptime){ if(st) *st = CAPTURE_READY; diff --git a/ccdcapture.c b/ccdcapture.c index a6df8a2..da59ea6 100644 --- a/ccdcapture.c +++ b/ccdcapture.c @@ -24,6 +24,7 @@ #include #include #include +#include #include // unix socket #include #include @@ -43,6 +44,82 @@ double __t0 = 0.; static int ntries = 2; // amount of tries to send messages controlling the answer double answer_timeout = 0.1; // timeout of waiting answer from server (not static for client.c) +static sem_t *sem = SEM_FAILED; + +// client-side SHM lock +int cc_lock_shm(int isserver){ + if(sem == SEM_FAILED){ + DBG("Can't lock NULL"); + return FALSE; + } + struct timespec ts = {.tv_sec = 0, .tv_nsec = 10000000}; + DBG("Try to lock"); + if(isserver){ + int locked = TRUE; + while(sem_timedwait(sem, &ts) && locked){ + switch(errno){ + case EINTR: + DBG("Interrupt -> try to lock again"); + break; + default: // timeout or other error -> force locking + DBG("Error locking -> force unlock"); + locked = FALSE; + } + } + if(locked){ + double t0 = sl_dtime(); + while(sem_trywait(sem) && sl_dtime() - t0 < 0.1) sem_post(sem); // force locking + } + }else{ + if(sem_timedwait(sem, &ts)){ + DBG("Image semaphore is locked too long by other side"); + return FALSE; // can't lock + } + } + DBG("Semaphore locked"); + return TRUE; +} + +void cc_unlock_shm(){ + if(sem == SEM_FAILED){ + DBG("Can't unlock NULL"); + return; + } + if(sem_post(sem)){ + switch(errno){ + case EOVERFLOW: // already unlocked + break; + default: // not a valid? or other? + WARN("Can't unlock image semaphore"); + LOGERR("Can't unlock image semaphore"); + return; + } + } + DBG("Semaphore unlocked"); +} + +void cc_init_sem(int isserver){ + if(sem != SEM_FAILED) return; + umask(0); // for read-write semaphore + // create samaphore if no + if(isserver){ + sem = sem_open(SEM_NAME, O_CREAT, 0666, 1); + }else{ + sem = sem_open(SEM_NAME, 0); + } + if(sem == SEM_FAILED){ + LOGERR("sem_open failed: %s", strerror(errno)); + } +} + +void cc_remove_sem(){ + if(sem == SEM_FAILED) return; + sem_close(sem); + DBG("semaphore closed\n"); + if(-1 == sem_unlink(SEM_NAME)) + LOGERR("Can't delete semaphore"); +} + /** * @brief cc_open_socket - create socket and open it * @param isserver - TRUE for server, FALSE for client @@ -253,7 +330,7 @@ char *cc_get_keyval(char **keyval){ cc_IMG *cc_getshm(key_t key, size_t imsize){ size_t shmsize = sizeof(cc_IMG) + imsize; shmsize = 1024 * (1 + shmsize / 1024); - DBG("Allocate %zd bytes in shared memory", shmsize); + DBG("Shared memory; sizeof(cc_IMG)=%zd, imsize=%zd", sizeof(cc_IMG), imsize); int shmid = -1; int flags = (imsize) ? IPC_CREAT | 0666 : 0; shmid = shmget(key, 0, flags); @@ -272,9 +349,15 @@ cc_IMG *cc_getshm(key_t key, size_t imsize){ WARN("Can't create shared memory segment %d", key); return NULL; } + }else{ +#ifdef EBUG + struct shmid_ds buf; + if(shmctl(shmid, IPC_STAT, &buf)) WARNX("Can't get SHM data"); + else DBG("SHM size = %zd", buf.shm_segsz); +#endif } flags = (imsize) ? 0 : SHM_RDONLY; // client opens memory in readonly mode - cc_IMG *ptr = shmat(shmid, NULL, flags); + cc_IMG *ptr = shmat(shmid, NULL, 0); if(ptr == (void*)-1){ if(imsize) WARN("Can't attach SHM segment %d", key); return NULL; diff --git a/ccdcapture.h b/ccdcapture.h index 38187bf..f4211fd 100644 --- a/ccdcapture.h +++ b/ccdcapture.h @@ -20,6 +20,7 @@ #include // FLEN_CARD #include +#include #include #include // for size_t @@ -29,10 +30,12 @@ // magic to mark our SHM #define CC_SHM_MAGIC (0xdeadbeef) +// semaphore for SHM protection +#define SEM_NAME "ccdcapture" + // base image parameters - sent by socket and stored in shared memory -typedef struct { +typedef struct __attribute__((packed)){ uint32_t MAGICK; // magick (DEADBEEF) - to mark our shm - pthread_mutex_t mutex; // shared mutex double timestamp; // timestamp of image taken uint8_t bitpix; // bits per pixel (8 or 16) int w, h; // image size @@ -221,7 +224,6 @@ typedef enum{ } cc_camera_state; // common information about everything -#define CC_CMD_INFO "info" #define CC_CMD_HELP "help" // restart server #define CC_CMD_RESTART "restartTheServer" @@ -240,7 +242,8 @@ typedef enum{ #define CC_CMD_HBIN "hbin" #define CC_CMD_VBIN "vbin" #define CC_CMD_CAMTEMPER "tcold" -#define CC_CMD_CAMFANSPD "ccdfanspeed" +#define CC_CMD_CAMFANSPD "camfanspeed" +#define CC_CMD_CAMFLAGS "camflags" #define CC_CMD_SHUTTER "shutter" #define CC_CMD_CONFIO "confio" #define CC_CMD_IO "io" @@ -269,11 +272,16 @@ typedef enum{ #define CC_CMD_FOCLIST "foclist" #define CC_CMD_FDEVNO "focdevno" #define CC_CMD_FGOTO "focpos" +#define CC_CMD_FMINPOS "focminpos" +#define CC_CMD_FMAXPOS "focmaxpos" +#define CC_CMD_FTEMP "foctemp" // wheel #define CC_CMD_WLIST "wlist" #define CC_CMD_WDEVNO "wdevno" #define CC_CMD_WPOS "wpos" +#define CC_CMD_WMAXPOS "wmaxpos" +#define CC_CMD_WTEMP "wtemp" typedef enum{ // parameter type CC_PAR_NONE, // no parameter @@ -337,3 +345,8 @@ cc_hresult cc_getfloat(int fd, cc_strbuff *cbuf, const char *cmd, float *val); char *cc_nextkw(char *buf, char record[FLEN_CARD], int newlines); size_t cc_kwfromfile(cc_IMG *img, char *filename); //int cc_charbuf2kw(cc_charbuff *b, fitsfile *f); + +void cc_init_sem(int isserver); +int cc_lock_shm(int isserver); +void cc_unlock_shm(); +void cc_remove_sem(); diff --git a/ccdfunc.c b/ccdfunc.c index dce7e36..c1b00dd 100644 --- a/ccdfunc.c +++ b/ccdfunc.c @@ -384,11 +384,11 @@ void focusers(){ if(num < 0) num = 0; if(num > focuser->Ndevices - 1){ WARNX(_("Found %d focusers, you point number %d"), focuser->Ndevices, num); - return; + goto retn; } if(!focuser->setDevNo(num)){ WARNX(_("Can't set active focuser number")); - return; + goto retn; } char buf[BUFSIZ]; if(focuser->getModelName(buf, BUFSIZ)){ @@ -402,18 +402,18 @@ void focusers(){ float minpos, maxpos, curpos; if(!focuser->getMinPos(&minpos) || !focuser->getMaxPos(&maxpos)){ WARNX(_("Can't get focuser limit positions")); - return; + goto retn; } verbose(1, "FOCMINPOS=%g", minpos); verbose(1, "FOCMAXPOS=%g", maxpos); DBG("FOCMINPOS=%g, FOCMAXPOS=%g", minpos, maxpos); if(!focuser->getPos(&curpos)){ WARNX(_("Can't get current focuser position")); - return; + goto retn; } verbose(1, "FOCPOS=%g", curpos); DBG("Curpos = %g", curpos); - if(isnan(GP->gotopos) && isnan(GP->addsteps)) return; // no focuser commands + if(isnan(GP->gotopos) && isnan(GP->addsteps)) goto retn; // no focuser commands float tagpos = 0.; if(!isnan(GP->gotopos)){ // set absolute position tagpos = GP->gotopos; @@ -423,13 +423,15 @@ void focusers(){ DBG("tagpos: %g", tagpos); if(tagpos < minpos || tagpos > maxpos){ WARNX(_("Can't set position %g: out of limits [%g, %g]"), tagpos, minpos, maxpos); - return; + goto retn; } if(tagpos - minpos < __FLT_EPSILON__){ if(!focuser->home(GP->async)) WARNX(_("Can't home focuser")); }else{ if(!focuser->setAbsPos(GP->async, tagpos)) WARNX(_("Can't set position %g"), tagpos); } +retn: + focclose(); } cc_Wheel *startWheel(){ @@ -472,11 +474,11 @@ void wheels(){ if(num < 0) num = 0; if(num > wheel->Ndevices - 1){ WARNX(_("Found %d wheels, you point number %d"), wheel->Ndevices, num); - return; + goto retn; } if(!wheel->setDevNo(num)){ WARNX(_("Can't set active wheel number")); - return; + goto retn; } char buf[BUFSIZ]; if(wheel->getModelName(buf, BUFSIZ)){ @@ -492,17 +494,19 @@ void wheels(){ }else WARNX("Can't get current wheel position"); if(!wheel->getMaxPos(&maxpos)){ WARNX(_("Can't get max wheel position")); - return; + goto retn; } verbose(1, "WHEELMAXPOS=%d", maxpos); pos = GP->setwheel; - if(pos == -1) return; // no wheel commands + if(pos == -1) goto retn; // no wheel commands if(pos < 0 || pos > maxpos){ WARNX(_("Wheel position should be from 0 to %d"), maxpos); - return; + goto retn; } if(!wheel->setPos(pos)) WARNX(_("Can't set wheel position %d"), pos); +retn: + closewheel(); } /* static void closeall(){ @@ -923,6 +927,7 @@ int start_socket(int isserver){ } if(isserver){ imsock = cc_open_socket(TRUE, GP->imageport, 2); // image socket should be networked + DBG("imsock=%d, image port=%s", imsock, GP->imageport); server(sock, imsock); }else{ #ifdef IMAGEVIEW diff --git a/client.c b/client.c index 69b67c1..3c6d6e6 100644 --- a/client.c +++ b/client.c @@ -53,7 +53,7 @@ static int oldgrabno = 0; // IPC key for shared memory static cc_IMG ima = {0}, *shmima = NULL; // ima - local storage, shmima - shm (if available) static size_t imbufsz = 0; // image buffer for allocated `ima` -static uint8_t *imbuf = NULL; +static uint8_t *imbuf = NULL; // we can't use shmima->data as it belongs to server, so we use `imbuf` and set ima.data = imbuf // read message from queue or file descriptor @@ -118,6 +118,7 @@ static int getans(int sock, const char *msg){ double t0 = sl_dtime(); char *ans = NULL; double tmout = answer_timeout; + // TODO: increase first timeout up to 15-30s (add key?) if(msg) tmout *= 5.; // make first timeout larger for slow networks cc_hresult res = CC_RESULT_FAIL; while(sl_dtime() - t0 < tmout){ @@ -161,16 +162,35 @@ static void send_headers(int sock){ DBG("infty=%d", GP->infty); if(GP->infty > -1) SENDMSGW(CC_CMD_INFTY, "=%d", GP->infty); // common information - SENDMSG(CC_CMD_INFO); + if(GP->listdevices) SENDCMDW(CC_CMD_CAMLIST); + if(GP->camdevno > -1) SENDMSGW(CC_CMD_CAMDEVNO, "=%d", GP->camdevno); + if(GP->info){ + SENDCMDW(CC_CMD_HBIN); + SENDCMDW(CC_CMD_VBIN); + SENDCMDW(CC_CMD_CAMTEMPER); + SENDCMDW(CC_CMD_EXPOSITION); + SENDCMDW(CC_CMD_EXPSTATE); + } // focuser if(GP->listdevices) SENDMSG(CC_CMD_FOCLIST); if(GP->focdevno > -1) SENDMSG(CC_CMD_FDEVNO "=%d", GP->focdevno); + if(GP->info){ + SENDCMDW(CC_CMD_FGOTO); + SENDCMDW(CC_CMD_FMINPOS); + SENDCMDW(CC_CMD_FMAXPOS); + SENDCMDW(CC_CMD_FTEMP); + } if(!isnan(GP->gotopos)){ SENDMSGW(CC_CMD_FGOTO, "=%g", GP->gotopos); } // wheel if(GP->listdevices) SENDCMDW(CC_CMD_WLIST); if(GP->whldevno > -1) SENDMSGW(CC_CMD_WDEVNO, "=%d", GP->whldevno); + if(GP->info){ + SENDCMDW(CC_CMD_WPOS); + SENDCMDW(CC_CMD_WMAXPOS); + SENDCMDW(CC_CMD_WTEMP); + } if(GP->setwheel > -1) SENDMSGW(CC_CMD_WPOS, "=%d", GP->setwheel); DBG("nxt"); // CCD/CMOS @@ -193,8 +213,6 @@ static void send_headers(int sock){ SENDMSGW(CC_CMD_FRAMEFORMAT, "=%d,%d,%d,%d", GP->X0, GP->Y0, GP->X1, GP->Y1); } if(GP->cancelexpose) SENDMSGW(CC_CMD_EXPSTATE, "=%d", CAMERA_IDLE); - if(GP->listdevices) SENDCMDW(CC_CMD_CAMLIST); - if(GP->camdevno > -1) SENDMSGW(CC_CMD_CAMDEVNO, "=%d", GP->camdevno); if(GP->hbin) SENDMSGW(CC_CMD_HBIN, "=%d", GP->hbin); if(GP->vbin) SENDMSGW(CC_CMD_VBIN, "=%d", GP->vbin); if(!isnan(GP->temperature)) SENDMSGW(CC_CMD_CAMTEMPER, "=%g", GP->temperature); @@ -255,9 +273,11 @@ static int getimage(int askheader){ static double oldtimestamp = -1.; TIMESTAMP("Get image sizes"); if(shmima){ // read image from shared memory + DBG("Try to read from SHM"); double t0 = sl_dtime(); while(sl_dtime() - t0 < MUTEX_LOCK_TMOUT){ - if(client_lock_shm(shmima)){ + if(cc_lock_shm(FALSE)){ + DBG("Locked"); shmlocked = TRUE; break; } @@ -269,6 +289,7 @@ static int getimage(int askheader){ } memcpy(&ima, shmima, sizeof(cc_IMG)); }else{ // get image by socket + DBG("Open socket @ %s", GP->imageport); imsock = cc_open_socket(FALSE, GP->imageport, TRUE); if(imsock < 0) ERRX("getimage(): can't open image transport socket"); // get image size @@ -292,11 +313,12 @@ static int getimage(int askheader){ ima.data = imbuf; // renew this value each time after getting `ima` from server TIMESTAMP("Start of data read"); if(shmima){ - uint8_t *datastart = (uint8_t*)shmima + sizeof(cc_IMG); + uint8_t *datastart = ((uint8_t*)shmima) + sizeof(cc_IMG); + DBG("first image byte: %d", *datastart); memcpy(imbuf, datastart, ima.bytelen); TIMESTAMP("Got by shared memory"); if(!askheader){ - unlock_shm(shmima); + cc_unlock_shm(); shmlocked = FALSE; } ret = TRUE; @@ -337,11 +359,17 @@ static int getimage(int askheader){ }else WARNX("Still got old image"); eofg: if(imsock != -1) close(imsock); - if(shmlocked) unlock_shm(shmima); + if(shmlocked) cc_unlock_shm(); return ret; } void client(int sock){ + if(sock < 0) ERRX("Can't run without command socket"); + if(!GP->forceimsock && !shmima){ // init shm buffer if user don't ask to force workign through image socket + shmima = cc_getshm(GP->shmkey, 0); // try to init client shm + cc_init_sem(FALSE); + DBG("Got access to shared memory: %s", shmima ? "OK" : "FAIL"); + } if(GP->restart){ SENDCMDW(CC_CMD_RESTART); return; @@ -349,89 +377,86 @@ void client(int sock){ TIMEINIT(); send_headers(sock); TIMESTAMP("Got headers"); - double t0 = sl_dtime(), tw = t0; + double t0 = sl_dtime(), tw, tstart; int Nremain = 0, nframe = 1; // if client gives filename/prefix or Nframes, make exposition if((GP->outfile && *GP->outfile) || (GP->outfileprefix && *GP->outfileprefix) || GP->nframes > 0){ Nremain = GP->nframes; if(Nremain < 1) Nremain = 1; - else GP->waitexpend = TRUE; // N>1 - wait for exp ends - SENDMSGW(CC_CMD_EXPSTATE, "=%d", CAMERA_CAPTURE); - }else{ - int cntr = 0; - while(sl_dtime() - t0 < CC_WAIT_TIMEOUT && cntr < 3) - if(!getans(sock, NULL)) ++cntr; - DBG("RETURN: no more data"); - return; - } - double timeout = GP->waitexpend ? CC_CLIENT_TIMEOUT : CC_WAIT_TIMEOUT; + SENDMSGW(CC_CMD_EXPSTATE, "=%d", CAMERA_CAPTURE); // call to start capture + } else return; // just send headers and exit + double timeout = CC_CLIENT_TIMEOUT; verbose(1, "Exposing frame 1..."); - if(GP->waitexpend){ - atomic_store(&expstate, CAMERA_CAPTURE); // could be changed earlier - verbose(2, "Wait for exposition end"); - } + atomic_store(&expstate, CAMERA_CAPTURE); // could be changed earlier + verbose(2, "Wait for exposition end"); + t0 = sl_dtime(); + tw = tstart = t0; while(sl_dtime() - t0 < timeout){ - if(GP->waitexpend && sl_dtime() - tw > CC_WAIT_TIMEOUT){ + if(sl_dtime() - tw > CC_WAIT_TIMEOUT){ SENDCMDW(CC_CMD_TREMAIN); // get remained time tw = sl_dtime(); - sprintf(sendbuf, "%s", CC_CMD_EXPSTATE); - cc_sendstrmessage(sock, sendbuf); } - if(getans(sock, NULL)){ // got next portion of data - DBG("server message"); - t0 = sl_dtime(); - int curst = atomic_load(&expstate); - if(curst == CAMERA_ERROR){ - WARNX(_("Can't make exposition")); - if(Nremain > 1){ - verbose(1, "Exposing frame %d...", nframe); - SENDMSGW(CC_CMD_EXPSTATE, "=%d", CAMERA_CAPTURE); - } - continue; + if(sl_dtime() - tstart < GP->exptime){ + t0 = sl_dtime(); // refresh timeout until exp not ends + }else{ + SENDCMDW(CC_CMD_EXPSTATE); +#ifdef EBUG + usleep(300000); +#endif + } + int curst = atomic_load(&expstate); + if(curst == CAMERA_ERROR){ + WARNX(_("Can't make exposition")); + if(Nremain > 1){ + verbose(1, "Exposing frame %d...", nframe); + SENDMSGW(CC_CMD_EXPSTATE, "=%d", CAMERA_CAPTURE); + tstart = sl_dtime(); } - //if(expstate != CAMERA_CAPTURE){ - if(curst == CAMERA_FRAMERDY){ - atomic_store(&expstate, CAMERA_IDLE); - if(Nremain > 1){ - verbose(1, "Exposing frame %d...", nframe+1); - SENDMSGW(CC_CMD_EXPSTATE, "=%d", CAMERA_CAPTURE); + continue; + } + //if(expstate != CAMERA_CAPTURE){ + if(curst == CAMERA_FRAMERDY){ + atomic_store(&expstate, CAMERA_IDLE); + if(Nremain > 1){ + verbose(1, "Exposing frame %d...", nframe+1); + SENDMSGW(CC_CMD_EXPSTATE, "=%d", CAMERA_CAPTURE); + tstart = sl_dtime(); + } + verbose(2, "Frame ready, try to grab"); + int failed = TRUE; + if(!getimage(TRUE)){ + WARNX("Can't get next image"); + }else{ + if(saveFITS(&ima, &lastfilename)){ + --Nremain; + ++nframe; + failed = FALSE; } - verbose(2, "Frame ready, try to grab"); - int failed = TRUE; - if(!getimage(TRUE)){ - WARNX("Can't get next image"); - }else{ - if(saveFITS(&ima, &lastfilename)){ - --Nremain; - ++nframe; - failed = FALSE; + } + if(failed && Nremain == 1){ // last image -> should re-expose + verbose(1, "Exposing frame %d...", nframe); + SENDMSGW(CC_CMD_EXPSTATE, "=%d", CAMERA_CAPTURE); + tstart = sl_dtime(); + } + if(Nremain > 0){ + if(GP->pause_len > 0){ + double delta, time1 = sl_dtime() + GP->pause_len; + while(1){ + SENDCMDW(CC_CMD_CAMTEMPER); + if((delta = time1 - sl_dtime()) < __FLT_EPSILON__) break; + if(delta > 1.) verbose(1, _("%d seconds till pause ends\n"), (int)delta); + if(delta > 6.) sleep(5); + else if(delta > 1.) sleep((int)delta); + else usleep((int)(delta*1e6 + 1)); } } - if(failed && Nremain == 1){ // last image -> should re-expose - verbose(1, "Exposing frame %d...", nframe); - SENDMSGW(CC_CMD_EXPSTATE, "=%d", CAMERA_CAPTURE); - } - if(Nremain > 0){ - if(GP->pause_len > 0){ - double delta, time1 = sl_dtime() + GP->pause_len; - while(1){ - SENDCMDW(CC_CMD_CAMTEMPER); - if((delta = time1 - sl_dtime()) < __FLT_EPSILON__) break; - if(delta > 1.) verbose(1, _("%d seconds till pause ends\n"), (int)delta); - if(delta > 6.) sleep(5); - else if(delta > 1.) sleep((int)delta); - else usleep((int)(delta*1e6 + 1)); - } - } - }else{ - GP->waitexpend = 0; - DBG("All images saved -> exit"); - break; - } + }else{ + DBG("All images saved -> exit"); + break; } } } - if(GP->waitexpend) WARNX(_("Server timeout")); + if(Nremain > 0) WARNX(_("Server timeout")); } #ifdef IMAGEVIEW @@ -442,6 +467,7 @@ void init_grab_sock(int sock){ send_headers(sock); if(!GP->forceimsock && !shmima){ // init shm buffer if user don't ask to work through image socket shmima = cc_getshm(GP->shmkey, 0); // try to init client shm + cc_init_sem(FALSE); } } diff --git a/cmdlnopts.c b/cmdlnopts.c index 9830898..6efbfaf 100644 --- a/cmdlnopts.c +++ b/cmdlnopts.c @@ -56,7 +56,7 @@ sl_option_t cmdlnopts[] = { {"focdevno",NEED_ARG, NULL, NA, arg_int, APTR(&G.focdevno), N_("focuser device number (if many: 0, 1, 2 etc)")}, {"help", NO_ARGS, &help, 1, arg_none, NULL, N_("show this help")}, {"rewrite", NO_ARGS, &G.rewrite,1, arg_none, NULL, N_("rewrite output file if exists")}, - {"verbose", NO_ARGS, NULL, 'V', arg_none, APTR(&G.verbose), N_("verbose level (-V - messages, -VV - debug, -VVV - all shit)")}, + {"verbose", NO_ARGS, NULL, 'V', arg_none, APTR(&G.verbose), N_("verbose level (-V - main messages, -VV - secondary messages, -VVV - debug)")}, {"dark", NO_ARGS, NULL, 'd', arg_int, APTR(&G.dark), N_("not open shutter, when exposing (\"dark frames\")")}, {"8bit", NO_ARGS, NULL, '8', arg_int, APTR(&G._8bit), N_("run in 8-bit mode")}, {"fast", NO_ARGS, NULL, 'f', arg_none, APTR(&G.fast), N_("fast readout mode")}, @@ -71,7 +71,6 @@ sl_option_t cmdlnopts[] = { {"prog-id", NEED_ARG, NULL, 'P', arg_string, APTR(&G.prog_id), N_("observing program name")}, {"addrec", MULT_PAR, NULL, 'r', arg_string, APTR(&G.addhdr), N_("add records to header from given file[s]")}, {"outfile", NEED_ARG, NULL, 'o', arg_string, APTR(&G.outfile), N_("output file name")}, - {"wait", NO_ARGS, &G.waitexpend,1,arg_none, NULL, N_("wait while exposition ends")}, {"nflushes",NEED_ARG, NULL, 'l', arg_int, APTR(&G.nflushes), N_("N flushes before exposing (default: 1)")}, {"hbin", NEED_ARG, NULL, 'h', arg_int, APTR(&G.hbin), N_("horizontal binning to N pixels")}, @@ -111,6 +110,7 @@ sl_option_t cmdlnopts[] = { {"viewer", NO_ARGS, &G.viewer,1, arg_none, NULL, N_("passive viewer (only get last images)")}, {"restart", NO_ARGS, &G.restart,1, arg_none, NULL, N_("restart image server")}, {"timeout", NEED_ARG, NULL, '0', arg_double, APTR(&G.anstmout), N_("network answer timeout (default: 0.1s)")}, + {"info", NO_ARGS, &G.info, 1, arg_none, NULL, N_("get base information about connected hardware (also increasing text messages level to 2)")}, {"shmkey", NEED_ARG, NULL, 'k', arg_int, APTR(&G.shmkey), N_("shared memory (with image data) key (default: 7777777)")}, {"forceimsock",NO_ARGS, &G.forceimsock,1, arg_none, NULL, N_("force using image through socket transition even if can use SHM")}, diff --git a/cmdlnopts.h b/cmdlnopts.h index 6ad6fbc..4074a55 100644 --- a/cmdlnopts.h +++ b/cmdlnopts.h @@ -42,9 +42,9 @@ typedef struct{ char **addhdr; // list of files from which to add header records char **plugincmd; // plugin commands int restart; // restart server - int waitexpend; // wait while exposition ends int cancelexpose; // cancel exp (for Grasshopper - forbid forever) int client; // run as client + int info; // show main information about camera etc. in client's terminal int viewer; // passive client (only get last images) int listdevices; // list connected devices int fanspeed; // fan speed: 0-2 diff --git a/locale/ru/LC_MESSAGES/ccd_capture.mo b/locale/ru/LC_MESSAGES/ccd_capture.mo index 37edc13..198f5c3 100644 Binary files a/locale/ru/LC_MESSAGES/ccd_capture.mo and b/locale/ru/LC_MESSAGES/ccd_capture.mo differ diff --git a/locale/ru/messages.po b/locale/ru/messages.po index 0ab00eb..e13cfd5 100644 --- a/locale/ru/messages.po +++ b/locale/ru/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-06-02 18:19+0300\n" +"POT-Creation-Date: 2026-06-04 18:22+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -62,7 +62,8 @@ msgid "rewrite output file if exists" msgstr "" #: cmdlnopts.c:59 -msgid "verbose level (-V - messages, -VV - debug, -VVV - all shit)" +msgid "" +"verbose level (-V - main messages, -VV - secondary messages, -VVV - debug)" msgstr "" #: cmdlnopts.c:60 @@ -117,142 +118,144 @@ msgstr "" msgid "output file name" msgstr "" -#: cmdlnopts.c:74 -msgid "wait while exposition ends" -msgstr "" - -#: cmdlnopts.c:76 +#: cmdlnopts.c:75 msgid "N flushes before exposing (default: 1)" msgstr "" -#: cmdlnopts.c:77 +#: cmdlnopts.c:76 msgid "horizontal binning to N pixels" msgstr "" -#: cmdlnopts.c:78 +#: cmdlnopts.c:77 msgid "vertical binning to N pixels" msgstr "" -#: cmdlnopts.c:79 +#: cmdlnopts.c:78 msgid "make series of N frames" msgstr "" -#: cmdlnopts.c:80 +#: cmdlnopts.c:79 msgid "make pause for N seconds between expositions" msgstr "" -#: cmdlnopts.c:81 +#: cmdlnopts.c:80 msgid "set exposure time to given value (seconds!)" msgstr "" -#: cmdlnopts.c:82 +#: cmdlnopts.c:81 msgid "cancel current exposition" msgstr "" -#: cmdlnopts.c:83 +#: cmdlnopts.c:82 msgid "" "absolute (not divided by binning!) frame X0 coordinate (-1 - all with " "overscan)" msgstr "" -#: cmdlnopts.c:84 +#: cmdlnopts.c:83 msgid "absolute frame Y0 coordinate (-1 - all with overscan)" msgstr "" -#: cmdlnopts.c:85 +#: cmdlnopts.c:84 msgid "absolute frame X1 coordinate (-1 - all with overscan)" msgstr "" -#: cmdlnopts.c:86 +#: cmdlnopts.c:85 msgid "absolute frame Y1 coordinate (-1 - all with overscan)" msgstr "" -#: cmdlnopts.c:88 +#: cmdlnopts.c:87 msgid "open shutter" msgstr "" -#: cmdlnopts.c:89 +#: cmdlnopts.c:88 msgid "close shutter" msgstr "" -#: cmdlnopts.c:90 +#: cmdlnopts.c:89 msgid "run exposition on LOW @ pin5 I/O port" msgstr "" -#: cmdlnopts.c:91 +#: cmdlnopts.c:90 msgid "run exposition on HIGH @ pin5 I/O port" msgstr "" -#: cmdlnopts.c:92 +#: cmdlnopts.c:91 msgid "get value of I/O port pins" msgstr "" -#: cmdlnopts.c:93 +#: cmdlnopts.c:92 msgid "move stepper motor asynchronous" msgstr "" -#: cmdlnopts.c:95 +#: cmdlnopts.c:94 msgid "set I/O port pins to given value (decimal number, pin1 is LSB)" msgstr "" -#: cmdlnopts.c:96 +#: cmdlnopts.c:95 msgid "" "configure I/O port pins to given value (decimal number, pin1 is LSB, 1 == " "output, 0 == input)" msgstr "" -#: cmdlnopts.c:98 +#: cmdlnopts.c:97 msgid "move focuser to absolute position, mm" msgstr "" -#: cmdlnopts.c:99 +#: cmdlnopts.c:98 msgid "move focuser to relative position, mm (only for standalone)" msgstr "" -#: cmdlnopts.c:101 +#: cmdlnopts.c:100 msgid "set wheel position" msgstr "" -#: cmdlnopts.c:103 +#: cmdlnopts.c:102 msgid "CMOS gain level" msgstr "" -#: cmdlnopts.c:104 +#: cmdlnopts.c:103 msgid "CMOS brightness level" msgstr "" -#: cmdlnopts.c:106 +#: cmdlnopts.c:105 msgid "logging file name (if run as server)" msgstr "" -#: cmdlnopts.c:107 +#: cmdlnopts.c:106 msgid "UNIX socket name (command socket)" msgstr "" -#: cmdlnopts.c:108 +#: cmdlnopts.c:107 msgid "local INET command socket port" msgstr "" -#: cmdlnopts.c:109 +#: cmdlnopts.c:108 msgid "INET image socket port" msgstr "" -#: cmdlnopts.c:110 +#: cmdlnopts.c:109 msgid "run as client" msgstr "" -#: cmdlnopts.c:111 +#: cmdlnopts.c:110 msgid "passive viewer (only get last images)" msgstr "" -#: cmdlnopts.c:112 +#: cmdlnopts.c:111 msgid "restart image server" msgstr "" -#: cmdlnopts.c:113 +#: cmdlnopts.c:112 msgid "network answer timeout (default: 0.1s)" msgstr "" +#: cmdlnopts.c:113 +msgid "" +"get base information about connected hardware (also increasing text messages " +"level to 2)" +msgstr "" + #: cmdlnopts.c:115 msgid "shared memory (with image data) key (default: 7777777)" msgstr "" @@ -491,12 +494,12 @@ msgstr "" msgid "Can't set brightness to %g" msgstr "" -#: ccdfunc.c:690 server.c:332 +#: ccdfunc.c:690 server.c:323 #, c-format msgid "Can't set binning %dx%d" msgstr "" -#: ccdfunc.c:702 server.c:333 +#: ccdfunc.c:702 server.c:324 msgid "Can't set given geometry" msgstr "" @@ -544,11 +547,11 @@ msgstr "" msgid "Capture frame %d" msgstr "" -#: ccdfunc.c:750 ccdfunc.c:827 server.c:202 server.c:203 +#: ccdfunc.c:750 ccdfunc.c:827 server.c:184 server.c:185 msgid "Camera plugin have no function `start exposition`" msgstr "" -#: ccdfunc.c:752 ccdfunc.c:829 server.c:208 server.c:209 +#: ccdfunc.c:752 ccdfunc.c:829 server.c:190 server.c:191 msgid "Can't start exposition" msgstr "" @@ -560,7 +563,7 @@ msgstr "" msgid "Read grabbed image" msgstr "" -#: ccdfunc.c:762 ccdfunc.c:842 server.c:226 server.c:227 +#: ccdfunc.c:762 ccdfunc.c:842 server.c:208 server.c:209 msgid "Camera plugin have no function `capture`" msgstr "" @@ -568,7 +571,7 @@ msgstr "" msgid "Can't grab image" msgstr "" -#: ccdfunc.c:778 client.c:420 +#: ccdfunc.c:778 client.c:447 #, c-format msgid "%d seconds till pause ends\n" msgstr "" @@ -577,15 +580,15 @@ msgstr "" msgid "Some error when capture" msgstr "" -#: server.c:253 +#: server.c:235 msgid "No camera device" msgstr "" -#: client.c:385 +#: client.c:409 msgid "Can't make exposition" msgstr "" -#: client.c:434 +#: client.c:459 msgid "Server timeout" msgstr "" diff --git a/locale/ru/ru.po b/locale/ru/ru.po index 9133992..e20c037 100644 --- a/locale/ru/ru.po +++ b/locale/ru/ru.po @@ -7,7 +7,7 @@ msgid "" msgstr "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" - "POT-Creation-Date: 2026-06-02 18:19+0300\n" + "POT-Creation-Date: 2026-06-04 18:16+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -21,7 +21,7 @@ msgstr "Project-Id-Version: PACKAGE VERSION\n" msgid "%.1f seconds till exposition ends" msgstr "%.1f секунд до окончания экспозиции" -#: ccdfunc.c:778 client.c:420 +#: ccdfunc.c:778 client.c:447 #, c-format msgid "%d seconds till pause ends\n" msgstr "%d секунд до окончания паузы\n" @@ -30,11 +30,11 @@ msgstr "%d msgid "Already initialized!" msgstr "Уже инициализировано!" -#: cmdlnopts.c:104 +#: cmdlnopts.c:103 msgid "CMOS brightness level" msgstr "уровень яркости CMOS" -#: cmdlnopts.c:103 +#: cmdlnopts.c:102 msgid "CMOS gain level" msgstr "уровень Gain CMOS" @@ -66,12 +66,12 @@ msgstr " msgid "Camera plugin have no fun speed setter" msgstr "У плагина камеры нет особых команд" -#: ccdfunc.c:762 ccdfunc.c:842 server.c:226 server.c:227 +#: ccdfunc.c:762 ccdfunc.c:842 server.c:208 server.c:209 #, fuzzy msgid "Camera plugin have no function `capture`" msgstr "У плагина камеры нет особых команд" -#: ccdfunc.c:750 ccdfunc.c:827 server.c:202 server.c:203 +#: ccdfunc.c:750 ccdfunc.c:827 server.c:184 server.c:185 #, fuzzy msgid "Camera plugin have no function `start exposition`" msgstr "У плагина камеры нет особых команд" @@ -130,7 +130,7 @@ msgstr " msgid "Can't init mutex!" msgstr "Не могу инициализировать мьютекс!" -#: client.c:385 +#: client.c:409 msgid "Can't make exposition" msgstr "Не могу выполнить экспозицию" @@ -174,7 +174,7 @@ msgstr " msgid "Can't set active wheel number" msgstr "Не могу установить номер активного колеса" -#: ccdfunc.c:690 server.c:332 +#: ccdfunc.c:690 server.c:323 #, c-format msgid "Can't set binning %dx%d" msgstr "Не могу установить биннинг %dx%d" @@ -202,7 +202,7 @@ msgstr " msgid "Can't set gain to %g" msgstr "Не могу установить Gain в %g" -#: ccdfunc.c:702 server.c:333 +#: ccdfunc.c:702 server.c:324 msgid "Can't set given geometry" msgstr "Не могу установить геометрию" @@ -225,7 +225,7 @@ msgstr " msgid "Can't set wheel position %d" msgstr "Не могу установить положение колеса %d" -#: ccdfunc.c:752 ccdfunc.c:829 server.c:208 server.c:209 +#: ccdfunc.c:752 ccdfunc.c:829 server.c:190 server.c:191 msgid "Can't start exposition" msgstr "Не могу начать экспозицию" @@ -291,7 +291,7 @@ msgstr " msgid "Histogram conversion: %s" msgstr "Преобразование гистограммы: %s" -#: cmdlnopts.c:109 +#: cmdlnopts.c:108 msgid "INET image socket port" msgstr "порт локального сетевого сокета передачи изображения" @@ -300,11 +300,11 @@ msgstr " msgid "Image stat:\n" msgstr "Статистика по изображению: \n" -#: cmdlnopts.c:76 +#: cmdlnopts.c:75 msgid "N flushes before exposing (default: 1)" msgstr "N засвечиваний перед экспозицией (по умолчанию: 1)" -#: server.c:253 +#: server.c:235 msgid "No camera device" msgstr "Не указано устройство камеры" @@ -338,7 +338,7 @@ msgstr " msgid "Readout mode: %s" msgstr "Режим считывания: %s" -#: client.c:434 +#: client.c:459 msgid "Server timeout" msgstr "Таймаут сервера" @@ -376,7 +376,7 @@ msgstr " msgid "Try to write %d to I/O port" msgstr "Попытка записи %d в порт I/O" -#: cmdlnopts.c:107 +#: cmdlnopts.c:106 msgid "UNIX socket name (command socket)" msgstr "имя UNIX-сокета" @@ -389,21 +389,21 @@ msgstr " msgid "Wheel position should be from 0 to %d" msgstr "Позиция колеса должна быть от 0 до %d" -#: cmdlnopts.c:83 +#: cmdlnopts.c:82 msgid "absolute (not divided by binning!) frame X0 coordinate (-1 - all " "with overscan)" msgstr "абсолютная (не деленная на биннинг!) координата X0 (-1 - включая " "оверскан)" -#: cmdlnopts.c:85 +#: cmdlnopts.c:84 msgid "absolute frame X1 coordinate (-1 - all with overscan)" msgstr "абсолютная координата X1 (-1 - включая оверскан)" -#: cmdlnopts.c:84 +#: cmdlnopts.c:83 msgid "absolute frame Y0 coordinate (-1 - all with overscan)" msgstr "абсолютная координата Y0 (-1 - включая оверскан)" -#: cmdlnopts.c:86 +#: cmdlnopts.c:85 msgid "absolute frame Y1 coordinate (-1 - all with overscan)" msgstr "абсолютная координата Y1 (-1 - включая оверскан)" @@ -419,11 +419,11 @@ msgstr " msgid "camera device plugin (e.g. devfli.so)" msgstr "плагин камеры (например, devfli.so)" -#: cmdlnopts.c:82 +#: cmdlnopts.c:81 msgid "cancel current exposition" msgstr "отмена текущей экспозиции" -#: cmdlnopts.c:89 +#: cmdlnopts.c:88 msgid "close shutter" msgstr "закрыть затвор" @@ -431,7 +431,7 @@ msgstr " msgid "common device plugin (e.g devfli.so)" msgstr "общий плагин для всех устройств (например, devfli.so)" -#: cmdlnopts.c:96 +#: cmdlnopts.c:95 msgid "configure I/O port pins to given value (decimal number, pin1 is LSB, " "1 == output, 0 == input)" msgstr "сконфигурировать порт I/O в заданное состояние (десятичное число, " @@ -462,11 +462,16 @@ msgid "force using image through socket transition even if can use SHM" msgstr "принудительно брать изображения из сокета даже если возможно " "пользоваться разделяемой памятью" -#: cmdlnopts.c:92 +#: cmdlnopts.c:113 +msgid "get base information about connected hardware (also increasing text " + "messages level to 2)" +msgstr "" + +#: cmdlnopts.c:91 msgid "get value of I/O port pins" msgstr "получить значение порта I/O" -#: cmdlnopts.c:77 +#: cmdlnopts.c:76 msgid "horizontal binning to N pixels" msgstr "горизонтальный биннинг в N пикселей" @@ -478,36 +483,36 @@ msgstr " msgid "list connected devices" msgstr "список подключенных устройств" -#: cmdlnopts.c:108 +#: cmdlnopts.c:107 msgid "local INET command socket port" msgstr "порт локального сетевого сокета" -#: cmdlnopts.c:106 +#: cmdlnopts.c:105 msgid "logging file name (if run as server)" msgstr "имя файла логгирования (если запущен сервер)" -#: cmdlnopts.c:80 +#: cmdlnopts.c:79 msgid "make pause for N seconds between expositions" msgstr "пауза в N секунд между экспозициями" -#: cmdlnopts.c:79 +#: cmdlnopts.c:78 msgid "make series of N frames" msgstr "последовательность из N кадров" -#: cmdlnopts.c:98 +#: cmdlnopts.c:97 msgid "move focuser to absolute position, mm" msgstr "переместить фокусер в абсолютное положение, мм" -#: cmdlnopts.c:99 +#: cmdlnopts.c:98 msgid "move focuser to relative position, mm (only for standalone)" msgstr "переместить фокусер в относительное положение, мм (не для сервер/" "клиент)" -#: cmdlnopts.c:93 +#: cmdlnopts.c:92 msgid "move stepper motor asynchronous" msgstr "асинхронное движение шагового двигателя" -#: cmdlnopts.c:113 +#: cmdlnopts.c:112 msgid "network answer timeout (default: 0.1s)" msgstr "" @@ -539,7 +544,7 @@ msgstr " msgid "on" msgstr "вкл" -#: cmdlnopts.c:88 +#: cmdlnopts.c:87 msgid "open shutter" msgstr "открыть затвор" @@ -547,7 +552,7 @@ msgstr " msgid "output file name" msgstr "имя файла" -#: cmdlnopts.c:111 +#: cmdlnopts.c:110 msgid "passive viewer (only get last images)" msgstr "пассивный просмотр (только последние кадры)" @@ -555,7 +560,7 @@ msgstr " msgid "program author" msgstr "автор программы" -#: cmdlnopts.c:112 +#: cmdlnopts.c:111 msgid "restart image server" msgstr "перезапуск сервера" @@ -563,15 +568,15 @@ msgstr " msgid "rewrite output file if exists" msgstr "перезапись выходного файла" -#: cmdlnopts.c:110 +#: cmdlnopts.c:109 msgid "run as client" msgstr "запустить клиент" -#: cmdlnopts.c:91 +#: cmdlnopts.c:90 msgid "run exposition on HIGH @ pin5 I/O port" msgstr "запуск экспозиции по ВЫСОКОМУ сигналу на контакте 5 порта I/O" -#: cmdlnopts.c:90 +#: cmdlnopts.c:89 msgid "run exposition on LOW @ pin5 I/O port" msgstr "запуск экспозиции по НИЗКОМУ сигналу на контакте 5 порта I/O" @@ -583,11 +588,11 @@ msgstr "8- msgid "set CCD temperature to given value (degr C)" msgstr "установить температуру светоприемника (градЦ)" -#: cmdlnopts.c:95 +#: cmdlnopts.c:94 msgid "set I/O port pins to given value (decimal number, pin1 is LSB)" msgstr "установить порт I/O (десятичное число, pin1 - младший бит)" -#: cmdlnopts.c:81 +#: cmdlnopts.c:80 msgid "set exposure time to given value (seconds!)" msgstr "установить время экспозиции (секунды!)" @@ -595,7 +600,7 @@ msgstr " msgid "set fan speed (0 - off, 1 - low, 2 - high)" msgstr "установить скорость вентилятора (0 - выкл, 1 - низкая, 2 - высокая)" -#: cmdlnopts.c:101 +#: cmdlnopts.c:100 msgid "set wheel position" msgstr "установить положение колеса" @@ -612,20 +617,21 @@ msgid "start (!=0) or stop(==0) infinity capturing loop" msgstr "начать (!=0) или закончить (==0) бесконечный цикл захвата изображений" #: cmdlnopts.c:59 -msgid "verbose level (-V - messages, -VV - debug, -VVV - all shit)" +#, fuzzy +msgid "verbose level (-V - main messages, -VV - secondary messages, -VVV - " + "debug)" msgstr "уровень болтливости (-V - сообщения, -VV - отладка, -VVV - все)" -#: cmdlnopts.c:78 +#: cmdlnopts.c:77 msgid "vertical binning to N pixels" msgstr "вертикальный биннинг в N пикселей" -#: cmdlnopts.c:74 -msgid "wait while exposition ends" -msgstr "ждать, пока не кончится экспозиция" - #: cmdlnopts.c:52 msgid "wheel device plugin (e.g. devdummy.so)" msgstr "плагин устройства турели (например, devdummy.so)" #~ msgid "Camera device unknown" #~ msgstr "Устройство свеоприемника не опознано" + +#~ msgid "wait while exposition ends" +#~ msgstr "ждать, пока не кончится экспозиция" diff --git a/main.c b/main.c index ab90c9e..50eda93 100644 --- a/main.c +++ b/main.c @@ -34,6 +34,7 @@ #ifdef IMAGEVIEW #include "imageview.h" #endif +#include "server.h" #include "socket.h" static int isserver = FALSE; @@ -56,6 +57,7 @@ void signals(int signo){ if(signo) WARNX("Get signal %d - exit", signo); if(!GP->client){ DBG("Cancel capturing and close all"); + stop_server(); camstop(); closewheel(); focclose(); @@ -107,11 +109,14 @@ int main(int argc, char **argv){ if(!GP->client) isserver = TRUE; } if(GP->path && !GP->client) isserver = TRUE; + if(!GP->path && !GP->port){ + WARNX("Point one of options: `port` or `path` for command socket"); + } if((isserver || GP->client) && !GP->imageport){ GP->imageport = MALLOC(char, 32); if(!GP->port) sprintf(GP->imageport, "12345"); else snprintf(GP->imageport, 31, "%d", 1+atoi(GP->port)); - verbose(1, "Set image port to %s", GP->imageport); + printf("Set image port to %s\n", GP->imageport); } if(GP->client && (GP->commondev || GP->focuserdev || GP->cameradev || GP->wheeldev)) ERRX("Can't be client and standalone in same time!"); @@ -123,6 +128,7 @@ int main(int argc, char **argv){ OPENLOG(GP->logfile, lvl, 1); if(!sl_globlog) WARNX("Can't create log file"); } + if(GP->info && GP->verbose < 2) GP->verbose = 2; // increase verbose messages level for `info` signal(SIGINT, signals); signal(SIGQUIT, signals); signal(SIGABRT, signals); diff --git a/server.c b/server.c index 0f1c309..08ca857 100644 --- a/server.c +++ b/server.c @@ -16,13 +16,16 @@ * along with this program. If not, see . */ -#include +#include #include #include #include +#include #include #include +#include #include +#include #include #include "ccdfunc.h" @@ -46,6 +49,7 @@ static float tremain = 0.; // time when capture done // IPC key for shared memory (for client's getter) static key_t shmkey = IPC_PRIVATE; +static int isrunning = 1; typedef struct{ const char *key; @@ -54,21 +58,24 @@ typedef struct{ // cat | awk '{print "{ " $3 ", \"\" }," }' | sort strpair allcommands[] = { - { CC_CMD_8BIT, "run in 8 bit mode instead of 16 bit" }, { CC_CMD_AUTHOR, "FITS 'AUTHOR' field" }, { CC_CMD_BRIGHTNESS, "camera brightness" }, { CC_CMD_CAMDEVNO, "camera device number" }, + { CC_CMD_CAMFLAGS, "get camflags (bits: 0-start capture, 1-cancel, 2-restart server"}, { CC_CMD_CAMLIST, "list all connected cameras" }, { CC_CMD_CAMFANSPD, "fan speed of camera" }, { CC_CMD_CONFIO, "camera IO configuration" }, { CC_CMD_DARK, "don't open shutter @ exposure" }, { CC_CMD_EXPSTATE, "get exposition state (0: cancel, 1: capturing, 2: frame ready, 3: error) " - "or set (0: cancel, 1: start exp)\n also get camflags (bits: 0-start capture, 1-cancel, 2-restart server" }, + "or set (0: cancel, 1: start exp)\n" }, { CC_CMD_EXPOSITION, "exposition time" }, { CC_CMD_FASTSPD, "fast readout speed" }, { CC_CMD_FDEVNO, "focuser device number" }, { CC_CMD_FOCLIST, "list all connected focusers" }, + { CC_CMD_FMAXPOS, "get maximal focuser position"}, + { CC_CMD_FMINPOS, "get minimal focuser position"}, { CC_CMD_FGOTO, "focuser position" }, + { CC_CMD_FTEMP, "get focuser body temperature"}, { CC_CMD_FRAMEFORMAT, "camera frame format (X0,Y0,X1,Y1)" }, { CC_CMD_GAIN, "camera gain" }, { CC_CMD_GETHEADERS, "get last file FITS headers" }, @@ -76,10 +83,10 @@ strpair allcommands[] = { { CC_CMD_HELP, "show this help" }, { CC_CMD_IMHEIGHT, "last image height" }, { CC_CMD_IMWIDTH, "last image width" }, - { CC_CMD_INFO, "connected devices state" }, { CC_CMD_INFTY, "an infinity loop taking images until there's connected clients" }, { CC_CMD_INSTRUMENT, "FITS 'INSTRUME' field" }, { CC_CMD_IO, "get/set camera IO" }, + { CC_CMD_8BIT, "run in 8 bit mode instead of 16 bit" }, { CC_CMD_FRAMEMAX, "camera maximal available format" }, { CC_CMD_NFLUSHES, "camera number of preflushes" }, { CC_CMD_OBJECT, "FITS 'OBJECT' field" }, @@ -95,7 +102,9 @@ strpair allcommands[] = { { CC_CMD_VBIN, "vertical binning" }, { CC_CMD_WDEVNO, "wheel device number" }, { CC_CMD_WLIST, "list all connected wheels" }, + { CC_CMD_WMAXPOS, "get maximap wheel position"}, { CC_CMD_WPOS, "wheel position" }, + { CC_CMD_WTEMP, "get wheel body temperature"}, {NULL, NULL}, }; @@ -118,39 +127,17 @@ static void unlock(){ static cc_IMG *ima = NULL; -// client-side SHM lock -int client_lock_shm(cc_IMG *img){ - if(!img) return FALSE; - if(pthread_mutex_trylock(&img->mutex)) - return FALSE; - return TRUE; -} - -// server-side SHM lock (first - try gracefully, next - force) -static void server_lock_shm(cc_IMG *img){ - if(!img) return; +// cleanup semaphore, stop server processes +void stop_server(){ + isrunning = 0; double t0 = sl_dtime(); - int locked = FALSE; - // lock socket operations - while(sl_dtime() - t0 < MUTEX_LOCK_TMOUT){ - int l = pthread_mutex_trylock(&img->mutex); - if(0 == l){ - locked = TRUE; - break; - } - if(l == EOWNERDEAD){ // locked while locking thread is dead - pthread_mutex_consistent(&img->mutex); - } - } - if(!locked) while(!client_lock_shm(img)) unlock_shm(img); -} - -void unlock_shm(cc_IMG *img){ - if(!img) return; - if(pthread_mutex_unlock(&img->mutex)){ - LOGERR("Can't unlock image mutex"); - ERR("Can't unlock image mutex"); + // wait no more than 1s for graceful closing + while(sl_dtime() - t0 < 1.){ + if(isrunning == -1) break; + usleep(1000); } + // destroy semaphore + cc_remove_sem(); } static void fixima(){ @@ -165,16 +152,11 @@ static void fixima(){ // allocate memory for largest possible image if(!ima){ ima = cc_getshm(GP->shmkey, camera->array.h * camera->array.w * 2); - // init shared robust mutex - pthread_mutexattr_t att; - pthread_mutexattr_init(&att); - pthread_mutexattr_setrobust(&att, PTHREAD_MUTEX_ROBUST); - pthread_mutexattr_setpshared(&att, PTHREAD_PROCESS_SHARED); - pthread_mutex_init(&ima->mutex, &att); + if(!ima) ERR("Can't allocate memory for image"); + // init shared semaphore + cc_init_sem(TRUE); } - if(!ima) ERRX("Can't allocate memory for image"); - locked = FALSE; - server_lock_shm(ima); + cc_lock_shm(TRUE); shmkey = GP->shmkey; //if(raw_width == ima->w && raw_height == ima->h) return; // all OK DBG("curformat: %dx%d", curformat.w, curformat.h); @@ -187,7 +169,7 @@ static void fixima(){ DBG("GP->_8bit=%d", GP->_8bit); ima->bytelen = raw_height * raw_width * cc_getNbytes(ima); DBG("new image: %dx%d", raw_width, raw_height); - unlock_shm(ima); + cc_unlock_shm(); unlock(); } @@ -228,7 +210,7 @@ static inline void cameracapturestate(){ // capturing - wait for exposition ends camstate = CAMERA_ERROR; return; } - server_lock_shm(ima); + cc_lock_shm(TRUE); if(!camera->capture(ima)){ LOGERR("Can't capture image"); camstate = CAMERA_ERROR; @@ -239,7 +221,7 @@ static inline void cameracapturestate(){ // capturing - wait for exposition ends fillFITSheader(ima); ++ima->imnumber; // increment counter } - unlock_shm(ima); + cc_unlock_shm(); } camstate = CAMERA_FRAMERDY; } @@ -253,11 +235,20 @@ static void* processCAM(_U_ void *d){ ERRX(_("No camera device")); } double logt = 0; - while(1){ +#ifdef EBUG + double T = sl_dtime(); +#endif + while(isrunning){ if(camflags & FLAG_RESTARTSERVER){ LOGERR("User asks to restart"); signals(1); } +#ifdef EBUG + if(sl_dtime() - T > 5.){ + T = sl_dtime(); + printf("\t\t\tprocessCAM(), 5 seconds\n"); + } +#endif usleep(100); if(tremain < 0.5 && tremain > 0.) usleep(tremain*1e6); if(lock()){ @@ -616,7 +607,7 @@ static cc_hresult nflusheshandler(_U_ int fd, _U_ const char *key, _U_ const cha if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; return CC_RESULT_SILENCE; } -static cc_hresult expstatehandler(_U_ int fd, _U_ const char *key, _U_ const char *val){ +static cc_hresult expstatehandler(int fd, _U_ const char *key, const char *val){ char buf[64]; if(val){ int n = atoi(val); @@ -625,11 +616,10 @@ static cc_hresult expstatehandler(_U_ int fd, _U_ const char *key, _U_ const cha return CC_RESULT_OK; } else if(n == CAMERA_CAPTURE){ // start exposition - if(GP->exptime < 1e-9){ - snprintf(buf, 63, CC_CMD_EXPOSITION "=%g", GP->exptime); - cc_sendstrmessage(fd, buf); + if(GP->exptime < 1e-9){ // need exposition time to be set return CC_RESULT_FAIL; } + if(camstate == CAMERA_CAPTURE) return CC_RESULT_BUSY; // in progress TIMESTAMP("Get FLAG_STARTCAPTURE"); TIMEINIT(); camflags |= FLAG_STARTCAPTURE; @@ -639,6 +629,10 @@ static cc_hresult expstatehandler(_U_ int fd, _U_ const char *key, _U_ const cha } snprintf(buf, 63, CC_CMD_EXPSTATE "=%d", camstate); if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; + return CC_RESULT_SILENCE; +} +static cc_hresult camflagshandler(int fd, _U_ const char *key, _U_ const char *val){ + char buf[64]; snprintf(buf, 63, "camflags=%d", camflags); if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; return CC_RESULT_SILENCE; @@ -722,6 +716,22 @@ static cc_hresult handler(_U_ int fd, _U_ const char *key, _U_ const char *val){ /******************************************************************************* ***************************** cc_Wheel handlers ********************************** ******************************************************************************/ +static cc_hresult wmaxposhandler(int fd, const char *key, _U_ const char *val){ + if(wheel->Ndevices < 1) return CC_RESULT_FAIL; + char buf[64]; + snprintf(buf, 63, "%s=%d", key, wmaxpos); + if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; + return CC_RESULT_SILENCE; +} +static cc_hresult wtemphandler(int fd, const char *key, _U_ const char *val){ + if(wheel->Ndevices < 1 || !wheel->getTbody) return CC_RESULT_FAIL; + char buf[64]; + float t; + if(!wheel->getTbody(&t)) return CC_RESULT_FAIL; + snprintf(buf, 63, "%s=%.1f", key, t); + if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; + return CC_RESULT_SILENCE; +} static cc_hresult wlisthandler(int fd, _U_ const char *key, _U_ const char *val){ if(wheel->Ndevices < 1) return CC_RESULT_FAIL; for(int i = 0; i < wheel->Ndevices; ++i){ @@ -769,18 +779,42 @@ static cc_hresult wgotohandler(_U_ int fd, _U_ const char *key, _U_ const char * /******************************************************************************* **************************** Focuser handlers ********************************* ******************************************************************************/ - +static cc_hresult fminposhandler(int fd, const char *key, _U_ const char *val){ + if(focuser->Ndevices < 1) return CC_RESULT_FAIL; + char buf[64]; + snprintf(buf, 63, "%s=%g", key, focminpos); + if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; + return CC_RESULT_SILENCE; +} +static cc_hresult fmaxposhandler(int fd, const char *key, _U_ const char *val){ + if(focuser->Ndevices < 1) return CC_RESULT_FAIL; + char buf[64]; + snprintf(buf, 63, "%s=%g", key, focmaxpos); + if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; + return CC_RESULT_SILENCE; +} +static cc_hresult ftemphandler(int fd, const char *key, _U_ const char *val){ + if(focuser->Ndevices < 1 || !focuser->getTbody) return CC_RESULT_FAIL; + char buf[64]; + float t; + if(!focuser->getTbody(&t)) return CC_RESULT_FAIL; + snprintf(buf, 63, "%s=%.1f", key, t); + if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; + return CC_RESULT_SILENCE; +} static cc_hresult foclisthandler(int fd, _U_ const char *key, _U_ const char *val){ if(focuser->Ndevices < 1) return CC_RESULT_FAIL; for(int i = 0; i < focuser->Ndevices; ++i){ char modname[256], buf[BUFSIZ]; - if(!focuser->setDevNo(i)) continue; + if(focuser->setDevNo){ + if(!focuser->setDevNo(i)) continue; + } focuser->getModelName(modname, 255); snprintf(buf, BUFSIZ-1, CC_CMD_FOCLIST "='%s'", modname); if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; } int devno = atomic_load(&focdevno); - if(devno > -1) focuser->setDevNo(devno); + if(devno > -1 && focuser->setDevNo) focuser->setDevNo(devno); return CC_RESULT_SILENCE; } static cc_hresult fsetNhandler(int fd, _U_ const char *key, const char *val){ @@ -821,63 +855,6 @@ static cc_hresult fgotohandler(int fd, _U_ const char *key, const char *val){ **************************** Common handlers ********************************** ******************************************************************************/ -// information about everything -static cc_hresult infohandler(int fd, _U_ const char *key, _U_ const char *val){ - char buf[BUFSIZ], buf1[256]; - float f; - int i; - if(camera){ - if(camera->getModelName && camera->getModelName(buf1, 255)){ - snprintf(buf, BUFSIZ-1, CC_CMD_CAMLIST "='%s'", buf1); - if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; - } -#define RUN(f, arg) do{if(CC_RESULT_DISCONNECTED == f(fd, arg, NULL)) return CC_RESULT_DISCONNECTED;}while(0) - RUN(binhandler, CC_CMD_HBIN); - RUN(binhandler, CC_CMD_VBIN); - RUN(temphandler, CC_CMD_CAMTEMPER); - RUN(exphandler, CC_CMD_EXPOSITION); - RUN(expstatehandler, CC_CMD_EXPSTATE); -#undef RUN - } - if(wheel){ - DBG("chk wheel"); - if(wheel->getModelName(buf1, 255)){ - snprintf(buf, BUFSIZ-1, CC_CMD_WLIST "='%s'", buf1); - if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; - } - if(wheel->getTbody(&f)){ - snprintf(buf, BUFSIZ-1, "wtemp=%.1f", f); - if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; - } - if(wheel->getPos(&i)){ - snprintf(buf, BUFSIZ-1, CC_CMD_WPOS "=%d", i); - if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; - } - snprintf(buf, BUFSIZ-1, "wmaxpos=%d", wmaxpos); - if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; - } - if(focuser){ - DBG("Chk focuser"); - if(focuser->getModelName(buf1, 255)){ - snprintf(buf, BUFSIZ-1, CC_CMD_FOCLIST "='%s'", buf1); - if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; - } - if(focuser->getTbody(&f)){ - snprintf(buf, BUFSIZ-1, "foctemp=%.1f", f); - if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; - } - snprintf(buf, BUFSIZ-1, "focminpos=%g", focminpos); - if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; - snprintf(buf, BUFSIZ-1, "focmaxpos=%g", focmaxpos); - if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; - if(focuser->getPos(&f)){ - snprintf(buf, BUFSIZ-1, CC_CMD_FGOTO "=%g", f); - if(!cc_sendstrmessage(fd, buf)) return CC_RESULT_DISCONNECTED; - } - } - DBG("EOF"); - return CC_RESULT_SILENCE; -} // show help static cc_hresult helphandler(int fd, _U_ const char *key, _U_ const char *val){ char buf[256]; @@ -971,10 +948,10 @@ static cc_hresult chkfoc(char *val){ return CC_RESULT_FAIL; } static cc_handleritem items[] = { - {chktrue,infohandler, CC_CMD_INFO}, {NULL, helphandler, CC_CMD_HELP}, {NULL, restarthandler, CC_CMD_RESTART}, {chkcc, camlisthandler, CC_CMD_CAMLIST}, + {chkcc, camflagshandler, CC_CMD_CAMFLAGS}, {chkcc, camsetNhandler, CC_CMD_CAMDEVNO}, {chkcc, camfanhandler, CC_CMD_CAMFANSPD}, {chkcc, exphandler, CC_CMD_EXPOSITION}, @@ -1009,9 +986,14 @@ static cc_handleritem items[] = { {chkfoc, foclisthandler, CC_CMD_FOCLIST}, {chkfoc, fsetNhandler, CC_CMD_FDEVNO}, {chkfoc, fgotohandler, CC_CMD_FGOTO}, + {chkfoc, fminposhandler, CC_CMD_FMINPOS}, + {chkfoc, fmaxposhandler, CC_CMD_FMAXPOS}, + {chkfoc, ftemphandler, CC_CMD_FTEMP}, {chkwhl, wlisthandler, CC_CMD_WLIST}, {chkwhl, wsetNhandler, CC_CMD_WDEVNO}, {chkwhl, wgotohandler, CC_CMD_WPOS}, + {chkwhl, wmaxposhandler, CC_CMD_WMAXPOS}, + {chkwhl, wtemphandler, CC_CMD_WTEMP}, {NULL, NULL, NULL}, }; @@ -1046,12 +1028,12 @@ void server(int sock, int imsock){ if(imsock < 0) WARNX("Server run without image transport socket"); else if(listen(imsock, CC_MAXCLIENTS) == -1){ WARN("listen()"); - LOGWARN("listen()"); + LOGERR("server(): error in listen() for image socket"); return; } if(listen(sock, CC_MAXCLIENTS) == -1){ WARN("listen()"); - LOGWARN("listen()"); + LOGERR("server(): error in listen() for command socket"); return; } // init everything @@ -1062,11 +1044,17 @@ void server(int sock, int imsock){ wheeldevini(0); if(startCCD()) --ctr; camdevini(0); - if(ctr == 3) ERRX("No devices found"); + if(ctr == 3){ + LOGERR("server(): no devices found"); + WARNX("No devices found"); + } // start camera thread pthread_t camthread; if(camera){ - if(pthread_create(&camthread, NULL, processCAM, NULL)) ERR("pthread_create()"); + if(pthread_create(&camthread, NULL, processCAM, NULL)){ + WARN("pthread_create()"); + LOGERR("server(): pthread_create()"); + } } int nfd = 2; // only two listening sockets @start: command and image struct pollfd poll_set[CC_MAXCLIENTS+2]; @@ -1080,8 +1068,19 @@ void server(int sock, int imsock){ poll_set[0].events = POLLIN; poll_set[1].fd = imsock; poll_set[1].events = POLLIN; - while(1){ +#ifdef EBUG + double T = sl_dtime(); +#endif + while(isrunning == 1){ +#ifdef EBUG + if(sl_dtime() - T > 5.){ + T = sl_dtime(); + printf("\t\t\tserver(), 5 seconds\n"); + } +#endif + DBG("poll"); poll(poll_set, nfd, 1); // max timeout - 1ms + DBG("chk imsock"); //if(imsock > -1 && sl_canread(imsock) > 0){ if(imsock > -1 && (poll_set[1].revents & POLLIN)){ //uint8_t buf[32]; @@ -1093,22 +1092,28 @@ void server(int sock, int imsock){ DBG("client=%d", client); // sending image could be a very long operation -> run it in separate thread if(client > -1){ - pthread_t sendthread; - if(pthread_create(&sendthread, NULL, sendimage, (void*)&client)){ - WARN("pthread_create()"); - LOGWARN("pthread_create() error"); + if(ima->imnumber == 0){ + WARNX("Client wants an image, but there's no data"); close(client); }else{ - DBG("Thread created -> detach"); - if(pthread_detach(sendthread)){ - WARN("pthread_detach()"); - LOGWARN("pthread_detach() error"); - pthread_cancel(sendthread); + pthread_t sendthread; + if(pthread_create(&sendthread, NULL, sendimage, (void*)&client)){ + WARN("pthread_create()"); + LOGWARN("pthread_create() error"); close(client); - }else DBG("Thread detached"); + }else{ + DBG("Thread created -> detach"); + if(pthread_detach(sendthread)){ + WARN("pthread_detach()"); + LOGWARN("pthread_detach() error"); + pthread_cancel(sendthread); + close(client); + }else DBG("Thread detached"); + } } }else{WARN("accept()"); DBG("disconnected");} } + DBG("chk cmd sock"); if(poll_set[0].revents & POLLIN){ // check main for accept() struct sockaddr_in addr; socklen_t len = sizeof(addr); @@ -1128,6 +1133,7 @@ void server(int sock, int imsock){ } } } +#if 0 // process some data & send messages to ALL if(camstate == CAMERA_FRAMERDY || camstate == CAMERA_ERROR){ DBG("new image: timestamp=%.1f, num=%zd", ima->timestamp, ima->imnumber); @@ -1140,6 +1146,8 @@ void server(int sock, int imsock){ } camstate = CAMERA_IDLE; } +#endif + DBG("scan connections"); // scan connections for(int fdidx = 2; fdidx < nfd; ++fdidx){ if((poll_set[fdidx].revents & POLLIN) == 0) continue; @@ -1165,8 +1173,9 @@ void server(int sock, int imsock){ --nfd; } } + DBG("Check infty"); // check `infty` - if(camstate == CAMERA_IDLE && infty){ // start new exposition + if(camstate != CAMERA_CAPTURE && infty){ // start new exposition // mark to start new capture in infinity loop when at least one client connected if(nfd > 2){ camflags |= FLAG_STARTCAPTURE; @@ -1174,11 +1183,13 @@ void server(int sock, int imsock){ TIMEINIT(); } } + DBG("OK ->"); } - // never reached + WARNX("SERVER STOPPED!"); + camstop(); focclose(); closewheel(); - closecam(); + isrunning = -1; } /** diff --git a/server.h b/server.h index 1562b8e..4abe272 100644 --- a/server.h +++ b/server.h @@ -30,5 +30,4 @@ void server(int fd, int imsock); char *makeabspath(const char *path, int shouldbe); -int client_lock_shm(cc_IMG *img); -void unlock_shm(cc_IMG *img); +void stop_server();