Files
sics/site_ansto/hardsup/cameradriver.c
Douglas Clowes a43461b200 Reworked AsyncQueue sendCommand processing
Squashed commit of the following:

commit 42fb7d3cde591d40060cc740ccbc47f1ae7a5a50
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Tue Aug 26 13:31:11 2014 +1000

    Get the MODBUS_AP working

commit da785c1434a04c4186d4174eb2dfbaefc850c8e7
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Mon Aug 25 18:01:50 2014 +1000

    Bring Modbus protocol closer to Huber, Knauer and Omron

commit ef06ed7b6911cb49b35c19fe73e55f7c57cfd049
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Mon Aug 25 18:01:18 2014 +1000

    Make Huber, Knauer and Omron protocols more aligned (diffable)

commit 3ef1bb06b3f865502ad7dffc4bf5dba4814d9334
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Fri Aug 22 17:47:50 2014 +1000

    Get the Huber and Knauer protocols to be more alike

commit 2c9932e83f6735e894278648afdcadece654d43b
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Fri Aug 22 17:12:31 2014 +1000

    Clean up the Knauer dual-mode protocol and refactor

commit 333300b19b0e61916e261300ac6ae2b6bab5df09
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Thu Aug 21 15:38:39 2014 +1000

    Get the Knauer dual-mode protocol working(-ish)

commit b1f9d82f1b9eb8a1ff54694adc3482984b0d3d72
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Thu Aug 21 15:37:44 2014 +1000

    Make private functions static (and not duplicated)

commit 0b077414eef9e4351956a2b971d7751cced0d3cd
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Thu Aug 21 12:46:10 2014 +1000

    Knauer moving toward dual protocol

commit 13199bea38a1595ce06923e83474b738b10db94d
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Thu Aug 21 12:42:48 2014 +1000

    Restructure default sendCommand processing in asyncqueue

commit 99a8ea3174ca0636503b0ce0cdb6016790315558
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Thu Aug 21 09:48:50 2014 +1000

    Add a Modbus Protocol handler derived from sct_tcpmodbus

commit 3adf49fb7c8402c8260a0bb20729d551ac88537b
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Thu Aug 21 09:43:54 2014 +1000

    Leave the free of private data to the asyncqueue mechanism
2014-08-27 16:52:13 +10:00

1048 lines
30 KiB
C

