Files
sinqepicsapp/sinqEPICSApp/src/devScalerEL737.c

662 lines
19 KiB
C

/**
* scaler device support for the PSI EL737 counter box.
* The EL737 has modes: preset timer, preset monitor.
* The EL7373 has also the option to set a threshold on a monitor.
* The EL737 has a maximum of 8 monitors which can be read.
* The up to 64 channels of the scaler record will be used as such:
* 1 = Time*1000
* 2-9 monitor values
* 10 extended status. Annotates rate low, paused conditions...
*
* The preset values are used as such:
* 1 = Time*1000 or preset monitor
* 2 = mode switch: 0 = preset time, > 0 preset monitor
* 2 = threshold monitor
* 3 = threshold monitor count
*
* This is less then ideal. But it is a workaround for the fact that the scalar record
* does not do all the tricks the EL737 knows:
* - Thresholding
* - two count modes
* A better solution would be to extend the scalar record to have a proper status and count
* mode field. And threshold control fields too. But this is much more work, breaks
* compatability with the scalar record completely and thus was not done for now.
*
* The driver will run a separate thread which does all the
* communication.
*
* Mark Koennecke, February 2013
*
* Enhanced by adding an addtional external pause and status flag in the database and code in here
* to handle this. Moreover an external MsgTxt field in the DB can be filled with an error message.
*
* Mark Koennecke, July 2017
*
* Enhanced with a separate thresholdCounter and threshold field in order to replace the
* hack for threshold handling. If these fields are present, the hack with presets 2 and
* 3 as described above is ignored.
*
* Mark Koennecke, August 2017
*/
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <asynOctetSyncIO.h>
#include <epicsEvent.h>
#include <epicsThread.h>
#include <epicsTime.h>
#include <epicsExport.h>
#include <errlog.h>
#include <callback.h>
#include <recSup.h>
#include <devSup.h>
#include <cantProceed.h>
#include <dbDefs.h>
#include <dbFldTypes.h>
#include <dbAccess.h>
#include <errlog.h>
#include <scalerRecord.h>
#include <devScaler.h>
#include <asynEpicsUtils.h>
/* dset functions */
static long el737_init_record(struct scalerRecord *psr, CALLBACK *pcallback);
static long el737_reset(scalerRecord *psr);
static long el737_read(scalerRecord *psr, epicsUInt32 *val);
static long el737_write_preset(scalerRecord *psr, int signal, unsigned long val);
static long el737_arm(scalerRecord *psr, int val);
static long el737_done(scalerRecord *psr);
/* thread function which runs the device */
static void el737Thread(void *param);
#define MODE 1
#define TIMER 0
#define MONITOR 1
#define THRESHMON 2
#define THRESHVAL 3
#define NCOUNT 10
#define COMLEN 132
SCALERDSET devScalerEL737 = {
7,
NULL,
NULL,
el737_init_record,
NULL,
el737_reset,
el737_read,
el737_write_preset,
el737_arm,
el737_done
};
epicsExportAddress(dset, devScalerEL737);
typedef struct {
unsigned long presets[13];
unsigned long values[NCOUNT];
unsigned int countCommand;
unsigned int counting;
unsigned long thresholdValue;
unsigned int sendThreshold;
scalerRecord *psr;
epicsEventId wakeUp;
asynUser *asynContext;
CALLBACK *pcallback;
DBADDR pause;
DBADDR status;
DBADDR msgTxt;
DBADDR threshCounter;
DBADDR threshold;
unsigned int dbInit;
}EL737priv;
static void dummyAsynCallback([[maybe_unused]] asynUser *pasynUser)
{
}
static void connectSlaveRecords(EL737priv *priv)
{
char slaveName[PVNAME_SZ + 18], errName[256];
long status;
priv->dbInit = 1;
snprintf(slaveName,sizeof(slaveName),"%s:Pause", priv->psr->name);
errlogPrintf("Name of pause variable: %s\n", slaveName);
status = dbNameToAddr(slaveName,&priv->pause);
if(status!= 0){
errSymLookup(status,errName,sizeof(errName));
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName, errName);
priv->dbInit = 0;
} else {
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",slaveName, priv->pause.precord->name);
}
snprintf(slaveName,sizeof(slaveName),"%s:MsgTxt", priv->psr->name);
errlogPrintf("Name of MsgTxt variable: %s\n", slaveName);
status = dbNameToAddr(slaveName,&priv->msgTxt);
if(status!= 0){
errSymLookup(status,errName,sizeof(errName));
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName, errName);
priv->dbInit = 0;
} else {
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",slaveName, priv->msgTxt.precord->name);
}
snprintf(slaveName,sizeof(slaveName),"%s:Status", priv->psr->name);
errlogPrintf("Name of status variable: %s\n", slaveName);
status = dbNameToAddr(slaveName,&priv->status);
if(status!= 0){
errSymLookup(status,errName,sizeof(errName));
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName, errName);
priv->dbInit = 0;
} else {
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",slaveName, priv->status.precord->name);
}
snprintf(slaveName,sizeof(slaveName),"%s:ThresholdCounter", priv->psr->name);
errlogPrintf("Name of thresholdCounter variable: %s\n", slaveName);
status = dbNameToAddr(slaveName,&priv->threshCounter);
if(status!= 0){
errSymLookup(status,errName,sizeof(errName));
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName, errName);
priv->dbInit = 0;
} else {
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",slaveName, priv->threshCounter.precord->name);
}
snprintf(slaveName, sizeof(slaveName), "%s:Threshold", priv->psr->name);
errlogPrintf("Name of thresholdCounter variable: %s\n", slaveName);
status = dbNameToAddr(slaveName,&priv->threshold);
if(status!= 0){
errSymLookup(status,errName,sizeof(errName));
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName, errName);
priv->dbInit = 0;
} else {
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",slaveName, priv->threshold.precord->name);
}
priv->thresholdValue = -999;
}
static long el737_init_record(scalerRecord *psr, CALLBACK *pcallback)
{
EL737priv *priv = NULL;
char *port, *userParam;
int signal, status, reason;
size_t in, out;
asynUser *dummyUser;
char command[80], reply[80];
/**
* initalize record fields
*/
psr->nch = NCOUNT;
psr->g1 = scalerG1_Y;
psr->g2 = scalerG1_Y;
psr->g3 = scalerG1_Y;
psr->g4 = scalerG1_Y;
psr->freq = 1000;
psr->cont = scalerCONT_OneShot;
/*
* private data structure
*/
priv = callocMustSucceed(1,sizeof(EL737priv), "devScalerEL737 init_record()");
priv->psr = psr;
priv->wakeUp = epicsEventCreate(epicsEventEmpty);
priv->pcallback = pcallback;
priv->dbInit = 0;
psr->dpvt = priv;
/*
* Hook up with device
*/
dummyUser = pasynManager->createAsynUser(dummyAsynCallback,0);
status = pasynEpicsUtils->parseLink(dummyUser, &psr->out,
&port, &signal, &userParam);
if (status != asynSuccess) {
errlogPrintf("devScalerEL737::init_record %s bad link %s\n",
psr->name, dummyUser->errorMessage);
psr->pact = 1;
return 0;
}
pasynManager->autoConnect(dummyUser, 1);
status = pasynOctetSyncIO->connect(port, 0, &priv->asynContext, NULL);
if (status) {
asynPrint(dummyUser, ASYN_TRACE_ERROR,
"%s: cannot connect to EL737 controller\n",
"el737_init_scaler");
psr->pact = 1;
return 0;
}
pasynOctetSyncIO->setOutputEos(priv->asynContext,"\r",strlen("\r"));
pasynOctetSyncIO->setInputEos(priv->asynContext,"\r",strlen("\r"));
priv->asynContext->timeout = 2.0;
strcpy(command,"RMT 1");
pasynOctetSyncIO->writeRead(priv->asynContext, command, strlen(command),
reply,sizeof(reply), 1.,&out,&in,&reason);
pasynOctetSyncIO->writeRead(priv->asynContext, command, strlen(command),
reply,sizeof(reply), 1.,&out,&in,&reason);
strcpy(command,"ECHO 2");
pasynOctetSyncIO->writeRead(priv->asynContext, command, strlen(command),
reply,sizeof(reply), 1.,&out,&in,&reason);
pasynManager->freeAsynUser(dummyUser);
connectSlaveRecords(priv);
/*
start the thread which actually runs the device
*/
epicsThreadCreate("EL737",
epicsThreadPriorityMedium,
epicsThreadStackMedium,
el737Thread,
priv);
//errlogPrintf("EL7373 thread started \n");
return 0;
}
static long el737_reset(scalerRecord *psr)
{
unsigned int i;
EL737priv *priv;
priv = (EL737priv *)psr->dpvt;
for(i = 0; i < NCOUNT; i++){
priv->values[i] = 0;
}
return 0;
}
static long el737_read(scalerRecord *psr, epicsUInt32 *val)
{
unsigned int i;
EL737priv *priv;
priv = (EL737priv *)psr->dpvt;
for(i = 0; i < NCOUNT; i++){
val[i] = (epicsUInt32)priv->values[i];
}
return 0;
}
static long el737_write_preset(scalerRecord *psr, int signal, unsigned long val)
{
EL737priv *priv;
// errlogPrintf("EL737: Setting preset %d to %ld\n", signal, val);
priv = (EL737priv *)psr->dpvt;
if(signal >= 0 && signal < 13){
priv->presets[signal] = val;
}
if(signal == THRESHVAL){
priv->sendThreshold = 1;
epicsEventSignal(priv->wakeUp);
// errlogPrintf("EL737: Setting threshold \n");
}
return 0;
}
static long el737_arm(scalerRecord *psr, int val)
{
EL737priv *priv;
priv = (EL737priv *)psr->dpvt;
priv->countCommand = val;
// errlogPrintf("EL737: Sending arm %d \n", val);
epicsEventSignal(priv->wakeUp);
return 0;
}
static long el737_done(scalerRecord *psr)
{
EL737priv *priv;
priv = (EL737priv *)psr->dpvt;
// errlogPrintf("EL737: Calling done with %d\n", psr->cnt);
if(priv->counting && psr->cnt == 0){
priv->countCommand = 0;
epicsEventSignal(priv->wakeUp);
}
if(priv->counting || priv->countCommand == 1){
return 1;
} else {
return 0;
}
}
static asynStatus el737_transactCommand(EL737priv *priv,char command[COMLEN],char reply[COMLEN])
{
asynStatus status;
size_t in, out;
int reason, dbStatus;
char myCommand[COMLEN], message[40];
pasynOctetSyncIO->flush(priv->asynContext);
// errlogPrintf("EL737: sending command %s\n", command);
strcpy(message,"");
status = pasynOctetSyncIO->writeRead(priv->asynContext, command, strlen(command),
reply,COMLEN, 1.,&out,&in,&reason);
if(status == asynSuccess){
if(strstr(reply,"?OF") != NULL){
strcpy(myCommand,"RMT 1");
status = pasynOctetSyncIO->writeRead(priv->asynContext, myCommand, strlen(myCommand),
reply,COMLEN, 1.,&out,&in,&reason);
strcpy(myCommand,"ECHO 2");
status = pasynOctetSyncIO->writeRead(priv->asynContext, myCommand, strlen(myCommand),
reply,COMLEN, 1.,&out,&in,&reason);
return pasynOctetSyncIO->writeRead(priv->asynContext, command, strlen(command),
reply,COMLEN, 1.,&out,&in,&reason);
} else {
if(strstr(reply,"?OV") != NULL){
strncpy(message,"Overflow",sizeof(message));
} else if(strstr(reply,"?1") != NULL) {
strncpy(message,"Par out of range",sizeof(message));
} else if(strstr(reply,"?2") != NULL){
strncpy(message,"Bad command",sizeof(message));
} else if(strstr(reply,"?3") != NULL){
strncpy(message,"Bad parameter",sizeof(message));
} else if(strstr(reply,"?4") != NULL){
strncpy(message,"Bad counter",sizeof(message));
} else if(strstr(reply,"?5") != NULL){
strncpy(message,"Parameter missing",sizeof(message));
} else if(strstr(reply,"?6") != NULL){
strncpy(message,"to many counts",sizeof(message));
} else if(strstr(reply,"?91") != NULL){
strncpy(message,"Start Failure",sizeof(message));
} else if(strstr(reply,"?92") != NULL){
strncpy(message,"Failure while counting",sizeof(message));
} else if(strstr(reply,"?93") != NULL){
strncpy(message,"Read Failure",sizeof(message));
} else {
if(strstr(reply,"?") != NULL) {
snprintf(message,sizeof(message),"HW error: %s", reply);
}
}
}
} else {
if(errno == EAGAIN){
errlogPrintf("Lost response to %s with EAGAIN\n", command);
} else {
snprintf(message,sizeof(message), "Lost communication with errno %d", errno);
/* force a reconnect */
pasynOctetSyncIO->disconnect(priv->asynContext);
}
}
if(priv->dbInit){
dbStatus = dbPutField(&priv->msgTxt, DBR_STRING,message, 1);
if(dbStatus!= 0){
errSymLookup(dbStatus,message,sizeof(message));
errlogPrintf("Setting external count message failed with %s\n", message);
}
}
return status;
}
static asynStatus sendStop(EL737priv *priv)
{
char command[COMLEN], reply[COMLEN];
// errlogPrintf("EL737: Sending stop\n");
strcpy(command,"S");
return el737_transactCommand(priv,command,reply);
}
static void runEvents(EL737priv *priv)
{
char command[COMLEN], reply[COMLEN], errName[256];
int status;
long dbStatus, nElements = 1, options = 0, threshCounter;
long unsigned int myThreshold;
/*
This is the better way to set the threshold rather then the old way below which uses
presets for that function. This one uses separate fields.
*/
if(priv->dbInit == 1 && priv->sendThreshold) {
dbStatus = dbGetField(&priv->threshold,DBR_LONG,&myThreshold,&options, &nElements,NULL);
if(dbStatus != 0){
errSymLookup(dbStatus,errName,sizeof(errName));
errlogPrintf("Reading threshold failed with %s\n", errName);
} else {
if(myThreshold != priv->thresholdValue){
/*
we have to set the threshold
*/
dbStatus = dbGetField(&priv->threshCounter,DBR_LONG,&threshCounter, &options, &nElements,NULL);
if(dbStatus != 0) {
errSymLookup(dbStatus,errName,sizeof(errName));
errlogPrintf("Reading thresholdCounter failed with %s\n", errName);
} else {
if(threshCounter == 0){
threshCounter = 1;
}
sprintf(command,"DL %d %d", (int)threshCounter,
(int)myThreshold);
// errlogPrintf("Sending threshold command %s\n", command);
status = el737_transactCommand(priv,command,reply);
sprintf(command,"DR %d", (int)threshCounter);
status = el737_transactCommand(priv, command,reply);
if(status == asynSuccess){
priv->sendThreshold = 0;
priv->thresholdValue = myThreshold;
}
}
}
}
}
if(priv->sendThreshold == 1) {
// fallback when we do not have the DB fields or threshCounter is invalid
if(priv->presets[THRESHMON] > 0) {
sprintf(command,"DL %d %d", (int)priv->presets[THRESHMON],
(int)priv->presets[THRESHVAL]);
// errlogPrintf("Sending threshold from presets, command %s\n", command);
status = el737_transactCommand(priv,command,reply);
sprintf(command,"DR %d", (int)priv->presets[THRESHMON]);
status = el737_transactCommand(priv, command,reply);
if(status == asynSuccess) {
priv->sendThreshold = 0;
}
}
}
errlogPrintf("EL737: Doing a count command: %d\n", priv->countCommand);
if(priv->countCommand == 1){
if(priv->presets[MODE] > 0) {
/* preset monitor */
sprintf(command,"MP %d", (int)priv->presets[MONITOR]);
// errlogPrintf("EL737: Starting preset monitor\n");
} else {
/* preset time */
sprintf(command,"TP %f", priv->presets[TIMER]/1000.);
// errlogPrintf("EL737: Starting preset timer\n");
}
status = el737_transactCommand(priv,command,reply);
priv->counting = 1;
} else {
if(priv->counting) {
/* Stop */
status = sendStop(priv);
} else {
status = asynSuccess;
}
}
if(status != asynSuccess){
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n, errno =%d",
priv->asynContext->errorMessage, errno);
}
}
static void updateValues(EL737priv *priv)
{
char command[COMLEN], reply[COMLEN];
int status;
float fTime;
long m1, m2 ,m3, m4, m5, m6 ,m7, m8;
strcpy(command,"RA");
status = el737_transactCommand(priv,command,reply);
if(status != asynSuccess){
if(errno == EAGAIN){
errlogPrintf("devScalerEL737::el737Thread lost response to %s with EAGAIN\n", command);
} else {
errlogPrintf("devScalerEL737::el737Thread communication problem %s, errno = %d\n",
priv->asynContext->errorMessage, errno);
}
return;
}
status = sscanf(reply, "%f %ld %ld %ld %ld %ld %ld %ld %ld",
&fTime, &m1, &m2, &m3, &m4, &m5, &m6, &m7, &m8);
if (status != 9) {
/*
old form with 4 monitors
*/
status = sscanf(reply, "%ld %ld %ld %ld %f", &m1, &m2, &m3, &m4, &fTime);
if (status != 5) {
errlogPrintf("devScalerEL737::el737Thread bad RA reply %s\n",
reply);
return;
}
}
priv->values[0] = (long)(fTime*1000);
priv->values[1] = m1;
priv->values[2] = m2;
priv->values[3] = m3;
priv->values[4] = m4;
priv->values[5] = m5;
priv->values[6] = m6;
priv->values[7] = m7;
priv->values[8] = m8;
}
static void el737Thread(void *param)
{
EL737priv *priv = (EL737priv *)param;
epicsEventWaitStatus evStatus;
double timeout = 60.;
char command[COMLEN], reply[COMLEN], errName[256];
asynStatus status;
int rs, ctStatus;
long dbStatus, options, nElements = 1, pauseFlag = 0;
// errlogPrintf("EL737: Within EL737 thread \n");
while(1){
evStatus = epicsEventWaitWithTimeout(priv->wakeUp,timeout);
// errlogPrintf("EL737 thread woke up with %d\n", evStatus);
if(evStatus == epicsEventWaitOK) {
/*
FEAR: we received a command!!!!
*/
runEvents(priv);
if(priv->counting == 1){
timeout = .2;
usleep(500);
}
} else {
/*
Just do a poll.....
*/
if(priv->counting && priv->countCommand == 0) {
/* Stop */
status = sendStop(priv);
}
if(priv->counting) {
strcpy(command,"RS");
status = el737_transactCommand(priv, command,reply);
if(status != asynSuccess){
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
priv->asynContext->errorMessage);
continue;
}
sscanf(reply,"%d",&rs);
//errlogPrintf("RS = %d\n", rs);
if(rs == 0) {
priv->counting = 0;
timeout = 60.;
priv->values[9] = 0;
ctStatus = 0;
} else if(rs == 1 || rs == 2){
/* counting */
priv->values[9] = 1;
ctStatus = 1;
} else if(rs == 5 || rs == 6){
/* low rate */
priv->values[9] = 2;
ctStatus = 2;
} else if(rs == 9 || rs == 13 || rs == 10 || rs == 14){
/* paused states */
priv->values[9] = 3;
ctStatus = 3;
}
}
if(priv->dbInit){
/*
update the external status field
*/
dbStatus = dbPutField(&priv->status, DBR_LONG,&ctStatus, 1);
if(dbStatus!= 0){
errSymLookup(dbStatus,errName,sizeof(errName));
errlogPrintf("Setting external count status failed with %s\n", errName);
}
/*
check and handle pause flag
*/
dbStatus = dbGetField(&priv->pause, DBR_LONG,&pauseFlag,&options, &nElements, NULL);
if(dbStatus!= 0){
errSymLookup(dbStatus,errName,sizeof(errName));
errlogPrintf("Reading pauseFlag failed with %s\n", errName);
}
/* errlogPrintf("Successfully read %ld pause flags as %d\n",nElements, pauseFlag); */
if(pauseFlag == 1 && ctStatus == 1){
strcpy(command,"PS");
status = el737_transactCommand(priv, command,reply);
if(status != asynSuccess){
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
priv->asynContext->errorMessage);
continue;
}
} else if(pauseFlag == 0 && ctStatus == 3){
strcpy(command,"CO");
status = el737_transactCommand(priv,command,reply);
if(status != asynSuccess){
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
priv->asynContext->errorMessage);
continue;
}
}
}
updateValues(priv);
if(rs == 0){
callbackRequest(priv->pcallback);
}
}
}
}