Compare commits
83 Commits
database-3
...
end-of-dat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e613d685fd | ||
|
|
a3c01bbf77 | ||
|
|
086bc961a4 | ||
|
|
58dc1ced9b | ||
|
|
d8802c8b24 | ||
|
|
b7d4609e57 | ||
|
|
7b5b23f6d3 | ||
|
|
c8a7e1597d | ||
|
|
0f7a7902e4 | ||
|
|
42403232e9 | ||
|
|
8333338f99 | ||
|
|
ceaff61c09 | ||
|
|
12bb8969ad | ||
|
|
ea408578e0 | ||
|
|
2307e94d1c | ||
|
|
f44da65942 | ||
|
|
be8f35d782 | ||
|
|
6cc623a7b4 | ||
|
|
d7e416e76a | ||
|
|
c05101bb3f | ||
|
|
958c81db89 | ||
|
|
9020c2ce1a | ||
|
|
1458f8640e | ||
|
|
a9764c8f62 | ||
|
|
98d9ea4545 | ||
|
|
8eb4eec7d2 | ||
|
|
98930eebc4 | ||
|
|
0e0a919567 | ||
|
|
cfdd689000 | ||
|
|
292141458c | ||
|
|
c18b6f2ccf | ||
|
|
e41f8bf518 | ||
|
|
ae548d3400 | ||
|
|
85c6e9bdfb | ||
|
|
550beeab9f | ||
|
|
bf91275200 | ||
|
|
ac4d5c95ac | ||
|
|
a4f072238a | ||
|
|
6603d778cb | ||
|
|
cd8fd8a08f | ||
|
|
61296b8cff | ||
|
|
de442e9584 | ||
|
|
ac367398b3 | ||
|
|
713c2d5080 | ||
|
|
eef6f3afbb | ||
|
|
af07016464 | ||
|
|
734d16291f | ||
|
|
f1e5e9689b | ||
|
|
1454f42a27 | ||
|
|
3174e22faf | ||
|
|
506be838af | ||
|
|
ddbdcf9462 | ||
|
|
a5e58829fd | ||
|
|
729e6fda4d | ||
|
|
0315e90e6e | ||
|
|
00f30ac53a | ||
|
|
66c6aaa44f | ||
|
|
c830a3a4ee | ||
|
|
1daab5fb35 | ||
|
|
f5cd555383 | ||
|
|
f527e5939e | ||
|
|
d41f2e6806 | ||
|
|
692b971e06 | ||
|
|
8766ce05aa | ||
|
|
84e0220852 | ||
|
|
8f62940265 | ||
|
|
8a1477ecab | ||
|
|
5c97e54cf7 | ||
|
|
3646493014 | ||
|
|
89cbb95c2c | ||
|
|
006ce1a240 | ||
|
|
276dee2c3e | ||
|
|
98a2871727 | ||
|
|
5ca1bb3bd5 | ||
|
|
07e8cf162d | ||
|
|
f6be3c7f70 | ||
|
|
00924dcba0 | ||
|
|
20312f82ed | ||
|
|
8e2b782b7c | ||
|
|
3b0f34e0be | ||
|
|
ab555a280b | ||
|
|
527a49bfc1 | ||
|
|
f36ce8ca3d |
@@ -1,4 +1,4 @@
|
||||
EPICS_DATABASE_MAJOR_VERSION = 3
|
||||
EPICS_DATABASE_MINOR_VERSION = 17
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 0
|
||||
EPICS_DATABASE_DEVELOPMENT_FLAG = 0
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 1
|
||||
EPICS_DATABASE_DEVELOPMENT_FLAG = 1
|
||||
|
||||
@@ -410,7 +410,7 @@ long dbd(const char *record_name)
|
||||
|
||||
precord = addr.precord;
|
||||
|
||||
if (! (precord->bkpt & BKPT_ON_MASK)) {
|
||||
if (!(precord->bkpt & BKPT_ON_MASK)) {
|
||||
printf(" BKPT> No breakpoint set in this record\n");
|
||||
return(S_db_bkptNotSet);
|
||||
}
|
||||
|
||||
@@ -309,6 +309,11 @@ long dbgf(const char *pname)
|
||||
if (nameToAddr(pname, &addr))
|
||||
return -1;
|
||||
|
||||
if (addr.precord->lset == NULL) {
|
||||
printf("dbgf only works after iocInit\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
no_elements = MIN(addr.no_elements, sizeof(buffer)/addr.field_size);
|
||||
if (addr.dbr_field_type == DBR_ENUM) {
|
||||
long status = dbGetField(&addr, DBR_STRING, pbuffer,
|
||||
@@ -345,6 +350,11 @@ long dbpf(const char *pname,const char *pvalue)
|
||||
if (nameToAddr(pname, &addr))
|
||||
return -1;
|
||||
|
||||
if (addr.precord->lset == NULL) {
|
||||
printf("dbpf only works after iocInit\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr.no_elements > 1 &&
|
||||
(addr.dbr_field_type == DBR_CHAR || addr.dbr_field_type == DBR_UCHAR)) {
|
||||
dbrType = addr.dbr_field_type;
|
||||
@@ -399,6 +409,11 @@ long dbtr(const char *pname)
|
||||
if (nameToAddr(pname, &addr))
|
||||
return -1;
|
||||
|
||||
if (addr.precord->lset == NULL) {
|
||||
printf("dbtr only works after iocInit\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
precord = (struct dbCommon*)addr.precord;
|
||||
if (precord->pact) {
|
||||
printf("record active\n");
|
||||
@@ -438,6 +453,11 @@ long dbtgf(const char *pname)
|
||||
if (nameToAddr(pname, &addr))
|
||||
return -1;
|
||||
|
||||
if (addr.precord->lset == NULL) {
|
||||
printf("dbtgf only works after iocInit\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* try all options first */
|
||||
req_options = 0xffffffff;
|
||||
ret_options = req_options;
|
||||
@@ -534,6 +554,11 @@ long dbtpf(const char *pname, const char *pvalue)
|
||||
if (nameToAddr(pname, &addr))
|
||||
return -1;
|
||||
|
||||
if (addr.precord->lset == NULL) {
|
||||
printf("dbtpf only works after iocInit\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (put_type = DBR_STRING; put_type <= DBF_ENUM; put_type++) {
|
||||
union {
|
||||
epicsInt8 i8;
|
||||
@@ -795,7 +820,7 @@ static void printBuffer(
|
||||
|
||||
printf("no_strs = %u:\n",
|
||||
pdbr_enumStrs->no_str);
|
||||
for (i = 0; i < pdbr_enumStrs->no_str; i++)
|
||||
for (i = 0; i < pdbr_enumStrs->no_str; i++)
|
||||
printf("\t\"%s\"\n", pdbr_enumStrs->strs[i]);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/* Author: Marty Kraimer Date: 13JUL95*/
|
||||
@@ -169,7 +169,7 @@ static char *dbOpenFile(DBBASE *pdbbase,const char *filename,FILE **fp)
|
||||
}
|
||||
pdbPathNode = (dbPathNode *)ellFirst(ppathList);
|
||||
while (pdbPathNode) {
|
||||
fullfilename = dbMalloc(strlen(pdbPathNode->directory) +
|
||||
fullfilename = dbMalloc(strlen(pdbPathNode->directory) +
|
||||
strlen(filename) + 2);
|
||||
strcpy(fullfilename, pdbPathNode->directory);
|
||||
strcat(fullfilename, "/");
|
||||
@@ -190,7 +190,7 @@ static void freeInputFileList(void)
|
||||
inputFile *pinputFileNow;
|
||||
|
||||
while((pinputFileNow=(inputFile *)ellFirst(&inputFileList))) {
|
||||
if(fclose(pinputFileNow->fp))
|
||||
if(fclose(pinputFileNow->fp))
|
||||
errPrintf(0,__FILE__, __LINE__,
|
||||
"Closing file %s",pinputFileNow->filename);
|
||||
free((void *)pinputFileNow->filename);
|
||||
@@ -215,7 +215,7 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
|
||||
inputFile *pinputFile = NULL;
|
||||
char *penv;
|
||||
char **macPairs;
|
||||
|
||||
|
||||
if(ellCount(&tempList)) {
|
||||
epicsPrintf("dbReadCOM: Parser stack dirty %d\n", ellCount(&tempList));
|
||||
}
|
||||
@@ -341,7 +341,7 @@ static int db_yyinput(char *buf, int max_size)
|
||||
{
|
||||
size_t l,n;
|
||||
char *fgetsRtn;
|
||||
|
||||
|
||||
if(yyAbort) return(0);
|
||||
if(*my_buffer_ptr==0) {
|
||||
while(TRUE) { /*until we get some input*/
|
||||
@@ -360,7 +360,7 @@ static int db_yyinput(char *buf, int max_size)
|
||||
fgetsRtn = fgets(my_buffer,MY_BUFFER_SIZE,pinputFileNow->fp);
|
||||
}
|
||||
if(fgetsRtn) break;
|
||||
if(fclose(pinputFileNow->fp))
|
||||
if(fclose(pinputFileNow->fp))
|
||||
errPrintf(0,__FILE__, __LINE__,
|
||||
"Closing file %s",pinputFileNow->filename);
|
||||
free((void *)pinputFileNow->filename);
|
||||
@@ -511,7 +511,7 @@ static void dbRecordtypeFieldHead(char *name,char *type)
|
||||
{
|
||||
dbFldDes *pdbFldDes;
|
||||
int i;
|
||||
|
||||
|
||||
if(duplicate) return;
|
||||
pdbFldDes = dbCalloc(1,sizeof(dbFldDes));
|
||||
allocTemp(pdbFldDes);
|
||||
@@ -544,7 +544,7 @@ static short findOrAddGuiGroup(const char *name)
|
||||
static void dbRecordtypeFieldItem(char *name,char *value)
|
||||
{
|
||||
dbFldDes *pdbFldDes;
|
||||
|
||||
|
||||
if(duplicate) return;
|
||||
pdbFldDes = (dbFldDes *)getLastTemp();
|
||||
if(strcmp(name,"asl")==0) {
|
||||
@@ -636,11 +636,11 @@ static void dbRecordtypeCdef(char *text) {
|
||||
dbText *pdbCdef;
|
||||
tempListNode *ptempListNode;
|
||||
dbRecordType *pdbRecordType;
|
||||
|
||||
|
||||
if (!pdbbase->loadCdefs || duplicate) return;
|
||||
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
||||
pdbRecordType = ptempListNode->item;
|
||||
|
||||
|
||||
pdbCdef = dbCalloc(1,sizeof(dbText));
|
||||
if (text[0] == ' ') text++; /* strip leading space if present */
|
||||
pdbCdef->text = epicsStrDup(text);
|
||||
@@ -699,7 +699,7 @@ static void dbRecordtypeBody(void)
|
||||
if((field_type==DBF_STRING) && (pdbFldDes->size==0))
|
||||
fprintf(stderr,"recordtype(%s).%s size not specified\n",
|
||||
pdbRecordType->name,pdbFldDes->name);
|
||||
if((field_type==DBF_NOACCESS) && (pdbFldDes->extra==0))
|
||||
if((field_type==DBF_NOACCESS) && (pdbFldDes->extra==0))
|
||||
fprintf(stderr,"recordtype(%s).%s extra not specified\n",
|
||||
pdbRecordType->name,pdbFldDes->name);
|
||||
}
|
||||
@@ -811,7 +811,7 @@ static void dbDriver(char *name)
|
||||
pgphentry = gphAdd(pdbbase->pgpHash,pdrvSup->name,&pdbbase->drvList);
|
||||
if(!pgphentry) {
|
||||
yyerrorAbort("gphAdd failed");
|
||||
}
|
||||
}
|
||||
pgphentry->userPvt = pdrvSup;
|
||||
ellAdd(&pdbbase->drvList,&pdrvSup->node);
|
||||
}
|
||||
@@ -831,7 +831,7 @@ static void dbLinkType(char *name, char *jlif_name)
|
||||
pgphentry = gphAdd(pdbbase->pgpHash, pLinkSup->name, &pdbbase->linkList);
|
||||
if (!pgphentry) {
|
||||
yyerrorAbort("gphAdd failed");
|
||||
}
|
||||
}
|
||||
pgphentry->userPvt = pLinkSup;
|
||||
ellAdd(&pdbbase->linkList, &pLinkSup->node);
|
||||
}
|
||||
@@ -850,7 +850,7 @@ static void dbRegistrar(char *name)
|
||||
pgphentry = gphAdd(pdbbase->pgpHash,ptext->text,&pdbbase->registrarList);
|
||||
if(!pgphentry) {
|
||||
yyerrorAbort("gphAdd failed");
|
||||
}
|
||||
}
|
||||
pgphentry->userPvt = ptext;
|
||||
ellAdd(&pdbbase->registrarList,&ptext->node);
|
||||
}
|
||||
@@ -889,7 +889,7 @@ static void dbVariable(char *name, char *type)
|
||||
pgphentry = gphAdd(pdbbase->pgpHash,pvar->name,&pdbbase->variableList);
|
||||
if(!pgphentry) {
|
||||
yyerrorAbort("gphAdd failed");
|
||||
}
|
||||
}
|
||||
pgphentry->userPvt = pvar;
|
||||
ellAdd(&pdbbase->variableList,&pvar->node);
|
||||
}
|
||||
@@ -948,11 +948,11 @@ static void dbBreakBody(void)
|
||||
pnewbrkTable->paBrkInt = paBrkInt = dbCalloc(number, sizeof(brkInt));
|
||||
for (i=0; i<number; i++) {
|
||||
char *str;
|
||||
|
||||
|
||||
str = (char *)popFirstTemp();
|
||||
(void) epicsScanDouble(str, &paBrkInt[i].raw);
|
||||
free(str);
|
||||
|
||||
|
||||
str = (char *)popFirstTemp();
|
||||
(void) epicsScanDouble(str, &paBrkInt[i].eng);
|
||||
free(str);
|
||||
@@ -1073,7 +1073,7 @@ static void dbRecordField(char *name,char *value)
|
||||
pdbentry = ptempListNode->item;
|
||||
status = dbFindField(pdbentry,name);
|
||||
if(status) {
|
||||
epicsPrintf("Record \"%s\" does not have a field \"%s\"\n",
|
||||
epicsPrintf("Record \"%s\" does not have a field \"%s\"\n",
|
||||
dbGetRecordName(pdbentry), name);
|
||||
yyerror(NULL);
|
||||
return;
|
||||
|
||||
@@ -51,8 +51,11 @@ static char *pNullString = "";
|
||||
#define messagesize 276
|
||||
#define RPCL_LEN INFIX_TO_POSTFIX_SIZE(80)
|
||||
|
||||
/* must be long enough to hold 32-bit signed integer in base 10 */
|
||||
STATIC_ASSERT(messagesize>=11);
|
||||
/* Must be big enough to hold a 64-bit integer in base 10, but in
|
||||
* the future when fields hold large JSON objects this fixed size
|
||||
* allocation will probably have to become variable sized.
|
||||
*/
|
||||
STATIC_ASSERT(messagesize >= 21);
|
||||
|
||||
static char *ppstring[5]={" NPP"," PP"," CA"," CP"," CPP"};
|
||||
static char *msstring[4]={" NMS"," MS"," MSI"," MSS"};
|
||||
@@ -208,11 +211,13 @@ static void zeroDbentry(DBENTRY *pdbentry)
|
||||
static char *getpMessage(DBENTRY *pdbentry)
|
||||
{
|
||||
char *msg = pdbentry->message;
|
||||
|
||||
if (!msg) {
|
||||
msg = dbCalloc(1, messagesize);
|
||||
pdbentry->message = msg;
|
||||
}
|
||||
*msg = '\0';
|
||||
else
|
||||
*msg = '\0';
|
||||
return msg;
|
||||
}
|
||||
|
||||
@@ -224,6 +229,17 @@ void dbMsgCpy(DBENTRY *pdbentry, const char *msg)
|
||||
pdbentry->message[messagesize-1] = '\0';
|
||||
}
|
||||
|
||||
static
|
||||
void dbMsgNCpy(DBENTRY *pdbentry, const char *msg, size_t len)
|
||||
{
|
||||
getpMessage(pdbentry);
|
||||
if (len >= messagesize)
|
||||
len = messagesize-1; /* FIXME: Quietly truncates */
|
||||
|
||||
strncpy(pdbentry->message, msg, len);
|
||||
pdbentry->message[len] = '\0';
|
||||
}
|
||||
|
||||
static
|
||||
void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...)
|
||||
{
|
||||
@@ -879,10 +895,14 @@ long dbWriteRecordFP(
|
||||
status=dbNextField(pdbentry,dctonly);
|
||||
}
|
||||
status = dbFirstInfo(pdbentry);
|
||||
while(!status) {
|
||||
fprintf(fp,"\tinfo(\"%s\",\"%s\")\n",
|
||||
dbGetInfoName(pdbentry), dbGetInfoString(pdbentry));
|
||||
status=dbNextInfo(pdbentry);
|
||||
while (!status) {
|
||||
const char *pinfostr = dbGetInfoString(pdbentry);
|
||||
|
||||
fprintf(fp, "\tinfo(\"%s\",\"",
|
||||
dbGetInfoName(pdbentry));
|
||||
epicsStrPrintEscaped(fp, pinfostr, strlen(pinfostr));
|
||||
fprintf(fp, "\")\n");
|
||||
status = dbNextInfo(pdbentry);
|
||||
}
|
||||
fprintf(fp,"}\n");
|
||||
status = dbNextRecord(pdbentry);
|
||||
@@ -1674,21 +1694,27 @@ int dbIsVisibleRecord(DBENTRY *pdbentry)
|
||||
|
||||
long dbCreateAlias(DBENTRY *pdbentry, const char *alias)
|
||||
{
|
||||
dbRecordType *precordType = pdbentry->precordType;
|
||||
dbRecordNode *precnode = pdbentry->precnode;
|
||||
dbRecordNode *pnewnode;
|
||||
PVDENTRY *ppvd;
|
||||
ELLLIST *preclist = NULL;
|
||||
if (!precordType) return S_dbLib_recordTypeNotFound;
|
||||
dbRecordType *precordType = pdbentry->precordType;
|
||||
dbRecordNode *precnode = pdbentry->precnode;
|
||||
dbRecordNode *pnewnode;
|
||||
DBENTRY tempEntry;
|
||||
PVDENTRY *ppvd;
|
||||
|
||||
if (!precordType)
|
||||
return S_dbLib_recordTypeNotFound;
|
||||
|
||||
/* alias of alias still references actual record */
|
||||
while(precnode && (precnode->flags&DBRN_FLAGS_ISALIAS))
|
||||
while (precnode && (precnode->flags & DBRN_FLAGS_ISALIAS))
|
||||
precnode = precnode->aliasedRecnode;
|
||||
if (!precnode) return S_dbLib_recNotFound;
|
||||
zeroDbentry(pdbentry);
|
||||
if (!dbFindRecord(pdbentry, alias)) return S_dbLib_recExists;
|
||||
zeroDbentry(pdbentry);
|
||||
pdbentry->precordType = precordType;
|
||||
preclist = &precordType->recList;
|
||||
|
||||
if (!precnode)
|
||||
return S_dbLib_recNotFound;
|
||||
|
||||
dbInitEntry(pdbentry->pdbbase, &tempEntry);
|
||||
if (!dbFindRecord(&tempEntry, alias))
|
||||
return S_dbLib_recExists;
|
||||
dbFinishEntry(&tempEntry);
|
||||
|
||||
pnewnode = dbCalloc(1, sizeof(dbRecordNode));
|
||||
pnewnode->recordname = epicsStrDup(alias);
|
||||
pnewnode->precord = precnode->precord;
|
||||
@@ -1696,11 +1722,16 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias)
|
||||
pnewnode->flags = DBRN_FLAGS_ISALIAS;
|
||||
precnode->flags |= DBRN_FLAGS_HASALIAS;
|
||||
ellInit(&pnewnode->infoList);
|
||||
ellAdd(preclist, &pnewnode->node);
|
||||
|
||||
ellAdd(&precordType->recList, &pnewnode->node);
|
||||
precordType->no_aliases++;
|
||||
pdbentry->precnode = pnewnode;
|
||||
|
||||
ppvd = dbPvdAdd(pdbentry->pdbbase, precordType, pnewnode);
|
||||
if (!ppvd) {errMessage(-1,"Logic Err: Could not add to PVD");return(-1);}
|
||||
if (!ppvd) {
|
||||
errMessage(-1, "dbCreateAlias: Add to PVD failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1884,7 +1915,8 @@ char * dbGetString(DBENTRY *pdbentry)
|
||||
|
||||
switch (pflddes->field_type) {
|
||||
case DBF_STRING:
|
||||
dbMsgCpy(pdbentry, (char *)pfield);
|
||||
/* Protect against a missing nil-terminator */
|
||||
dbMsgNCpy(pdbentry, (char *)pfield, pflddes->size);
|
||||
break;
|
||||
case DBF_CHAR:
|
||||
case DBF_UCHAR:
|
||||
@@ -1907,6 +1939,8 @@ char * dbGetString(DBENTRY *pdbentry)
|
||||
case CONSTANT:
|
||||
if (plink->value.constantStr) {
|
||||
dbMsgCpy(pdbentry, plink->value.constantStr);
|
||||
} else if (plink->text) {
|
||||
dbMsgCpy(pdbentry, plink->text);
|
||||
} else {
|
||||
dbMsgCpy(pdbentry, "");
|
||||
}
|
||||
@@ -2007,7 +2041,13 @@ char * dbGetString(DBENTRY *pdbentry)
|
||||
|
||||
switch(plink->type) {
|
||||
case CONSTANT:
|
||||
dbMsgCpy(pdbentry, "0");
|
||||
if (plink->value.constantStr) {
|
||||
dbMsgCpy(pdbentry, plink->value.constantStr);
|
||||
} else if (plink->text) {
|
||||
dbMsgCpy(pdbentry, plink->text);
|
||||
} else {
|
||||
dbMsgCpy(pdbentry, "");
|
||||
}
|
||||
break;
|
||||
case MACRO_LINK:
|
||||
if (plink->value.macro_link.macroStr) {
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
#define epicsExportSharedSymbols
|
||||
#include "epicsRelease.h"
|
||||
|
||||
static const char id[] EPICS_UNUSED =
|
||||
"@(#) " EPICS_VERSION_STRING ", Misc. Utilities Library" __DATE__;
|
||||
|
||||
epicsShareFunc int coreRelease(void)
|
||||
{
|
||||
printf ( "############################################################################\n" );
|
||||
|
||||
@@ -45,7 +45,6 @@ void rsrv_online_notify_task(void *pParm)
|
||||
caHdr msg;
|
||||
int status;
|
||||
ca_uint32_t beaconCounter = 0;
|
||||
char buf[16];
|
||||
|
||||
taskwdInsert (epicsThreadGetIdSelf(),NULL,NULL);
|
||||
|
||||
@@ -85,10 +84,12 @@ void rsrv_online_notify_task(void *pParm)
|
||||
&pAddr->addr.sa, sizeof(pAddr->addr));
|
||||
if (status < 0) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
ipAddrToDottedIP (&pAddr->addr.ia, buf, sizeof(buf));
|
||||
char sockDipBuf[22];
|
||||
|
||||
epicsSocketConvertErrnoToString(sockErrBuf, sizeof(sockErrBuf));
|
||||
ipAddrToDottedIP(&pAddr->addr.ia, sockDipBuf, sizeof(sockDipBuf));
|
||||
errlogPrintf ( "CAS: CA beacon send to %s error: %s\n",
|
||||
buf, sockErrBuf);
|
||||
sockDipBuf, sockErrBuf);
|
||||
}
|
||||
else {
|
||||
assert (status == sizeof(msg));
|
||||
|
||||
@@ -96,6 +96,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
|
||||
{
|
||||
struct stringinRecord *prec = (struct stringinRecord *)pcommon;
|
||||
STATIC_ASSERT(sizeof(prec->oval)==sizeof(prec->val));
|
||||
STATIC_ASSERT(sizeof(prec->sval)==sizeof(prec->val));
|
||||
struct stringindset *pdset = (struct stringindset *) prec->dset;
|
||||
|
||||
if (pass == 0) return 0;
|
||||
@@ -120,7 +121,8 @@ static long init_record(struct dbCommon *pcommon, int pass)
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
strcpy(prec->oval, prec->val);
|
||||
|
||||
strncpy(prec->oval, prec->val, sizeof(prec->val));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -213,7 +215,7 @@ static long readValue(stringinRecord *prec)
|
||||
if (prec->pact || (prec->sdly < 0.)) {
|
||||
status = dbGetLink(&prec->siol, DBR_STRING, prec->sval, 0, 0);
|
||||
if (status == 0) {
|
||||
strcpy(prec->val, prec->sval);
|
||||
strncpy(prec->val, prec->sval, sizeof(prec->val));
|
||||
prec->udf = FALSE;
|
||||
}
|
||||
prec->pact = FALSE;
|
||||
|
||||
@@ -98,6 +98,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
|
||||
{
|
||||
struct stringoutRecord *prec = (struct stringoutRecord *)pcommon;
|
||||
STATIC_ASSERT(sizeof(prec->oval)==sizeof(prec->val));
|
||||
STATIC_ASSERT(sizeof(prec->ivov)==sizeof(prec->val));
|
||||
struct stringoutdset *pdset = (struct stringoutdset *) prec->dset;
|
||||
|
||||
if (pass == 0) return 0;
|
||||
@@ -126,7 +127,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
|
||||
return status;
|
||||
}
|
||||
|
||||
strcpy(prec->oval, prec->val);
|
||||
strncpy(prec->oval, prec->val, sizeof(prec->val));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -165,7 +166,7 @@ static long process(struct dbCommon *pcommon)
|
||||
break;
|
||||
case (menuIvoaSet_output_to_IVOV) :
|
||||
if(prec->pact == FALSE){
|
||||
strcpy(prec->val,prec->ivov);
|
||||
strncpy(prec->val, prec->ivov, sizeof(prec->val));
|
||||
}
|
||||
status=writeValue(prec); /* write the new value */
|
||||
break;
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
*
|
||||
* Two time intervals are measured. The time to queue and run each of
|
||||
* the immediate callbacks, and the actual delay of the delayed callback.
|
||||
*
|
||||
* Slow callbacks no longer fail the test, they just emit a diagnostic.
|
||||
*/
|
||||
|
||||
#define NCALLBACKS 169
|
||||
@@ -108,7 +110,7 @@ MAIN(callbackParallelTest)
|
||||
for (j = 0; j < 5; j++)
|
||||
setupError[i][j] = timeError[i][j] = defaultError[j];
|
||||
|
||||
testPlan(4);
|
||||
testPlan(2);
|
||||
|
||||
testDiag("Starting %d parallel callback threads", noCpus);
|
||||
|
||||
@@ -165,7 +167,8 @@ MAIN(callbackParallelTest)
|
||||
}
|
||||
}
|
||||
testOk(faults == 0, "%d faults during callback setup", faults);
|
||||
testOk(slowups <= 1, "%d slowups during callback setup", slowups);
|
||||
if (slowups)
|
||||
testDiag("%d slowups during callback setup", slowups);
|
||||
|
||||
slowups = 0;
|
||||
for (i = 0; i < NCALLBACKS ; i++) {
|
||||
@@ -182,7 +185,8 @@ MAIN(callbackParallelTest)
|
||||
}
|
||||
updateStats(timeError[i%NUM_CALLBACK_PRIORITIES], error);
|
||||
}
|
||||
testOk(slowups < 5, "%d slowups during callbacks", slowups);
|
||||
if (slowups)
|
||||
testDiag("%d slowups during callback setup", slowups);
|
||||
|
||||
testDiag("Setup time statistics");
|
||||
printStats(setupError[0], "LOW");
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
*
|
||||
* Two time intervals are measured. The time to queue and run each of
|
||||
* the immediate callbacks, and the actual delay of the delayed callback.
|
||||
*
|
||||
* Slow callbacks no longer fail the test, they just emit a diagnostic.
|
||||
*/
|
||||
|
||||
#define NCALLBACKS 169
|
||||
@@ -108,7 +110,7 @@ MAIN(callbackTest)
|
||||
for (j = 0; j < 5; j++)
|
||||
setupError[i][j] = timeError[i][j] = defaultError[j];
|
||||
|
||||
testPlan(4);
|
||||
testPlan(2);
|
||||
|
||||
callbackInit();
|
||||
epicsThreadSleep(1.0);
|
||||
@@ -162,7 +164,8 @@ MAIN(callbackTest)
|
||||
}
|
||||
}
|
||||
testOk(faults == 0, "%d faults during callback setup", faults);
|
||||
testOk(slowups <= 1, "%d slowups during callback setup", slowups);
|
||||
if (slowups)
|
||||
testDiag("%d slowups during callback setup", slowups);
|
||||
|
||||
slowups = 0;
|
||||
for (i = 0; i < NCALLBACKS ; i++) {
|
||||
@@ -179,7 +182,8 @@ MAIN(callbackTest)
|
||||
}
|
||||
updateStats(timeError[i%NUM_CALLBACK_PRIORITIES], error);
|
||||
}
|
||||
testOk(slowups < 5, "%d slowups during callbacks", slowups);
|
||||
if (slowups)
|
||||
testDiag("%d slowups during callback setup", slowups);
|
||||
|
||||
testDiag("Setup time statistics");
|
||||
printStats(setupError[0], "LOW");
|
||||
|
||||
@@ -38,7 +38,7 @@ static void testBasicGet(void)
|
||||
|
||||
getter(&addr, scratch, 1, s_input_len, 0);
|
||||
|
||||
testOk1(scratch[0]==s_input[0]);
|
||||
testOk1(scratch[0]==s_input[0] && scratch[1]==0);
|
||||
|
||||
memset(scratch, 0x42, sizeof(s_input));
|
||||
}
|
||||
@@ -128,7 +128,7 @@ static void testBasicPut(void)
|
||||
|
||||
putter(&addr, s_input, 1, s_input_len, 0);
|
||||
|
||||
testOk1(scratch[0]==s_input[0]);
|
||||
testOk1(scratch[0]==s_input[0] && scratch[1]==0);
|
||||
|
||||
memset(scratch, 0x42, sizeof(s_input));
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ TESTFILES += $(COMMON_DIR)/analogMonitorTest.dbd ../analogMonitorTest.db
|
||||
TESTS += analogMonitorTest
|
||||
|
||||
TARGETS += $(COMMON_DIR)/regressTest.dbd
|
||||
DBDDEPENDS_FILES += regressTest.dbd$(DEP)
|
||||
regressTest_DBD += base.dbd
|
||||
TESTPROD_HOST += regressTest
|
||||
regressTest_SRCS += regressTest.c
|
||||
|
||||
@@ -109,11 +109,7 @@ static void hookPass0(initHookState state)
|
||||
else
|
||||
testFail("Wrong link type: %d", (int)prec->out.type);
|
||||
|
||||
/* note that dbGetString() reads an empty string before links are initialized
|
||||
* should probably be considered a bug, but has been the case for so long
|
||||
* we call it a 'feature'.
|
||||
*/
|
||||
checkGetString(&entry, "");
|
||||
checkGetString(&entry, "rec0.DISV");
|
||||
|
||||
testOk1(dbPutString(&entry, "rec0.SEVR")==0);
|
||||
} else{
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbTest.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "errlog.h"
|
||||
#include "registryFunction.h"
|
||||
#include "subRecord.h"
|
||||
@@ -39,10 +41,10 @@ void doProcess(const char *rec)
|
||||
testdbPutFieldOk(proc, DBR_CHAR, 1);
|
||||
}
|
||||
|
||||
/* Group 0 are all soft-channel input records with INP being a DB link
|
||||
/* Group 0 are soft-channel input records with INP being a DB or CA link
|
||||
* to the PV 'source'. Their VAL fields all start out with the default
|
||||
* value for the type, i.e. 0 or an empty string. Triggering record
|
||||
* processing should read the integer value from the 'source' PV.
|
||||
* processing reads the value from the 'source' PV.
|
||||
*/
|
||||
|
||||
static
|
||||
@@ -50,14 +52,22 @@ void testGroup0(void)
|
||||
{
|
||||
const char ** rec;
|
||||
const char * records[] = {
|
||||
"ai0", "bi0", "di0", "ii0", "li0", "lsi0", "mi0", "si0", NULL
|
||||
"ai0", "bi0", "di0", "ii0", "li0", "lsi0", "mi0", "si0",
|
||||
"ai0c", "bi0c", "di0c", "ii0c", "li0c", "lsi0c", "mi0c", "si0c",
|
||||
NULL
|
||||
};
|
||||
|
||||
testDiag("============ Starting %s ============", EPICS_FUNCTION);
|
||||
|
||||
testdbPutFieldOk("source", DBR_LONG, 1);
|
||||
/* The above put sends CA monitors to all of the CA links, but
|
||||
* doesn't trigger record processing (the links are not CP/CPP).
|
||||
* How could we wait until all of those monitors have arrived,
|
||||
* instead of just waiting for an arbitrary time period?
|
||||
*/
|
||||
epicsThreadSleep(1.0); /* FIXME: Wait here? */
|
||||
for (rec = records; *rec; rec++) {
|
||||
if (strcmp(*rec, "lsi0") != 0)
|
||||
if (strncmp(*rec, "lsi0", 4) != 0)
|
||||
testdbGetFieldEqual(*rec, DBR_LONG, 0);
|
||||
checkDtyp(*rec);
|
||||
doProcess(*rec);
|
||||
@@ -65,6 +75,7 @@ void testGroup0(void)
|
||||
}
|
||||
|
||||
testdbPutFieldOk("source", DBR_LONG, 0);
|
||||
epicsThreadSleep(1.0); /* FIXME: Wait here as above */
|
||||
for (rec = records; *rec; rec++) {
|
||||
doProcess(*rec);
|
||||
testdbGetFieldEqual(*rec, DBR_LONG, 0);
|
||||
@@ -124,49 +135,70 @@ void testGroup2(void)
|
||||
|
||||
|
||||
int dest;
|
||||
epicsEventId destEvent;
|
||||
|
||||
static
|
||||
long destSubr(subRecord *prec)
|
||||
{
|
||||
dest = prec->val;
|
||||
prec->val = -1;
|
||||
epicsEventMustTrigger(destEvent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void checkOutput(const char *rec, int value)
|
||||
void checkOutput3(const char *rec, int value)
|
||||
{
|
||||
testDiag("Checking record '%s'", rec);
|
||||
|
||||
testdbPutFieldOk(rec, DBR_LONG, value);
|
||||
|
||||
epicsEventMustWait(destEvent);
|
||||
testOk(dest == value, "value %d output -> %d", value, dest);
|
||||
}
|
||||
|
||||
/* Group 3 are all soft-channel output records with OUT being a DB link
|
||||
* to the PV 'dest' with PP. Putting a value to the record writes that
|
||||
* value to 'dest' and processes it.
|
||||
/* Group 3 are all soft-channel output records with OUT being a DB or
|
||||
* local CA link to the subRecord 'dest'; DB links have the PP flag,
|
||||
* for CA links the VAL field is marked PP. Putting a value to the
|
||||
* output record writes that value to 'dest'.
|
||||
*/
|
||||
static
|
||||
void testGroup3(void)
|
||||
{
|
||||
const char ** rec;
|
||||
const char * records[] = {
|
||||
"ao0", "bo0", "io0", "lo0", "lso0", "mo0", "so0", NULL,
|
||||
"ao3", "bo3", "io3", "lo3", "lso3", "mo3", "so3",
|
||||
"ao3c", "bo3c", "io3c", "lo3c", "lso3c", "mo3c", "so3c",
|
||||
NULL,
|
||||
};
|
||||
|
||||
destEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
testDiag("============ Starting %s ============", EPICS_FUNCTION);
|
||||
|
||||
for (rec = records; *rec; rec++) {
|
||||
checkOutput(*rec, 1);
|
||||
checkOutput3(*rec, 1);
|
||||
checkDtyp(*rec);
|
||||
}
|
||||
checkOutput("do0.B0", 1);
|
||||
checkDtyp("do0");
|
||||
checkOutput3("do3.B0", 1);
|
||||
checkDtyp("do3");
|
||||
checkOutput3("do3c.B0", 1);
|
||||
checkDtyp("do3c");
|
||||
|
||||
for (rec = records; *rec; rec++) {
|
||||
checkOutput(*rec, 0);
|
||||
checkOutput3(*rec, 0);
|
||||
}
|
||||
checkOutput("do0.B0", 0);
|
||||
checkOutput3("do3.B0", 0);
|
||||
checkOutput3("do3c.B0", 0);
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
void checkOutput4(const char *rec, int value)
|
||||
{
|
||||
testDiag("Checking record '%s'", rec);
|
||||
|
||||
testdbPutFieldOk(rec, DBR_LONG, value);
|
||||
}
|
||||
|
||||
/* Group 4 are all soft-channel output records with OUT being empty
|
||||
@@ -177,21 +209,22 @@ void testGroup4(void)
|
||||
{
|
||||
const char ** rec;
|
||||
const char * records[] = {
|
||||
"ao1", "bo1", "do1.B0", "io1", "lo1", "lso1", "mo1", "so1", NULL,
|
||||
"ao4", "bo4", "do4.B0", "io4", "lo4", "lso4", "mo4", "so4", NULL,
|
||||
};
|
||||
|
||||
testDiag("============ Starting %s ============", EPICS_FUNCTION);
|
||||
|
||||
for (rec = records; *rec; rec++) {
|
||||
checkOutput(*rec, 0);
|
||||
checkOutput4(*rec, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
|
||||
MAIN(softTest)
|
||||
{
|
||||
testPlan(163);
|
||||
testPlan(258);
|
||||
|
||||
testdbPrepare();
|
||||
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
|
||||
|
||||
@@ -59,6 +59,60 @@ record(stringin, "si0") {
|
||||
field(INP, "source")
|
||||
}
|
||||
|
||||
record(ai, "ai0c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, "source CA")
|
||||
}
|
||||
record(bi, "bi0c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, "source CA")
|
||||
field(ZNAM, "Zero")
|
||||
field(ONAM, "One")
|
||||
}
|
||||
record(int64in, "ii0c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, "source CA")
|
||||
}
|
||||
record(longin, "li0c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, "source CA")
|
||||
}
|
||||
record(mbbiDirect, "di0c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(INP, "source CA")
|
||||
}
|
||||
record(mbbi, "mi0c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(INP, "source CA")
|
||||
field(ZRST, "Zero")
|
||||
field(ONST, "One")
|
||||
field(TWST, "Two")
|
||||
field(THST, "Three")
|
||||
field(FRST, "Four")
|
||||
field(FVST, "Five")
|
||||
field(SXST, "Six")
|
||||
field(SVST, "Seven")
|
||||
field(EIST, "Eight")
|
||||
field(NIST, "Nine")
|
||||
field(TEST, "Ten")
|
||||
field(ELST, "Eleven")
|
||||
field(TWST, "Twelve")
|
||||
field(TTST, "Thirteen")
|
||||
field(FTST, "Fourteen")
|
||||
field(FFST, "Fifteen")
|
||||
}
|
||||
record(lsi, "lsi0c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(SIZV, 40)
|
||||
field(INP, "source CA")
|
||||
}
|
||||
record(stringin, "si0c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, "source CA")
|
||||
}
|
||||
|
||||
|
||||
# Group 1 are all soft-channel input records with INP being a non-zero
|
||||
# "const" JSON-link, 9 for most records, 1 for the binary. Their VAL
|
||||
@@ -171,38 +225,39 @@ record(mbbi, "mi2") {
|
||||
}
|
||||
|
||||
|
||||
# Group 3 are all soft-channel output records with OUT being a DB link
|
||||
# to the PV 'dest' with PP. Putting a value to the record writes that
|
||||
# value to 'dest' and processes it.
|
||||
# Group 3 are all soft-channel output records with OUT being a DB or
|
||||
# CA link to the PV 'dest' with PP. Putting a value to the record
|
||||
# under test writes the value to 'dest' and processes it.
|
||||
|
||||
record(sub, "dest") {
|
||||
field(SNAM, "destSubr")
|
||||
field(VAL, -1)
|
||||
}
|
||||
|
||||
record(ao, "ao0") {
|
||||
record(ao, "ao3") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest PP")
|
||||
}
|
||||
record(bo, "bo0") {
|
||||
record(bo, "bo3") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest PP")
|
||||
field(ZNAM, "Zero")
|
||||
field(ONAM, "One")
|
||||
}
|
||||
record(int64out, "io0") {
|
||||
record(int64out, "io3") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest PP")
|
||||
}
|
||||
record(longout, "lo0") {
|
||||
record(longout, "lo3") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest PP")
|
||||
}
|
||||
record(mbboDirect, "do0") {
|
||||
record(mbboDirect, "do3") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(OUT, "dest PP")
|
||||
}
|
||||
record(mbbo, "mo0") {
|
||||
record(mbbo, "mo3") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(OUT, "dest PP")
|
||||
@@ -223,39 +278,93 @@ record(mbbo, "mo0") {
|
||||
field(FTST, "Fourteen")
|
||||
field(FFST, "Fifteen")
|
||||
}
|
||||
record(lso, "lso0") {
|
||||
record(lso, "lso3") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest PP")
|
||||
field(SIZV, 40)
|
||||
}
|
||||
record(stringout, "so0") {
|
||||
record(stringout, "so3") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest PP")
|
||||
}
|
||||
|
||||
record(ao, "ao3c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest CA")
|
||||
}
|
||||
record(bo, "bo3c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest CA")
|
||||
field(ZNAM, "Zero")
|
||||
field(ONAM, "One")
|
||||
}
|
||||
record(int64out, "io3c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest CA")
|
||||
}
|
||||
record(longout, "lo3c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest CA")
|
||||
}
|
||||
record(mbboDirect, "do3c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(OUT, "dest CA")
|
||||
}
|
||||
record(mbbo, "mo3c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(OUT, "dest CA")
|
||||
field(ZRST, "Zero")
|
||||
field(ONST, "One")
|
||||
field(TWST, "Two")
|
||||
field(THST, "Three")
|
||||
field(FRST, "Four")
|
||||
field(FVST, "Five")
|
||||
field(SXST, "Six")
|
||||
field(SVST, "Seven")
|
||||
field(EIST, "Eight")
|
||||
field(NIST, "Nine")
|
||||
field(TEST, "Ten")
|
||||
field(ELST, "Eleven")
|
||||
field(TWST, "Twelve")
|
||||
field(TTST, "Thirteen")
|
||||
field(FTST, "Fourteen")
|
||||
field(FFST, "Fifteen")
|
||||
}
|
||||
record(lso, "lso3c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest CA")
|
||||
field(SIZV, 40)
|
||||
}
|
||||
record(stringout, "so3c") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "dest CA")
|
||||
}
|
||||
|
||||
|
||||
# Group 4 are all soft-channel output records with OUT being empty
|
||||
# (i.e. a CONSTANT link). Putting a value to the record must succeed.
|
||||
|
||||
record(ao, "ao1") {
|
||||
record(ao, "ao4") {
|
||||
field(DTYP, "Soft Channel")
|
||||
}
|
||||
record(bo, "bo1") {
|
||||
record(bo, "bo4") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(ZNAM, "Zero")
|
||||
field(ONAM, "One")
|
||||
}
|
||||
record(int64out, "io1") {
|
||||
record(int64out, "io4") {
|
||||
field(DTYP, "Soft Channel")
|
||||
}
|
||||
record(longout, "lo1") {
|
||||
record(longout, "lo4") {
|
||||
field(DTYP, "Soft Channel")
|
||||
}
|
||||
record(mbboDirect, "do1") {
|
||||
record(mbboDirect, "do4") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
}
|
||||
record(mbbo, "mo1") {
|
||||
record(mbbo, "mo4") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(NOBT, 4)
|
||||
field(ZRST, "Zero")
|
||||
@@ -275,10 +384,10 @@ record(mbbo, "mo1") {
|
||||
field(FTST, "Fourteen")
|
||||
field(FFST, "Fifteen")
|
||||
}
|
||||
record(lso, "lso1") {
|
||||
record(lso, "lso4") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(SIZV, 40)
|
||||
}
|
||||
record(stringout, "so1") {
|
||||
record(stringout, "so4") {
|
||||
field(DTYP, "Soft Channel")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user