/**
* \file cameradriver.c
* \brief This provides the IO layer which feeds inputs from a user or
* the camera server to the state machine, and then sends any state machine
* output back to SICS or the camera.
*
* \Author Ferdi Franceschini February 2013
*
* Copyright: see file Copyright.txt
*/
#include <limits.h>
#include <errno.h>
#include <string.h>
#include <sics.h>
#include <obdes.h>
#include <countdriv.h>
#include <nwatch.h>
#include <asyncqueue.h>
#include <math.h>
#include "camera.h"
#define NAMELEN 32
#define IFMTLEN 4
#define ERRLEN 256
#define MSGLEN 512
#define MAX_META_COUNT 32
#define MAX_SCANVAR_COUNT 16
enum camstates {idle, acquiring, processing, saving};
#define CAMDRIV_ERRTABLE \
TR(ENONE, "OK") \
TR(EFAIL, "command failed") \
TR(EBUSYACQ, "camera busy acquiring image") \
TR(EBUSYSAVE, "camera busy saving image") \
TE(EBUSYPROC, "camera busy processing image")
#define TR(a,b) a,
#define TE(a,b) a
enum errcodes {CAMDRIV_ERRTABLE};
#undef TR
#undef TE
#define TR(a,b) b,
#define TE(a,b) b
static char *errmsg[] = {CAMDRIV_ERRTABLE};
#undef TR
#undef TE
#define CAMDRIV_PARTABLE \
TR(CLOCK, "clock") \
TR(BIN, "bin") \
TR(SIZE, "size") \
TR(GAIN, "gain") \
TR(FLIP, "flip") \
TR(XSTART, "xstart") \
TR(YSTART, "ystart") \
TR(XEND, "xend") \
TR(YEND, "yend") \
TR(EXPOSURE, "exposure") \
TR(TEMP, "temperature") \
TR(THRESH, "threshold") \
TR(SHOPT, "shutteropentime") \
TE(SHCLT, "shutterclosetime")
#define TR(a,b) 1+
#define TE(a,b) 1
static int NUMCAMPAR = CAMDRIV_PARTABLE;
#undef TR
#undef TE
#define FLIP_TABLE \
TR(NORMAL, "normal") \
TR(INVINT, "invintensity") \
TR(NORMFH, "normfh") \
TR(NORMFV, "normfv") \
TR(NORMFHV, "normfhv") \
TR(INVFH, "invfh") \
TR(INVFV, "invfv") \
TE(INVFHV, "invfhv")
#define TR(a,b) a,
#define TE(a,b) a
enum campar {CAMDRIV_PARTABLE, MULTI, DEBUG};
enum flipval {FLIP_TABLE};
#undef TR
#undef TE
#define TR(a,b) b,
#define TE(a,b) b
static char *cacmdstr[] = {CAMDRIV_PARTABLE, "multi", "debug", NULL};
static char *flipcmdstr[] = {FLIP_TABLE, NULL};
#undef TR
#undef TE
#define UPDATE_SETTINGS_CFG 1
#define CLEAR_META_CFG 2
#define UPDATE_META_CFG 4
#define UPDATE_SCANVAR_CFG 8
#define UPDATE_FILE_CFG 16
typedef struct {
char path[MSGLEN];
char basename[MSGLEN];
long startnumber;
char imageformat[IFMTLEN];
char experimentdetail[MSGLEN];
} filecfg_t;
typedef struct {
float clockMHz;
float bin;
float size;
float gain;
enum flipval flip;
float xstart;
float ystart;
float xend;
float yend;
float exposure;
float temp;
float thresh;
float shopt;
float shclt;
int updatecfg;
filecfg_t file;
char meta_list[MAX_META_COUNT][MSGLEN];
char scanvar_list[MAX_SCANVAR_COUNT][NAMELEN];
pDummy scanvar_dummy[MAX_SCANVAR_COUNT];
pIDrivable pscanvar[MAX_SCANVAR_COUNT];
int meta_count;
int scanvar_count;
} camcfg_t;
typedef struct {
pObjectDescriptor pDes;
int debug;
char *asynq;
camsm_t state_machine;
pNWTimer state_timer;
int status;
enum errcodes camError;
camcfg_t camera;
pAsyncUnit asyncUnit;
SicsInterp *pSics;
} CamObj;
/* Camera communications and protocol handlers */
static pAsyncProtocol CAM_Protocol = NULL;
static int cb_state_timer(void *ctx, int mode);
static int cb_getstate(pAsyncTxn txn);
static int CamSet(CounterDriver *cntrData, char *name, int iCter, float fVal);
/* if emsg == NULL conversion OK else if emsg == num not an integer else emsg == error message */
long getlong(char *num, char *emsg) {
long val = 0;
char *endptr;
errno = 0;
emsg = NULL;
val = strtol(num, &endptr, 10);
if ( (errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || (errno != 0 && val == 0) )
emsg = strerror(errno);
if (endptr == num)
emsg = num;
return val;
}
static void CAM_Notify(void* context, int event) {
CamObj *self = (CamObj *) context;
switch (event) {
case AQU_DISCONNECT:
SICSLogWrite("CAM:(AQU_DISCONNECT)", eLogError);
if (self->state_timer) {
NetWatchRemoveTimer(self->state_timer);
self->state_timer=0;
}
break;
case AQU_RECONNECT:
SICSLogWrite("CAM:(AQU_RECONNECT)", eLogError);
NetWatchRegisterTimerPeriodic(&self->state_timer, 2000, 500, cb_state_timer, self);
break;
}
return;
}
int defaultHandleInput(pAsyncProtocol p, pAsyncTxn txn, int ch);
static int CAM_Rx(pAsyncProtocol p, pAsyncTxn txn, int ch) {
int ret = 1;
if (ch == '\r')
ret = 1;
else if (ch == '\n')
ret = AQU_POP_CMD;
else if (txn->inp_idx < txn->inp_len)
txn->inp_buf[txn->inp_idx++] = ch;
else
ret = AQU_POP_CMD;
return ret;
}
static int CAM_Ev(pAsyncProtocol p, pAsyncTxn pTxn, int event) {
if (event == AQU_TIMEOUT) {
pTxn->txn_status = ATX_TIMEOUT;
return AQU_POP_CMD;
}
return AQU_POP_CMD;
}
void CameraInitProtocol(SicsInterp *pSics) {
if (CAM_Protocol == NULL) {
CAM_Protocol = AsyncProtocolCreate(pSics, "CAMERA", NULL, NULL);
CAM_Protocol->sendCommand = NULL;
CAM_Protocol->handleInput = CAM_Rx;
CAM_Protocol->prepareTxn = NULL;
CAM_Protocol->killPrivate = NULL;
CAM_Protocol->handleEvent = CAM_Ev;
#if 0
CAM_Protocol->sendTerminator = strdup("\r\n");
CAM_Protocol->replyTerminator[0] = strdup("\r\n");
#endif
}
}
/* CounterDriver interface functions */
static int CamGetStatus(CounterDriver *cntrData, float *fControl) {
CamObj *camdrv= (CamObj *)cntrData->pData;
return camdrv->status;
}
/* \brief run_sm, call the state machine with the given input.
* \param self, driver context including current state
* \param ev_sym, input event
*/
static void run_sm(CamObj *self, enum event_codes ev_sym) {
char *sscur, *ssnext, *esout, message[MSGLEN+1];
if (self->debug) {
sscur = strstate(self->state_machine.Sc);
snprintf(message, MSGLEN, "DEBUG:(run_sm) Scurr:%s Ei:%s",
sscur,event_names[ev_sym]);
SICSLogWrite(message, eLog);
}
camdriv_input(self, &self->state_machine, ev_sym);
if (self->debug) {
ssnext = strstate(self->state_machine.Sc), SSLEN;
esout = strevent(self->state_machine.Eo), ESLEN;
snprintf(message, MSGLEN, "DEBUG:(run_sm) Snext:%s Eo:%s", ssnext, esout);
SICSLogWrite(message, eLog);
}
}
/* Called by the scan command and via the count and countnb subcommands of a
* counter object. Will update the configuration if necessary.
*/
static int CamStart(CounterDriver *cntrData) {
CamObj *self = NULL;
enum event_codes cd_sym;
char logmsg[MSGLEN+1];
self = cntrData->pData;
if (self->debug) {
snprintf(logmsg, MSGLEN, "DEBUG:(CamStart): preset=%f\n",cntrData->fPreset);
SICSLogWrite(logmsg, eLog);
}
CamSet(cntrData, "exposure", 0, cntrData->fPreset);
if (self->state_machine.multi) {
cd_sym = ECD_MLTI_ON;
} else {
cd_sym = ECD_TK_SHOT;
}
run_sm(self, cd_sym);
return 1;
}
static int CamPause(CounterDriver *cntrData) {
return 1;
}
static int CamContinue(CounterDriver *cntrData) {
return 1;
}
static int CamHalt(CounterDriver *cntrData) {
CamObj *self = cntrData->pData;
state_t start_state = {.cl=SCL_RDY, .cm=SCM_IDLE, .dr=SDR_IDLE};
if (self->state_machine.multi) {
run_sm(self, ECD_MLTI_OFF);
} else {
STset(&self->state_machine.Sc, start_state);
EVclr(&self->state_machine.Eo);
self->status = HWIdle;
}
return 1;
}
/* FIXME what should the counter data be set to? Total intensity? */
static int CamReadValues(CounterDriver *cntrData) {
int status, iReplyLen=MSGLEN;
char *cmd="TODO ", pReply[MSGLEN];
CamObj *self = NULL;
return 1;
self = cntrData->pData;
status = AsyncUnitTransact(self->asyncUnit, cmd, strlen(cmd), pReply, &iReplyLen);
return 1;
}
static int CamGetError(CounterDriver *cntrData, int *iCode, char *error, int iErrLen) {
CamObj *camdrv=NULL;
camdrv = (CamObj *) cntrData->pData;
*iCode = camdrv->camError;
camdrv->camError = ENONE;
switch (*iCode) {
case EBUSYACQ:
snprintf(error, (size_t) iErrLen,
"CAM: Can't complete operation, %s", errmsg[EBUSYACQ]);
break;
case EBUSYSAVE:
snprintf(error, (size_t) iErrLen,
"CAM: Can't complete operation, %s", errmsg[EBUSYSAVE]);
break;
case EBUSYPROC:
snprintf(error, (size_t) iErrLen,
"CAM: Can't complete operation, %s", errmsg[EBUSYPROC]);
break;
case ENONE:
snprintf(error, (size_t) iErrLen,
"CAM: Can't complete operation, %s", errmsg[ENONE]);
break;
case EFAIL:
snprintf(error, (size_t) iErrLen, "CAM: %s", errmsg[EFAIL]);
break;
}
return 1;
}
static int CamTryAndFixIt(CounterDriver *cntrData, int iCode) {
return COTERM;
}
static int CamSet(CounterDriver *cntrData, char *name, int iCter, float fVal) {
CamObj *camdriv= (CamObj *)cntrData->pData;
int found=0;
char *cmd;
enum campar id;
enum flipval flip;
for (id=0; (cmd = cacmdstr[id]) != NULL; id++) {
if (strcmp(cmd,name) == 0) {
found=1;
break;
}
}
if (!found) {
return 0;
}
switch (id) {
case DEBUG:
if (fVal == 1)
camdriv->debug = 1;
else
camdriv->debug = 0;
break;
case MULTI:
if (fVal != 0 && fVal != 1) {
return 0;
} else {
camdriv->state_machine.multi = fVal;
}
break;
case CLOCK:
if (fVal > 0) {
camdriv->camera.clockMHz = fVal;
camdriv->camera.updatecfg |= UPDATE_SETTINGS_CFG;
} else {
return 0;
}
break;
case BIN:
if (fVal > 0) {
camdriv->camera.bin = fVal;
camdriv->camera.updatecfg |= UPDATE_SETTINGS_CFG;
} else {
return 0;
}
break;
case SIZE:
if (fVal > 0) {
camdriv->camera.size = fVal;
camdriv->camera.updatecfg |= UPDATE_SETTINGS_CFG;
} else {
return 0;
}
break;
case GAIN:
if (fVal > 0) {
camdriv->camera.gain = fVal;
camdriv->camera.updatecfg |= UPDATE_SETTINGS_CFG;
} else {
return 0;
}
break;
case FLIP:
flip = fVal;
switch (flip) {
case NORMAL:
case INVINT:
case NORMFH:
case NORMFV:
case NORMFHV:
case INVFH:
case INVFV:
case INVFHV:
camdriv->camera.flip = flip;
camdriv->camera.updatecfg |= UPDATE_SETTINGS_CFG;
break;
default:
return 0;
break;
}
break;
case XSTART:
if (fVal > 0) {
camdriv->camera.xstart = fVal;
camdriv->camera.updatecfg |= UPDATE_SETTINGS_CFG;
} else {
return 0;
}
break;
case YSTART:
if (fVal > 0) {
camdriv->camera.ystart = fVal;
camdriv->camera.updatecfg |= UPDATE_SETTINGS_CFG;
} else {
return 0;
}
break;
case XEND:
if (fVal > 0) {
camdriv->camera.xend = fVal;
camdriv->camera.updatecfg |= UPDATE_SETTINGS_CFG;
} else {
return 0;
}
break;
case YEND:
if (fVal > 0) {
camdriv->camera.yend = fVal;
camdriv->camera.updatecfg |= UPDATE_SETTINGS_CFG;
} else {
return 0;
}
break;
case EXPOSURE:
if (fVal > 0) {
camdriv->camera.exposure = fVal;
camdriv->camera.updatecfg |= UPDATE_SETTINGS_CFG;
} else {
return 0;
}
break;
case TEMP:
camdriv->camera.temp = fVal;
camdriv->camera.updatecfg |= UPDATE_SETTINGS_CFG;
break;
case THRESH:
if (fVal > 0) {
camdriv->camera.thresh = fVal;
camdriv->camera.updatecfg |= UPDATE_SETTINGS_CFG;
} else {
return 0;
}
break;
case SHOPT:
if (fVal > 0) {
camdriv->camera.shopt = fVal;
camdriv->camera.updatecfg |= UPDATE_SETTINGS_CFG;
} else {
return 0;
}
break;
case SHCLT:
if (fVal > 0) {
camdriv->camera.shclt = fVal;
camdriv->camera.updatecfg |= UPDATE_SETTINGS_CFG;
} else {
return 0;
}
break;
default:
return 0;
}
return 1;
}
static int CamGet(CounterDriver *cntrData, char *name, int iCter, float *fVal) {
CamObj *camdriv= (CamObj *)cntrData->pData;
int found=0;
char *cmd;
enum campar id;
for (id=0; (cmd = cacmdstr[id]) != NULL; id++) {
if (strcmp(cmd,name) == 0) {
found=1;
break;
}
}
if (!found) {
return 0;
}
switch (id) {
case MULTI:
*fVal = (float) camdriv->state_machine.multi;
break;
case CLOCK:
*fVal = camdriv->camera.clockMHz;
break;
case BIN:
*fVal = camdriv->camera.bin;
break;
case SIZE:
*fVal = camdriv->camera.size;
break;
case GAIN:
*fVal = camdriv->camera.gain;
break;
case FLIP:
*fVal = camdriv->camera.flip;
break;
case XSTART:
*fVal = camdriv->camera.xstart;
break;
case YSTART:
*fVal = camdriv->camera.ystart;
break;
case XEND:
*fVal = camdriv->camera.xend;
break;
case YEND:
*fVal = camdriv->camera.yend;
break;
case EXPOSURE:
*fVal = camdriv->camera.exposure;
break;
case TEMP:
*fVal = camdriv->camera.temp;
break;
case THRESH:
*fVal = camdriv->camera.thresh;
break;
case SHOPT:
*fVal = camdriv->camera.shopt;
break;
case SHCLT:
*fVal = camdriv->camera.shclt;
break;
default:
return 0;
}
return 1;
}
static int CamSend(CounterDriver *cntrData, char *pText, char *pReply, int iReplyLen) {
int status;
CamObj *self = NULL;
self = cntrData->pData;
//char *pBuf = (char *)calloc(strlen(pText)+10, sizeof(char));
//sprintf(pBuf, "set meta, %s", pText);
//status = AsyncUnitTransact(self->asyncUnit, pBuf, strlen(pBuf), pReply, &iReplyLen);
status = AsyncUnitTransact(self->asyncUnit, pText, strlen(pText), pReply, &iReplyLen);
//free(pBuf);
if (status <= 0)
return 0;
else
return 1;
}
static int cb_shotcmd(pAsyncTxn txn) {
CamObj *self = (CamObj *) txn->cntx;
char *resp = txn->inp_buf, message[MSGLEN+1];
if (self->debug) {
snprintf(message, MSGLEN, "DEBUG:(cb_shotcmd) Camera reply: %s", resp);
SICSLogWrite(message, eLog);
}
if (strncmp(resp, "OK", 2) != 0) {
self->camError = EFAIL;
return 0;
}
return 1;
}
/**
* \brief Decides if an output message is targeted at the camera or SICS or if
* it should be fed back into the state transition function.
*/
int camdriv_out(void *me, event_t Eo) {
int i, len, pend=0;
char cmd[MSGLEN]="", logmsg[MSGLEN+1]="";
float clock, fVal;
CamObj *self = (CamObj *)me;
if (Eo.ca) {
/* send command to camera */
if (self->debug) {
snprintf(logmsg, MSGLEN, "DEBUG:(camdriv_out:Eo.ca): ev=%s, output=%s\n",
event_names[Eo.ca], event_signatures[Eo.ca]);
SICSLogWrite(logmsg, eLog);
}
switch (Eo.ca) {
case ECA_TK_SHOT:
len = strlen(event_signatures[ECA_TK_SHOT]);
strncpy(cmd, event_signatures[ECA_TK_SHOT], len);
len = strlen(cmd);
AsyncUnitSendTxn(self->asyncUnit, cmd, len, cb_shotcmd, self, MSGLEN);
break;
case ECA_MLTI_ON:
len = strlen(event_signatures[ECA_MLTI_ON]);
strncpy(cmd, event_signatures[ECA_MLTI_ON], len);
len = strlen(cmd);
AsyncUnitSendTxn(self->asyncUnit, cmd, len, cb_shotcmd, self, MSGLEN);
break;
case ECA_MLTI_OFF:
len = strlen(event_signatures[ECA_MLTI_OFF]);
strncpy(cmd, event_signatures[ECA_MLTI_OFF], len);
len = strlen(cmd);
AsyncUnitSendTxn(self->asyncUnit, cmd, len, cb_shotcmd, self, MSGLEN);
break;
case ECA_CFG:
/* TODO Should we retry sending a command if AsyncUnitSendTxn fails?
* The driver should abort the shot and then try to send the
* configuration the next time a shot is taken. */
if( (self->camera.updatecfg & UPDATE_SETTINGS_CFG) == UPDATE_SETTINGS_CFG) {
clock = self->camera.clockMHz;
snprintf(cmd, MSGLEN,
"set camera,clock=%.*fmhz,bin=%dx,size=%d,gain=%dxhs,flip=%s,xstart=%d,ystart=%d,xend=%d,yend=%d,exposure=%f,temperature=%f,threshold=%d,shutteropentime=%d,shutterclosetime=%d",
clock>=1 ? 0 : 1, clock, (int)self->camera.bin, (int)self->camera.size,
(int)self->camera.gain, flipcmdstr[self->camera.flip],
(int)self->camera.xstart, (int)self->camera.ystart,
(int)self->camera.xend, (int)self->camera.yend, self->camera.exposure,
self->camera.temp, (int)self->camera.thresh, (int)self->camera.shopt,
(int)self->camera.shclt
);
len = strlen(cmd);
self->camera.updatecfg &= ~UPDATE_SETTINGS_CFG;
if (self->debug) {
snprintf(logmsg, MSGLEN, "DEBUG:(camdriv_out:UPDATE_SETTINGS_CFG): cmd=%s\n", cmd);
SICSLogWrite(logmsg, eLog);
}
AsyncUnitSendTxn(self->asyncUnit, cmd, len, cb_shotcmd, self, MSGLEN);
}
if( (self->camera.updatecfg & CLEAR_META_CFG) == CLEAR_META_CFG) {
snprintf(cmd, MSGLEN, "clear meta");
len = strlen(cmd);
if (self->debug) {
snprintf(logmsg, MSGLEN, "DEBUG:(camdriv_out:CLEAR_META_CFG): cmd=%s\n", cmd);
SICSLogWrite(logmsg, eLog);
}
AsyncUnitSendTxn(self->asyncUnit, cmd, len, cb_shotcmd, self, MSGLEN);
}
if( (self->camera.updatecfg & UPDATE_META_CFG) == UPDATE_META_CFG) {
//TODO concatenate the meta_list into a comma separated string and send it in MSGLEN chunks.
for (i=0; i < self->camera.meta_count && pend < MSGLEN; i++) {
pend = sprintf(cmd, "set meta,");
pend += snprintf(&cmd[pend], MSGLEN, "%s", self->camera.meta_list[i]);
len = strlen(cmd);
if (self->debug) {
snprintf(logmsg, MSGLEN, "DEBUG:(camdriv_out:UPDATE_META_CFG): cmd=%s\n", cmd);
SICSLogWrite(logmsg, eLog);
}
AsyncUnitSendTxn(self->asyncUnit, cmd, len, cb_shotcmd, self, MSGLEN);
}
}
if( (self->camera.updatecfg & UPDATE_SCANVAR_CFG) == UPDATE_SCANVAR_CFG) {
pend = sprintf(cmd, "set meta");
//TODO Send in MSGLEN chunks even though the string may never exceed MSGLEN
for (i=0; i < self->camera.scanvar_count && pend < MSGLEN; i++) {
fVal = self->camera.pscanvar[i]->GetValue(self->camera.scanvar_dummy[i], pServ->dummyCon);
pend += snprintf(&cmd[pend], MSGLEN, ",%s=%f", self->camera.scanvar_list[i], fVal);
}
len = strlen(cmd);
if (self->debug) {
snprintf(logmsg, MSGLEN, "DEBUG:(camdriv_out:UPDATE_SCANVAR_CFG): cmd=%s\n", cmd);
SICSLogWrite(logmsg, eLog);
}
AsyncUnitSendTxn(self->asyncUnit, cmd, len, cb_shotcmd, self, MSGLEN);
}
if( (self->camera.updatecfg & UPDATE_FILE_CFG) == UPDATE_FILE_CFG) {
snprintf(cmd, MSGLEN, "set file,path=%s,basename=%s,startnumber=%ld,imageformat=%s,experimentdetail=%s",
self->camera.file.path, self->camera.file.basename, self->camera.file.startnumber, self->camera.file.imageformat,self->camera.file.experimentdetail);
len = strlen(cmd);
if (self->debug) {
snprintf(logmsg, MSGLEN, "DEBUG:(camdriv_out:UPDATE_FILE_CFG): cmd=%s\n", cmd);
SICSLogWrite(logmsg, eLog);
}
AsyncUnitSendTxn(self->asyncUnit, cmd, len, cb_shotcmd, self, MSGLEN);
self->camera.updatecfg &= ~UPDATE_FILE_CFG;
}
break;
default:
snprintf(logmsg, MSGLEN, "CAM:(camdriv_out) Unhandled event %s", event_names[Eo.ca]);
SICSLogWrite(logmsg, eLogError);
return -1;
}
return -1;
}
if (Eo.cm) {
snprintf(logmsg, MSGLEN, "TODO:(camdriv_out:Eo.cm): ev=%s, output=%s\n",
event_names[Eo.cm], event_signatures[Eo.cm]);
SICSLogWrite(logmsg, eLogError);
return -1;
}
if (Eo.cd) {
snprintf(logmsg, MSGLEN, "TODO:(camdriv_out:Eo.cm): ev=%s, output=%s\n",
event_names[Eo.cd], event_signatures[Eo.cd]);
SICSLogWrite(logmsg, eLogError);
return -1;
}
if (Eo.dr) {
/* send msg to SICS */
if (self->debug) {
snprintf(logmsg, MSGLEN, "DEBUG:(camdriv_out): ev=%s, output=%s\n",
event_names[Eo.dr], event_signatures[Eo.dr]);
SICSLogWrite(logmsg, eLog);
}
switch (Eo.dr) {
case EDR_IDLE:
self->status = HWIdle;
break;
case EDR_BUSY:
self->status = HWBusy;
break;
case EDR_FAULT:
self->status = HWFault;
break;
default:
snprintf(logmsg, MSGLEN, "CAM:(camdriv_out) Unhandled event %s", event_names[Eo.dr]);
SICSLogWrite(logmsg, eLogError);
return -1;
}
return -1;
}
if (Eo.cl) {
switch (Eo.cl) {
case ECL_RDY:
return ECL_RDY;
break;
default:
snprintf(logmsg, MSGLEN, "CAM:(camdriv_out) Unhandled event %s", event_names[Eo.cl]);
SICSLogWrite(logmsg, eLogError);
return -1;
}
return -1;
}
return -1;
}
static int cb_state_timer(void *ctx, int mode) {
CamObj *self = (CamObj *) ctx;
char errmsg[32]="";
char cmd[MSGLEN];
int len, status;
len = strlen(event_signatures[ECA_GET_STATE]);
strncpy(cmd, event_signatures[ECA_GET_STATE], len);
status = AsyncUnitSendTxn(self->asyncUnit, cmd, len, cb_getstate, self, MSGLEN);
if (status==1) {
return 1;
} else {
snprintf(errmsg, 31, "CAM:(cb_getstate) AsyncUnitSendTxn failed");
SICSLogWrite(errmsg, eLogError);
return 0;
}
}
/* Input from camera state feedback */
static int cb_getstate(pAsyncTxn txn) {
CamObj *self = (CamObj *) txn->cntx;
char *resp = txn->inp_buf, message[MSGLEN+1];
int ret=1, time_rem, time_tot;
enum event_codes ca_sym, cm_sym;
if (txn->txn_status == ATX_TIMEOUT) {
ret = 0;
} else if ( cam_parse_status(resp, &ca_sym, &time_rem, &time_tot) == -1) {
snprintf(message, MSGLEN,
"CAM:(cb_getstate) cam_parse_status failed to parse '%s'",resp);
SICSLogWrite(message, eLogError);
ret = 0;
} else {
if (self->debug) {
snprintf(message, MSGLEN, "DEBUG:(cb_getstate) Camera reply: %s", resp);
SICSLogWrite(message, eLog);
}
cm_sym = camera_model(ca_sym);
run_sm(self, cm_sym);
}
return ret;
}
/* Auxiliary Camera Driver commands */
int cmd_code(char **cmd_list, char *cmd) {
int i;
for (i=0; cmd_list[i] != NULL; i++)
if (strcasecmp(cmd_list[i], cmd) == 0)
return i;
return -1;
}
int file_cmd(CamObj *camdriv, SConnection *pCon, char *subcmd, char *data) {
int i, subcmd_code, retval = 1;
long numval;
enum file_subcmd_codes {PATH, BASENAME, IMAGEFMT, STARTNUM, EXPDETAIL};
char *file_subcmd_names[] = {"path", "name", "format", "startnumber", "detail", NULL};
char *glemsg = NULL, emsg[MSGLEN];
if ( (subcmd_code = cmd_code(file_subcmd_names, subcmd)) == -1) {
return 0;
}
switch (subcmd_code) {
case PATH:
snprintf(camdriv->camera.file.path, MSGLEN, "%s", data);
break;
case BASENAME:
snprintf(camdriv->camera.file.basename, MSGLEN, "%s", data);
break;
case STARTNUM:
numval = getlong(data, glemsg);
if (glemsg == NULL) {
camdriv->camera.file.startnumber = numval;
} else if (glemsg == data) {
snprintf(emsg, MSGLEN, "CAM:(file_cmd) %s is not an integer", data);
SCWrite(pCon, emsg, eError);
retval = 0;
} else {
snprintf(emsg, MSGLEN, "CAM:(file_cmd) %s", glemsg);
SCWrite(pCon, emsg, eError);
retval = 0;
}
break;
case IMAGEFMT:
if (strcasecmp(data, "tif") == 0) {
for (i=0; data[i]; i++) {
data[i] = tolower(data[i]);
}
snprintf(camdriv->camera.file.imageformat, IFMTLEN, "%s", data);
} else {
retval = 0;
}
break;
case EXPDETAIL:
snprintf(camdriv->camera.file.experimentdetail, MSGLEN, "%s", data);
break;
default:
retval = 0;
}
return retval;
}
/**
* \brief Auxiliary command interface which gets around type and string length
* limitations of the CountAction command.
*/
int CamAuxCmd(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) {
char *cmd = NULL;
char message[MSGLEN];
enum auxcmd_codes {CLEAR_META, META, FILECMD, SCANVAR, MOTOR};
char *auxcmd_names[] = {"clearmeta", "meta", "file", "scanvar", "motor", NULL};
int i, code, retval = 1, pend=0;
CamObj *camdriv = (CamObj *)pData;
CommandList *pCom = NULL;
pIDrivable pDriv = NULL;
pDummy pDumData = NULL;
if (argc < 2) {
snprintf(message, MSGLEN, "CAM: You must specify a command");
SCWrite(pCon, message, eError);
return 0;
}
if ( (code = cmd_code(auxcmd_names, argv[1])) == -1 ) {
snprintf(message, MSGLEN, "CAM: %s is not a valid command", argv[1]);
SCWrite(pCon, message, eError);
retval = 0;
goto END;
}
switch (code) {
case CLEAR_META:
camdriv->camera.meta_count = 0;
camdriv->camera.scanvar_count = 0;
camdriv->camera.updatecfg |= CLEAR_META_CFG;
camdriv->camera.updatecfg &= ~UPDATE_META_CFG;
camdriv->camera.updatecfg &= ~UPDATE_SCANVAR_CFG;
break;
case META:
if (argc < 3) {
snprintf(message, MSGLEN, "CAM: Insufficient arguments, the META command requires name=val data");
SCWrite(pCon, message, eError);
retval = 0;
goto END;
}
if (camdriv->camera.meta_count < MAX_META_COUNT) {
for (i=2; i < argc; i++) {
pend += snprintf(&(camdriv->camera.meta_list[camdriv->camera.meta_count][pend]), MSGLEN, "%s ", argv[i]);
}
camdriv->camera.meta_count++;
camdriv->camera.updatecfg |= UPDATE_META_CFG;
} else {
goto END;
}
break;
case SCANVAR:
case MOTOR:
if (argc < 3) {
snprintf(message, MSGLEN, "CAM: Insufficient arguments, the MOTOR/SCANVAR command requires the name of something you can drive like a motor or environment control");
SCWrite(pCon, message, eError);
retval = 0;
goto END;
}
if (camdriv->camera.scanvar_count < MAX_SCANVAR_COUNT) {
pCom = FindCommand(pSics, argv[2]);
if (!pCom) {
snprintf(message, MSGLEN, "CAM: There is no command object for a driveable with this name %s", argv[2]);
SCWrite(pCon, message, eError);
retval = 0;
goto END;
}
pDumData = (pDummy) pCom->pData;
if (!pDumData) {
snprintf(message, MSGLEN, "CAM: There is no data for the command object of a driveable with this name %s", argv[2]);
SCWrite(pCon, message, eError);
retval = 0;
goto END;
}
pDriv = (pIDrivable) pDumData->pDescriptor->GetInterface(pDumData, DRIVEID);
if (!pDriv) {
snprintf(message, MSGLEN, "CAM: %s is not a driveable object", argv[2]);
SCWrite(pCon, message, eError);
retval = 0;
goto END;
}
camdriv->camera.scanvar_dummy[camdriv->camera.scanvar_count] = pDumData;
camdriv->camera.pscanvar[camdriv->camera.scanvar_count] = pDriv;
snprintf(camdriv->camera.scanvar_list[camdriv->camera.scanvar_count++], NAMELEN, "%s", argv[2]);
camdriv->camera.updatecfg |= UPDATE_SCANVAR_CFG;
} else {
snprintf(message, MSGLEN, "CAM: Exceeded maximum number (%d) of driveable objects", MAX_SCANVAR_COUNT);
SCWrite(pCon, message, eError);
retval = 0;
goto END;
}
break;
case FILECMD:
if (argc < 4) {
snprintf(message, MSGLEN, "CAM: Insufficient arguments for the FILE command");
SCWrite(pCon, message, eError);
retval = 0;
goto END;
}
if (file_cmd(camdriv, pCon, argv[2], argv[3]))
camdriv->camera.updatecfg |= UPDATE_FILE_CFG;
else {
snprintf(message, MSGLEN, "CAM: Failed to create camera file configuration");
SCWrite(pCon, message, eError);
retval = 0;
goto END;
}
break;
}
END:
if (cmd != NULL)
free(cmd);
return retval;
}
pCounterDriver CreateCam(SConnection *pCon, char *name, char *asynq) {
char msg[ERRLEN], cmd[MSGLEN], auxcmd[NAMELEN];
int len;
state_t start_state = {.cl=SCL_RDY, .cm=SCM_IDLE, .dr=SDR_IDLE};
pCounterDriver pCntDriv = NULL;
CamObj *pNewCam = NULL;
pNewCam = (CamObj *) malloc(sizeof(CamObj));
memset(pNewCam, 0, sizeof(CamObj));
pNewCam->pDes = CreateDescriptor("CameraCommandObject");
STset(&pNewCam->state_machine.Sc, start_state);
EVclr(&pNewCam->state_machine.Eo);
pNewCam->state_machine.output_fn = camdriv_out;
pNewCam->state_machine.multi = 0;
pNewCam->state_timer = 0;
pNewCam->status = HWIdle;
pNewCam->camError = ENONE;
pNewCam->debug = 1;
pNewCam->camera.updatecfg = 0;
pNewCam->camera.meta_count = 0;
pNewCam->camera.scanvar_count = 0;
pNewCam->camera.file.startnumber = 1;
snprintf(pNewCam->camera.file.imageformat, IFMTLEN, "tif");
pNewCam->camera.file.experimentdetail[0] = '\0';
pNewCam->asynq = strdup(asynq);
pNewCam->pSics = pServ->pSics;
if (!AsyncUnitCreate(asynq, &pNewCam->asyncUnit)) {
snprintf(msg, ERRLEN, "CAM:AsyncQueue %s has not been defined", asynq);
SCWrite(pCon, msg, eError);
return NULL;
}
AsyncUnitSetTimeout(pNewCam->asyncUnit, 60000);
AsyncUnitSetNotify(pNewCam->asyncUnit, pNewCam, CAM_Notify);
pCntDriv = CreateCounterDriver(name, "anstocamera");
if (pCntDriv == NULL)
return NULL;
pCntDriv->GetStatus = CamGetStatus;
pCntDriv->Start = CamStart;
pCntDriv->Pause = CamPause;
pCntDriv->Continue = CamContinue;
pCntDriv->Halt = CamHalt;
pCntDriv->ReadValues = CamReadValues;
pCntDriv->GetError = CamGetError;
pCntDriv->TryAndFixIt = CamTryAndFixIt;
pCntDriv->Set = CamSet;
pCntDriv->Get = CamGet;
pCntDriv->Send = CamSend;
pCntDriv->iNoOfMonitors = 1;
pCntDriv->pData = pNewCam;
len = strlen(event_signatures[ECA_GET_STATE]);
strncpy(cmd, event_signatures[ECA_GET_STATE], len);
NetWatchRegisterTimerPeriodic(&pNewCam->state_timer, 2000, 500, cb_state_timer, pNewCam);
snprintf(auxcmd, NAMELEN, "%s_cmd", name);
AddCommand(pNewCam->pSics, auxcmd, CamAuxCmd, NULL, pNewCam);
return pCntDriv;
}