Compare commits

...

10 Commits

Author SHA1 Message Date
Michael Davidsaver
46e74328dc minor 2016-03-24 14:41:43 -04:00
Michael Davidsaver
cb35889a20 dbCa: make private all functions which assume CA_LINK is really dbCa 2016-03-24 14:41:43 -04:00
Michael Davidsaver
29afb88006 add broken lset 2016-03-24 14:41:43 -04:00
Michael Davidsaver
bd8977c51d more stats 2016-03-24 14:41:43 -04:00
Michael Davidsaver
32c3231f5d link: not pv_link backend type
informational, and a hint at what pvt points to
2016-03-24 14:41:43 -04:00
Michael Davidsaver
33033e6027 more close CA_LINK 2016-03-24 14:41:43 -04:00
Michael Davidsaver
905c5e3336 lset version and report 2016-03-24 14:41:43 -04:00
Michael Davidsaver
47b3c68af1 dbLink: dbAddLinkHook hack
Add a hook for outside code the hijack CA_LINKs
2016-03-24 14:41:43 -04:00
Michael Davidsaver
4b3bdf31c2 iocInit: close CA_LINKs through lset 2016-03-24 14:41:43 -04:00
Michael Davidsaver
3740082b61 iocInit: announce "init" hooks during iocShutdown 2016-03-24 14:41:43 -04:00
19 changed files with 257 additions and 58 deletions

View File

