diff --git a/CMakeLists.txt b/CMakeLists.txt index 20cf250..0f7c528 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ add_definitions(-D_XOPEN_SOURCE=1234 -D_DEFAULT_SOURCE -D_GNU_SOURCE -DLOCALEDIR set(CMAKE_COLOR_MAKEFILE ON) -set(SOURCES main.c cmdlnopts.c ccdfunc.c) +set(SOURCES main.c cmdlnopts.c ccdfunc.c socket.c server.c client.c) # cmake -DDEBUG=yes -> debugging if(DEFINED DEBUG AND DEBUG STREQUAL "yes") diff --git a/Dummy_cameras/dummyfunc.c b/Dummy_cameras/dummyfunc.c index d993d64..05976e5 100644 --- a/Dummy_cameras/dummyfunc.c +++ b/Dummy_cameras/dummyfunc.c @@ -25,7 +25,7 @@ #include #include -#include "ccdfunc.h" +#include "basestructs.h" extern Camera camera; extern Focuser focuser; diff --git a/FLI_cameras/flifunc.c b/FLI_cameras/flifunc.c index d74cd8c..be114c2 100644 --- a/FLI_cameras/flifunc.c +++ b/FLI_cameras/flifunc.c @@ -21,7 +21,7 @@ #include #include -#include "ccdfunc.h" +#include "basestructs.h" extern Camera camera; extern Focuser focuser; diff --git a/ZWO_cameras/zwofunc.c b/ZWO_cameras/zwofunc.c index 6a70be1..0d5c6d3 100644 --- a/ZWO_cameras/zwofunc.c +++ b/ZWO_cameras/zwofunc.c @@ -26,7 +26,7 @@ #include #include -#include "ccdfunc.h" +#include "basestructs.h" extern Camera camera; extern Focuser focuser; @@ -47,6 +47,7 @@ static struct{ float mingain; float maxbright; float minbright; + int maxbin; } extrvalues = {0}; // extremal values static double starttime = 0.; // time when exposure started @@ -69,6 +70,7 @@ static int zwo_getfloat(float *f, ASI_CONTROL_TYPE t){ long val; ASI_BOOL aut = ASI_FALSE; if(ASI_SUCCESS != ASIGetControlValue(caminfo.CameraID, t, &val, &aut)) return FALSE; + if(aut) DBG("VALUE IS AUTO!!!"); *f = (float) val; return TRUE; } @@ -142,6 +144,7 @@ static int startcapt(){ static void asi_closecam(){ FNAME(); if(caminfo.CameraID){ + camcancel(); ASICloseCamera(caminfo.CameraID); caminfo.CameraID = 0; } @@ -154,12 +157,10 @@ static int setdevno(int n){ DBG("Camera #%d, name: %s, ID: %d", n, caminfo.Name, caminfo.CameraID); DBG("WxH: %ldx%ld, %s", caminfo.MaxWidth, caminfo.MaxHeight, caminfo.IsColorCam == ASI_TRUE ? "color" : "monochrome"); DBG("Pixel size: %1.1f mkm; gain: %1.2f e/ADU", caminfo.PixelSize, caminfo.ElecPerADU); -#ifdef EBUG int *sup = caminfo.SupportedBins; while(*sup){ - green("Supported bin: %d\n", *sup++); + extrvalues.maxbin = *sup++; } -#endif camera.pixX = camera.pixY = (float)caminfo.PixelSize / 1e6; // um -> m camera.array = (frameformat){.w = caminfo.MaxWidth, .h = caminfo.MaxHeight, .xoff = 0, .yoff = 0}; camera.field = camera.array; // initial setup (will update later) @@ -217,7 +218,10 @@ static int setdevno(int n){ } static int camsetbrig(float b){ - if(b < extrvalues.minbright || b > extrvalues.maxbright) return FALSE; + if(b < extrvalues.minbright || b > extrvalues.maxbright){ + WARNX(_("Brightness should be from %g to %g"), extrvalues.minbright, extrvalues.maxbright); + return FALSE; + } return zwo_setfloat(b, ASI_BRIGHTNESS); } @@ -233,7 +237,10 @@ static int camsetexp(float t){ } static int camsetgain(float g){ - if(g < extrvalues.mingain || g > extrvalues.maxgain) return FALSE; + if(g < extrvalues.mingain || g > extrvalues.maxgain){ + WARNX(_("Gain should be from %g to %g"), extrvalues.mingain, extrvalues.maxgain); + return FALSE; + } return zwo_setfloat(g, ASI_GAIN); } @@ -243,6 +250,10 @@ static int camgetgain(float *g){ } static int camsett(float t){ + if(caminfo.IsCoolerCam == ASI_FALSE){ + DBG("Cooling unsupported"); + return FALSE; + } if(!zwo_setfloat(1., ASI_FAN_ON)){ DBG("Can't set fan on"); return FALSE; @@ -251,6 +262,10 @@ static int camsett(float t){ if(zwo_getfloat(&f, ASI_FAN_ON)){ DBG("FAN: %g", f); } + if(!zwo_setfloat(1., ASI_COOLER_ON)){ + DBG("Can't set cooler on"); + return FALSE; + } if(!zwo_setfloat(t, ASI_TARGET_TEMP)){ DBG("Can't set target temperature"); return FALSE; @@ -258,30 +273,25 @@ static int camsett(float t){ if(zwo_getfloat(&f, ASI_TARGET_TEMP)){ DBG("Ttarg = %g", f); } - if(!zwo_setfloat(1., ASI_COOLER_ON)){ - DBG("Can't set cooler on"); - return FALSE; - } if(!zwo_getfloat(&f, ASI_COOLER_ON)) return FALSE; DBG("COOLERON = %g", f); - usleep(100000); +#ifdef EBUG double t0 = dtime(); float c, p, tn; - while(dtime() - t0 < 10.){ - green("%.1f", dtime()-t0); + while(dtime() - t0 < 1200.){ + usleep(100000); // without this first ASI_FAN_ON will show false data + green("%.1f ", dtime()-t0); zwo_getfloat(&f, ASI_FAN_ON); zwo_getfloat(&t, ASI_TARGET_TEMP); zwo_getfloat(&c, ASI_COOLER_ON); zwo_getfloat(&tn, ASI_TEMPERATURE); zwo_getfloat(&p, ASI_COOLER_POWER_PERC); printf("fan: %g, t: %g, cooler: %g, perc: %g, tnow: %g\n", f, t, c, p, tn/10.); - if(f > 0.) break; - usleep(100000); } +#endif return TRUE; } - static int camgett(float *t){ if(!t) return FALSE; float curt; @@ -295,7 +305,13 @@ static int gett(_U_ float *t){ } static int camsetbin(int h, int v){ - if(h != v) return FALSE; + if(h != v){ + WARNX(_("BinX and BinY should be equal")); + return FALSE; + } + if(h > extrvalues.maxbin){ + WARNX(_("Maximal binning value is %d"), extrvalues.maxbin); + } if(zwo_setfloat(1., ASI_HARDWARE_BIN)){ curbin = h; return TRUE; @@ -322,8 +338,9 @@ static int camsetgeom(frameformat *f){ // w,h, xoff, yoff DBG(_("Can't get geometry")); return FALSE; } + DBG("curformat: w=%d, h=%d, bin=%d", f->w, f->h, curbin); DBG("w=%d, h=%d, bin=%d", f->w, f->h, curbin); - if(ASI_SUCCESS != ASISetStartPos(caminfo.CameraID, f->xoff, f->yoff)){ + if(ASI_SUCCESS != ASISetStartPos(caminfo.CameraID, f->xoff/curbin, f->yoff/curbin)){ DBG("Can't set start pos"); return FALSE; } @@ -331,6 +348,7 @@ static int camsetgeom(frameformat *f){ // w,h, xoff, yoff DBG("Can't get start pos"); return FALSE; } + DBG("curstartpos: x=%d, y=%d", f->xoff, f->yoff); camera.geometry = *f; return TRUE; } @@ -373,7 +391,20 @@ static int camgetio(_U_ int *io){ // not supported return FALSE; } -static int camfan(_U_ fan_speed spd){ // not supported +static int camfan(_U_ fan_speed spd){ // not supported, just turn it on/off + switch(spd){ + case FAN_OFF: + if(!zwo_setfloat(0., ASI_FAN_ON)){ + DBG("Can't set fan off"); + return FALSE; + } + break; + default: // turn ON + if(!zwo_setfloat(1., ASI_FAN_ON)){ + DBG("Can't set fan on"); + return FALSE; + } + } return TRUE; } diff --git a/basestructs.h b/basestructs.h new file mode 100644 index 0000000..f75f632 --- /dev/null +++ b/basestructs.h @@ -0,0 +1,138 @@ +/* + * This file is part of the CCD_Capture project. + * Copyright 2022 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#ifndef BASESTRUCTS_H__ +#define BASESTRUCTS_H__ + +#include + +typedef struct{ + uint16_t *data; // image data + int w, h; // image size + uint16_t max, min; // min/max values + float avr, std; // statistics +} IMG; + +// format of single frame +typedef struct{ + int w; int h; // width & height + int xoff; int yoff; // X and Y offset +} frameformat; + +typedef enum{ + SHUTTER_OPEN, // open shutter now + SHUTTER_CLOSE, // close shutter now + SHUTTER_OPENATLOW, // ext. expose control @low + SHUTTER_OPENATHIGH, // -//- @high + SHUTTER_AMOUNT, // amount of entries +} shutter_op; + +typedef enum{ + CAPTURE_NO, // no capture initiated + CAPTURE_PROCESS, // in progress + CAPTURE_CANTSTART, // can't start + CAPTURE_ABORTED, // some error - aborted + CAPTURE_READY, // ready - user can read image +} capture_status; + +typedef enum{ + FAN_OFF, + FAN_LOW, + FAN_MID, + FAN_HIGH, +} fan_speed; + +// all setters and getters of Camera, Focuser and Wheel should return TRUE if success or FALSE if failed or unsupported +// camera +typedef struct{ + int (*check)(); // check if the device is available, connect and init + int Ndevices; // amount of devices found + void (*close)(); // disconnect & close device + int (*startexposition)(); // start exposition + int (*pollcapture)(capture_status *st, float *remain);// start or poll capture process, `remain` - time remain (s) + int (*capture)(IMG *ima); // capture an image, struct `ima` should be prepared before + void (*cancel)(); // cancel exposition + // setters: + int (*setDevNo)(int n); // set active device number + int (*setbrightness)(float b); + int (*setexp)(float e); + int (*setgain)(float g); + int (*setT)(float t); + int (*setbin)(int binh, int binv); // binning + int (*setnflushes)(int N); // flushes amount + int (*shuttercmd)(shutter_op s); // work with shutter + int (*confio)(int s); // configure IO-port + int (*setio)(int s); // set IO-port to given state + int (*setframetype)(int l); // set frametype: 1 - light, 0 - dark + int (*setbitdepth)(int h); // set bit depth: 1 - high, 0 - low + int (*setfastspeed)(int s); // set readout speed: 1 - fast, 0 - low + // geometry (if TRUE, all args are changed to suitable values) + int (*setgeometry)(frameformat *fmt); // set geometry in UNBINNED coordinates + int (*setfanspeed)(fan_speed spd); // set fan speed + // getters: + int (*getbrightness)(float *b);// get brightnes level + int (*getModelName)(char *n, int l);// string with model name (l - length of n in bytes) + int (*getgain)(float *g); // get gain value + int (*getmaxgain)(float *g);// get max available gain value + // get limits of geometry: maximal values and steps + int (*getgeomlimits)(frameformat *max, frameformat *step); + int (*getTcold)(float *t); // cold-side T + int (*getThot)(float *t); // hot-side T + int (*getTbody)(float *t); // body T + int (*getbin)(int *binh, int *binv); + int (*getio)(int *s); // get IO-port state + float pixX, pixY; // pixel size in um + frameformat field; // max field of view + frameformat array; // array format + frameformat geometry; // current geometry settings (as in setgeometry) +} Camera; + +// focuser +typedef struct{ + int (*check)(); // check if the device is available + int Ndevices; + void (*close)(); + // setters: + int (*setDevNo)(int n); // set active device number + int (*setAbsPos)(int async, float n);// set absolute position (in millimeters!!!) + int (*home)(int async); // home device + // getters: + int (*getModelName)(char *n, int l);// string with model name (l - length of n in bytes) + int (*getTbody)(float *t); // body T + int (*getPos)(float *p); // current position number (starting from zero) + int (*getMaxPos)(float *p); // max position + int (*getMinPos)(float *p); // min position +} Focuser; + +// wheel +typedef struct{ + int (*check)(); // check if the device is available + int Ndevices; + void (*close)(); + // setters: + int (*setDevNo)(int n); // set active device number + int (*setPos)(int n); // set absolute position (starting from 0) + // getters: + int (*getModelName)(char *n, int l);// string with model name (l - length of n in bytes) + int (*getTbody)(float *t); // body T + int (*getPos)(int *p); // current position number (starting from zero) + int (*getMaxPos)(int *p); // amount of positions +} Wheel; + +#endif // BASESTRUCTS_H__ diff --git a/ccdfunc.c b/ccdfunc.c index 2aae835..cb27148 100644 --- a/ccdfunc.c +++ b/ccdfunc.c @@ -41,9 +41,10 @@ static int fitserror = 0; #define TRYFITS(f, ...) \ do{ int status = 0; \ f(__VA_ARGS__, &status); \ - if(status){ \ + if(status){ \ fits_report_error(stderr, status); \ - fitserror = status;} \ + LOGERR("Fits error %d", status); \ + fitserror = status;} \ }while(0) #define WRITEKEY(...) \ do{ int status = 0; \ @@ -105,16 +106,16 @@ static size_t curtime(char *s_time){ // current date/time return strftime(s_time, TMBUFSIZ, "%d/%m/%Y,%H:%M:%S", localtime(&tm)); }*/ -static int check_filename(char *buff, char *outfile, char *ext){ - struct stat filestat; - int num; - for(num = 1; num < 10000; num++){ - if(snprintf(buff, PATH_MAX, "%s_%04d.%s", outfile, num, ext) < 1) - return 0; +// check if I can create file prefix_XXXX.fits +static int check_filenameprefix(char *buff, int buflen){ + for(int num = 1; num < 10000; num++){ + if(snprintf(buff, buflen-1, "%s_%04d.fits", GP->outfileprefix, num) < 1) + return FALSE; + struct stat filestat; if(stat(buff, &filestat)) // no such file or can't stat() - return 1; + return TRUE; } - return 0; + return FALSE; } /** @@ -144,21 +145,37 @@ static void addrec(fitsfile *f, char *filename){ } } -void saveFITS(IMG *img, char *filename){ +// save FITS file `img` into GP->outfile or GP->outfileprefix_XXXX.fits +void saveFITS(IMG *img){ if(!camera){ + LOGERR("Can't save image: no camera device"); WARNX(_("Camera device unknown")); return; } char buff[PATH_MAX], fnam[PATH_MAX]; - if(filename == NULL) return; - fitserror = 0; - if(!check_filename(fnam, filename, "fits") && !GP->rewrite){ - // - WARNX(_("Can't save file")); - }else{ - if(GP->rewrite){ - DBG("REW"); - snprintf(fnam, PATH_MAX, "!%s.fits", filename); + if(!GP->outfile && !GP->outfileprefix){ + LOGERR("Can't save image: neither filename nor filename prefix pointed"); + WARNX(_("Neither filename nor filename prefix pointed!")); + return; + } + if(GP->outfile){ // pointed specific output file name like "file.fits", check it + struct stat filestat; + int s = stat(GP->outfile, &filestat); + if(s){ // not exists + snprintf(fnam, PATH_MAX-1, "%s", GP->outfile); + }else{ // exists + if(!GP->rewrite){ + LOGERR("Can't save image: file %s exists", GP->outfile); + WARNX("File %s exists!", GP->outfile); + return; + } + snprintf(fnam, PATH_MAX-1, "!%s", GP->outfile); + } + }else{ // user pointed output file prefix + if(!check_filenameprefix(fnam, PATH_MAX)){ + // + WARNX(_("Can't save file with prefix %s"), GP->outfileprefix); + LOGERR("Can't save image with prefix %s", GP->outfileprefix); } } int width = img->w, height = img->h; @@ -171,12 +188,13 @@ void saveFITS(IMG *img, char *filename){ char bufc[FLEN_CARD]; time_t savetime = time(NULL); fitsfile *fp; + fitserror = 0; TRYFITS(fits_create_file, &fp, fnam); if(fitserror) goto cloerr; TRYFITS(fits_create_img, fp, USHORT_IMG, 2, naxes); if(fitserror) goto cloerr; // FILE / Input file original name - WRITEKEY(fp, TSTRING, "FILE", filename, "Input file original name"); + WRITEKEY(fp, TSTRING, "FILE", fnam, "Input file original name"); // ORIGIN / organization responsible for the data WRITEKEY(fp, TSTRING, "ORIGIN", "SAO RAS", "organization responsible for the data"); // OBSERVAT / Observatory name @@ -200,8 +218,8 @@ void saveFITS(IMG *img, char *filename){ camera->array.xoff + camera->array.w, camera->array.yoff + camera->array.h); WRITEKEY(fp, TSTRING, "ARRAYFLD", bufc, "Camera full array size"); // CRVAL1, CRVAL2 / Offset in X, Y - if(GP->X0 > -1) WRITEKEY(fp, TINT, "X0", &GP->X0, "Subframe left border"); - if(GP->Y0 > -1) WRITEKEY(fp, TINT, "Y0", &GP->Y0, "Subframe upper border"); + if(GP->X0 > -1) WRITEKEY(fp, TINT, "X0", &GP->X0, "Subframe left border without binning"); + if(GP->Y0 > -1) WRITEKEY(fp, TINT, "Y0", &GP->Y0, "Subframe upper border without binning"); if(GP->objtype) strncpy(bufc, GP->objtype, FLEN_CARD-1); else if(GP->dark) sprintf(bufc, "dark"); else sprintf(bufc, "light"); @@ -221,7 +239,7 @@ void saveFITS(IMG *img, char *filename){ WRITEKEY(fp, TFLOAT, "STATAVR", &tmpf, "Average data value"); tmpf = img->std; WRITEKEY(fp, TFLOAT, "STATSTD", &tmpf, "Std. of data value"); - WRITEKEY(fp, TFLOAT, "CAMTEMP0", &GP->temperature, "Camera temperature at exp. start, degr C"); +// WRITEKEY(fp, TFLOAT, "CAMTEMP0", &GP->temperature, "Camera temperature at exp. start, degr C"); if(camera->getTcold(&tmpf)) WRITEKEY(fp, TFLOAT, "CAMTEMP", &tmpf, "Camera temperature at exp. end, degr C"); if(camera->getTbody(&tmpf)) @@ -231,6 +249,12 @@ void saveFITS(IMG *img, char *filename){ // EXPTIME / actual exposition time (sec) tmpd = GP->exptime; WRITEKEY(fp, TDOUBLE, "EXPTIME", &tmpd, "Actual exposition time (sec)"); + if(camera->getgain(&tmpf)){ + WRITEKEY(fp, TFLOAT, "CAMGAIN", &tmpf, "CMOS gain value"); + } + if(camera->getbrightness(&tmpf)){ + WRITEKEY(fp, TFLOAT, "CAMBRIGH", &tmpf, "CMOS brightness value"); + } // DATE / Creation date (YYYY-MM-DDThh:mm:ss, UTC) strftime(bufc, FLEN_VALUE, "%Y-%m-%dT%H:%M:%S", gmtime(&savetime)); WRITEKEY(fp, TSTRING, "DATE", bufc, "Creation date (YYYY-MM-DDThh:mm:ss, UTC)"); @@ -301,14 +325,16 @@ void saveFITS(IMG *img, char *filename){ TRYFITS(fits_close_file, fp); cloerr: if(fitserror == 0){ + LOGMSG("Save file '%s'", fnam); verbose(1, _("File saved as '%s'"), fnam); }else{ + LOGERR("Can't save %s", fnam); WARNX(_("Error saving file")); fitserror = 0; } } -static void calculate_stat(IMG *image){ +void calculate_stat(IMG *image){ uint64_t Noverld = 0L, size = image->h*image->w; double sum = 0., sum2 = 0.; uint16_t max = 0, min = 65535; @@ -340,6 +366,7 @@ static void calculate_stat(IMG *image){ double avr = sum/sz; image->avr = avr; image->std = sqrt(fabs(sum2/sz - avr*avr)); + image->max = max; image->min = min; if(GP->verbose){ printf(_("Image stat:\n")); printf("avr = %.1f, std = %.1f, Noverload = %ld\n", avr, image->std, Noverld); @@ -347,32 +374,45 @@ static void calculate_stat(IMG *image){ } } +int startFocuser(void **dlh){ + if(!GP->focuserdev && !GP->commondev){ + verbose(3, _("Focuser device not pointed")); + return FALSE; + }else{ + char *plugin = GP->commondev ? GP->commondev : GP->focuserdev; + if(!(*dlh = init_focuser(plugin))) return FALSE; + } + if(!focuser->check()){ + verbose(3, _("No focusers found")); + focuser = NULL; + return FALSE; + } + return TRUE; +} + +void focclose(void *dlh){ + focuser->close(); + dlclose(dlh); + focuser = NULL; +} + /* * Find focusers and work with each of them */ void focusers(){ FNAME(); void *dlh = NULL; - if(!GP->focuserdev && !GP->commondev){ - verbose(3, _("Focuser device not pointed")); - return; - }else{ - char *plugin = GP->commondev ? GP->commondev : GP->focuserdev; - if(!(dlh = init_focuser(plugin))) return; - } - if(!focuser->check()){ - verbose(3, _("No focusers found")); - focuser = NULL; - return; - } + if(!startFocuser(&dlh)) return; if(GP->listdevices){ for(int i = 0; i < focuser->Ndevices; ++i){ + if(!focuser->setDevNo(i)) continue; char modname[256]; focuser->getModelName(modname, 255); printf("Found focuser #%d: %s\n", i, modname); } } int num = GP->focdevno; + if(num < 0) num = 0; if(num > focuser->Ndevices - 1){ WARNX(_("Found %d focusers, you point number %d"), focuser->Ndevices, num); goto retn; @@ -422,9 +462,29 @@ void focusers(){ if(!focuser->setAbsPos(GP->async, tagpos)) WARNX(_("Can't set position %g"), tagpos); } retn: - focuser->close(); + focclose(dlh); +} + +int startWheel(void **dlh){ + if(!GP->wheeldev && !GP->commondev){ + verbose(3, _("Wheel device not pointed")); + return FALSE; + }else{ + char *plugin = GP->commondev ? GP->commondev : GP->wheeldev; + if(!(*dlh = init_wheel(plugin))) return FALSE; + } + if(!wheel->check()){ + verbose(3, _("No wheels found")); + wheel = NULL; + return FALSE; + } + return TRUE; +} + +void closewheel(void *dlh){ + wheel->close(); dlclose(dlh); - focuser = NULL; + wheel = NULL; } /* @@ -433,26 +493,17 @@ retn: void wheels(){ FNAME(); void *dlh = NULL; - if(!GP->wheeldev && !GP->commondev){ - verbose(3, _("Wheel device not pointed")); - return; - }else{ - char *plugin = GP->commondev ? GP->commondev : GP->wheeldev; - if(!(dlh = init_wheel(plugin))) return; - } - if(!wheel->check()){ - verbose(3, _("No wheels found")); - wheel = NULL; - return; - } + if(!startWheel(&dlh)) return; if(GP->listdevices){ for(int i = 0; i < wheel->Ndevices; ++i){ + if(!wheel->setDevNo(i)) continue; char modname[256]; wheel->getModelName(modname, 255); printf("Found wheel #%d: %s\n", i, modname); } } int num = GP->whldevno; + if(num < 0) num = 0; if(num > wheel->Ndevices - 1){ WARNX(_("Found %d wheels, you point number %d"), wheel->Ndevices, num); goto retn; @@ -487,9 +538,7 @@ void wheels(){ if(!wheel->setPos(pos)) WARNX(_("Can't set wheel position %d"), pos); retn: - wheel->close(); - dlclose(dlh); - wheel = NULL; + closewheel(dlh); } /* static void closeall(){ @@ -500,22 +549,47 @@ static void closeall(){ static capture_status capt(){ capture_status cs; - float tleave, tmpf; - while(camera->pollcapture(&cs, &tleave)){ + float tremain, tmpf; + while(camera->pollcapture(&cs, &tremain)){ if(cs != CAPTURE_PROCESS) break; - if(tleave > 0.1){ - verbose(2, _("%.1f seconds till exposition ends"), tleave); + if(tremain > 0.1){ + verbose(2, _("%.1f seconds till exposition ends"), tremain); if(camera->getTcold(&tmpf)) verbose(1, "CCDTEMP=%.1f", tmpf); if(camera->getTbody(&tmpf)) verbose(1, "BODYTEMP=%.1f", tmpf); } - if(tleave > 6.) sleep(5); - else if(tleave > 0.9) sleep((int)(tleave+0.99)); - else usleep((int)(1e6*tleave) + 100000); + if(tremain > 6.) sleep(5); + else if(tremain > 0.9) sleep((int)(tremain+0.99)); + else usleep((int)(1e6*tremain) + 100000); if(!camera) return CAPTURE_ABORTED; } return cs; } +int startCCD(void **dlh){ + if(!GP->cameradev && !GP->commondev){ + verbose(3, _("Camera device not pointed")); + return FALSE; + }else{ + char *plugin = GP->commondev ? GP->commondev : GP->cameradev; + if(!(*dlh = init_camera(plugin))) return FALSE; + } + if(!camera->check()){ + verbose(3, _("No cameras found")); + LOGWARN(_("No cameras found")); + return FALSE; + } + return TRUE; +} + +void closecam(void *dlh){ + if(!dlh) return; + DBG("Close cam"); + camera->close(); + DBG("close dlh"); + dlclose(dlh); + camera = NULL; +} + /* * Find CCDs and work with each of them */ @@ -524,25 +598,17 @@ void ccds(){ float tmpf; int tmpi; void *dlh = NULL; - if(!GP->cameradev && !GP->commondev){ - verbose(3, _("Camera device not pointed")); - return; - }else{ - char *plugin = GP->commondev ? GP->commondev : GP->cameradev; - if(!(dlh = init_camera(plugin))) return; - } - if(!camera->check()){ - verbose(3, _("No cameras found")); - return; - } + if(!startCCD(&dlh)) return; if(GP->listdevices){ for(int i = 0; i < camera->Ndevices; ++i){ + if(!camera->setDevNo(i)) continue; char modname[256]; camera->getModelName(modname, 255); printf("Found camera #%d: %s\n", i, modname); } } int num = GP->camdevno; + if(num < 0) num = 0; if(num > camera->Ndevices - 1){ WARNX(_("Found %d cameras, you point number %d"), camera->Ndevices, num); goto retn; @@ -571,7 +637,7 @@ void ccds(){ snprintf(buf, BUFSIZ, "(%d, %d)(%d, %d)", camera->field.xoff, camera->field.yoff, camera->field.xoff + camera->field.w, camera->field.yoff + camera->field.h); verbose(2, _("Field of view: %s"), buf); - if(GP->temperature < 40.){ + if(!isnan(GP->temperature)){ if(!camera->setT((float)GP->temperature)) WARNX(_("Can't set T to %g degC"), GP->temperature); verbose(3, "SetT=%.1f", GP->temperature); @@ -603,29 +669,40 @@ void ccds(){ WARNX(_("Can't set IOport")); } if(GP->exptime < 0.) goto retn; + if(!isnan(GP->gain)){ + DBG("Change gain to %g", GP->gain); + if(camera->setgain(GP->gain)){ + camera->getgain(&GP->gain); + verbose(1, _("Set gain to %g"), GP->gain); + }else WARNX(_("Can't set gain to %g"), GP->gain); + } + if(!isnan(GP->brightness)){ + if(camera->setbrightness(GP->brightness)){ + camera->getbrightness(&GP->brightness); + verbose(1, _("Set brightness to %g"), GP->brightness); + }else WARNX(_("Can't set brightness to %g"), GP->brightness); + } /*********************** expose control ***********************/ // cancel previous exp camera->cancel(); if(!camera->setbin(GP->hbin, GP->vbin)) WARNX(_("Can't set binning %dx%d"), GP->hbin, GP->vbin); - if(GP->fullframe){ - DBG("FULLFRAME"); - GP->X0 = x0; GP->Y0 = y0; GP->X1 = x1; GP->Y1 = y1; - } - if(GP->X0 == -1) GP->X0 = x0; // default values - if(GP->Y0 == -1) GP->Y0 = y0; - if(GP->X1 == -1) GP->X1 = x1; + if(GP->X0 < 0) GP->X0 = x0; // default values + if(GP->Y0 < 0) GP->Y0 = y0; + if(GP->X1 < 0) GP->X1 = x1; else if(GP->X1 > x1) GP->X1 = x1; - if(GP->Y1 == -1) GP->Y1 = y1; + if(GP->Y1 < 0) GP->Y1 = y1; else if(GP->Y1 > y1) GP->Y1 = y1; frameformat fmt = {.w = GP->X1 - GP->X0, .h = GP->Y1 - GP->Y0, .xoff = GP->X0, .yoff = GP->Y0}; int raw_width = fmt.w / GP->hbin, raw_height = fmt.h / GP->vbin; if(!camera->setgeometry(&fmt)) WARNX(_("Can't set given geometry")); verbose(3, "Geometry: off=%d/%d, wh=%d/%d", fmt.xoff, fmt.yoff, fmt.w, fmt.h); - if(!camera->setnflushes(GP->nflushes)) - WARNX(_("Can't set %d flushes"), GP->nflushes); - verbose(3, "Nflushes=%d", GP->nflushes); + if(GP->nflushes > 0){ + if(!camera->setnflushes(GP->nflushes)) + WARNX(_("Can't set %d flushes"), GP->nflushes); + else verbose(3, "Nflushes=%d", GP->nflushes); + } if(!camera->setexp(GP->exptime)) WARNX(_("Can't set exposure time to %f seconds"), GP->exptime); tmpi = (GP->dark) ? 0 : 1; @@ -675,7 +752,7 @@ void ccds(){ break; } calculate_stat(&ima); - saveFITS(&ima, GP->outfile); + saveFITS(&ima); #ifdef IMAGEVIEW if(GP->showimage){ // display image if((mainwin = getWin())){ @@ -748,11 +825,7 @@ void ccds(){ DBG("FREE img"); FREE(img); retn: - DBG("Close cam"); - camera->close(); - DBG("close dlh"); - dlclose(dlh); - camera = NULL; + closecam(dlh); } void cancel(){ diff --git a/ccdfunc.h b/ccdfunc.h index f000ead..8e651f6 100644 --- a/ccdfunc.h +++ b/ccdfunc.h @@ -20,125 +20,24 @@ #ifndef CCDFUNC_H__ #define CCDFUNC_H__ -#include +#include "basestructs.h" -typedef struct{ - uint16_t *data; // image data - int w, h; // image size - uint16_t max, min; // min/max values - float avr, std; // statistics -} IMG; +extern Camera *camera; +extern Focuser *focuser; +extern Wheel *wheel; -// format of single frame -typedef struct{ - int w; int h; // width & height - int xoff; int yoff; // X and Y offset -} frameformat; - -typedef enum{ - SHUTTER_OPEN, // open shutter now - SHUTTER_CLOSE, // close shutter now - SHUTTER_OPENATLOW, // ext. expose control @low - SHUTTER_OPENATHIGH, // -//- @high - SHUTTER_AMOUNT, // amount of entries -} shutter_op; - -typedef enum{ - CAPTURE_NO, // no capture initiated - CAPTURE_PROCESS, // in progress - CAPTURE_CANTSTART, // can't start - CAPTURE_ABORTED, // some error - aborted - CAPTURE_READY, // ready - user can read image -} capture_status; - -typedef enum{ - FAN_OFF, - FAN_LOW, - FAN_MID, - FAN_HIGH, -} fan_speed; - -// all setters and getters of Camera, Focuser and Wheel should return TRUE if success or FALSE if failed or unsupported -// camera -typedef struct{ - int (*check)(); // check if the device is available, connect and init - int Ndevices; // amount of devices found - void (*close)(); // disconnect & close device - int (*startexposition)(); // start exposition - int (*pollcapture)(capture_status *st, float *remain);// start or poll capture process, `remain` - time remain (s) - int (*capture)(IMG *ima); // capture an image, struct `ima` should be prepared before - void (*cancel)(); // cancel exposition - // setters: - int (*setDevNo)(int n); // set active device number - int (*setbrightness)(float b); - int (*setexp)(float e); - int (*setgain)(float g); - int (*setT)(float t); - int (*setbin)(int binh, int binv); // binning - int (*setnflushes)(int N); // flushes amount - int (*shuttercmd)(shutter_op s); // work with shutter - int (*confio)(int s); // configure IO-port - int (*setio)(int s); // set IO-port to given state - int (*setframetype)(int l); // set frametype: 1 - light, 0 - dark - int (*setbitdepth)(int h); // set bit depth: 1 - high, 0 - low - int (*setfastspeed)(int s); // set readout speed: 1 - fast, 0 - low - // geometry (if TRUE, all args are changed to suitable values) - int (*setgeometry)(frameformat *fmt); // set geometry in UNBINNED coordinates - int (*setfanspeed)(fan_speed spd); // set fan speed - // getters: - int (*getbrightness)(float *b);// get brightnes level - int (*getModelName)(char *n, int l);// string with model name (l - length of n in bytes) - int (*getgain)(float *g); // get gain value - int (*getmaxgain)(float *g);// get max available gain value - // get limits of geometry: maximal values and steps - int (*getgeomlimits)(frameformat *max, frameformat *step); - int (*getTcold)(float *t); // cold-side T - int (*getThot)(float *t); // hot-side T - int (*getTbody)(float *t); // body T - int (*getbin)(int *binh, int *binv); - int (*getio)(int *s); // get IO-port state - float pixX, pixY; // pixel size in um - frameformat field; // max field of view - frameformat array; // array format - frameformat geometry; // current geometry settings (as in setgeometry) -} Camera; - -// focuser -typedef struct{ - int (*check)(); // check if the device is available - int Ndevices; - void (*close)(); - // setters: - int (*setDevNo)(int n); // set active device number - int (*setAbsPos)(int async, float n);// set absolute position (in millimeters!!!) - int (*home)(int async); // home device - // getters: - int (*getModelName)(char *n, int l);// string with model name (l - length of n in bytes) - int (*getTbody)(float *t); // body T - int (*getPos)(float *p); // current position number (starting from zero) - int (*getMaxPos)(float *p); // max position - int (*getMinPos)(float *p); // min position -} Focuser; - -// wheel -typedef struct{ - int (*check)(); // check if the device is available - int Ndevices; - void (*close)(); - // setters: - int (*setDevNo)(int n); // set active device number - int (*setPos)(int n); // set absolute position (starting from 0) - // getters: - int (*getModelName)(char *n, int l);// string with model name (l - length of n in bytes) - int (*getTbody)(float *t); // body T - int (*getPos)(int *p); // current position number (starting from zero) - int (*getMaxPos)(int *p); // amount of positions -} Wheel; - -void saveFITS(IMG *img, char *filename); // for imageview module +void calculate_stat(IMG *image); +void saveFITS(IMG *img); // for imageview module void focusers(); void wheels(); void ccds(); void cancel(); +int startCCD(void **dlh); +int startWheel(void **dlh); +int startFocuser(void **dlh); +void focclose(void *dlh); +void closewheel(void *dlh); +void closecam(void *dlh); + #endif // CCDFUNC_H__ diff --git a/client.c b/client.c new file mode 100644 index 0000000..a5f8700 --- /dev/null +++ b/client.c @@ -0,0 +1,176 @@ +/* + * This file is part of the CCD_Capture project. + * Copyright 2022 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// client-side functions +#include // isnan +#include +#include +#include +#include + +#include "client.h" +#include "cmdlnopts.h" +#include "server.h" // for common commands names +#include "socket.h" + +static char sendbuf[BUFSIZ]; +#define SENDMSG(...) do{snprintf(sendbuf, BUFSIZ-1, __VA_ARGS__); verbose(2, "%s", sendbuf); sendstrmessage(sock, sendbuf); getans(sock);}while(0) + +/** + * check data from fd (polling function for client) + * @param fd - file descriptor + * @return 0 in case of timeout, 1 in case of fd have data, -1 if error + */ +static int canberead(int fd){ + fd_set fds; + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 100; + FD_ZERO(&fds); + FD_SET(fd, &fds); + do{ + int rc = select(fd+1, &fds, NULL, NULL, &timeout); + if(rc < 0){ + if(errno != EINTR){ + LOGWARN("select()"); + WARN("select()"); + return -1; + } + continue; + } + break; + }while(1); + if(FD_ISSET(fd, &fds)){ + return 1; + } + return 0; +} + +static char *getans(int sock){ + static char buf[BUFSIZ]; + double t0 = dtime(); + char *ans = NULL; + while(dtime() - t0 < ANSWER_TIMEOUT){ + if(1 != canberead(sock)) continue; + int n = read(sock, buf, BUFSIZ-1); + if(n == 0){ + WARNX("Server disconnected"); + signals(1); + } + ans = buf; + buf[n] = 0; + DBG("Got from server: %s", buf); + verbose(1, "%s", buf); + } + return ans; +} + +static char *makeabspath(const char *path){ + static char buf[PATH_MAX]; + if(!path) return NULL; + char *ret = NULL; + int unl = 0; + FILE *f = fopen(path, "r"); + if(!f){ + f = fopen(path, "a"); + if(!f){ + ERR("Can't create %s", path); + return NULL; + } + unl = 1; + } + if(!realpath(path, buf)){ + ERR("realpath()"); + }else ret = buf; + fclose(f); + if(unl) unlink(path); + return ret; +} + +/** + * @brief processData - process here some actions and make messages for server + */ +static void process_data(int sock){ + // focuser + if(GP->listdevices) SENDMSG(CMD_FOCLIST); + if(GP->focdevno > -1) SENDMSG(CMD_FDEVNO "=%d", GP->focdevno); + if(!isnan(GP->gotopos)){ + SENDMSG(CMD_FGOTO "=%g", GP->gotopos); + } + // wheel + if(GP->listdevices) SENDMSG(CMD_WLIST); + if(GP->whldevno > -1) SENDMSG(CMD_WDEVNO "=%d", GP->whldevno); + if(GP->setwheel > -1) SENDMSG(CMD_WPOS "=%d", GP->setwheel); + // CCD/CMOS + if(GP->cancelexpose) SENDMSG(CMD_EXPSTATE "=%d", CAMERA_IDLE); + if(GP->listdevices) SENDMSG(CMD_CAMLIST); + if(GP->camdevno > -1) SENDMSG(CMD_CAMDEVNO "=%d", GP->camdevno); + if(GP->hbin) SENDMSG(CMD_HBIN "=%d", GP->hbin); + if(GP->vbin) SENDMSG(CMD_VBIN "=%d", GP->vbin); + if(!isnan(GP->temperature)) SENDMSG(CMD_CAMTEMPER "=%g", GP->temperature); + if(GP->shtr_cmd > -1) SENDMSG(CMD_SHUTTER "=%d", GP->shtr_cmd); + if(GP->confio > -1) SENDMSG(CMD_CONFIO "=%d", GP->confio); + if(GP->setio > -1) SENDMSG(CMD_IO "=%d", GP->setio);\ + if(!isnan(GP->gain)) SENDMSG(CMD_GAIN "=%g", GP->gain); + if(!isnan(GP->brightness)) SENDMSG(CMD_BRIGHTNESS "=%g", GP->brightness); + if(GP->nflushes > 0) SENDMSG(CMD_NFLUSHES "=%d", GP->nflushes); + if(GP->rewrite) SENDMSG(CMD_REWRITE "=1"); + else SENDMSG(CMD_REWRITE "=0"); + if(GP->outfile) SENDMSG(CMD_FILENAME "=%s", makeabspath(GP->outfile)); + if(GP->outfileprefix) SENDMSG(CMD_FILENAMEPREFIX "=%s", makeabspath(GP->outfileprefix)); + // if client gives filename and exptime, make exposition + if(GP->exptime > -DBL_EPSILON){ + SENDMSG(CMD_EXPOSITION "=%g", GP->exptime); + if(GP->outfile || GP->outfileprefix) SENDMSG(CMD_EXPSTATE "=%d", CAMERA_CAPTURE); + } + // common information + SENDMSG(CMD_INFO); +} + +void client(int sock){ + process_data(sock); + if(!GP->waitexpend) return; + double t0 = dtime(), tw = t0; + while(dtime() - t0 < CLIENT_TIMEOUT){ + if(GP->waitexpend && dtime() - tw > WAIT_TIMEOUT){ + SENDMSG(CMD_TREMAIN); // get remained time + tw = dtime(); + sprintf(sendbuf, "%s", CMD_EXPSTATE); + verbose(2, "%s", sendbuf); + sendstrmessage(sock, sendbuf); + } + char *ans = getans(sock); + if(ans){ + t0 = dtime(); + char *val = get_keyval(ans); + if(val && 0 == strcmp(ans, CMD_EXPSTATE)){ + int state = atoi(val); + if(state == CAMERA_ERROR){ + WARNX(_("Can't make exposition")); + return; + } + if(state != CAMERA_CAPTURE){ + verbose(2, "Frame ready!"); + return; + } + } + } + } + WARNX(_("Server timeout")); + DBG("Timeout"); +} diff --git a/client.h b/client.h new file mode 100644 index 0000000..87cfe2b --- /dev/null +++ b/client.h @@ -0,0 +1,33 @@ +/* + * This file is part of the CCD_Capture project. + * Copyright 2022 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#ifndef CLIENT_H__ +#define CLIENT_H__ + +// waiting for answer timeout +#define ANSWER_TIMEOUT 1.0 +// wait for exposition ends (between subsequent check calls) +#define WAIT_TIMEOUT 2.0 +// client will disconnect after this time from last server message +#define CLIENT_TIMEOUT 10.0 + +// client-side functions +void client(int fd); + +#endif // CLIENT_H__ diff --git a/cmdlnopts.c b/cmdlnopts.c index 50eca45..5508f95 100644 --- a/cmdlnopts.c +++ b/cmdlnopts.c @@ -9,24 +9,30 @@ #include "cmdlnopts.h" +#define DEFAULT_PID_FILE "/tmp/CCD_Capture.pid" + static int help; glob_pars *GP = NULL; // DEFAULTS // default global parameters static glob_pars G = { - .instrument = "direct imaging", - .exptime = -1, + .instrument = NULL, + .exptime = -1., .nframes = 1, .hbin = 1, .vbin = 1, .X0 = -1, .Y0 = -1, .X1 = -1, .Y1 = -1, - .temperature = 1e6, + .focdevno = -1, + .camdevno = -1, + .whldevno = -1, + .temperature = NAN, .shtr_cmd = -1, .confio = -1, .setio = -1, .gotopos = NAN, .addsteps = NAN, + .pidfile = DEFAULT_PID_FILE, + .brightness = NAN, .gain = NAN, .setwheel = -1, .fanspeed = -1, - .nflushes = 1 }; /* @@ -47,7 +53,7 @@ myoption cmdlnopts[] = { {"verbose", NO_ARGS, NULL, 'V', arg_none, APTR(&G.verbose), N_("verbose level (each -v increase it)")}, {"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 (8MHz) readout mode")}, + {"fast", NO_ARGS, NULL, 'f', arg_none, APTR(&G.fast), N_("fast readout mode")}, {"set-temp",NEED_ARG, NULL, 't', arg_double, APTR(&G.temperature),N_("set CCD temperature to given value (degr C)")}, {"set-fan", NEED_ARG, NULL, 0, arg_int, APTR(&G.fanspeed), N_("set fan speed (0 - off, 1 - low, 2 - high)")}, @@ -58,6 +64,8 @@ myoption cmdlnopts[] = { {"obsname", NEED_ARG, NULL, 'N', arg_string, APTR(&G.observers), N_("observers' names")}, {"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")}, @@ -65,11 +73,11 @@ myoption cmdlnopts[] = { {"nframes", NEED_ARG, NULL, 'n', arg_int, APTR(&G.nframes), N_("make series of N frames")}, {"pause", NEED_ARG, NULL, 'p', arg_int, APTR(&G.pause_len), N_("make pause for N seconds between expositions")}, {"exptime", NEED_ARG, NULL, 'x', arg_double, APTR(&G.exptime), N_("set exposure time to given value (seconds!)")}, - {"X0", NEED_ARG, NULL, 0, arg_int, APTR(&G.X0), N_("frame X0 coordinate (-1 - all with overscan)")}, - {"Y0", NEED_ARG, NULL, 0, arg_int, APTR(&G.Y0), N_("frame Y0 coordinate (-1 - all with overscan)")}, - {"X1", NEED_ARG, NULL, 0, arg_int, APTR(&G.X1), N_("frame X1 coordinate (-1 - all with overscan)")}, - {"Y1", NEED_ARG, NULL, 0, arg_int, APTR(&G.Y1), N_("frame Y1 coordinate (-1 - all with overscan)")}, - {"fullframe",NO_ARGS, NULL, 0, arg_int, APTR(&G.fullframe), N_("grab full frame (with overscans)")}, + {"cancel", NO_ARGS, &G.cancelexpose, 1,arg_none, NULL, N_("cancel current exposition")}, + {"X0", NEED_ARG, NULL, 0, arg_int, APTR(&G.X0), N_("absolute (not divided by binning!) frame X0 coordinate (-1 - all with overscan)")}, + {"Y0", NEED_ARG, NULL, 0, arg_int, APTR(&G.Y0), N_("absolute frame Y0 coordinate (-1 - all with overscan)")}, + {"X1", NEED_ARG, NULL, 0, arg_int, APTR(&G.X1), N_("absolute frame X1 coordinate (-1 - all with overscan)")}, + {"Y1", NEED_ARG, NULL, 0, arg_int, APTR(&G.Y1), N_("absolute frame Y1 coordinate (-1 - all with overscan)")}, {"open-shutter",NO_ARGS,&G.shtr_cmd, SHUTTER_OPEN,arg_none,NULL, N_("open shutter")}, {"close-shutter",NO_ARGS,&G.shtr_cmd, SHUTTER_CLOSE,arg_none,NULL, N_("close shutter")}, @@ -86,6 +94,14 @@ myoption cmdlnopts[] = { {"wheel-set",NEED_ARG, NULL, 'w', arg_int, APTR(&G.setwheel), N_("set wheel position")}, + {"gain", NEED_ARG, NULL, 0, arg_float, APTR(&G.gain), N_("CMOS gain level")}, + {"brightness",NEED_ARG, NULL, 0, arg_float, APTR(&G.brightness),N_("CMOS brightness level")}, + + {"logfile", NEED_ARG, NULL, 0, arg_string, APTR(&G.logfile), N_("logging file name (if run as server)")}, + {"path", NEED_ARG, NULL, 0, arg_string, APTR(&G.path), N_("UNIX socket name")}, + {"port", NEED_ARG, NULL, 0, arg_string, APTR(&G.port), N_("local INET socket port")}, + {"pidfile", NEED_ARG, NULL, 0, arg_string, APTR(&G.pidfile), N_("PID file (default: " DEFAULT_PID_FILE ")")}, + #ifdef IMAGEVIEW {"display", NO_ARGS, NULL, 'D', arg_int, APTR(&G.showimage), N_("Display image in OpenGL window")}, #endif @@ -94,7 +110,6 @@ myoption cmdlnopts[] = { end_option }; - /** * Parse command line options and return dynamically allocated structure * to global parameters @@ -104,12 +119,12 @@ myoption cmdlnopts[] = { */ glob_pars *parse_args(int argc, char **argv){ // format of help: "Usage: progname [args]\n" - change_helpstring("Usage: %s [args] \n\n\tWhere args are:\n"); + change_helpstring("Usage: %s [args] [output file prefix]\n\n\tWhere args are:\n"); // parse arguments parseargs(&argc, &argv, cmdlnopts); if(help) showhelp(-1, cmdlnopts); if(argc > 0){ - G.outfile = strdup(argv[0]); + G.outfileprefix = strdup(argv[0]); if(argc > 1){ WARNX("%d unused parameters:\n", argc - 1); for(int i = 1; i < argc; ++i) diff --git a/cmdlnopts.h b/cmdlnopts.h index 4eac6e7..2f5fb0a 100644 --- a/cmdlnopts.h +++ b/cmdlnopts.h @@ -32,24 +32,32 @@ typedef struct{ char *focuserdev; // focuser ... char *wheeldev; // wheel ... char *objname; // object's name - char *outfile; // output filename prefix + char *outfile; // output filename + char *outfileprefix;// output filename prefix char *objtype; // type of object (dark/obj/bias) char *instrument; // instrument's name char *observers; // observers' names char *prog_id; // programm identificator char *author; // programm author + char *logfile; // when run as server log here + char *path; // UNIX socket name + char *port; // local INET socket port + char *pidfile; // PID file (default: /tmp/CCD_Capture.pid) + char **addhdr; // list of files from which to add header records + int waitexpend; // wait while exposition ends + int cancelexpose; // cancel exp + int client; // run as client int listdevices; // list connected devices int fanspeed; // fan speed: 0-2 int noflush; // turn off bg flushing int camdevno; // camera number (0, 1, 2 etc) - int focdevno; - int whldevno; + int focdevno; // focuser -//- + int whldevno; // wheel -//- int dark; // dark frame int nframes; // amount of frames to take int hbin; int vbin; // binning int X0; int Y0; // top left corner coordinate (-1 - all, including overscan) int X1; int Y1; // bottom right corner coordinate - int fullframe; // grab full frame (with overscans) int nflushes; // amount of flushes int pause_len; // pause (in seconds) between expositions int shtr_cmd; // shutter command (flishutter_t) @@ -58,16 +66,17 @@ typedef struct{ int getio; // get value of ioport int setio; // set value of ioport int confio; // configure ioport - double exptime; // time of exposition in seconds - double temperature; // temperature of CCD - double gotopos; // move stepper motor of focuser to absolute position - double addsteps; // move stepper motor of focuser to relative position int setwheel; // set wheel position int async; // asynchronous moving int verbose; // each '-V' increases it int rewrite; // rewrite file int showimage; // show image preview - char **addhdr; // list of files from which to add header records + float gain; // gain level (only for CMOS) + float brightness; // brightness (only for CMOS) + double exptime; // time of exposition in seconds + double temperature; // temperature of CCD + double gotopos; // move stepper motor of focuser to absolute position + double addsteps; // move stepper motor of focuser to relative position } glob_pars; diff --git a/imageview.c b/imageview.c index 8b4e65e..06e6bd3 100644 --- a/imageview.c +++ b/imageview.c @@ -489,7 +489,7 @@ void* image_thread(_U_ void *data){ if(win && win->winevt){ if(win->winevt & WINEVT_SAVEIMAGE){ // save image verbose(2, "Make screenshot\n"); - saveFITS(img, "ScreenShot"); + saveFITS(img); win->winevt &= ~WINEVT_SAVEIMAGE; } if(win->winevt & WINEVT_ROLLCOLORFUN){ diff --git a/locale/ru/messages.po b/locale/ru/messages.po index cb72308..927c85e 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: 2022-03-01 20:35+0300\n" +"POT-Creation-Date: 2022-03-17 18:04+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,472 +17,543 @@ msgstr "" "Content-Type: text/plain; charset=koi8-r\n" "Content-Transfer-Encoding: 8bit\n" -#: cmdlnopts.c:37 +#: cmdlnopts.c:43 msgid "common device plugin (e.g devfli.so)" msgstr "" -#: cmdlnopts.c:38 +#: cmdlnopts.c:44 msgid "camera device plugin (e.g. devfli.so)" msgstr "" -#: cmdlnopts.c:39 +#: cmdlnopts.c:45 msgid "focuser device plugin (e.g. devzwo.so)" msgstr "" -#: cmdlnopts.c:40 +#: cmdlnopts.c:46 msgid "wheel device plugin (e.g. devdummy.so)" msgstr "" -#: cmdlnopts.c:41 +#: cmdlnopts.c:47 msgid "list connected devices" msgstr "" -#: cmdlnopts.c:42 +#: cmdlnopts.c:48 msgid "camera device number (if many: 0, 1, 2 etc)" msgstr "" -#: cmdlnopts.c:43 +#: cmdlnopts.c:49 msgid "filter wheel device number (if many: 0, 1, 2 etc)" msgstr "" -#: cmdlnopts.c:44 +#: cmdlnopts.c:50 msgid "focuser device number (if many: 0, 1, 2 etc)" msgstr "" -#: cmdlnopts.c:45 +#: cmdlnopts.c:51 msgid "show this help" msgstr "" -#: cmdlnopts.c:46 +#: cmdlnopts.c:52 msgid "rewrite output file if exists" msgstr "" -#: cmdlnopts.c:47 +#: cmdlnopts.c:53 msgid "verbose level (each -v increase it)" msgstr "" -#: cmdlnopts.c:48 +#: cmdlnopts.c:54 msgid "not open shutter, when exposing (\"dark frames\")" msgstr "" -#: cmdlnopts.c:49 +#: cmdlnopts.c:55 msgid "run in 8-bit mode" msgstr "" -#: cmdlnopts.c:50 -msgid "fast (8MHz) readout mode" -msgstr "" - -#: cmdlnopts.c:51 -msgid "set CCD temperature to given value (degr C)" -msgstr "" - -#: cmdlnopts.c:52 -msgid "set fan speed (0 - off, 1 - low, 2 - high)" -msgstr "" - -#: cmdlnopts.c:54 -msgid "program author" -msgstr "" - -#: cmdlnopts.c:55 -msgid "object type (neon, object, flat etc)" -msgstr "" - #: cmdlnopts.c:56 -msgid "instrument name" +msgid "fast readout mode" msgstr "" #: cmdlnopts.c:57 -msgid "object name" +msgid "set CCD temperature to given value (degr C)" msgstr "" #: cmdlnopts.c:58 -msgid "observers' names" -msgstr "" - -#: cmdlnopts.c:59 -msgid "observing program name" +msgid "set fan speed (0 - off, 1 - low, 2 - high)" msgstr "" #: cmdlnopts.c:60 -msgid "add records to header from given file[s]" +msgid "program author" +msgstr "" + +#: cmdlnopts.c:61 +msgid "object type (neon, object, flat etc)" msgstr "" #: cmdlnopts.c:62 -msgid "N flushes before exposing (default: 1)" +msgid "instrument name" msgstr "" #: cmdlnopts.c:63 -msgid "horizontal binning to N pixels" +msgid "object name" msgstr "" #: cmdlnopts.c:64 -msgid "vertical binning to N pixels" +msgid "observers' names" msgstr "" #: cmdlnopts.c:65 -msgid "make series of N frames" +msgid "observing program name" msgstr "" #: cmdlnopts.c:66 -msgid "make pause for N seconds between expositions" +msgid "add records to header from given file[s]" msgstr "" #: cmdlnopts.c:67 -msgid "set exposure time to given value (seconds!)" +msgid "output file name" msgstr "" #: cmdlnopts.c:68 -msgid "frame X0 coordinate (-1 - all with overscan)" -msgstr "" - -#: cmdlnopts.c:69 -msgid "frame Y0 coordinate (-1 - all with overscan)" +msgid "wait while exposition ends" msgstr "" #: cmdlnopts.c:70 -msgid "frame X1 coordinate (-1 - all with overscan)" +msgid "N flushes before exposing (default: 1)" msgstr "" #: cmdlnopts.c:71 -msgid "frame Y1 coordinate (-1 - all with overscan)" +msgid "horizontal binning to N pixels" msgstr "" #: cmdlnopts.c:72 -msgid "grab full frame (with overscans)" +msgid "vertical binning to N pixels" +msgstr "" + +#: cmdlnopts.c:73 +msgid "make series of N frames" msgstr "" #: cmdlnopts.c:74 -msgid "open shutter" +msgid "make pause for N seconds between expositions" msgstr "" #: cmdlnopts.c:75 -msgid "close shutter" +msgid "set exposure time to given value (seconds!)" msgstr "" #: cmdlnopts.c:76 -msgid "run exposition on LOW @ pin5 I/O port" +msgid "cancel current exposition" msgstr "" #: cmdlnopts.c:77 -msgid "run exposition on HIGH @ pin5 I/O port" +msgid "" +"absolute (not divided by binning!) frame X0 coordinate (-1 - all with " +"overscan)" msgstr "" #: cmdlnopts.c:78 -msgid "get value of I/O port pins" +msgid "absolute frame Y0 coordinate (-1 - all with overscan)" msgstr "" #: cmdlnopts.c:79 -msgid "move stepper motor asynchronous" +msgid "absolute frame X1 coordinate (-1 - all with overscan)" msgstr "" -#: cmdlnopts.c:81 -msgid "set I/O port pins to given value (decimal number, pin1 is LSB)" +#: cmdlnopts.c:80 +msgid "absolute frame Y1 coordinate (-1 - all with overscan)" msgstr "" #: cmdlnopts.c:82 +msgid "open shutter" +msgstr "" + +#: cmdlnopts.c:83 +msgid "close shutter" +msgstr "" + +#: cmdlnopts.c:84 +msgid "run exposition on LOW @ pin5 I/O port" +msgstr "" + +#: cmdlnopts.c:85 +msgid "run exposition on HIGH @ pin5 I/O port" +msgstr "" + +#: cmdlnopts.c:86 +msgid "get value of I/O port pins" +msgstr "" + +#: cmdlnopts.c:87 +msgid "move stepper motor asynchronous" +msgstr "" + +#: cmdlnopts.c:89 +msgid "set I/O port pins to given value (decimal number, pin1 is LSB)" +msgstr "" + +#: cmdlnopts.c:90 msgid "" "configure I/O port pins to given value (decimal number, pin1 is LSB, 1 == " "output, 0 == input)" msgstr "" -#: cmdlnopts.c:84 +#: cmdlnopts.c:92 msgid "move focuser to absolute position, mm" msgstr "" -#: cmdlnopts.c:85 +#: cmdlnopts.c:93 msgid "move focuser to relative position, mm" msgstr "" -#: cmdlnopts.c:87 +#: cmdlnopts.c:95 msgid "set wheel position" msgstr "" -#: cmdlnopts.c:90 +#: cmdlnopts.c:97 +msgid "CMOS gain level" +msgstr "" + +#: cmdlnopts.c:98 +msgid "CMOS brightness level" +msgstr "" + +#: cmdlnopts.c:100 +msgid "logging file name (if run as server)" +msgstr "" + +#: cmdlnopts.c:101 +msgid "UNIX socket name" +msgstr "" + +#: cmdlnopts.c:102 +msgid "local INET socket port" +msgstr "" + +#: cmdlnopts.c:103 +msgid "PID file (default: " +msgstr "" + +#: cmdlnopts.c:106 msgid "Display image in OpenGL window" msgstr "" -#: ccdfunc.c:62 +#: ccdfunc.c:63 #, c-format msgid "Can't find plugin %s: %s" msgstr "" -#: ccdfunc.c:74 +#: ccdfunc.c:75 #, c-format msgid "Can't find focuser in plugin %s: %s" msgstr "" -#: ccdfunc.c:85 +#: ccdfunc.c:86 #, c-format msgid "Can't find camera in plugin %s: %s" msgstr "" -#: ccdfunc.c:96 +#: ccdfunc.c:97 #, c-format msgid "Can't find wheel in plugin %s: %s" msgstr "" -#: ccdfunc.c:149 +#: ccdfunc.c:152 msgid "Camera device unknown" msgstr "" -#. Не могу сохранить файл -#: ccdfunc.c:157 -msgid "Can't save file" +#: ccdfunc.c:158 +msgid "Neither filename nor filename prefix pointed!" msgstr "" -#: ccdfunc.c:304 +#. Не могу сохранить файл +#: ccdfunc.c:177 +#, c-format +msgid "Can't save file with prefix %s" +msgstr "" + +#: ccdfunc.c:329 #, c-format msgid "File saved as '%s'" msgstr "" -#: ccdfunc.c:306 +#: ccdfunc.c:332 msgid "Error saving file" msgstr "" -#: ccdfunc.c:344 +#: ccdfunc.c:371 #, c-format msgid "Image stat:\n" msgstr "" -#: ccdfunc.c:357 +#: ccdfunc.c:379 msgid "Focuser device not pointed" msgstr "" -#: ccdfunc.c:364 +#: ccdfunc.c:386 msgid "No focusers found" msgstr "" -#: ccdfunc.c:377 +#: ccdfunc.c:417 #, c-format msgid "Found %d focusers, you point number %d" msgstr "" -#: ccdfunc.c:381 +#: ccdfunc.c:421 msgid "Can't set active focuser number" msgstr "" -#: ccdfunc.c:395 +#: ccdfunc.c:435 msgid "Can't get focuser limit positions" msgstr "" -#: ccdfunc.c:402 +#: ccdfunc.c:442 msgid "Can't get current focuser position" msgstr "" -#: ccdfunc.c:416 +#: ccdfunc.c:456 #, c-format msgid "Can't set position %g: out of limits [%g, %g]" msgstr "" -#: ccdfunc.c:420 +#: ccdfunc.c:460 msgid "Can't home focuser" msgstr "" -#: ccdfunc.c:422 +#: ccdfunc.c:462 #, c-format msgid "Can't set position %g" msgstr "" -#: ccdfunc.c:437 +#: ccdfunc.c:470 msgid "Wheel device not pointed" msgstr "" -#: ccdfunc.c:444 +#: ccdfunc.c:477 msgid "No wheels found" msgstr "" -#: ccdfunc.c:457 +#: ccdfunc.c:508 #, c-format msgid "Found %d wheels, you point number %d" msgstr "" -#: ccdfunc.c:461 +#: ccdfunc.c:512 msgid "Can't set active wheel number" msgstr "" -#: ccdfunc.c:477 +#: ccdfunc.c:528 msgid "Can't get max wheel position" msgstr "" -#: ccdfunc.c:484 +#: ccdfunc.c:535 #, c-format msgid "Wheel position should be from 0 to %d" msgstr "" -#: ccdfunc.c:488 +#: ccdfunc.c:539 #, c-format msgid "Can't set wheel position %d" msgstr "" -#: ccdfunc.c:507 +#: ccdfunc.c:556 #, c-format msgid "%.1f seconds till exposition ends" msgstr "" -#: ccdfunc.c:528 +#: ccdfunc.c:570 msgid "Camera device not pointed" msgstr "" -#: ccdfunc.c:535 +#: ccdfunc.c:577 ccdfunc.c:578 msgid "No cameras found" msgstr "" -#: ccdfunc.c:547 +#: ccdfunc.c:613 #, c-format msgid "Found %d cameras, you point number %d" msgstr "" -#: ccdfunc.c:551 +#: ccdfunc.c:617 msgid "Can't set active camera number" msgstr "" -#: ccdfunc.c:557 +#: ccdfunc.c:623 msgid "Can't set fan speed" msgstr "" -#: ccdfunc.c:558 +#: ccdfunc.c:624 #, c-format msgid "Set fan speed to %d" msgstr "" -#: ccdfunc.c:563 +#: ccdfunc.c:629 #, c-format msgid "Camera model: %s" msgstr "" -#: ccdfunc.c:564 +#: ccdfunc.c:630 #, c-format msgid "Pixel size: %g x %g" msgstr "" -#: ccdfunc.c:570 +#: ccdfunc.c:636 #, c-format msgid "Full array: %s" msgstr "" -#: ccdfunc.c:573 +#: ccdfunc.c:639 #, c-format msgid "Field of view: %s" msgstr "" -#: ccdfunc.c:576 +#: ccdfunc.c:642 #, c-format msgid "Can't set T to %g degC" msgstr "" -#: ccdfunc.c:583 +#: ccdfunc.c:649 #, c-format msgid "Shutter command: %s\n" msgstr "" -#: ccdfunc.c:585 +#: ccdfunc.c:651 #, c-format msgid "Can't run shutter command %s (unsupported?)" msgstr "" #. "Попытка сконфигурировать порт I/O как %d\n" -#: ccdfunc.c:589 +#: ccdfunc.c:655 #, c-format msgid "Try to configure I/O port as %d" msgstr "" -#: ccdfunc.c:591 +#: ccdfunc.c:657 msgid "Can't configure (unsupported?)" msgstr "" -#: ccdfunc.c:597 +#: ccdfunc.c:663 msgid "Can't get IOport state (unsupported?)" msgstr "" #. "Попытка записи %d в порт I/O\n" -#: ccdfunc.c:601 +#: ccdfunc.c:667 #, c-format msgid "Try to write %d to I/O port" msgstr "" -#: ccdfunc.c:603 +#: ccdfunc.c:669 msgid "Can't set IOport" msgstr "" -#: ccdfunc.c:610 +#: ccdfunc.c:676 +#, c-format +msgid "Set gain to %g" +msgstr "" + +#: ccdfunc.c:677 +#, c-format +msgid "Can't set gain to %g" +msgstr "" + +#: ccdfunc.c:682 +#, c-format +msgid "Set brightness to %g" +msgstr "" + +#: ccdfunc.c:683 +#, c-format +msgid "Can't set brightness to %g" +msgstr "" + +#: ccdfunc.c:689 #, c-format msgid "Can't set binning %dx%d" msgstr "" -#: ccdfunc.c:624 +#: ccdfunc.c:699 msgid "Can't set given geometry" msgstr "" -#: ccdfunc.c:627 +#: ccdfunc.c:703 #, c-format msgid "Can't set %d flushes" msgstr "" -#: ccdfunc.c:630 +#: ccdfunc.c:707 #, c-format msgid "Can't set exposure time to %f seconds" msgstr "" -#: ccdfunc.c:633 +#: ccdfunc.c:710 msgid "Can't change frame type" msgstr "" -#: ccdfunc.c:636 +#: ccdfunc.c:713 msgid "Can't set bit depth" msgstr "" -#: ccdfunc.c:638 +#: ccdfunc.c:715 msgid "Can't set readout speed" msgstr "" -#: ccdfunc.c:639 +#: ccdfunc.c:716 #, c-format msgid "Readout mode: %s" msgstr "" -#: ccdfunc.c:640 +#: ccdfunc.c:717 msgid "Only show statistics" msgstr "" #. GET binning should be AFTER setgeometry! -#: ccdfunc.c:642 +#: ccdfunc.c:719 msgid "Can't get current binning" msgstr "" -#: ccdfunc.c:655 +#: ccdfunc.c:732 msgid "Can't open OpenGL window, image preview will be inaccessible" msgstr "" #. Захват кадра %d\n -#: ccdfunc.c:662 +#: ccdfunc.c:739 #, c-format msgid "Capture frame %d" msgstr "" -#: ccdfunc.c:664 +#: ccdfunc.c:741 server.c:64 msgid "Can't start exposition" msgstr "" -#: ccdfunc.c:668 ccdfunc.c:690 ccdfunc.c:731 +#: ccdfunc.c:745 ccdfunc.c:767 ccdfunc.c:808 msgid "Can't capture image" msgstr "" -#: ccdfunc.c:671 +#: ccdfunc.c:748 msgid "Read grabbed image" msgstr "" -#: ccdfunc.c:674 ccdfunc.c:694 ccdfunc.c:735 +#: ccdfunc.c:751 ccdfunc.c:771 ccdfunc.c:812 msgid "Can't grab image" msgstr "" #. %d секунд до окончания паузы\n -#: ccdfunc.c:711 +#: ccdfunc.c:788 #, c-format msgid "%d seconds till pause ends\n" msgstr "" +#: server.c:89 +msgid "No camera device" +msgstr "" + +#: client.c:164 +msgid "Can't make exposition" +msgstr "" + +#: client.c:174 +msgid "Server timeout" +msgstr "" + #: imageview.c:264 msgid "Can't init mutex!" msgstr "" diff --git a/locale/ru/ru.po b/locale/ru/ru.po index 7b24e53..f1dcb0f 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: 2022-03-01 20:27+0300\n" + "POT-Creation-Date: 2022-03-17 18:04+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -16,13 +16,13 @@ msgstr "Project-Id-Version: PACKAGE VERSION\n" "Content-Type: text/plain; charset=koi8-r\n" "Content-Transfer-Encoding: 8bit\n" -#: ccdfunc.c:507 +#: ccdfunc.c:556 #, c-format msgid "%.1f seconds till exposition ends" msgstr "" #. %d секунд до окончания паузы\n -#: ccdfunc.c:711 +#: ccdfunc.c:788 #, c-format msgid "%d seconds till pause ends\n" msgstr "" @@ -31,77 +31,85 @@ msgstr "" msgid "Already initialized!" msgstr "" -#: ccdfunc.c:528 +#: cmdlnopts.c:98 +msgid "CMOS brightness level" +msgstr "" + +#: cmdlnopts.c:97 +msgid "CMOS gain level" +msgstr "" + +#: ccdfunc.c:570 msgid "Camera device not pointed" msgstr "" -#: ccdfunc.c:149 +#: ccdfunc.c:152 msgid "Camera device unknown" msgstr "" -#: ccdfunc.c:563 +#: ccdfunc.c:629 #, c-format msgid "Camera model: %s" msgstr "" -#: ccdfunc.c:668 ccdfunc.c:690 ccdfunc.c:731 +#: ccdfunc.c:745 ccdfunc.c:767 ccdfunc.c:808 msgid "Can't capture image" msgstr "" -#: ccdfunc.c:633 +#: ccdfunc.c:710 msgid "Can't change frame type" msgstr "" -#: ccdfunc.c:591 +#: ccdfunc.c:657 msgid "Can't configure (unsupported?)" msgstr "" -#: ccdfunc.c:85 +#: ccdfunc.c:86 #, c-format msgid "Can't find camera in plugin %s: %s" msgstr "" -#: ccdfunc.c:74 +#: ccdfunc.c:75 #, c-format msgid "Can't find focuser in plugin %s: %s" msgstr "" -#: ccdfunc.c:62 +#: ccdfunc.c:63 #, c-format msgid "Can't find plugin %s: %s" msgstr "" -#: ccdfunc.c:96 +#: ccdfunc.c:97 #, c-format msgid "Can't find wheel in plugin %s: %s" msgstr "" -#: ccdfunc.c:597 +#: ccdfunc.c:663 msgid "Can't get IOport state (unsupported?)" msgstr "" #. GET binning should be AFTER setgeometry! -#: ccdfunc.c:642 +#: ccdfunc.c:719 msgid "Can't get current binning" msgstr "" -#: ccdfunc.c:402 +#: ccdfunc.c:442 msgid "Can't get current focuser position" msgstr "" -#: ccdfunc.c:395 +#: ccdfunc.c:435 msgid "Can't get focuser limit positions" msgstr "" -#: ccdfunc.c:477 +#: ccdfunc.c:528 msgid "Can't get max wheel position" msgstr "" -#: ccdfunc.c:674 ccdfunc.c:694 ccdfunc.c:735 +#: ccdfunc.c:751 ccdfunc.c:771 ccdfunc.c:812 msgid "Can't grab image" msgstr "" -#: ccdfunc.c:420 +#: ccdfunc.c:460 msgid "Can't home focuser" msgstr "" @@ -109,98 +117,113 @@ msgstr "" msgid "Can't init mutex!" msgstr "" -#: ccdfunc.c:655 +#: client.c:164 +msgid "Can't make exposition" +msgstr "" + +#: ccdfunc.c:732 msgid "Can't open OpenGL window, image preview will be inaccessible" msgstr "" -#: ccdfunc.c:585 +#: ccdfunc.c:651 #, c-format msgid "Can't run shutter command %s (unsupported?)" msgstr "" #. Не могу сохранить файл -#: ccdfunc.c:157 -msgid "Can't save file" +#: ccdfunc.c:177 +#, c-format +msgid "Can't save file with prefix %s" msgstr "" -#: ccdfunc.c:627 +#: ccdfunc.c:703 #, c-format msgid "Can't set %d flushes" msgstr "" -#: ccdfunc.c:603 +#: ccdfunc.c:669 msgid "Can't set IOport" msgstr "" -#: ccdfunc.c:576 +#: ccdfunc.c:642 #, c-format msgid "Can't set T to %g degC" msgstr "" -#: ccdfunc.c:551 +#: ccdfunc.c:617 msgid "Can't set active camera number" msgstr "" -#: ccdfunc.c:381 +#: ccdfunc.c:421 msgid "Can't set active focuser number" msgstr "" -#: ccdfunc.c:461 +#: ccdfunc.c:512 msgid "Can't set active wheel number" msgstr "" -#: ccdfunc.c:610 +#: ccdfunc.c:689 #, c-format msgid "Can't set binning %dx%d" msgstr "" -#: ccdfunc.c:636 +#: ccdfunc.c:713 msgid "Can't set bit depth" msgstr "" -#: ccdfunc.c:630 +#: ccdfunc.c:683 +#, c-format +msgid "Can't set brightness to %g" +msgstr "" + +#: ccdfunc.c:707 #, c-format msgid "Can't set exposure time to %f seconds" msgstr "" -#: ccdfunc.c:557 +#: ccdfunc.c:623 msgid "Can't set fan speed" msgstr "" -#: ccdfunc.c:624 +#: ccdfunc.c:677 +#, c-format +msgid "Can't set gain to %g" +msgstr "" + +#: ccdfunc.c:699 msgid "Can't set given geometry" msgstr "" -#: ccdfunc.c:422 +#: ccdfunc.c:462 #, c-format msgid "Can't set position %g" msgstr "" -#: ccdfunc.c:416 +#: ccdfunc.c:456 #, c-format msgid "Can't set position %g: out of limits [%g, %g]" msgstr "" -#: ccdfunc.c:638 +#: ccdfunc.c:715 msgid "Can't set readout speed" msgstr "" -#: ccdfunc.c:488 +#: ccdfunc.c:539 #, c-format msgid "Can't set wheel position %d" msgstr "" -#: ccdfunc.c:664 +#: ccdfunc.c:741 server.c:64 msgid "Can't start exposition" msgstr "" #. Захват кадра %d\n -#: ccdfunc.c:662 +#: ccdfunc.c:739 #, c-format msgid "Capture frame %d" msgstr "" -#: cmdlnopts.c:90 +#: cmdlnopts.c:106 msgid "Display image in OpenGL window" msgstr "" @@ -209,40 +232,40 @@ msgstr "" msgid "Equalization of histogram: %s" msgstr "" -#: ccdfunc.c:306 +#: ccdfunc.c:332 msgid "Error saving file" msgstr "" -#: ccdfunc.c:573 +#: ccdfunc.c:639 #, c-format msgid "Field of view: %s" msgstr "" -#: ccdfunc.c:304 +#: ccdfunc.c:329 #, c-format msgid "File saved as '%s'" msgstr "" -#: ccdfunc.c:357 +#: ccdfunc.c:379 msgid "Focuser device not pointed" msgstr "" -#: ccdfunc.c:547 +#: ccdfunc.c:613 #, c-format msgid "Found %d cameras, you point number %d" msgstr "" -#: ccdfunc.c:377 +#: ccdfunc.c:417 #, c-format msgid "Found %d focusers, you point number %d" msgstr "" -#: ccdfunc.c:457 +#: ccdfunc.c:508 #, c-format msgid "Found %d wheels, you point number %d" msgstr "" -#: ccdfunc.c:570 +#: ccdfunc.c:636 #, c-format msgid "Full array: %s" msgstr "" @@ -252,190 +275,229 @@ msgstr "" msgid "Histogram conversion: %s" msgstr "" -#: ccdfunc.c:344 +#: ccdfunc.c:371 #, c-format msgid "Image stat:\n" msgstr "" -#: cmdlnopts.c:62 +#: cmdlnopts.c:70 msgid "N flushes before exposing (default: 1)" msgstr "" -#: ccdfunc.c:535 +#: ccdfunc.c:158 +msgid "Neither filename nor filename prefix pointed!" +msgstr "" + +#: server.c:89 +msgid "No camera device" +msgstr "" + +#: ccdfunc.c:577 ccdfunc.c:578 msgid "No cameras found" msgstr "" -#: ccdfunc.c:364 +#: ccdfunc.c:386 msgid "No focusers found" msgstr "" -#: ccdfunc.c:444 +#: ccdfunc.c:477 msgid "No wheels found" msgstr "" -#: ccdfunc.c:640 +#: ccdfunc.c:717 msgid "Only show statistics" msgstr "" -#: ccdfunc.c:564 +#: cmdlnopts.c:103 +msgid "PID file (default: " +msgstr "" + +#: ccdfunc.c:630 #, c-format msgid "Pixel size: %g x %g" msgstr "" -#: ccdfunc.c:671 +#: ccdfunc.c:748 msgid "Read grabbed image" msgstr "" -#: ccdfunc.c:639 +#: ccdfunc.c:716 #, c-format msgid "Readout mode: %s" msgstr "" -#: ccdfunc.c:558 +#: client.c:174 +msgid "Server timeout" +msgstr "" + +#: ccdfunc.c:682 +#, c-format +msgid "Set brightness to %g" +msgstr "" + +#: ccdfunc.c:624 #, c-format msgid "Set fan speed to %d" msgstr "" -#: ccdfunc.c:583 +#: ccdfunc.c:676 +#, c-format +msgid "Set gain to %g" +msgstr "" + +#: ccdfunc.c:649 #, c-format msgid "Shutter command: %s\n" msgstr "" #. "Попытка сконфигурировать порт I/O как %d\n" -#: ccdfunc.c:589 +#: ccdfunc.c:655 #, c-format msgid "Try to configure I/O port as %d" msgstr "" #. "Попытка записи %d в порт I/O\n" -#: ccdfunc.c:601 +#: ccdfunc.c:667 #, c-format msgid "Try to write %d to I/O port" msgstr "" -#: ccdfunc.c:437 +#: cmdlnopts.c:101 +msgid "UNIX socket name" +msgstr "" + +#: ccdfunc.c:470 msgid "Wheel device not pointed" msgstr "" -#: ccdfunc.c:484 +#: ccdfunc.c:535 #, c-format msgid "Wheel position should be from 0 to %d" msgstr "" -#: cmdlnopts.c:60 +#: cmdlnopts.c:77 +msgid "absolute (not divided by binning!) frame X0 coordinate (-1 - all " + "with overscan)" +msgstr "" + +#: cmdlnopts.c:79 +msgid "absolute frame X1 coordinate (-1 - all with overscan)" +msgstr "" + +#: cmdlnopts.c:78 +msgid "absolute frame Y0 coordinate (-1 - all with overscan)" +msgstr "" + +#: cmdlnopts.c:80 +msgid "absolute frame Y1 coordinate (-1 - all with overscan)" +msgstr "" + +#: cmdlnopts.c:66 msgid "add records to header from given file[s]" msgstr "" -#: cmdlnopts.c:42 +#: cmdlnopts.c:48 msgid "camera device number (if many: 0, 1, 2 etc)" msgstr "" -#: cmdlnopts.c:38 +#: cmdlnopts.c:44 msgid "camera device plugin (e.g. devfli.so)" msgstr "" -#: cmdlnopts.c:75 +#: cmdlnopts.c:76 +msgid "cancel current exposition" +msgstr "" + +#: cmdlnopts.c:83 msgid "close shutter" msgstr "" -#: cmdlnopts.c:37 +#: cmdlnopts.c:43 msgid "common device plugin (e.g devfli.so)" msgstr "" -#: cmdlnopts.c:82 +#: cmdlnopts.c:90 msgid "configure I/O port pins to given value (decimal number, pin1 is LSB, " "1 == output, 0 == input)" msgstr "" -#: cmdlnopts.c:50 -msgid "fast (8MHz) readout mode" +#: cmdlnopts.c:56 +msgid "fast readout mode" msgstr "" -#: cmdlnopts.c:43 +#: cmdlnopts.c:49 msgid "filter wheel device number (if many: 0, 1, 2 etc)" msgstr "" -#: cmdlnopts.c:44 +#: cmdlnopts.c:50 msgid "focuser device number (if many: 0, 1, 2 etc)" msgstr "" -#: cmdlnopts.c:39 +#: cmdlnopts.c:45 msgid "focuser device plugin (e.g. devzwo.so)" msgstr "" -#: cmdlnopts.c:68 -msgid "frame X0 coordinate (-1 - all with overscan)" -msgstr "" - -#: cmdlnopts.c:70 -msgid "frame X1 coordinate (-1 - all with overscan)" -msgstr "" - -#: cmdlnopts.c:69 -msgid "frame Y0 coordinate (-1 - all with overscan)" -msgstr "" - -#: cmdlnopts.c:71 -msgid "frame Y1 coordinate (-1 - all with overscan)" -msgstr "" - -#: cmdlnopts.c:78 +#: cmdlnopts.c:86 msgid "get value of I/O port pins" msgstr "" -#: cmdlnopts.c:72 -msgid "grab full frame (with overscans)" -msgstr "" - -#: cmdlnopts.c:63 +#: cmdlnopts.c:71 msgid "horizontal binning to N pixels" msgstr "" -#: cmdlnopts.c:56 +#: cmdlnopts.c:62 msgid "instrument name" msgstr "" -#: cmdlnopts.c:41 +#: cmdlnopts.c:47 msgid "list connected devices" msgstr "" -#: cmdlnopts.c:66 +#: cmdlnopts.c:102 +msgid "local INET socket port" +msgstr "" + +#: cmdlnopts.c:100 +msgid "logging file name (if run as server)" +msgstr "" + +#: cmdlnopts.c:74 msgid "make pause for N seconds between expositions" msgstr "" -#: cmdlnopts.c:65 +#: cmdlnopts.c:73 msgid "make series of N frames" msgstr "" -#: cmdlnopts.c:84 +#: cmdlnopts.c:92 msgid "move focuser to absolute position, mm" msgstr "" -#: cmdlnopts.c:85 +#: cmdlnopts.c:93 msgid "move focuser to relative position, mm" msgstr "" -#: cmdlnopts.c:79 +#: cmdlnopts.c:87 msgid "move stepper motor asynchronous" msgstr "" -#: cmdlnopts.c:48 +#: cmdlnopts.c:54 msgid "not open shutter, when exposing (\"dark frames\")" msgstr "" -#: cmdlnopts.c:57 +#: cmdlnopts.c:63 msgid "object name" msgstr "" -#: cmdlnopts.c:55 +#: cmdlnopts.c:61 msgid "object type (neon, object, flat etc)" msgstr "" -#: cmdlnopts.c:58 +#: cmdlnopts.c:64 msgid "observers' names" msgstr "" -#: cmdlnopts.c:59 +#: cmdlnopts.c:65 msgid "observing program name" msgstr "" @@ -447,62 +509,70 @@ msgstr "" msgid "on" msgstr "" -#: cmdlnopts.c:74 +#: cmdlnopts.c:82 msgid "open shutter" msgstr "" -#: cmdlnopts.c:54 +#: cmdlnopts.c:67 +msgid "output file name" +msgstr "" + +#: cmdlnopts.c:60 msgid "program author" msgstr "" -#: cmdlnopts.c:46 +#: cmdlnopts.c:52 msgid "rewrite output file if exists" msgstr "" -#: cmdlnopts.c:77 +#: cmdlnopts.c:85 msgid "run exposition on HIGH @ pin5 I/O port" msgstr "" -#: cmdlnopts.c:76 +#: cmdlnopts.c:84 msgid "run exposition on LOW @ pin5 I/O port" msgstr "" -#: cmdlnopts.c:49 +#: cmdlnopts.c:55 msgid "run in 8-bit mode" msgstr "" -#: cmdlnopts.c:51 +#: cmdlnopts.c:57 msgid "set CCD temperature to given value (degr C)" msgstr "" -#: cmdlnopts.c:81 +#: cmdlnopts.c:89 msgid "set I/O port pins to given value (decimal number, pin1 is LSB)" msgstr "" -#: cmdlnopts.c:67 +#: cmdlnopts.c:75 msgid "set exposure time to given value (seconds!)" msgstr "" -#: cmdlnopts.c:52 +#: cmdlnopts.c:58 msgid "set fan speed (0 - off, 1 - low, 2 - high)" msgstr "" -#: cmdlnopts.c:87 +#: cmdlnopts.c:95 msgid "set wheel position" msgstr "" -#: cmdlnopts.c:45 +#: cmdlnopts.c:51 msgid "show this help" msgstr "" -#: cmdlnopts.c:47 +#: cmdlnopts.c:53 msgid "verbose level (each -v increase it)" msgstr "" -#: cmdlnopts.c:64 +#: cmdlnopts.c:72 msgid "vertical binning to N pixels" msgstr "" -#: cmdlnopts.c:40 +#: cmdlnopts.c:68 +msgid "wait while exposition ends" +msgstr "" + +#: cmdlnopts.c:46 msgid "wheel device plugin (e.g. devdummy.so)" msgstr "" diff --git a/main.c b/main.c index a03b823..ab3b8c3 100644 --- a/main.c +++ b/main.c @@ -15,12 +15,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + #include #include #include #include -#include +#include #include +#include +#include #include #include #include @@ -31,8 +34,22 @@ #include "imageview.h" #endif #include "omp.h" +#include "socket.h" + +static int isserver = FALSE; +static pid_t childpid = 0; void signals(int signo){ + if(childpid){ // master process + DBG("Master killed with sig=%d", signo); + LOGWARN("Master killed with sig=%d", signo); + if(!GP->client){ + DBG("Unlink pid file"); + unlink(GP->pidfile); + } + exit(signo); + } + // slave: cancel exposition WARNX("Get signal %d - exit", signo); DBG("Cancel capturing"); cancel(); @@ -45,6 +62,7 @@ void signals(int signo){ } int main(int argc, char **argv){ + char *self = strdup(argv[0]); initial_setup(); /* int cpunumber = sysconf(_SC_NPROCESSORS_ONLN); @@ -52,13 +70,70 @@ int main(int argc, char **argv){ omp_set_num_threads(cpunumber); */ parse_args(argc, argv); + if(GP->outfile && GP->outfileprefix) ERRX("Can't use outfile name and prefix together"); + if(GP->outfile && !GP->rewrite){ + struct stat filestat; + if(0 == stat(GP->outfile, &filestat)) ERRX("File %s exists!", GP->outfile); + } + if(GP->port || GP->path){ + if(GP->path){ + WARNX("Options `port` and `path` can't be used together! Point `port` for TCP socket or `path` for UNIX."); + return 1; + } + int port = atoi(GP->port); + if(port < PORTN_MIN || port > PORTN_MAX){ + WARNX("Wrong port value: %d", port); + return 1; + } + if(!GP->client) isserver = TRUE; + } + if(GP->logfile){ + int lvl = LOGLEVEL_WARN + GP->verbose; + DBG("level = %d", lvl); + if(lvl > LOGLEVEL_ANY) lvl = LOGLEVEL_ANY; + verbose(1, "Log file %s @ level %d\n", GP->logfile, lvl); + OPENLOG(GP->logfile, lvl, 1); + if(!globlog) WARNX("Can't create log file"); + } signal(SIGINT, signals); signal(SIGQUIT, signals); signal(SIGABRT, signals); signal(SIGTERM, signals); - focusers(); - wheels(); - ccds(); - return 0; + signal(SIGHUP, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + // check for another running process in server and standalone mode + if(!GP->client) check4running(self, GP->pidfile); + if(!isserver && !GP->client){ // standalone mode + focusers(); + wheels(); + ccds(); + return 0; + } + + LOGMSG("Started"); +#ifndef EBUG + if(isserver){ + unsigned int pause = 5; + while(1){ + childpid = fork(); + if(childpid){ // master + double t0 = dtime(); + LOGMSG("Created child with pid %d", childpid); + wait(NULL); + LOGWARN("Child %d died", childpid); + if(dtime() - t0 < 1.) pause += 5; + else pause = 1; + if(pause > 900) pause = 900; + sleep(pause); // wait a little before respawn + }else{ // slave + prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies + break; + } + } + } +#endif + + if(GP->path) return start_socket(isserver, GP->path, FALSE); + if(GP->port) return start_socket(isserver, GP->port, TRUE); } diff --git a/server.c b/server.c new file mode 100644 index 0000000..c4454ce --- /dev/null +++ b/server.c @@ -0,0 +1,808 @@ +/* + * This file is part of the CCD_Capture project. + * Copyright 2022 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ccdfunc.h" +#include "cmdlnopts.h" +#include "server.h" +#include "socket.h" + +static atomic_int camdevno = 0, wheeldevno = 0, focdevno = 0; // current devices numbers +static _Atomic camera_state camstate = CAMERA_IDLE; +#define FLAG_STARTCAPTURE (1<<0) +#define FLAG_CANCEL (1<<1) +static atomic_int camflags = 0, camfanspd = 0, confio = 0, nflushes; +static char *outfile = NULL; +static pthread_mutex_t locmutex = PTHREAD_MUTEX_INITIALIZER; // mutex for wheel/camera/focuser functions +static frameformat frmformatmax = {0}, curformat = {0}; // maximal format +static void *camdev = NULL, *focdev = NULL, *wheeldev = NULL; + +static float focmaxpos = 0., focminpos = 0.; // focuser extremal positions +static int wmaxpos = 0.; // wheel max pos +static float tremain = 0.; // time when capture done + +static IMG ima = {0}; +static void fixima(){ + FREE(ima.data); + int raw_width = curformat.w / GP->hbin, raw_height = curformat.h / GP->vbin; + ima.h = curformat.h; + ima.w = curformat.w; + ima.data = MALLOC(uint16_t, raw_width * raw_height); +} + +// functions for processCAM finite state machine +static inline void cameraidlestate(){ // idle - wait for capture commands + if(camflags & FLAG_STARTCAPTURE){ // start capturing + camflags &= ~FLAG_STARTCAPTURE; + camstate = CAMERA_CAPTURE; + camera->cancel(); + if(!camera->startexposition()){ + LOGERR("Can't start exposition"); + WARNX(_("Can't start exposition")); + camstate = CAMERA_ERROR; + return; + } + } +} +static inline void cameracapturestate(){ // capturing - wait for exposition ends + if(camflags & FLAG_CANCEL){ // cancel all expositions + camflags &= ~FLAG_CANCEL; + camera->cancel(); + camstate = CAMERA_IDLE; + return; + } + capture_status cs; + if(camera->pollcapture(&cs, &tremain)){ + if(cs != CAPTURE_PROCESS){ + tremain = 0.; + camstate = CAMERA_FRAMERDY; + return; + } + } +} + +// base camera thread +static void* processCAM(_U_ void *d){ + if(!camera) ERRX(_("No camera device")); + camera_state curstate = camstate; + double logt = dtime(); + while(1){ + // log + if(dtime() - logt > TLOG_PAUSE){ + logt = dtime(); + float t; + if(camera->getTcold(&t)){ + LOGMSG("CCDTEMP=%f", t); + } + if(camera->getThot(&t)){ + LOGMSG("HOTTEMP=%f", t); + } + if(camera->getTbody(&t)){ + LOGMSG("BODYTEMP=%f", t); + } + } + switch(curstate){ + case CAMERA_IDLE: + cameraidlestate(); + break; + case CAMERA_CAPTURE: + cameracapturestate(); + break; + case CAMERA_FRAMERDY: +// do nothing: when `server` got this state it sends "expstate=2" to all clients and changes state to IDLE + break; + case CAMERA_ERROR: +// do nothing: when `server` got this state it sends "expstate=3" to all clients and changes state to IDLE + break; + } + } + return NULL; +} + +// functions running @ each devno change +static int camdevini(int n){ + if(!camera) return FALSE; + pthread_mutex_lock(&locmutex); + if(!camera->setDevNo(n)){ + LOGERR("Can't set active camera number"); + pthread_mutex_unlock(&locmutex); + return FALSE; + } + camdevno = n; + LOGMSG("Set camera device number to %d", camdevno); + frameformat step; + camera->getgeomlimits(&frmformatmax, &step); + curformat = frmformatmax; + if(GP->hbin < 1) GP->hbin = 1; + if(GP->vbin < 1) GP->vbin = 1; + fixima(); + pthread_mutex_unlock(&locmutex); + return TRUE; +} +static int focdevini(int n){ + if(!focuser) return FALSE; + pthread_mutex_lock(&locmutex); + if(!focuser->setDevNo(n)){ + LOGERR("Can't set active focuser number"); + pthread_mutex_unlock(&locmutex); + return FALSE; + } + focdevno = n; + LOGMSG("Set focuser device number to %d", focdevno); + focuser->getMaxPos(&focmaxpos); + focuser->getMinPos(&focminpos); + pthread_mutex_unlock(&locmutex); + return TRUE; +} +static int wheeldevini(int n){ + if(!wheel) return FALSE; + pthread_mutex_unlock(&locmutex); + if(!wheel->setDevNo(n)){ + LOGERR("Can't set active wheel number"); + pthread_mutex_unlock(&locmutex); + return FALSE; + } + wheeldevno = n; + LOGMSG("Set wheel device number to %d", wheeldevno); + wheel->getMaxPos(&wmaxpos); + pthread_mutex_unlock(&locmutex); + return TRUE; +} + +/******************************************************************************* + *************************** CCD/CMOS handlers ********************************* + ******************************************************************************/ +static hresult camlisthandler(int fd, _U_ const char *key, _U_ const char *val){ + char buf[BUFSIZ], modname[256]; + pthread_mutex_lock(&locmutex); + for(int i = 0; i < camera->Ndevices; ++i){ + if(!camera->setDevNo(i)) continue; + camera->getModelName(modname, 255); + snprintf(buf, BUFSIZ-1, CMD_CAMLIST "='%s'", modname); + sendstrmessage(fd, buf); + } + if(camdevno > -1) camera->setDevNo(camdevno); + pthread_mutex_unlock(&locmutex); + return RESULT_SILENCE; +} +static hresult camsetNhandler(_U_ int fd, _U_ const char *key, _U_ const char *val){ + char buf[64]; + if(val){ + int num = atoi(val); + if(num > camera->Ndevices - 1 || num < 0){ + return RESULT_BADVAL; + } + if(!camdevini(num)) return RESULT_FAIL; + } + snprintf(buf, 63, CMD_CAMDEVNO "=%d", camdevno); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} +// exposition time setter/getter +static hresult exphandler(int fd, _U_ const char *key, const char *val){ + char buf[64]; + if(val){ + double v = atof(val); + if(v < DBL_EPSILON) return RESULT_BADVAL; + if(camstate != CAMERA_CAPTURE){ + pthread_mutex_lock(&locmutex); + if(camera->setexp(v)){ + GP->exptime = v; + }else LOGWARN("Can't set exptime to %g", v); + pthread_mutex_unlock(&locmutex); + } + } + snprintf(buf, 63, CMD_EXPOSITION "=%g", GP->exptime); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} +// filename setter/getter +static hresult namehandler(int fd, _U_ const char *key, const char *val){ + char buf[64]; + if(val){ + pthread_mutex_lock(&locmutex); + FREE(outfile); + outfile = strdup(val); + GP->outfile = outfile; + GP->outfileprefix = NULL; + pthread_mutex_unlock(&locmutex); + } + if(!GP->outfile) return RESULT_FAIL; + snprintf(buf, 63, CMD_FILENAME "=%s", GP->outfile); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} +// filename prefix +static hresult nameprefixhandler(_U_ int fd, _U_ const char *key, const char *val){ + char buf[64]; + if(val){ + pthread_mutex_lock(&locmutex); + FREE(outfile); + outfile = strdup(val); + GP->outfileprefix = outfile; + GP->outfile = NULL; + pthread_mutex_unlock(&locmutex); + } + if(!GP->outfileprefix) return RESULT_FAIL; + snprintf(buf, 63, CMD_FILENAMEPREFIX "=%s", GP->outfileprefix); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} +// rewrite +static hresult rewritefilehandler(_U_ int fd, _U_ const char *key, const char *val){ + char buf[64]; + if(val){ + int n = atoi(val); + if(n < 0 || n > 1) return RESULT_BADVAL; + pthread_mutex_lock(&locmutex); + GP->rewrite = n; + pthread_mutex_unlock(&locmutex); + } + snprintf(buf, 63, CMD_REWRITE "=%d", GP->rewrite); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} +static hresult binhandler(_U_ int fd, const char *key, const char *val){ + char buf[64]; + if(val){ + int b = atoi(val); + if(b < 1) return RESULT_BADVAL; + if(0 == strcmp(key, CMD_HBIN)) GP->hbin = b; + else GP->vbin = b; + pthread_mutex_lock(&locmutex); + if(!camera->setbin(GP->hbin, GP->vbin)){ + pthread_mutex_unlock(&locmutex); + return RESULT_BADVAL; + } + fixima(); + } + pthread_mutex_lock(&locmutex); + int r = camera->getbin(&GP->hbin, &GP->vbin); + pthread_mutex_unlock(&locmutex); + if(r){ + if(0 == strcmp(key, CMD_HBIN)) snprintf(buf, 63, "%s=%d", key, GP->hbin); + else snprintf(buf, 63, "%s=%d", key, GP->vbin); + sendstrmessage(fd, buf); + return RESULT_SILENCE; + } + return RESULT_FAIL; +} +static hresult temphandler(_U_ int fd, _U_ const char *key, const char *val){ + float f; + char buf[64]; + if(val){ + f = atof(val); + pthread_mutex_lock(&locmutex); + if(!camera->setT((float)f)){ + pthread_mutex_unlock(&locmutex); + LOGWARN("Can't set camera T to %.1f", f); + return RESULT_FAIL; + } + LOGMSG("Set camera T to %.1f", f); + } + pthread_mutex_lock(&locmutex); + int r = camera->getTcold(&f); + pthread_mutex_unlock(&locmutex); + if(r){ + snprintf(buf, 63, CMD_CAMTEMPER "=%.1f", f); + sendstrmessage(fd, buf); + pthread_mutex_lock(&locmutex); + r = camera->getTbody(&f); + pthread_mutex_unlock(&locmutex); + if(r){ + snprintf(buf, 63, "tbody=%.1f", f); + sendstrmessage(fd, buf); + } + pthread_mutex_lock(&locmutex); + r = camera->getThot(&f); + pthread_mutex_unlock(&locmutex); + if(r){ + snprintf(buf, 63, "thot=%.1f", f); + sendstrmessage(fd, buf); + } + return RESULT_SILENCE; + }else return RESULT_FAIL; +} +static hresult camfanhandler(_U_ int fd, _U_ const char *key, _U_ const char *val){ + char buf[64]; + if(val){ + int spd = atoi(val); + if(spd < 0) return RESULT_BADVAL; + if(spd > FAN_HIGH) spd = FAN_HIGH; + pthread_mutex_lock(&locmutex); + int r = camera->setfanspeed((fan_speed)spd); + pthread_mutex_unlock(&locmutex); + if(!r) return RESULT_FAIL; + camfanspd = spd; + } + snprintf(buf, 63, CMD_CAMFANSPD "=%d", camfanspd); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} +const char *shutterstr[] = {"open", "close", "expose @high", "expose @low"}; +static hresult shutterhandler(_U_ int fd, _U_ const char *key, const char *val){ + if(val){ + int x = atoi(val); + if(x < 0 || x >= SHUTTER_AMOUNT) return RESULT_BADVAL; + pthread_mutex_lock(&locmutex); + int r = camera->shuttercmd((shutter_op)x); + pthread_mutex_unlock(&locmutex); + if(r){ + LOGMSG("Shutter command '%s'", shutterstr[x]); + }else{ + LOGWARN("Can't run shutter command '%s'", shutterstr[x]); + return RESULT_FAIL; + } + } + return RESULT_OK; +} +static hresult confiohandler(_U_ int fd, _U_ const char *key, _U_ const char *val){ + char buf[64]; + if(val){ + int io = atoi(val); + pthread_mutex_lock(&locmutex); + int r = camera->confio(io); + pthread_mutex_unlock(&locmutex); + if(!r) return RESULT_FAIL; + confio = io; + } + snprintf(buf, 63, CMD_CONFIO "=%d", confio); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} +static hresult iohandler(_U_ int fd, _U_ const char *key, _U_ const char *val){ + char buf[64]; + int io; + if(val){ + io = atoi(val); + pthread_mutex_lock(&locmutex); + int r = camera->setio(io); + pthread_mutex_unlock(&locmutex); + if(!r) return RESULT_FAIL; + } + pthread_mutex_lock(&locmutex); + int r = camera->getio(&io); + pthread_mutex_unlock(&locmutex); + if(!r) return RESULT_FAIL; + snprintf(buf, 63, CMD_IO "=%d", io); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} +static hresult gainhandler(_U_ int fd, _U_ const char *key, _U_ const char *val){ + char buf[64]; + float f; + if(val){ + f = atof(val); + pthread_mutex_lock(&locmutex); + int r = camera->setgain(f); + pthread_mutex_unlock(&locmutex); + if(!r) return RESULT_FAIL; + } + pthread_mutex_lock(&locmutex); + int r = camera->getgain(&f); + pthread_mutex_unlock(&locmutex); + if(!r) return RESULT_FAIL; + snprintf(buf, 63, CMD_GAIN "=%.1f", f); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} +static hresult brightnesshandler(_U_ int fd, _U_ const char *key, _U_ const char *val){ + char buf[64]; + float b; + if(val){ + b = atof(val); + pthread_mutex_lock(&locmutex); + int r = camera->setbrightness(b); + pthread_mutex_unlock(&locmutex); + if(!r) return RESULT_FAIL; + } + pthread_mutex_lock(&locmutex); + int r = camera->getbrightness(&b); + pthread_mutex_unlock(&locmutex); + if(!r) return RESULT_FAIL; + snprintf(buf, 63, CMD_BRIGHTNESS "=%.1f", b); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} +// set format: `format=X0,X1,Y0,Y1` +// get geomlimits: `maxformat=X0,X1,Y0,Y1` +static hresult formathandler(int fd, const char *key, const char *val){ + char buf[64]; + frameformat fmt; + if(val){ + if(strcmp(key, CMD_FRAMEFORMAT)) return RESULT_BADKEY; // can't set maxformat + if(4 != sscanf(val, "%d,%d,%d,%d", &fmt.xoff, &fmt.yoff, &fmt.w, &fmt.h)) return RESULT_BADVAL; + fmt.w -= fmt.xoff; fmt.h -= fmt.yoff; + pthread_mutex_lock(&locmutex); + int r = camera->setgeometry(&fmt); + pthread_mutex_unlock(&locmutex); + if(!r) return RESULT_FAIL; + curformat = fmt; + fixima(); + } + if(strcmp(key, CMD_FRAMEMAX)) snprintf(buf, 63, CMD_FRAMEMAX "=%d,%d,%d,%d", + frmformatmax.xoff, frmformatmax.yoff, frmformatmax.xoff+frmformatmax.w, frmformatmax.yoff+frmformatmax.w); + else snprintf(buf, 63, CMD_FRAMEFORMAT "=%d,%d,%d,%d", + camera->array.xoff, camera->array.yoff, camera->array.xoff+camera->array.w, camera->array.yoff+camera->array.w); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} +static hresult nflusheshandler(_U_ int fd, _U_ const char *key, _U_ const char *val){ + char buf[64]; + if(val){ + int n = atoi(val); + if(n < 1) return RESULT_BADVAL; + pthread_mutex_lock(&locmutex); + if(!camera->setnflushes(n)){ + pthread_mutex_unlock(&locmutex); + return RESULT_FAIL; + } + nflushes = n; + pthread_mutex_unlock(&locmutex); + } + snprintf(buf, 63, CMD_NFLUSHES "=%d", nflushes); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} +static hresult expstatehandler(_U_ int fd, _U_ const char *key, _U_ const char *val){ + char buf[64]; + if(val){ + int n = atoi(val); + if(n == CAMERA_IDLE){ // cancel expositions + camflags |= FLAG_CANCEL; + return RESULT_OK; + } + else if(n == CAMERA_CAPTURE){ // start exposition + camflags |= FLAG_STARTCAPTURE; + return RESULT_OK; + } + else return RESULT_BADVAL; + } + snprintf(buf, 63, CMD_EXPSTATE "=%d", camstate); + sendstrmessage(fd, buf); + snprintf(buf, 63, "camflags=%d", camflags); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} +static hresult tremainhandler(_U_ int fd, _U_ const char *key, _U_ const char *val){ + char buf[64]; + snprintf(buf, 63, CMD_TREMAIN "=%g", tremain); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} +/* +static hresult handler(_U_ int fd, _U_ const char *key, _U_ const char *val){ + char buf[64]; + return RESULT_SILENCE; +} +*/ +/******************************************************************************* + ***************************** Wheel handlers ********************************** + ******************************************************************************/ +static hresult wlisthandler(int fd, _U_ const char *key, _U_ const char *val){ + if(wheel->Ndevices < 1) return RESULT_FAIL; + pthread_mutex_lock(&locmutex); + for(int i = 0; i < wheel->Ndevices; ++i){ + if(!wheel->setDevNo(i)) continue; + char modname[256], buf[BUFSIZ]; + wheel->getModelName(modname, 255); + snprintf(buf, BUFSIZ-1, CMD_WLIST "='%s'", modname); + sendstrmessage(fd, buf); + } + if(wheeldevno > -1) wheel->setDevNo(wheeldevno); + pthread_mutex_unlock(&locmutex); + return RESULT_SILENCE; +} +static hresult wsetNhandler(int fd, _U_ const char *key, const char *val){ + char buf[64]; + if(val){ + int num = atoi(val); + if(num > wheel->Ndevices - 1 || num < 0){ + return RESULT_BADVAL; + } + if(!wheeldevini(num)) return RESULT_FAIL; + } + snprintf(buf, 63, CMD_WDEVNO "=%d", wheeldevno); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} + +static hresult wgotohandler(_U_ int fd, _U_ const char *key, _U_ const char *val){ + char buf[64]; + int pos; + if(val){ + pos = atoi(val); + pthread_mutex_lock(&locmutex); + int r = wheel->setPos(pos); + pthread_mutex_unlock(&locmutex); + if(!r) return RESULT_BADVAL; + } + pthread_mutex_lock(&locmutex); + int r = wheel->getPos(&pos); + pthread_mutex_unlock(&locmutex); + if(!r) return RESULT_FAIL; + snprintf(buf, 63, CMD_WPOS "=%d", pos); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} + +/******************************************************************************* + **************************** Focuser handlers ********************************* + ******************************************************************************/ + +static hresult foclisthandler(int fd, _U_ const char *key, _U_ const char *val){ + if(focuser->Ndevices < 1) return RESULT_FAIL; + pthread_mutex_lock(&locmutex); + for(int i = 0; i < focuser->Ndevices; ++i){ + char modname[256], buf[BUFSIZ]; + if(!focuser->setDevNo(i)) continue; + focuser->getModelName(modname, 255); + snprintf(buf, BUFSIZ-1, CMD_FOCLIST "='%s'", modname); + sendstrmessage(fd, buf); + } + if(focdevno > -1) focuser->setDevNo(focdevno); + pthread_mutex_unlock(&locmutex); + return RESULT_SILENCE; +} +static hresult fsetNhandler(int fd, _U_ const char *key, const char *val){ + char buf[64]; + if(val){ + int num = atoi(val); + if(num > focuser->Ndevices - 1 || num < 0){ + return RESULT_BADVAL; + } + if(!focdevini(num)) return RESULT_FAIL; + } + snprintf(buf, 63, CMD_FDEVNO "=%d", focdevno); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} +static hresult fgotohandler(int fd, _U_ const char *key, const char *val){ + char buf[64]; + float f; + int r; + if(val){ + f = atof(val); + if(f < focminpos || f > focmaxpos) return RESULT_BADVAL; + pthread_mutex_lock(&locmutex); + if(f - focminpos < __FLT_EPSILON__){ + r = focuser->home(1); + }else{ + r = focuser->setAbsPos(1, f); + } + pthread_mutex_unlock(&locmutex); + if(!r) return RESULT_FAIL; + } + pthread_mutex_lock(&locmutex); + r = focuser->getPos(&f); + pthread_mutex_unlock(&locmutex); + if(!r) return RESULT_FAIL; + snprintf(buf, 63, "FOCPOS=%g", f); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} + +/* +static hresult handler(_U_ int fd, _U_ const char *key, _U_ const char *val){ + char buf[64]; + return RESULT_SILENCE; +} +*/ + +// information about everything +static 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(buf1, 255)){ + snprintf(buf, BUFSIZ-1, CMD_CAMLIST "='%s'", buf1); + sendstrmessage(fd, buf); + } + namehandler(fd, CMD_FILENAME, NULL); + binhandler(fd, CMD_HBIN, NULL); + binhandler(fd, CMD_VBIN, NULL); + temphandler(fd, CMD_CAMTEMPER, NULL); + exphandler(fd, CMD_EXPOSITION, NULL); + } + if(wheel){ + if(wheel->getModelName(buf1, 255)){ + snprintf(buf, BUFSIZ-1, CMD_WLIST "='%s'", buf1); + sendstrmessage(fd, buf); + } + if(wheel->getTbody(&f)){ + snprintf(buf, BUFSIZ-1, "WHEELTEMP=%.1f", f); + sendstrmessage(fd, buf); + } + if(wheel->getPos(&i)){ + snprintf(buf, BUFSIZ-1, "WHEELPOS=%d", i); + sendstrmessage(fd, buf); + } + snprintf(buf, BUFSIZ-1, "WHEELMAXPOS=%d", wmaxpos); + sendstrmessage(fd, buf); + } + if(focuser){ + if(focuser->getModelName(buf1, 255)){ + snprintf(buf, BUFSIZ-1, CMD_FOCLIST "='%s'", buf1); + sendstrmessage(fd, buf); + } + if(focuser->getTbody(&f)){ + snprintf(buf, BUFSIZ-1, "FOCTEMP=%.1f", f); + sendstrmessage(fd, buf); + } + snprintf(buf, BUFSIZ-1, "FOCMINPOS=%g", focminpos); + sendstrmessage(fd, buf); + snprintf(buf, BUFSIZ-1, "FOCMAXPOS=%g", focmaxpos); + sendstrmessage(fd, buf); + if(focuser->getPos(&f)){ + snprintf(buf, BUFSIZ-1, "FOCPOS=%g", f); + sendstrmessage(fd, buf); + } + } + return RESULT_SILENCE; +} + +// for setters: do nothing when camera not in idle state +static int CAMbusy(){ + if(camera && camstate != CAMERA_IDLE) return TRUE; + return TRUE; +} + +static int chktrue(_U_ char *val){ + return TRUE; +} +static int chkcam(char *val){ + if(val && CAMbusy()) return RESULT_BUSY; + if(camera) return TRUE; + return FALSE; +} +static int chkwheel(char *val){ + if(val && CAMbusy()) return RESULT_BUSY; + if(wheel) return TRUE; + return FALSE; +} +static int chkfoc(char *val){ + if(val && CAMbusy()) return RESULT_BUSY; + if(focuser) return TRUE; + return FALSE; +} +static handleritem items[] = { + {chktrue, infohandler, CMD_INFO}, + {chkcam, camlisthandler, CMD_CAMLIST}, + {chkcam, camsetNhandler, CMD_CAMDEVNO}, + {chkcam, camfanhandler, CMD_CAMFANSPD}, + {chkcam, exphandler, CMD_EXPOSITION}, + {chkcam, namehandler, CMD_FILENAME}, + {chkcam, binhandler, CMD_HBIN}, + {chkcam, binhandler, CMD_VBIN}, + {chkcam, temphandler, CMD_CAMTEMPER}, + {chkcam, shutterhandler, CMD_SHUTTER}, + {chkcam, confiohandler, CMD_CONFIO}, + {chkcam, iohandler, CMD_IO}, + {chkcam, gainhandler, CMD_GAIN}, + {chkcam, brightnesshandler, CMD_BRIGHTNESS}, + {chkcam, formathandler, CMD_FRAMEFORMAT}, + {chkcam, formathandler, CMD_FRAMEMAX}, + {chkcam, nflusheshandler, CMD_NFLUSHES}, + {chkcam, expstatehandler, CMD_EXPSTATE}, + {chkcam, nameprefixhandler, CMD_FILENAMEPREFIX}, + {chkcam, rewritefilehandler, CMD_REWRITE}, + {chktrue, tremainhandler, CMD_TREMAIN}, + {chkfoc, foclisthandler, CMD_FOCLIST}, + {chkfoc, fsetNhandler, CMD_FDEVNO}, + {chkfoc, fgotohandler, CMD_FGOTO}, + {chkwheel, wlisthandler, CMD_WLIST}, + {chkwheel, wsetNhandler, CMD_WDEVNO}, + {chkwheel, wgotohandler, CMD_WPOS}, + {NULL, NULL, NULL}, +}; + +#define CLBUFSZ BUFSIZ + +void server(int sock){ + // init everything + startCCD(&camdev); + camdevini(0); + startFocuser(&focdev); + focdevini(0); + startWheel(&wheeldev); + wheeldevini(0); + if(listen(sock, MAXCLIENTS) == -1){ + WARN("listen"); + LOGWARN("listen"); + return; + } + // start camera thread + pthread_t camthread; + if(camera){ + if(pthread_create(&camthread, NULL, processCAM, NULL)) ERR("pthread_create()"); + } + int nfd = 1; // only one socket @start + struct pollfd poll_set[MAXCLIENTS+1]; + char buffers[MAXCLIENTS][CLBUFSZ]; // buffers for data reading + bzero(poll_set, sizeof(poll_set)); + // ZERO - listening server socket + poll_set[0].fd = sock; + poll_set[0].events = POLLIN; + while(1){ + poll(poll_set, nfd, 1); // max timeout - 1ms + if(poll_set[0].revents & POLLIN){ // check main for accept() + struct sockaddr_in addr; + socklen_t len = sizeof(addr); + int client = accept(sock, (struct sockaddr*)&addr, &len); + DBG("New connection"); + LOGMSG("SERVER got connection, fd=%d", client); + if(nfd == MAXCLIENTS + 1){ + LOGWARN("Max amount of connections, disconnect fd=%d", client); + WARNX("Limit of connections reached"); + close(client); + }else{ + memset(&poll_set[nfd], 0, sizeof(struct pollfd)); + poll_set[nfd].fd = client; + poll_set[nfd].events = POLLIN; + ++nfd; + } + } + // process some data & send messages to ALL + if(camstate == CAMERA_FRAMERDY || camstate == CAMERA_ERROR){ + char buff[32]; + int l = 0; + snprintf(buff, 31, CMD_EXPSTATE "=%d", camstate); + DBG("Send %s to %d clients", buff, nfd - 1); + for(int i = 1; i < nfd; ++i) + sendmessage(poll_set[i].fd, buff, l); + if(camstate == CAMERA_FRAMERDY){ // save frame + if(!ima.data) LOGERR("Can't save image: not initialized"); + else{ + if(!camera->capture(&ima)) LOGERR("Can't capture image"); + else{ + calculate_stat(&ima); + saveFITS(&ima); + } + } + } + camstate = CAMERA_IDLE; + } + // scan connections + for(int fdidx = 1; fdidx < nfd; ++fdidx){ + if((poll_set[fdidx].revents & POLLIN) == 0) continue; + int fd = poll_set[fdidx].fd; + if(!processData(fd, items, buffers[fdidx-1], CLBUFSZ)){ // socket closed + DBG("Client fd=%d disconnected", fd); + LOGMSG("SERVER client fd=%d disconnected", fd); + buffers[fdidx-1][0] = 0; // clear rest of data in buffer + close(fd); + // move last FD to current position + poll_set[fdidx] = poll_set[nfd - 1]; + --nfd; + } + } + } + focclose(focdev); + closewheel(wheeldev); + closecam(camdev); +} + diff --git a/server.h b/server.h new file mode 100644 index 0000000..fc86052 --- /dev/null +++ b/server.h @@ -0,0 +1,73 @@ +/* + * This file is part of the CCD_Capture project. + * Copyright 2022 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#ifndef SERVER_H__ +#define SERVER_H__ + +typedef enum{ + CAMERA_IDLE, // idle state, client send this to cancel capture + CAMERA_CAPTURE, // capturing frame, client send this to start capture + CAMERA_FRAMERDY, // frame ready to be saved + CAMERA_ERROR // can't do exposition +} camera_state; + +// pause (seconds) between temperature logging +#define TLOG_PAUSE 60. + +// server-side functions +void server(int fd); + +// common information about everything +#define CMD_INFO "info" + +// CCD/CMOS +#define CMD_CAMLIST "camlist" +#define CMD_CAMDEVNO "camdevno" +#define CMD_EXPOSITION "exptime" +#define CMD_FILENAME "filename" +#define CMD_FILENAMEPREFIX "filenameprefix" +// rewrite=1 will rewrite files, =0 - not (only for `filename`) +#define CMD_REWRITE "rewrite" +#define CMD_HBIN "hbin" +#define CMD_VBIN "vbin" +#define CMD_CAMTEMPER "tcold" +#define CMD_CAMFANSPD "ccdfanspeed" +#define CMD_SHUTTER "shutter" +#define CMD_CONFIO "confio" +#define CMD_IO "io" +#define CMD_GAIN "gain" +#define CMD_BRIGHTNESS "brightness" +#define CMD_FRAMEFORMAT "format" +#define CMD_FRAMEMAX "maxformat" +#define CMD_NFLUSHES "nflushes" +// expstate=CAMERA_CAPTURE will start exposition, CAMERA_IDLE - cancel +#define CMD_EXPSTATE "expstate" +#define CMD_TREMAIN "tremain" + +// focuser +#define CMD_FOCLIST "foclist" +#define CMD_FDEVNO "focdevno" +#define CMD_FGOTO "focgoto" + +// wheel +#define CMD_WLIST "wlist" +#define CMD_WDEVNO "wdevno" +#define CMD_WPOS "wpos" + +#endif // SERVER_H__ diff --git a/socket.c b/socket.c new file mode 100644 index 0000000..a88bc8d --- /dev/null +++ b/socket.c @@ -0,0 +1,229 @@ +/* + * This file is part of the CCD_Capture project. + * Copyright 2022 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include // isspace +#include +#include +#include +#include +#include // unix socket +#include + +#include "cmdlnopts.h" +#include "client.h" +#include "server.h" +#include "socket.h" + +/** + * @brief start_socket - create socket and run client or server + * @param isserver - TRUE for server, FALSE for client + * @param path - UNIX-socket path or local INET socket port + * @param isnet - TRUE for INET socket, FALSE for UNIX + * @return 0 if OK + */ +int start_socket(int isserver, char *path, int isnet){ + if(!path) return 1; + DBG("path/port: %s", path); + int sock = -1; + struct addrinfo hints = {0}, *res; + struct sockaddr_un unaddr = {0}; + if(isnet){ + DBG("Network socket"); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + if(getaddrinfo("127.0.0.1", path, &hints, &res) != 0){ + ERR("getaddrinfo"); + } + }else{ + DBG("UNIX socket"); + char apath[128]; + if(*path == 0){ + DBG("convert name"); + apath[0] = 0; + strncpy(apath+1, path+1, 126); + }else if(strncmp("\\0", path, 2) == 0){ + DBG("convert name"); + apath[0] = 0; + strncpy(apath+1, path+2, 126); + }else strcpy(apath, path); + unaddr.sun_family = AF_UNIX; + hints.ai_addr = (struct sockaddr*) &unaddr; + hints.ai_addrlen = sizeof(unaddr); + memcpy(unaddr.sun_path, apath, 106); // if sun_path[0] == 0 we don't create a file + hints.ai_family = AF_UNIX; + hints.ai_socktype = SOCK_SEQPACKET; + res = &hints; + } + for(struct addrinfo *p = res; p; p = p->ai_next){ + if((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0){ // or SOCK_STREAM? + LOGWARN("socket()"); + WARN("socket()"); + continue; + } + if(isserver){ + int reuseaddr = 1; + if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1){ + LOGWARN("setsockopt()"); + WARN("setsockopt()"); + close(sock); sock = -1; + continue; + } + if(bind(sock, p->ai_addr, p->ai_addrlen) == -1){ + LOGWARN("bind()"); + WARN("bind()"); + close(sock); sock = -1; + continue; + } + int enable = 1; + if(ioctl(sock, FIONBIO, (void *)&enable) < 0){ // make socket nonblocking + LOGERR("Can't make socket nonblocking"); + ERRX("ioctl()"); + } + }else{ + if(connect(sock, p->ai_addr, p->ai_addrlen) == -1){ + LOGWARN("connect()"); + WARN("connect()"); + close(sock); sock = -1; + } + } + break; + } + if(sock < 0){ + LOGERR("Can't open socket"); + ERRX("Can't open socket"); + } + if(isnet) freeaddrinfo(res); + if(isserver) server(sock); + else client(sock); + close(sock); + signals(0); + return 0; +} + +// simple wrapper over write: add missed newline and log data +void sendmessage(int fd, const char *msg, int l){ + if(fd < 1 || !msg || l < 1) return; + DBG("send to fd %d: %s [%d]", fd, msg, l); + char *tmpbuf = MALLOC(char, l+1); + memcpy(tmpbuf, msg, l); + if(msg[l-1] != '\n') tmpbuf[l++] = '\n'; + if(l != write(fd, tmpbuf, l)){ + LOGWARN("write()"); + WARN("write()"); + }else{ + if(globlog){ // logging turned ON + tmpbuf[l-1] = 0; // remove trailing '\n' for logging + LOGMSG("SEND '%s'", tmpbuf); + } + } + FREE(tmpbuf); +} +void sendstrmessage(int fd, const char *msg){ + if(fd < 1 || !msg) return; + int l = strlen(msg); + sendmessage(fd, msg, l); +} + +// text messages for `hresult` +static const char *resmessages[] = { + [RESULT_OK] = "OK", + [RESULT_BUSY] = "BUSY", + [RESULT_FAIL] = "FAIL", + [RESULT_BADKEY] = "BADKEY", + [RESULT_BADVAL] = "BADVAL", + [RESULT_SILENCE] = "", +}; + +const char *hresult2str(hresult r){ + if(r >= RESULT_NUM) return "BADRESULT"; + return resmessages[r]; +} + +/** + * @brief get_keyval - get value of `key = val` + * @param keyval (io) - pair `key = val`, return `key` + * @return `val` + */ +char *get_keyval(char *keyval){ + DBG("Got string %s", keyval); + // remove starting spaces in key + while(isspace(*keyval)) ++keyval; + char *val = strchr(keyval, '='); + if(val){ // got value: remove starting spaces in val + *val++ = 0; + while(isspace(*val)) ++val; + } + // remove trailing spaces in key + char *e = keyval + strlen(keyval) - 1; // last key symbol + while(isspace(*e) && e > keyval) --e; + e[1] = 0; + // now we have key (`str`) and val (or NULL) + DBG("key=%s, val=%s", keyval, val); + return val; +} + +// parse string of data (command or key=val) +// the CONTENT of buffer `str` WILL BE BROKEN! +static void parsestring(int fd, handleritem *handlers, char *str){ + if(fd < 1 || !handlers || !handlers->key || !str || !*str) return; + char *val = get_keyval(str); + if(val) LOGMSG("RECEIVE '%s=%s'", str, val); + else LOGMSG("RECEIVE '%s'", str); + for(handleritem *h = handlers; h->key; ++h){ + if(strcmp(str, h->key) == 0){ // found command + if(h->chkfunction && !h->chkfunction(val)) sendstrmessage(fd, resmessages[RESULT_FAIL]); + else{ + if(h->handler){ + hresult r = h->handler(fd, str, val); + sendstrmessage(fd, hresult2str(r)); + }else sendstrmessage(fd, resmessages[RESULT_FAIL]); + } + return; + } + } + sendstrmessage(fd, resmessages[RESULT_BADKEY]); +} + +/** + * @brief processData - read (if available) data from fd and run processing, sending to fd messages for each command + * @param fd - socket file descriptor + * @param handlers - NULL-terminated array of handlers + * @param buf (io) - zero-terminated buffer for storing rest of data (without newline), its content will be changed + * @param buflen - its length + * @return FALSE if client closed (nothing to read) + */ +int processData(int fd, handleritem *handlers, char *buf, int buflen){ + int curlen = strlen(buf); + if(curlen == buflen-1) curlen = 0; // buffer overflow - clear old content + ssize_t rd = read(fd, buf + curlen, buflen-1 - curlen); + if(rd <= 0) return FALSE; + DBG("got %s[%zd] from %d", buf, rd, fd); + char *restofdata = buf, *eptr = buf + curlen + rd; + *eptr = 0; + do{ + char *nl = strchr(restofdata, '\n'); + if(!nl) break; + *nl++ = 0; + parsestring(fd, handlers, restofdata); + restofdata = nl; + DBG("rest of data: %s", restofdata); + }while(1); + if(restofdata != buf) memmove(buf, restofdata, eptr - restofdata + 1); + return TRUE; +} diff --git a/socket.h b/socket.h new file mode 100644 index 0000000..2a0a4d1 --- /dev/null +++ b/socket.h @@ -0,0 +1,59 @@ +/* + * This file is part of the CCD_Capture project. + * Copyright 2022 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#ifndef SERSOCK_H__ +#define SERSOCK_H__ + +// max & min TCP socket port number +#define PORTN_MAX (65535) +#define PORTN_MIN (1024) + +#define BUFLEN (1024) +// Max amount of connections +#define MAXCLIENTS (30) + +typedef enum{ + RESULT_OK, // all OK + RESULT_BUSY, // camera busy and no setters can be done + RESULT_FAIL, // failed running command + RESULT_BADVAL, // bad key's value + RESULT_BADKEY, // bad key + RESULT_SILENCE, // send nothing to client + RESULT_NUM +} hresult; + +const char *hresult2str(hresult r); + +// fd - socket fd to send private messages, key, val - key and its value +typedef hresult (*mesghandler)(int fd, const char *key, const char *val); + +typedef struct{ + int (*chkfunction)(char *val); // function to check device is ready + mesghandler handler; // handler function + const char *key; // keyword +} handleritem; + +int start_socket(int server, char *path, int isnet); +void sendmessage(int fd, const char *msg, int l); +void sendstrmessage(int fd, const char *msg); +char *get_keyval(char *keyval); + +int processData(int fd, handleritem *handlers, char *buf, int buflen); + +#endif // SERSOCK_H__