436 lines
10 KiB
C
436 lines
10 KiB
C
/*----------------------------------------------------------------------------
|
|
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 <stdlib.h>
|
|
#include <assert.h>
|
|
#include <sics.h>
|
|
#include <status.h>
|
|
#include <countdriv.h>
|
|
#include <counter.h>
|
|
|
|
/* EPICS stuff */
|
|
#include <tsDefs.h>
|
|
#include <cadef.h>
|
|
#include <ezca.h>
|
|
/* #include <alarmString.h>*/
|
|
#include <menuAlarmStat.h>
|
|
#include <db_access.h>
|
|
#include <epicsThread.h>
|
|
|
|
/*------------------ 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;
|
|
}
|