diff --git a/epicscounter.c b/epicscounter.c new file mode 100644 index 0000000..4f7a175 --- /dev/null +++ b/epicscounter.c @@ -0,0 +1,435 @@ +/*---------------------------------------------------------------------------- + This is a single counter implemented on top of EPICS slaer record. + This is not general: In order to support the special features of a + neutron counter, we use the scaler record in a special way. + + copyright: see file COPYRIGHT + + Mark Koennecke, February 2013 + ---------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include + +/* EPICS stuff */ +#include +#include +#include +/* #include */ +#include +#include +#include + +/*------------------ our private data structure ------------------------*/ +typedef struct { + char *rootName; + int thresholdCounter; + int thresholdValue; + int thresholdChanged; + char *ezcaError; + int ezcaCode; + int connectCount; +} EPICSCounter, *pEPICSCounter; + + +/*======================================================================== + These two functions currently rely on the idea that the EPICS stops + and starts without clearing counters in between. The sequence of + things necessary to start it, suggests this. If this is not the case then + this will not work. +===========================================================================*/ +static int EPICSPause(struct __COUNTER *self) +{ + int status; + pEPICSCounter pPriv = NULL; + + /* + not implemented + */ + + return OKOK; +} + +/*=======================================================================*/ +static int EPICSContinue(struct __COUNTER *self) +{ + int status; + pEPICSCounter pPriv = NULL; + + /* + not implemented + */ + return OKOK; +} + +/*=======================================================================*/ +static void CountHaltThreadFunc(void *param) +{ + char *pvName = (char *)param; + short stop = 0; + chid cid; + + ca_context_create(ca_disable_preemptive_callback); + ca_create_channel(pvName,NULL,NULL,10,&cid); + ca_pend_io(5.); + ca_put(DBR_SHORT,cid,&stop); + ca_pend_io(5.0); + free(pvName); + printf("HaltThread ends\n"); + +} +/*----------------------------------------------------------------------------*/ +static int EPICSHalt(struct __COUNTER *self) +{ + int status; + pEPICSCounter pPriv = NULL; + char pvName[132]; + short stop = 0; + + assert(self); + pPriv = (pEPICSCounter) self->pData; + assert(pPriv); + + snprintf(pvName,sizeof(pvName),"%s.CNT", pPriv->rootName); + /* ezcaPut(pvName,ezcaShort,1,&stop); */ + epicsThreadCreate("Lieselotte", + epicsThreadPriorityHigh, + epicsThreadStackMedium, + CountHaltThreadFunc, + (void *)strdup(pvName)); + + return OKOK; +} + +/*=======================================================================*/ +static int EPICSTransfer(struct __COUNTER *self) +{ + int status, i; + pEPICSCounter priv = NULL; + char pvName[132]; + long m; + + assert(self); + priv = (pEPICSCounter) self->pData; + assert(priv); + + /* + load monitors + */ + for(i = 2; i < 10; i++){ + snprintf(pvName,sizeof(pvName),"%s.S%d",priv->rootName,i); + status = ezcaGet(pvName,ezcaLong,1,&m); + if(status == EZCA_OK) { + self->lCounts[i-2] = m; + } else { + ezcaGetErrorString("ERROR: EPICS: ", &priv->ezcaError); + priv->ezcaCode = status; + return HWFault; + } + } + + /* + read time + */ + snprintf(pvName,sizeof(pvName),"%s.S1",priv->rootName); + status = ezcaGet(pvName,ezcaLong,1,&m); + if(status == EZCA_OK) { + self->fTime = (float)m/1000.; + } else { + ezcaGetErrorString("ERROR: EPICS: ", &priv->ezcaError); + priv->ezcaCode = status; + return HWFault; + } + + return OKOK; +} + +/*-----------------------------------------------------------------------*/ +static int EPICSGetStatus(struct __COUNTER *self, float *fControl) +{ + pEPICSCounter priv = (pEPICSCounter) self->pData; + char pvName[132]; + short cnt; + long m; + int i, valChange = 0, status; + + + assert(priv); + + if((status = EPICSTransfer(self)) != OKOK){ + return status; + } + priv->connectCount = 0; + if(self->eMode == eTimer){ + *fControl = self->fTime; + } else { + *fControl = (float)self->lCounts[1]; + } + + /* + read extended status + */ + snprintf(pvName,sizeof(pvName),"%s.S10",priv->rootName); + status = ezcaGet(pvName,ezcaLong,1,&m); + if(status != EZCA_OK) { + ezcaGetErrorString("ERROR: EPICS: ", &priv->ezcaError); + priv->ezcaCode = status; + return HWFault; + } + + /* + read cnt + */ + snprintf(pvName,sizeof(pvName),"%s.CNT",priv->rootName); + status = ezcaGet(pvName,ezcaShort,1,&cnt); + if(status != EZCA_OK) { + ezcaGetErrorString("ERROR: EPICS: ", &priv->ezcaError); + priv->ezcaCode = status; + return HWFault; + } + + /* + analyse status + */ + switch(m){ + case 0: + if(cnt == 0){ + return HWIdle; + } else { + return HWBusy; + } + break; + case 1: + return HWBusy; + case 2: + return HWNoBeam; + case 3: + return HWPause; + default: + /* + bad status code form EPICS + */ + assert(1); + } + return HWFault; +} +/*-------------- dummy callback for Start -------------------------*/ +static void StartDummy(struct event_handler_args d) +{ +} +/*-----------------------------------------------------------------------*/ +static int EPICSStart(struct __COUNTER *self) +{ + pEPICSCounter priv = NULL; + char pvName[132]; + int status, pr2; + double dTime; + long lPreset; + short cnt = 1; + chid *cid; + + assert(self); + priv = (pEPICSCounter) self->pData; + assert(priv); + + if(priv->thresholdChanged){ + snprintf(pvName,sizeof(pvName),"%s.PR3",priv->rootName); + status = ezcaPut(pvName,ezcaLong,1,&priv->thresholdCounter); + if(status != EZCA_OK) { + ezcaGetErrorString("ERROR: EPICS: ", &priv->ezcaError); + priv->ezcaCode = status; + return HWFault; + } + snprintf(pvName,sizeof(pvName),"%s.PR4",priv->rootName); + status = ezcaPut(pvName,ezcaLong,1,&priv->thresholdValue); + if(status != EZCA_OK) { + ezcaGetErrorString("ERROR: EPICS: ", &priv->ezcaError); + priv->ezcaCode = status; + return HWFault; + } else { + priv->thresholdChanged = 0; + } + } + + if(self->eMode == eTimer){ + dTime = self->fPreset; + snprintf(pvName,sizeof(pvName),"%s.TP",priv->rootName); + status = ezcaPut(pvName,ezcaDouble,1,&dTime); + if(status != EZCA_OK) { + ezcaGetErrorString("ERROR: EPICS: ", &priv->ezcaError); + priv->ezcaCode = status; + return HWFault; + } + pr2 = 0; + } else { + snprintf(pvName,sizeof(pvName),"%s.PR1",priv->rootName); + lPreset = (long)self->fPreset; + status = ezcaPut(pvName,ezcaLong,1,&lPreset); + if(status != EZCA_OK) { + ezcaGetErrorString("ERROR: EPICS: ", &priv->ezcaError); + priv->ezcaCode = status; + return HWFault; + } + pr2 = 10; + } + + snprintf(pvName,sizeof(pvName),"%s.PR2",priv->rootName); + status = ezcaPut(pvName,ezcaLong,1,&pr2); + if(status != EZCA_OK) { + ezcaGetErrorString("ERROR: EPICS: ", &priv->ezcaError); + priv->ezcaCode = status; + return HWFault; + } + + snprintf(pvName,sizeof(pvName),"%s.CNT",priv->rootName); + ezcaPvToChid(pvName,&cid); + status = ca_put_callback(DBR_SHORT,*cid,&cnt, StartDummy,NULL); + if(status != ECA_NORMAL){ + snprintf(pvName, sizeof(pvName),"Bad CA status %d", status); + priv->ezcaError = strdup(pvName); + priv->ezcaCode = status; + return HWFault; + } else { + ca_pend_event(.05); + priv->connectCount = 0; + return OKOK; + } + + return OKOK; +} + +/*======================================================================*/ +static int EPICSGetError(struct __COUNTER *self, int *iCode, + char *errorText, int errlen) +{ + char pBueffel[132]; + pEPICSCounter priv = NULL; + + priv = (pEPICSCounter) self->pData; + if(priv->ezcaError != NULL){ + strncpy(errorText,priv->ezcaError,errlen); + ezcaFree(priv->ezcaError); + priv->ezcaError = NULL; + } else { + strncpy(errorText,"Unknown error",errlen); + } + + return 1; +} + +/*=======================================================================*/ +static int EPICSFixIt(struct __COUNTER *self, int iCode) +{ + pEPICSCounter priv = NULL; + + priv = (pEPICSCounter) self->pData; + if(priv->ezcaCode == EZCA_NOTCONNECTED && priv->connectCount < 2) { + priv->connectCount++; + return COREDO; + } + return COTERM; +} + +/*-----------------------------------------------------------------------*/ +static int EPICSSet(struct __COUNTER *self, char *name, + int iCter, float fVal) +{ + pEPICSCounter pPriv = NULL; + int iVal; + + assert(self); + pPriv = (pEPICSCounter) self->pData; + assert(pPriv); + + if(strcmp(name,"threshold") == 0){ + pPriv->thresholdCounter = iCter; + pPriv->thresholdValue = (int)fVal; + pPriv->thresholdChanged = 1; + return OKOK; + } else { + return HWFault; + } + +} + +/*===================================================================*/ +static int EPICSGet(struct __COUNTER *self, char *name, + int iCter, float *fVal) +{ + pEPICSCounter pPriv = NULL; + + assert(self); + pPriv = (pEPICSCounter) self->pData; + assert(pPriv); + + if(strcmp(name,"threshold") == 0){ + *fVal = pPriv->thresholdValue; + return OKOK; + } else { + return HWFault; + } +} + +/*=====================================================================*/ +static int EPICSSend(struct __COUNTER *self, char *text, + char *reply, int replylen) +{ + strlcpy(reply, "EPICS does not feast on ASCII strings, refused!", + replylen); + return OKOK; +} + +/*====================================================================*/ +pCounterDriver MakeEPICSCounter(char *rootname) +{ + pEPICSCounter pPriv = NULL; + pCounterDriver self = NULL; + int i; + char pvName[132]; + + + /* + memory for everybody + */ + self = CreateCounterDriver("epics", "epics"); + pPriv = (pEPICSCounter) malloc(sizeof(EPICSCounter)); + if (self == NULL || pPriv == NULL) { + return NULL; + } + memset(pPriv, 0, sizeof(EPICSCounter)); + pPriv->thresholdChanged = 1; + pPriv->rootName = strdup(rootname); + + /* + install monitors + */ + for(i = 1; i < 11; i++){ + snprintf(pvName,sizeof(pvName),"%s.S%d", rootname,i); + ezcaSetMonitor(pvName,ezcaLong,1); + } + snprintf(pvName,sizeof(pvName),"%s.CNT", rootname); + ezcaSetMonitor(pvName,ezcaShort,1); + + /* + assign function pointers + */ + self->GetStatus = EPICSGetStatus; + self->Start = EPICSStart; + self->Pause = EPICSPause; + self->Continue = EPICSContinue; + self->Halt = EPICSHalt; + self->ReadValues = EPICSTransfer; + self->GetError = EPICSGetError; + self->TryAndFixIt = EPICSFixIt; + self->Set = EPICSSet; + self->Get = EPICSGet; + self->Send = EPICSSend; + self->KillPrivate = NULL; + self->iNoOfMonitors = 8; + + self->pData = pPriv; + return self; +}