Reading DPICS works
This commit is contained in:
273
epicsadapter.c
Normal file
273
epicsadapter.c
Normal 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;
|
||||
}
|
Reference in New Issue
Block a user