First working version of the epics SICS adapter
This commit is contained in:
189
epicsadapter.c
189
epicsadapter.c
@ -5,13 +5,14 @@
|
||||
*
|
||||
* copyright: see file COPYRIGHT
|
||||
*
|
||||
* Mark Koennecke, October-November 2014
|
||||
* Mark Koennecke, October 2014
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <sics.h>
|
||||
#include <sicshipadaba.h>
|
||||
#include <cadef.h>
|
||||
#include <epicsMessageQueue.h>
|
||||
#include <epicsThread.h>
|
||||
#include <messagepipe.h>
|
||||
|
||||
/*
|
||||
@ -38,10 +39,26 @@ typedef struct {
|
||||
/*======================================================================================
|
||||
Code for reading PV's
|
||||
======================================================================================*/
|
||||
/*--------------------------------------------------------------------------------------*/
|
||||
static int EpicsTask(void *userData)
|
||||
{
|
||||
WriteMessage mes;
|
||||
|
||||
/*
|
||||
drive the main EPICS loop for subscriptions
|
||||
*/
|
||||
ca_poll();
|
||||
|
||||
/*
|
||||
process possible messages from the writing threads
|
||||
*/
|
||||
if(epicsMessageQueueTryReceive(writeQueue,&mes,sizeof(mes)) > 0){
|
||||
if(mes.pCon == NULL){
|
||||
traceIO("epics",mes.message);
|
||||
} else {
|
||||
SCWrite(mes.pCon,mes.message,eError);
|
||||
SCDeleteConnection(mes.pCon);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/*---------------------------------------------------------------------------------------*/
|
||||
@ -90,6 +107,7 @@ static void epicsDataCallback(struct event_handler_args args)
|
||||
{
|
||||
pEpicsPriv priv = NULL;
|
||||
hdbValue v;
|
||||
char error[256];
|
||||
|
||||
priv = (pEpicsPriv)args.usr;
|
||||
|
||||
@ -116,8 +134,13 @@ static void epicsDataCallback(struct event_handler_args args)
|
||||
copyHdbValue(&v,&priv->node->value);
|
||||
break;
|
||||
}
|
||||
SetHdbProperty(priv->node,"geterror",NULL);
|
||||
traceIO("epics","Received data for %s", priv->node->name);
|
||||
NotifyHipadabaPar(priv->node,NULL);
|
||||
} else {
|
||||
snprintf(error,sizeof(error),"ERROR: %s for node %s", ca_message(args.status), priv->node->name);
|
||||
traceIO("epics",error);
|
||||
SetHdbProperty(priv->node,"geterror",error);
|
||||
}
|
||||
}
|
||||
/*--------------------------------------------------------------------------------------*/
|
||||
@ -146,7 +169,7 @@ static int epicsSubscribePV(void *message, void *userData)
|
||||
}
|
||||
|
||||
status = ca_create_subscription(subType,0,priv->pvchid,
|
||||
DBE_VALUE,epicsDataCallback,priv,&eid);
|
||||
DBE_VALUE|DBE_ALARM,epicsDataCallback,priv,&eid);
|
||||
if(status != ECA_NORMAL){
|
||||
SetHdbProperty(priv->node,"geterror", "Failed to subscribe to PV");
|
||||
return MPSTOP;
|
||||
@ -187,6 +210,7 @@ static void connectPV(pHdb node, pEpicsPriv priv)
|
||||
SConnection *con = NULL;
|
||||
char *geterror;
|
||||
char error[256];
|
||||
enum channel_state cs;
|
||||
|
||||
assert(priv != NULL);
|
||||
|
||||
@ -197,6 +221,11 @@ static void connectPV(pHdb node, pEpicsPriv priv)
|
||||
if(priv->connected != 1){
|
||||
connectPV(currentNode, priv);
|
||||
}
|
||||
cs = ca_state(priv->pvchid);
|
||||
if(cs != cs_conn){
|
||||
SCWrite(con,"ERROR: epics disconnected", eError);
|
||||
return hdbAbort;
|
||||
}
|
||||
geterror = GetHdbProp(currentNode, "geterror");
|
||||
if (geterror != NULL) {
|
||||
snprintf(error,sizeof(error),"ERROR: %s", geterror);
|
||||
@ -244,11 +273,157 @@ static void connectPV(pHdb node, pEpicsPriv priv)
|
||||
}
|
||||
/*==============================================================================================
|
||||
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
|
||||
question 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.
|
||||
================================================================================================*/
|
||||
typedef struct {
|
||||
SConnection *pCon;
|
||||
hdbValue v;
|
||||
char pvName[64];
|
||||
} WritePar, *pWritePar;
|
||||
/*----------------------------------------------------------------------------------------------*/
|
||||
static void writeEpicsMessage(void *target, char *txt)
|
||||
{
|
||||
pWriteMessage wm = NULL;
|
||||
|
||||
wm = calloc(1,sizeof(WriteMessage));
|
||||
if(wm != NULL){
|
||||
if(target != NULL){
|
||||
wm->pCon = SCCopyConnection(target);
|
||||
}
|
||||
strncpy(wm->message,txt,sizeof(wm->message));
|
||||
epicsMessageQueueSend(writeQueue,wm,sizeof(WriteMessage));
|
||||
}
|
||||
}
|
||||
/*----------------------------------------------------------------------------------------------*/
|
||||
static void epicsEndCallback(struct event_handler_args args)
|
||||
{
|
||||
char message[512];
|
||||
snprintf(message,sizeof(message),"%s finished with %s", (char *)args.usr,
|
||||
ca_message(args.status));
|
||||
writeEpicsMessage(NULL,message);
|
||||
free(args.usr);
|
||||
}
|
||||
/*----------------------------------------------------------------------------------------------*/
|
||||
static void EpicsWriteFunc(void *param)
|
||||
{
|
||||
pWritePar wp = (pWritePar)param;
|
||||
pWriteMessage wm = NULL;
|
||||
int status;
|
||||
chid cid;
|
||||
char error[512];
|
||||
char *pv;
|
||||
|
||||
status = ca_context_create(ca_disable_preemptive_callback);
|
||||
if(status != ECA_NORMAL){
|
||||
writeEpicsMessage(wp->pCon,"ERROR: failed to create EPICS context for write");
|
||||
goto cleanup;
|
||||
}
|
||||
status = ca_create_channel(wp->pvName,NULL,NULL,10,&cid);
|
||||
if(status != ECA_NORMAL){
|
||||
snprintf(error,sizeof(error),"ERROR: failed to create EPICS channel for %s", wp->pvName);
|
||||
writeEpicsMessage(wp->pCon,error);
|
||||
goto cleanup;
|
||||
}
|
||||
status = ca_pend_io(5.);
|
||||
if(status != ECA_NORMAL){
|
||||
snprintf(error,sizeof(error),"ERROR: failed to connect EPICS channel for %s", wp->pvName);
|
||||
writeEpicsMessage(wp->pCon,error);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
pv = strdup(wp->pvName);
|
||||
switch(wp->v.dataType){
|
||||
case HIPINT:
|
||||
status = ca_put_callback(DBR_LONG,cid,&wp->v.v.intValue,epicsEndCallback,pv);
|
||||
break;
|
||||
case HIPFLOAT:
|
||||
status = ca_put_callback(DBR_DOUBLE,cid,&wp->v.v.doubleValue,epicsEndCallback,pv);
|
||||
break;
|
||||
case HIPTEXT:
|
||||
status = ca_put_callback(DBR_STRING,cid,wp->v.v.text,epicsEndCallback, pv);
|
||||
break;
|
||||
case HIPINTVARAR:
|
||||
case HIPINTAR:
|
||||
status = ca_array_put_callback(DBR_LONG,wp->v.arrayLength,
|
||||
cid, wp->v.v.intArray,epicsEndCallback,pv);
|
||||
break;
|
||||
case HIPFLOATVARAR:
|
||||
case HIPFLOATAR:
|
||||
status = ca_array_put_callback(DBR_DOUBLE,wp->v.arrayLength,
|
||||
cid, wp->v.v.floatArray,epicsEndCallback,pv);
|
||||
break;
|
||||
}
|
||||
if(status != ECA_NORMAL){
|
||||
snprintf(error,sizeof(error),"ERROR: failed to write to EPICS channel for %s with %d", wp->pvName, status);
|
||||
writeEpicsMessage(wp->pCon,error);
|
||||
goto cleanup;
|
||||
}
|
||||
writeEpicsMessage(wp->pCon,"OK");
|
||||
|
||||
ca_pend_io(0);
|
||||
|
||||
goto cleanup;
|
||||
cleanup:
|
||||
SCDeleteConnection(wp->pCon);
|
||||
ReleaseHdbValue(&wp->v);
|
||||
free(wp);
|
||||
}
|
||||
/*----------------------------------------------------------------------------------------------*/
|
||||
static hdbCallbackReturn EPICSWriteCallback(pHdb currentNode,
|
||||
void *userData,
|
||||
pHdbMessage message)
|
||||
{
|
||||
hdbDataMessage *mm = NULL;
|
||||
pWritePar par = NULL;
|
||||
|
||||
mm = GetHdbSetMessage(message);
|
||||
if(mm != NULL){
|
||||
par = calloc(1,sizeof(WritePar));
|
||||
if(par == NULL){
|
||||
SCWrite(mm->callData,"ERROR: out of memory in EPICSWriteCallback", eError);
|
||||
return hdbAbort;
|
||||
}
|
||||
par->pCon = SCCopyConnection(mm->callData);
|
||||
cloneHdbValue(mm->v,&par->v);
|
||||
strncpy(par->pvName,(char *)userData,sizeof(par->pvName));
|
||||
epicsThreadCreate("Write",
|
||||
epicsThreadPriorityHigh,
|
||||
epicsThreadStackMedium,
|
||||
EpicsWriteFunc,
|
||||
par);
|
||||
}
|
||||
|
||||
return hdbContinue;
|
||||
}
|
||||
/*------------------------------------------------------------------------------------*/
|
||||
static int EpicsConnectWrite(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;
|
||||
}
|
||||
|
||||
SetHdbProperty(node,"writepv", par[1]->value.v.text);
|
||||
RemoveSetUpdateCallback(node);
|
||||
AppendHipadabaCallback(node,MakeHipadabaCallback(EPICSWriteCallback,
|
||||
strdup(par[1]->value.v.text),free));
|
||||
|
||||
SCSendOK(con);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*==============================================================================================
|
||||
SICS Hydraulics
|
||||
@ -262,12 +437,18 @@ int MakeEpicsAdapter(SConnection * con, SicsInterp * sics,
|
||||
|
||||
self = MakeSICSOBJv("epicsadapter", "EpicsAdapter", HIPNONE, usMugger);
|
||||
createEPICSReadPipe();
|
||||
writeQueue = epicsMessageQueueCreate(64,sizeof(WriteMessage));
|
||||
|
||||
child = AddSICSHdbPar(self->objectNode,
|
||||
"connectread", usMugger, MakeSICSFunc(EpicsConnectRead));
|
||||
AddSICSHdbPar(child, "node", usMugger, MakeHdbText(""));
|
||||
AddSICSHdbPar(child, "pvname", usMugger, MakeHdbText(""));
|
||||
|
||||
child = AddSICSHdbPar(self->objectNode,
|
||||
"connectwrite", usMugger, MakeSICSFunc(EpicsConnectWrite));
|
||||
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