@@ -314,6 +314,7 @@ void dbCaAddLinkCallback(struct link *plink,
plink->lset = &dbCa_lset;
plink->type = CA_LINK;
plink->value.pv_link.pvt = pca;
plink->value.pv_link.backend = "ca";
addAction(pca, CA_CONNECT);
epicsMutexUnlock(pca->lock);
}
@@ -332,6 +333,7 @@ void dbCaRemoveLink(struct dbLocker *locker, struct link *plink)
epicsMutexMustLock(pca->lock);
pca->plink = 0;
plink->value.pv_link.pvt = 0;
plink->value.pv_link.backend = NULL;
plink->value.pv_link.pvlMask = 0;
plink->type = PV_LINK;
plink->lset = NULL;
@@ -709,6 +711,8 @@ static void scanLinkOnce(dbCommon *prec, caLink *pca) {
}
static lset dbCa_lset = {
LSET_API_VERSION,
dbCaReportLink,
dbCaRemoveLink,
isConnected,
getDBFtype, getElements,

View File

@@ -20,6 +20,8 @@ extern "C" {
typedef void (*dbCaCallback)(void *userPvt);
epicsShareFunc void dbCaCallbackProcess(void *usrPvt);
#ifdef EPICS_DBCA_PRIVATE_API
epicsShareFunc void dbCaLinkInit(void); /* internal initialization for iocBuild() */
epicsShareFunc void dbCaLinkInitIsolated(void); /* internal initialization for iocBuildIsolated() */
epicsShareFunc void dbCaRun(void);
@@ -47,8 +49,10 @@ epicsShareFunc long dbCaPutLink(struct link *plink,short dbrType,
extern struct ca_client_context * dbCaClientContext;
#ifdef EPICS_DBCA_PRIVATE_API
epicsShareFunc void dbCaSync(void);
epicsShareFunc void dbCaReportLink(const struct link *plink, dbLinkReportInfo *pinfo);
epicsShareExtern void (*dbAddLinkHook)(struct link *link, short dbfType);
epicsShareFunc void dbSetBrokenLink(struct link *link, short dbfType);
#endif
/* These macros are for backwards compatibility */

View File

@@ -20,6 +20,7 @@
#include <string.h>
#include <errno.h>
#define EPICS_DBCA_PRIVATE_API
#include "dbDefs.h"
#include "epicsEvent.h"
#include "epicsPrint.h"
@@ -52,7 +53,61 @@
#include "dbLock.h"
#include "link.h"
void dbCaReportLink(const struct link *plink, dbLinkReportInfo *pinfo)
{
caLink * const pca = (caLink *)plink->value.pv_link.pvt;
const char * fname = dbGetFieldName(pinfo->pentry),
* rname = dbGetRecordName(pinfo->pentry);
assert(pca);
epicsMutexLock(pca->lock);
assert(pca->plink==plink);
pinfo->connected = ca_field_type(pca->chid) != TYPENOTCONN;
pinfo->nWriteFail = pca->nNoWrite;
pinfo->nDisconnect = pca->nDisconnect;
if (pinfo->connected) {
pinfo->readable = ca_read_access(pca->chid);
pinfo->writable = ca_write_access(pca->chid);
if (pinfo->filter==dbLinkReportAll || pinfo->filter==dbLinkReportConnected) {
int rw = pinfo->readable |
pinfo->writable << 1;
static const char *rights[4] = {
"No Access", "Read Only",
"Write Only", "Read/Write"
};
int mask = plink->value.pv_link.pvlMask;
printf(LSET_REPORT_INDENT "%28s.%-4s ==> %-28s (%lu, %lu)\n",
rname,
fname,
plink->value.pv_link.pvname,
pca->nDisconnect,
pca->nNoWrite);
printf(LSET_REPORT_INDENT "%21s [%s%s%s%s] host %s, %s\n", "",
mask & pvlOptInpNative ? "IN" : " ",
mask & pvlOptInpString ? "IS" : " ",
mask & pvlOptOutNative ? "ON" : " ",
mask & pvlOptOutString ? "OS" : " ",
ca_host_name(pca->chid),
rights[rw]);
}
} else {
if (pinfo->filter==dbLinkReportAll || pinfo->filter==dbLinkReportDisconnected) {
printf(LSET_REPORT_INDENT "%28s.%-4s --> %-28s (%lu, %lu)\n",
rname,
fname,
plink->value.pv_link.pvname,
pca->nDisconnect,
pca->nNoWrite);
}
}
epicsMutexUnlock(pca->lock);
}
long dbcar(char *precordname, int level)
{
DBENTRY dbentry;
@@ -68,7 +123,7 @@ long dbcar(char *precordname, int level)
int noWriteAccess=0;
unsigned long nDisconnect=0;
unsigned long nNoWrite=0;
caLink *pca;
int j;
if (!precordname || precordname[0] == '\0' || !strcmp(precordname, "*")) {
@@ -90,50 +145,26 @@ long dbcar(char *precordname, int level)
dbScanLock(precord);
for (j=0; j<pdbRecordType->no_links; j++) {
pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[j]];
pdbentry->pflddes = pdbFldDes;
plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
if (plink->type == CA_LINK) {
ncalinks++;
pca = (caLink *)plink->value.pv_link.pvt;
if (pca
&& pca->chid
&& (ca_field_type(pca->chid) != TYPENOTCONN)) {
nconnected++;
nDisconnect += pca->nDisconnect;
nNoWrite += pca->nNoWrite;
if (!ca_read_access(pca->chid)) noReadAccess++;
if (!ca_write_access(pca->chid)) noWriteAccess++;
if (level>1) {
int rw = ca_read_access(pca->chid) |
ca_write_access(pca->chid) << 1;
static const char *rights[4] = {
"No Access", "Read Only",
"Write Only", "Read/Write"
};
int mask = plink->value.pv_link.pvlMask;
printf("%28s.%-4s ==> %-28s (%lu, %lu)\n",
precord->name,
pdbFldDes->name,
plink->value.pv_link.pvname,
pca->nDisconnect,
pca->nNoWrite);
printf("%21s [%s%s%s%s] host %s, %s\n", "",
mask & pvlOptInpNative ? "IN" : " ",
mask & pvlOptInpString ? "IS" : " ",
mask & pvlOptOutNative ? "ON" : " ",
mask & pvlOptOutString ? "OS" : " ",
ca_host_name(pca->chid),
rights[rw]);
}
} else {
if (level>0) {
printf("%28s.%-4s --> %-28s (%lu, %lu)\n",
precord->name,
pdbFldDes->name,
plink->value.pv_link.pvname,
pca->nDisconnect,
pca->nNoWrite);
}
}
dbLinkReportInfo linfo;
memset(&linfo, 0, sizeof(linfo));
linfo.pentry = pdbentry;
if(level==0)
linfo.filter = dbLinkReportNone;
else if(level==1)
linfo.filter = dbLinkReportDisconnected;
else
linfo.filter = dbLinkReportAll;
if(level>2)
linfo.detailLevel = level-2;
dbReportLink(plink, &linfo);
nconnected += linfo.connected;
nDisconnect += linfo.nDisconnect;
noReadAccess += !linfo.readable;
noWriteAccess += !linfo.writable;
}
}
dbScanUnlock(precord);
@@ -190,7 +221,7 @@ void dbcaStats(int *pchans, int *pdiscon)
plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
if (plink->type == CA_LINK) {
ncalinks++;
if (dbCaIsLinkConnected(plink)) {
if (dbIsLinkConnected(plink)) {
nconnected++;
}
}

View File

@@ -18,6 +18,7 @@
#include <stdio.h>
#include <string.h>
#define EPICS_DBCA_PRIVATE_API
#include "alarm.h"
#include "cantProceed.h"
#include "cvtFast.h"
@@ -139,6 +140,8 @@ static long dbConstGetValue(struct link *plink, short dbrType, void *pbuffer,
}
static lset dbConst_lset = {
LSET_API_VERSION,
NULL,
NULL,
NULL,
NULL, dbConstGetNelements,
@@ -170,6 +173,7 @@ static long dbDbInitLink(struct link *plink, short dbfType)
pdbAddr = dbCalloc(1, sizeof(struct dbAddr));
*pdbAddr = dbaddr; /* structure copy */
plink->value.pv_link.pvt = pdbAddr;
plink->value.pv_link.backend = "db";
ellAdd(&dbaddr.precord->bklnk, &plink->value.pv_link.backlinknode);
/* merging into the same lockset is deferred to the caller.
* cf. initPVLinks()
@@ -184,6 +188,7 @@ static void dbDbAddLink(dbLocker *locker, struct link *plink, short dbfType, DBA
plink->lset = &dbDb_lset;
plink->type = DB_LINK;
plink->value.pv_link.pvt = ptarget;
plink->value.pv_link.backend = "db";
ellAdd(&ptarget->precord->bklnk, &plink->value.pv_link.backlinknode);
/* target record is already locked in dbPutFieldLink() */
@@ -194,6 +199,7 @@ static void dbDbRemoveLink(dbLocker *locker, struct link *plink)
{
DBADDR *pdbAddr = (DBADDR *) plink->value.pv_link.pvt;
plink->value.pv_link.pvt = 0;
plink->value.pv_link.backend = NULL;
plink->value.pv_link.getCvt = 0;
plink->value.pv_link.pvlMask = 0;
plink->value.pv_link.lastGetdbrType = 0;
@@ -431,6 +437,8 @@ static void dbDbScanFwdLink(struct link *plink)
}
static lset dbDb_lset = {
LSET_API_VERSION,
NULL,
dbDbRemoveLink,
dbDbIsConnected,
dbDbGetDBFtype, dbDbGetElements,
@@ -442,8 +450,87 @@ static lset dbDb_lset = {
dbDbScanFwdLink
};
static void dbBrokenReportLink(const struct link *plink, dbLinkReportInfo *pinfo)
{
const char * fname = dbGetFieldName(pinfo->pentry),
* rname = dbGetRecordName(pinfo->pentry);
if (pinfo->filter==dbLinkReportAll || pinfo->filter==dbLinkReportDisconnected) {
printf("%28s.%-4s --> %-28s <invalid link type>\n",
rname,
fname,
plink->value.pv_link.pvname);
}
}
static void dbBrokenRemoveLink(dbLocker *locker, struct link *plink)
{
assert(!plink->value.pv_link.pvt);
plink->value.pv_link.pvt = 0;
plink->value.pv_link.backend = NULL;
plink->value.pv_link.getCvt = 0;
plink->value.pv_link.pvlMask = 0;
plink->value.pv_link.lastGetdbrType = 0;
plink->type = PV_LINK;
plink->lset = NULL;
}
static lset broken_lset = {
LSET_API_VERSION,
dbBrokenReportLink,
dbBrokenRemoveLink
};
/***************************** Generic Link API *****************************/
void dbSetBrokenLink(struct link *plink, short dbfType)
{
plink->lset = &broken_lset;
plink->type = CA_LINK;
plink->value.pv_link.pvt = NULL;
plink->value.pv_link.backend = "invalid";
}
static
void dbCaAddLinkHook(struct link *plink, short dbfType)
{
dbCaAddLink(NULL, plink, dbfType);
}
void (*dbAddLinkHook)(struct link *link, short dbfType) = &dbCaAddLinkHook;
/* initialize CA_LINK with possibly custom lset */
static
void customlset(struct link *plink, short dbfType)
{
int oops = 0;
plink->lset = NULL;
if(dbAddLinkHook)
(*dbAddLinkHook)(plink, dbfType);
if((plink->lset==NULL) ^ (plink->type==PV_LINK)) {
oops = 1;
errlogPrintf("custom link types must set both type and lset.\n");
}
if(plink->lset && plink->lset->version!=LSET_API_VERSION) {
oops = 1;
errlogPrintf("custom link types must set .version to LSET_API_VERSION (%u) not %u\n",
LSET_API_VERSION, plink->lset->version);
}
if(oops)
{
plink->lset = NULL;
plink->type = PV_LINK;
plink->value.pv_link.pvt = NULL; /* leaking */
}
if(!plink->lset)
dbSetBrokenLink(plink, dbfType); /* install "invalid" lset as fallback */
assert(plink->lset);
assert(plink->type==CA_LINK);
assert(plink->lset->version==LSET_API_VERSION);
}
void dbInitLink(struct link *plink, short dbfType)
{
struct dbCommon *precord = plink->precord;
@@ -469,7 +556,7 @@ void dbInitLink(struct link *plink, short dbfType)
if (dbfType == DBF_INLINK)
plink->value.pv_link.pvlMask |= pvlOptInpNative;
dbCaAddLink(NULL, plink, dbfType);
customlset(plink, dbfType);
if (dbfType == DBF_FWDLINK) {
char *pperiod = strrchr(plink->value.pv_link.pvname, '.');
@@ -508,7 +595,7 @@ void dbAddLink(dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptar
if (dbfType == DBF_INLINK)
plink->value.pv_link.pvlMask |= pvlOptInpNative;
dbCaAddLink(locker, plink, dbfType);
customlset(plink, dbfType);
if (dbfType == DBF_FWDLINK) {
char *pperiod = strrchr(plink->value.pv_link.pvname, '.');
@@ -537,6 +624,15 @@ void dbRemoveLink(dbLocker *locker, struct link *plink)
}
}
void dbReportLink(const struct link *plink, dbLinkReportInfo *pinfo)
{
lset *plset = plink->lset;
if (plset && plset->reportLink) {
plset->reportLink(plink, pinfo);
}
}
int dbIsLinkConnected(const struct link *plink)
{
lset *plset = plink->lset;

View File

@@ -26,8 +26,37 @@ extern "C" {
#endif
struct dbLocker;
struct DBENTRY;
#define LSET_API_VERSION 1
#define LSET_REPORT_INDENT ""
typedef enum {
dbLinkReportNone,
dbLinkReportAll,
dbLinkReportConnected,
dbLinkReportDisconnected,
} dbLinkReportFilter;
typedef struct {
/* from caller */
dbLinkReportFilter filter;
int detailLevel;
struct DBENTRY *pentry;
unsigned clearstats:1; /* after reporting, zero stat counters */
/* callee fills in current state */
unsigned connected:1; /* is this link connected to it's underlying data source */
unsigned readable:1; /* would a dbGetLink() succeed at this moment */
unsigned writable:1; /* would a dbPutLink() succeed at this moment */
/* callee fills in statistics */
unsigned nDisconnect; /* number of times this link has entered a not connected state */
unsigned nEvents; /* number of times new data has been received from the underlying data source */
unsigned nWriteFail; /* number of times dbPutLink() has failed for this link */
} dbLinkReportInfo;
typedef struct lset {
unsigned version; /* must be set to LSET_API_VERSION */
void (*reportLink)(const struct link *plink, dbLinkReportInfo *pinfo);
void (*removeLink)(struct dbLocker *locker, struct link *plink);
int (*isConnected)(const struct link *plink);
int (*getDBFtype)(const struct link *plink);
@@ -58,6 +87,8 @@ epicsShareFunc long dbLoadLink(struct link *plink, short dbrType,
void *pbuffer);
epicsShareFunc void dbRemoveLink(struct dbLocker *locker, struct link *plink);
epicsShareFunc void dbReportLink(const struct link *plink, dbLinkReportInfo *pinfo);
epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements);
epicsShareFunc int dbIsLinkConnected(const struct link *plink);
epicsShareFunc int dbGetLinkDBFtype(const struct link *plink);

View File

@@ -48,6 +48,14 @@ typedef enum {
initHookAfterDatabasePaused,
initHookAfterIocPaused, /* End of iocPause command */
initHookAtIocShutdown, /* Start of iocShutdown */
initHookAfterCaLinkClose,
initHookAfterScanShutdown,
initHookAfterCallbackShutdown,
initHookAfterCaServerStopped,
initHookAfterDatabaseStopped,
initHookAfterIocShutdown, /* End of iocShutdown */
/* Deprecated states, provided for backwards compatibility.
* These states are announced at the same point they were before,
* but will not be repeated if the IOC gets paused and restarted.

View File

@@ -13,6 +13,7 @@
#include <string.h>
#include <math.h>
#define EPICS_DBCA_PRIVATE_API
#include "epicsString.h"
#include "dbUnitTest.h"
#include "epicsThread.h"

View File

@@ -47,7 +47,7 @@ extern "C" {
typedef dbBase DBBASE;
typedef struct{
typedef struct DBENTRY {
DBBASE *pdbbase;
dbRecordType *precordType;
dbFldDes *pflddes;

View File

@@ -85,6 +85,7 @@ struct pv_link {
LINKCVT getCvt; /* input conversion function */
short pvlMask; /* Options mask */
short lastGetdbrType; /* last dbrType for DB or CA get */
const char *backend;/* informational string describing the backend */
};
/* structure of a VME io channel */

View File

@@ -23,6 +23,7 @@
#include <errno.h>
#include <limits.h>
#define EPICS_DBCA_PRIVATE_API
#include "dbDefs.h"
#include "ellLib.h"
#include "envDefs.h"
@@ -641,7 +642,7 @@ static void doCloseLinks(dbRecordType *pdbRecordType, dbCommon *precord,
dbScanLock(precord);
locked = 1;
}
dbCaRemoveLink(NULL, plink);
dbRemoveLink(NULL, plink);
} else if (iocBuildMode==buildIsolated && plink->type == DB_LINK) {
/* free link, but don't split lockset like dbDbRemoveLink() */
@@ -688,13 +689,20 @@ static void doFreeRecord(dbRecordType *pdbRecordType, dbCommon *precord,
int iocShutdown(void)
{
if (iocState == iocVirgin || iocState == iocStopped) return 0;
initHookAnnounce(initHookAtIocShutdown); // TODO: iterate hooks in reverse
iterateRecords(doCloseLinks, NULL);
initHookAnnounce(initHookAfterCaLinkClose);
if (iocBuildMode==buildIsolated) {
/* stop and "join" threads */
scanStop();
initHookAnnounce(initHookAfterScanShutdown);
callbackStop();
initHookAnnounce(initHookAfterCallbackShutdown);
}
dbCaShutdown(); /* must be before dbFreeRecord and dbChannelExit */
initHookAnnounce(initHookAfterCaServerStopped);
/* placeholder, RSRV will eventually stop here */
initHookAnnounce(initHookAfterDatabaseStopped);
if (iocBuildMode==buildIsolated) {
/* free resources */
scanCleanup();
@@ -708,6 +716,7 @@ int iocShutdown(void)
}
iocState = iocStopped;
iocBuildMode = buildRSRV;
initHookAnnounce(initHookAfterIocShutdown);
return 0;
}

View File

@@ -17,6 +17,7 @@
#include <stdio.h>
#include <string.h>
#define EPICS_DBCA_PRIVATE_API
#include "alarm.h"
#include "dbDefs.h"
#include "dbAccess.h"
@@ -55,7 +56,7 @@ static long write_ao(aoRecord *prec)
long status;
if(prec->pact) return(0);
if(plink->type!=CA_LINK) {
if(plink->type!=CA_LINK || strcmp(plink->value.pv_link.backend,"ca")!=0) {
status = dbPutLink(plink,DBR_DOUBLE,&prec->oval,1);
return(status);
}

View File

@@ -19,6 +19,7 @@
#include <stdio.h>
#include <string.h>
#define EPICS_DBCA_PRIVATE_API
#include "alarm.h"
#include "dbDefs.h"
#include "dbLock.h"
@@ -55,7 +56,7 @@ static long write_bo(boRecord *prec)
long status;
if(prec->pact) return(0);
if(plink->type!=CA_LINK) {
if(plink->type!=CA_LINK || strcmp(plink->value.pv_link.backend,"ca")!=0) {
status = dbPutLink(plink,DBR_USHORT,&prec->val,1);
return(status);
}

View File

@@ -17,6 +17,7 @@
#include <stdio.h>
#include <string.h>
#define EPICS_DBCA_PRIVATE_API
#include "alarm.h"
#include "dbDefs.h"
#include "dbAccess.h"
@@ -49,7 +50,7 @@ static long write_calcout(calcoutRecord *prec)
long status;
if (prec->pact) return 0;
if (plink->type != CA_LINK) {
if (plink->type != CA_LINK || strcmp(plink->value.pv_link.backend,"ca")!=0) {
status = dbPutLink(plink, DBR_DOUBLE, &prec->oval, 1);
return status;
}

View File

@@ -18,6 +18,7 @@
#include <stdio.h>
#include <string.h>
#define EPICS_DBCA_PRIVATE_API
#include "alarm.h"
#include "dbDefs.h"
#include "dbAccess.h"
@@ -52,7 +53,7 @@ static long write_longout(longoutRecord *prec)
long status;
if(prec->pact) return(0);
if(plink->type!=CA_LINK) {
if(plink->type!=CA_LINK || strcmp(plink->value.pv_link.backend,"ca")!=0) {
status = dbPutLink(plink,DBR_LONG,&prec->val,1);
return(status);
}

View File

@@ -10,6 +10,9 @@
* Date: 30 Nov 2012
*/
#include <string.h>
#define EPICS_DBCA_PRIVATE_API
#include "alarm.h"
#include "dbAccess.h"
#include "recGbl.h"
@@ -31,7 +34,7 @@ static long write_string(lsoRecord *prec)
len = 1;
}
if (plink->type != CA_LINK)
if (plink->type != CA_LINK || strcmp(plink->value.pv_link.backend,"ca")!=0)
return dbPutLink(plink, dtyp, prec->val, len);
status = dbCaPutLinkCallback(plink, dtyp, prec->val, len,

View File

@@ -13,7 +13,9 @@
*/
#include <stdio.h>
#include <string.h>
#define EPICS_DBCA_PRIVATE_API
#include "alarm.h"
#include "dbAccess.h"
#include "recGbl.h"
@@ -29,7 +31,7 @@ static long write_mbbo(mbboDirectRecord *prec)
if (prec->pact)
return 0;
if (plink->type != CA_LINK) {
if (plink->type != CA_LINK || strcmp(plink->value.pv_link.backend,"ca")!=0) {
status = dbPutLink(plink, DBR_USHORT, &prec->val, 1);
return status;
}

View File

@@ -17,6 +17,7 @@
#include <stdio.h>
#include <string.h>
#define EPICS_DBCA_PRIVATE_API
#include "alarm.h"
#include "dbDefs.h"
#include "dbAccess.h"
@@ -51,7 +52,7 @@ static long write_mbbo(mbboRecord *prec)
long status;
if(prec->pact) return(0);
if(plink->type!=CA_LINK) {
if(plink->type!=CA_LINK || strcmp(plink->value.pv_link.backend,"ca")!=0) {
status = dbPutLink(plink,DBR_USHORT,&prec->val,1);
return(status);
}

View File

@@ -10,6 +10,9 @@
* Date: 28 Sept 2012
*/
#include <string.h>
#define EPICS_DBCA_PRIVATE_API
#include "alarm.h"
#include "dbAccess.h"
#include "recGbl.h"
@@ -31,7 +34,7 @@ static long write_string(printfRecord *prec)
len = 1;
}
if (plink->type != CA_LINK)
if (plink->type != CA_LINK || strcmp(plink->value.pv_link.backend,"ca")!=0)
return dbPutLink(plink, dtyp, prec->val, len);
status = dbCaPutLinkCallback(plink, dtyp, prec->val, len,

View File

@@ -17,6 +17,7 @@
#include <stdio.h>
#include <string.h>
#define EPICS_DBCA_PRIVATE_API
#include "alarm.h"
#include "dbDefs.h"
#include "dbAccess.h"
@@ -52,7 +53,7 @@ static long write_stringout(stringoutRecord *prec)
if (prec->pact) return 0;
if (plink->type != CA_LINK) {
if (plink->type != CA_LINK || strcmp(plink->value.pv_link.backend,"ca")!=0) {
return dbPutLink(plink, DBR_STRING, &prec->val, 1);
}