Reading DPICS works

This commit is contained in:
2014-10-24 10:42:23 +02:00
parent 68d3a16700
commit b2a63d59f0
2 changed files with 274 additions and 0 deletions

273
epicsadapter.c Normal file
View File

@ -0,0 +1,273 @@
/**
* This is a general purpose adapter between SICS and EPICS. It provides callbacks
* which allows Hipadaba nodes to be connected to EPICS PV's for both reading and
* writing.
*
* copyright: see file COPYRIGHT
*
* Mark Koennecke, October-November 2014
*/
#include <assert.h>
#include <sics.h>
#include <sicshipadaba.h>
#include <cadef.h>
#include <epicsMessageQueue.h>
#include <messagepipe.h>
/*
* we have a SICS tasks which polls EPICS regularly.
*/
static long epicsTaskID = -1L;
static pMP readPipe = NULL;
static epicsMessageQueueId writeQueue;
typedef struct {
char pvname[64];
int connected;
pHdb node;
chid pvchid;
chtype pvtype;
}EpicsPriv, *pEpicsPriv;
typedef struct {
SConnection *pCon;
char message[512];
} WriteMessage, *pWriteMessage;
/*======================================================================================
Code for reading PV's
======================================================================================*/
/*--------------------------------------------------------------------------------------*/
static int EpicsTask(void *userData)
{
ca_poll();
return 1;
}
/*---------------------------------------------------------------------------------------*/
static int makeEPICSContext(void *message, void *userData)
{
int status;
pEpicsPriv priv = NULL;
if(epicsTaskID < 0){
status = ca_context_create(ca_disable_preemptive_callback);
if(status != ECA_NORMAL){
priv = (pEpicsPriv)message;
SetHdbProperty(priv->node,"geterror", "Failed to create EPICS context");
return MPSTOP;
}
epicsTaskID = TaskRegisterN(pServ->pTasker,
"epics",
EpicsTask,
NULL,NULL,NULL,1
);
}
return MPCONTINUE;
}
/*--------------------------------------------------------------------------------------*/
static int epicsConnectPV(void *message, void *userData)
{
pEpicsPriv priv = NULL;
int status;
priv = (pEpicsPriv)message;
status = ca_create_channel(priv->pvname,NULL,NULL,10,&priv->pvchid);
if(status != ECA_NORMAL){
SetHdbProperty(priv->node,"geterror", "Failed to connect to PV");
return MPSTOP;
}
status = ca_pend_io(3.);
if(status != ECA_NORMAL){
SetHdbProperty(priv->node,"geterror", "Timeout connecting to PV");
return MPSTOP;
}
return MPCONTINUE;
}
/*--------------------------------------------------------------------------------------*/
static void epicsDataCallback(struct event_handler_args args)
{
pEpicsPriv priv = NULL;
hdbValue v;
priv = (pEpicsPriv)args.usr;
if(args.status == ECA_NORMAL){
switch(priv->node->value.dataType){
case HIPTEXT:
free(priv->node->value.v.text);
priv->node->value.v.text = strdup((char *)args.dbr);
break;
case HIPINT:
priv->node->value.v.intValue = *(int *)args.dbr;
break;
case HIPFLOAT:
priv->node->value.v.doubleValue = *(double *)args.dbr;
break;
case HIPINTAR:
case HIPINTVARAR:
v = MakeHdbIntArray(args.count,(int *)args.dbr);
copyHdbValue(&v,&priv->node->value);
break;
case HIPFLOATAR:
case HIPFLOATVARAR:
v = MakeHdbFloatArray(args.count,(double *)args.dbr);
copyHdbValue(&v,&priv->node->value);
break;
}
traceIO("epics","Received data for %s", priv->node->name);
NotifyHipadabaPar(priv->node,NULL);
}
}
/*--------------------------------------------------------------------------------------*/
static int epicsSubscribePV(void *message, void *userData)
{
pEpicsPriv priv = NULL;
int status;
chtype subType = DBR_STRING;
evid eid;
priv = (pEpicsPriv)message;
switch(priv->node->value.dataType){
case HIPTEXT:
subType = DBR_STRING;
break;
case HIPINT:
case HIPINTAR:
case HIPINTVARAR:
subType = DBR_LONG;
break;
case HIPFLOAT:
case HIPFLOATAR:
case HIPFLOATVARAR:
subType = DBR_DOUBLE;
break;
}
status = ca_create_subscription(subType,0,priv->pvchid,
DBE_VALUE,epicsDataCallback,priv,&eid);
if(status != ECA_NORMAL){
SetHdbProperty(priv->node,"geterror", "Failed to subscribe to PV");
return MPSTOP;
}
return MPCONTINUE;
}
/*--------------------------------------------------------------------------------------*/
static void createEPICSReadPipe()
{
readPipe = MakeMP();
AppendMPFilter(readPipe,makeEPICSContext,NULL,NULL);
AppendMPFilter(readPipe,epicsConnectPV,NULL,NULL);
AppendMPFilter(readPipe,epicsSubscribePV,NULL,NULL);
}
/*--------------------------------------------------------------------------------------*/
static void connectPV(pHdb node, pEpicsPriv priv)
{
int status;
SetHdbProperty(node,"geterror", NULL);
priv->node = node;
status = MPprocess(readPipe, priv);
if(status == MPCONTINUE){
priv->connected = 1;
}
}
/*--------------------------------------------------------------------------------------
This is the Hipadaba callback function
--------------------------------------------------------------------------------------*/
static hdbCallbackReturn EPICSReadCallback(pHdb currentNode,
void *userData,
pHdbMessage message)
{
pEpicsPriv priv = (pEpicsPriv)userData;
hdbDataMessage *mm = NULL;
SConnection *con = NULL;
char *geterror;
char error[256];
assert(priv != NULL);
mm = GetHdbGetMessage(message);
if (mm != NULL) {
con = mm->callData;
if(priv->connected != 1){
connectPV(currentNode, priv);
}
geterror = GetHdbProp(currentNode, "geterror");
if (geterror != NULL) {
snprintf(error,sizeof(error),"ERROR: %s", geterror);
SCWrite(con, error, eError);
if (mm->v->dataType == HIPTEXT) {
if (mm->v->v.text != NULL) {
free(mm->v->v.text);
}
mm->v->v.text = strdup(error);
}
return hdbAbort;
}
return hdbContinue;
}
return hdbContinue;
}
/*------------------------------------------------------------------------------------*/
static int EpicsConnectRead(pSICSOBJ ccmd, SConnection * con,
Hdb * cmdNode, Hdb * par[], int nPar)
{
pHdb node = NULL;
pEpicsPriv priv = NULL;
if(nPar < 2){
SCWrite(con,"ERROR: need node and PV-name arguments to connectread", eError);
return 0;
}
node = FindHdbNode(NULL,par[0]->value.v.text,con);
if(node == NULL){
SCPrintf(con,eError,"ERROR: failed to locate node %s", par[0]->value.v.text);
return 0;
}
priv = calloc(1,sizeof(EpicsPriv));
priv->node = node;
strncpy(priv->pvname,par[1]->value.v.text,sizeof(priv->pvname));
SetHdbProperty(node,"readpv", par[1]->value.v.text);
AppendHipadabaCallback(node,MakeHipadabaCallback(EPICSReadCallback, priv,free));
SCSendOK(con);
return 1;
}
/*==============================================================================================
Writing Things. Writing can block, thus it has to run in its own thread. This raises the
question is how to propagate error messages. The solution is a EPICS message queue to which
writing threads post. The epics task will read this queue and do the actual printing in the
SICS main thread. A convention: NULL means to print to trace.
================================================================================================*/
/*==============================================================================================
SICS Hydraulics
================================================================================================*/
int MakeEpicsAdapter(SConnection * con, SicsInterp * sics,
void *object, int argc, char *argv[])
{
pSICSOBJ self = NULL;
pHdb child, par;
self = MakeSICSOBJv("epicsadapter", "EpicsAdapter", HIPNONE, usMugger);
createEPICSReadPipe();
child = AddSICSHdbPar(self->objectNode,
"connectread", usMugger, MakeSICSFunc(EpicsConnectRead));
AddSICSHdbPar(child, "node", usMugger, MakeHdbText(""));
AddSICSHdbPar(child, "pvname", usMugger, MakeHdbText(""));
AddCommand(pServ->pSics, "epicsadapter", InterInvokeSICSOBJ, KillSICSOBJ, self);
return 1;
}

1
psi.c
View File

@ -130,6 +130,7 @@ static void AddPsiCommands(SicsInterp * pInter)
SCMD("MakeEiger", InitEiger); SCMD("MakeEiger", InitEiger);
SCMD("MakeEigerMono", InitEigerMono); SCMD("MakeEigerMono", InitEigerMono);
PCMD("cnvrt", CnvrtAction); PCMD("cnvrt", CnvrtAction);
SCMD("MakeEpicsAdapter", MakeEpicsAdapter);
/* /*
* Tcl 8.5 has implemented the clock command in tcl rather then C. * Tcl 8.5 has implemented the clock command in tcl rather then C.