Merge remote-tracking branch 'int64b/master'
* int64b/master: (25 commits) std/rec: streamline variable names in int64in/int64out std/rec/test: recMiscTest check in64in/out ioc/db: dbUnitTest helps support int64 std/rec: ioc64in/outRecord typed rset minor std/rec: fix copyright in int64in sources std: remove SCM keywords from int64in and int64out sources std/rec: fix promptgroups in dbd.pod files std/rec: add and improve pod for int64in and int64out std/rec: add pod documentation content for int64out std/rec: add pod documentation for int64out record dbStatic: dbPutStringNum() overflow handling dbStatic: more informative error for bad field value dbStatic: dbPutStringNum(, "") not an error Release Notes cvtFastPerform: Rename reserved member names, fix for vxWorks Simplify epicsConvertDoubleToFloat() More cvtFastPerform changes Collate and report by precision Restructure cvtFastPerform, fix VxWorks build ...
This commit is contained in:
@@ -21,6 +21,45 @@
|
||||
|
||||
-->
|
||||
|
||||
<h3>IOC Database Support for 64-bit integers</h3>
|
||||
|
||||
<p>The IOC now supports the 64-bit integer field types <tt>DBF_INT64</tt> and
|
||||
<tt>DBF_UINT64</tt>, and there are new record types <tt>int64in</tt> and
|
||||
<tt>int64out</tt> derived from the <tt>longin</tt> and <tt>longout</tt> types
|
||||
respectively that use the <tt>DBF_INT64</tt> data type for their VAL and related
|
||||
fields. The usual range of Soft Channel device support are included for these
|
||||
new record types.</p>
|
||||
|
||||
<p>All internal IOC APIs such as dbAccess can handle the new field types and
|
||||
their associated request values <tt>DBR_INT64</tt> and <tt>DBR_UINT64</tt>,
|
||||
which are implemented using the <tt>epicsInt64</tt> and <tt>epicsUInt64</tt>
|
||||
typedef's from the <tt>epicsTypes.h</tt> header.</p>
|
||||
|
||||
<p>The waveform record type has been updated to support these new field types.
|
||||
<strong>All waveform device support layers must be updated to recognize the new
|
||||
type enumeration values</strong>, which had to be inserted before the
|
||||
<tt>FLOAT</tt> value in the enum <tt>dbfType</tt> and in <tt>menuFtype</tt>. C
|
||||
or C++ code can detect at compile-time whether this version of base provides
|
||||
64-bit support by checking for the presence of the <tt>DBR_INT64</tt> macro as
|
||||
follows (Note that <tt>DB<b>F</b>_INT64</tt> is an enum tag and not a
|
||||
preprocessor macro):</p>
|
||||
|
||||
<blockquote><pre>
|
||||
#ifdef DBR_INT64
|
||||
/* Code where Base has INT64 support */
|
||||
#else
|
||||
/* Code for older versions */
|
||||
#endif
|
||||
</pre></blockquote>
|
||||
|
||||
<p>Channel Access does not (and probably never will) directly support 64-bit
|
||||
integer types, so the new field types are presented to the CA server as
|
||||
<tt>DBF_DOUBLE</tt> values. This means that field values larger than 2^52
|
||||
(0x10_0000_0000_0000 = 4503599627370496) cannot be transported over Channel
|
||||
Access without their least significant bits being truncated. The EPICS V4
|
||||
pvAccess network protocol <em>can</em> transport 64-bit data types however, and
|
||||
a future release of the pvaSrv module will connect this ability to the IOC.</p>
|
||||
|
||||
<h3>Add EPICS_CA_MCAST_TTL</h3>
|
||||
|
||||
<p>Add option EPICS_CA_MCAST_TTL to set the Time To Live (TTL) of any IP multi-cast
|
||||
|
||||
@@ -77,6 +77,8 @@ static short mapDBFToDBR[DBF_NTYPES] = {
|
||||
/* DBF_USHORT => */ DBR_USHORT,
|
||||
/* DBF_LONG => */ DBR_LONG,
|
||||
/* DBF_ULONG => */ DBR_ULONG,
|
||||
/* DBF_INT64 => */ DBR_INT64,
|
||||
/* DBF_UINT64 => */ DBR_UINT64,
|
||||
/* DBF_FLOAT => */ DBR_FLOAT,
|
||||
/* DBF_DOUBLE => */ DBR_DOUBLE,
|
||||
/* DBF_ENUM, => */ DBR_ENUM,
|
||||
@@ -718,6 +720,8 @@ long dbValueSize(short dbr_type)
|
||||
sizeof(epicsUInt16), /* USHORT */
|
||||
sizeof(epicsInt32), /* LONG */
|
||||
sizeof(epicsUInt32), /* ULONG */
|
||||
sizeof(epicsInt64), /* INT64 */
|
||||
sizeof(epicsUInt64), /* UINT64 */
|
||||
sizeof(epicsFloat32), /* FLOAT */
|
||||
sizeof(epicsFloat64), /* DOUBLE */
|
||||
sizeof(epicsEnum16)}; /* ENUM */
|
||||
|
||||
@@ -454,6 +454,8 @@ static short mapDBFToDBR[DBF_NTYPES] =
|
||||
/* DBF_USHORT => */DBR_USHORT,
|
||||
/* DBF_LONG => */DBR_LONG,
|
||||
/* DBF_ULONG => */DBR_ULONG,
|
||||
/* DBF_INT64 => */DBR_INT64,
|
||||
/* DBF_UINT64 => */DBR_UINT64,
|
||||
/* DBF_FLOAT => */DBR_FLOAT,
|
||||
/* DBF_DOUBLE => */DBR_DOUBLE,
|
||||
/* DBF_ENUM, => */DBR_ENUM,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "cvtFast.h"
|
||||
#include "dbDefs.h"
|
||||
#include "ellLib.h"
|
||||
#include "epicsMutex.h"
|
||||
@@ -72,13 +73,13 @@ static void dbpr_msg_flush(TAB_BUFFER *pMsgBuff,int tab_size);
|
||||
|
||||
static char *dbf[DBF_NTYPES] = {
|
||||
"STRING","CHAR","UCHAR","SHORT","USHORT","LONG","ULONG",
|
||||
"FLOAT","DOUBLE","ENUM","MENU","DEVICE",
|
||||
"INT64","UINT64","FLOAT","DOUBLE","ENUM","MENU","DEVICE",
|
||||
"INLINK","OUTLINK","FWDLINK","NOACCESS"
|
||||
};
|
||||
|
||||
static char *dbr[DBR_ENUM+2] = {
|
||||
"STRING","CHAR","UCHAR","SHORT","USHORT","LONG","ULONG",
|
||||
"FLOAT","DOUBLE","ENUM","NOACCESS"
|
||||
"INT64","UINT64","FLOAT","DOUBLE","ENUM","NOACCESS"
|
||||
};
|
||||
|
||||
long dba(const char*pname)
|
||||
@@ -484,6 +485,16 @@ long dbtgf(const char *pname)
|
||||
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
|
||||
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
|
||||
|
||||
dbr_type = DBR_INT64;
|
||||
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt64)));
|
||||
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
|
||||
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
|
||||
|
||||
dbr_type = DBR_UINT64;
|
||||
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt64)));
|
||||
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
|
||||
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
|
||||
|
||||
dbr_type = DBR_FLOAT;
|
||||
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat32)));
|
||||
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
|
||||
@@ -510,17 +521,7 @@ long dbtpf(const char *pname, const char *pvalue)
|
||||
long buffer[100];
|
||||
long *pbuffer = buffer;
|
||||
DBADDR addr;
|
||||
long status = 0;
|
||||
long options, no_elements;
|
||||
char *pend;
|
||||
long val_long;
|
||||
int validLong;
|
||||
unsigned long val_ulong;
|
||||
int validULong;
|
||||
int valid = 1;
|
||||
int put_type;
|
||||
epicsFloat32 fvalue;
|
||||
epicsFloat64 dvalue;
|
||||
static TAB_BUFFER msg_Buff;
|
||||
TAB_BUFFER *pMsgBuff = &msg_Buff;
|
||||
char *pmsg = pMsgBuff->message;
|
||||
@@ -530,91 +531,90 @@ long dbtpf(const char *pname, const char *pvalue)
|
||||
printf("Usage: dbtpf \"pv name\", \"value\"\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (nameToAddr(pname, &addr))
|
||||
return -1;
|
||||
|
||||
val_long = strtol(pvalue, &pend, 10);
|
||||
validLong = (*pend == 0);
|
||||
|
||||
val_ulong = strtoul(pvalue, &pend, 10);
|
||||
validULong = (*pend == 0);
|
||||
|
||||
for (put_type = DBR_STRING; put_type <= DBF_ENUM; put_type++) {
|
||||
union {
|
||||
epicsInt8 i8;
|
||||
epicsUInt8 u8;
|
||||
epicsInt16 i16;
|
||||
epicsUInt16 u16;
|
||||
epicsInt32 i32;
|
||||
epicsUInt32 u32;
|
||||
epicsInt64 i64;
|
||||
epicsUInt64 u64;
|
||||
epicsFloat32 f32;
|
||||
epicsFloat64 f64;
|
||||
epicsEnum16 e16;
|
||||
} val;
|
||||
const void *pval = &val;
|
||||
int valid = 1;
|
||||
|
||||
switch (put_type) {
|
||||
case DBR_STRING:
|
||||
status = dbPutField(&addr, put_type, pvalue, 1L);
|
||||
pval = pvalue;
|
||||
break;
|
||||
case DBR_CHAR:
|
||||
if ((valid = validLong)) {
|
||||
epicsInt8 val_i8 = (epicsInt8)val_long;
|
||||
status = dbPutField(&addr, put_type, &val_i8, 1L);
|
||||
}
|
||||
valid = !epicsParseInt8(pvalue, &val.i8, 10, NULL);
|
||||
break;
|
||||
case DBR_UCHAR:
|
||||
if ((valid = validULong)) {
|
||||
epicsUInt8 val_u8 = (epicsUInt8)val_ulong;
|
||||
status = dbPutField(&addr, put_type, &val_u8, 1L);
|
||||
}
|
||||
valid = !epicsParseUInt8(pvalue, &val.u8, 10, NULL);
|
||||
break;
|
||||
case DBR_SHORT:
|
||||
if ((valid = validLong)) {
|
||||
epicsInt16 val_i16 = (epicsInt16) val_long;
|
||||
status = dbPutField(&addr, put_type, &val_i16,1L);
|
||||
}
|
||||
valid = !epicsParseInt16(pvalue, &val.i16, 10, NULL);
|
||||
break;
|
||||
case DBR_USHORT:
|
||||
if ((valid = validULong)) {
|
||||
epicsUInt16 val_u16 = (epicsUInt16) val_ulong;
|
||||
status = dbPutField(&addr, put_type, &val_u16, 1L);
|
||||
}
|
||||
valid = !epicsParseUInt16(pvalue, &val.u16, 10, NULL);
|
||||
break;
|
||||
case DBR_LONG:
|
||||
if ((valid = validLong)) {
|
||||
epicsInt32 val_i32 = val_long;
|
||||
status = dbPutField(&addr, put_type,&val_i32,1L);
|
||||
}
|
||||
valid = !epicsParseInt32(pvalue, &val.i32, 10, NULL);
|
||||
break;
|
||||
case DBR_ULONG:
|
||||
if ((valid = validULong)) {
|
||||
epicsUInt32 val_u32 = val_ulong;
|
||||
status = dbPutField(&addr, put_type, &val_u32, 1L);
|
||||
}
|
||||
valid = !epicsParseUInt32(pvalue, &val.u32, 10, NULL);
|
||||
break;
|
||||
case DBR_INT64:
|
||||
valid = !epicsParseInt64(pvalue, &val.i64, 10, NULL);
|
||||
break;
|
||||
case DBR_UINT64:
|
||||
valid = !epicsParseUInt64(pvalue, &val.u64, 10, NULL);
|
||||
break;
|
||||
case DBR_FLOAT:
|
||||
if ((valid = epicsScanFloat(pvalue, &fvalue) == 1))
|
||||
status = dbPutField(&addr, put_type, &fvalue, 1L);
|
||||
valid = !epicsParseFloat32(pvalue, &val.f32, NULL);
|
||||
break;
|
||||
case DBR_DOUBLE:
|
||||
if ((valid = epicsScanDouble(pvalue, &dvalue) == 1))
|
||||
status = dbPutField(&addr, put_type, &dvalue, 1L);
|
||||
valid = !epicsParseFloat64(pvalue, &val.f64, NULL);
|
||||
break;
|
||||
case DBR_ENUM:
|
||||
if ((valid = validULong)) {
|
||||
epicsEnum16 val_e16 = (epicsEnum16) val_ulong;
|
||||
status = dbPutField(&addr, put_type, &val_e16, 1L);
|
||||
}
|
||||
valid = !epicsParseUInt16(pvalue, &val.e16, 10, NULL);
|
||||
break;
|
||||
}
|
||||
if (valid) {
|
||||
long status = dbPutField(&addr, put_type, pval, 1);
|
||||
|
||||
if (status) {
|
||||
printf("Put as DBR_%s Failed.\n", dbr[put_type]);
|
||||
} else {
|
||||
printf("Put as DBR_%-6s Ok, result as ", dbr[put_type]);
|
||||
no_elements = MIN(addr.no_elements,
|
||||
printf("Put as DBR_%-6s Failed.\n", dbr[put_type]);
|
||||
}
|
||||
else {
|
||||
long options = 0;
|
||||
long no_elements = MIN(addr.no_elements,
|
||||
((sizeof(buffer))/addr.field_size));
|
||||
options = 0;
|
||||
|
||||
printf("Put as DBR_%-6s Ok, result as ", dbr[put_type]);
|
||||
status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
|
||||
&options, &no_elements, NULL);
|
||||
printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L,
|
||||
no_elements, pMsgBuff, tab_size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("Cvt to DBR_%s failed.\n", dbr[put_type]);
|
||||
}
|
||||
}
|
||||
|
||||
pmsg[0] = '\0';
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
return(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
long dbior(const char *pdrvName,int interest_level)
|
||||
@@ -732,10 +732,8 @@ static void printBuffer(
|
||||
long status, short dbr_type, void *pbuffer, long reqOptions,
|
||||
long retOptions, long no_elements, TAB_BUFFER *pMsgBuff, int tab_size)
|
||||
{
|
||||
epicsInt32 val_i32;
|
||||
epicsUInt32 val_u32;
|
||||
char *pmsg = pMsgBuff->message;
|
||||
size_t i, len;
|
||||
int i;
|
||||
|
||||
if (reqOptions & DBR_STATUS) {
|
||||
if (retOptions & DBR_STATUS) {
|
||||
@@ -898,221 +896,166 @@ static void printBuffer(
|
||||
if (no_elements == 0)
|
||||
return;
|
||||
|
||||
switch (dbr_type) {
|
||||
case (DBR_STRING):
|
||||
if (no_elements == 1)
|
||||
sprintf(pmsg, "DBR_STRING: ");
|
||||
else
|
||||
sprintf(pmsg, "DBR_STRING[%ld]: ", no_elements);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
if (status != 0) {
|
||||
sprintf(pmsg, "DBR_STRING: failed.");
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
break;
|
||||
}
|
||||
if (no_elements == 1)
|
||||
sprintf(pmsg, "DBF_%s: ", dbr[dbr_type]);
|
||||
else
|
||||
sprintf(pmsg, "DBF_%s[%ld]: ", dbr[dbr_type], no_elements);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
|
||||
for(i=0; i<no_elements; i++) {
|
||||
len = strlen(pbuffer);
|
||||
if (len > 0) {
|
||||
sprintf(pmsg, " \"%s\"", (char *)pbuffer);
|
||||
if (status != 0) {
|
||||
strcpy(pmsg, "failed.");
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
}
|
||||
else {
|
||||
switch (dbr_type) {
|
||||
case DBR_STRING:
|
||||
for(i=0; i<no_elements; i++) {
|
||||
size_t len = strlen(pbuffer);
|
||||
|
||||
strcpy(pmsg, "\"");
|
||||
epicsStrnEscapedFromRaw(pmsg+1, MAXLINE - 3,
|
||||
(char *)pbuffer, len);
|
||||
strcat(pmsg, "\"");
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pbuffer = (char *)pbuffer + MAX_STRING_SIZE;
|
||||
}
|
||||
pbuffer = (char *)pbuffer + MAX_STRING_SIZE;
|
||||
}
|
||||
break;
|
||||
|
||||
case (DBR_CHAR):
|
||||
if (no_elements == 1)
|
||||
sprintf(pmsg, "DBR_CHAR: ");
|
||||
else
|
||||
sprintf(pmsg, "DBR_CHAR[%ld]: ", no_elements);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
if (status != 0) {
|
||||
sprintf(pmsg, " failed.");
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
break;
|
||||
}
|
||||
|
||||
if (no_elements == 1) {
|
||||
val_i32 = *(epicsInt8 *) pbuffer;
|
||||
sprintf(pmsg, "%-9d 0x%-9x", val_i32, val_i32);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
}
|
||||
else {
|
||||
for (i = 0; i < no_elements; i+= MAXLINE - 5) {
|
||||
int width = no_elements - i;
|
||||
if (width > MAXLINE - 5) width = MAXLINE - 5;
|
||||
sprintf(pmsg, " \"%.*s\"", width, (char *)pbuffer + i);
|
||||
if (i + MAXLINE - 5 < no_elements) strcat(pmsg, " +");
|
||||
case DBR_CHAR:
|
||||
if (no_elements == 1) {
|
||||
epicsInt32 val = *(epicsInt8 *) pbuffer;
|
||||
|
||||
if (isprint(val))
|
||||
sprintf(pmsg, "%d = 0x%x = '%c'", val, val & 0xff, val);
|
||||
else
|
||||
sprintf(pmsg, "%d = 0x%x", val, val & 0xff);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
} else {
|
||||
size_t len = epicsStrnLen(pbuffer, no_elements);
|
||||
|
||||
i = 0;
|
||||
while (len > 0) {
|
||||
int chunk = (len > MAXLINE - 5) ? MAXLINE - 5 : len;
|
||||
|
||||
sprintf(pmsg, "\"%.*s\"", chunk, (char *)pbuffer + i);
|
||||
len -= chunk;
|
||||
if (len > 0)
|
||||
strcat(pmsg, " +");
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case (DBR_UCHAR):
|
||||
if (no_elements == 1)
|
||||
sprintf(pmsg, "DBR_UCHAR: ");
|
||||
else
|
||||
sprintf(pmsg, "DBR_UCHAR[%ld]: ", no_elements);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
if (status != 0) {
|
||||
sprintf(pmsg, " failed.");
|
||||
case DBR_UCHAR:
|
||||
for (i = 0; i < no_elements; i++) {
|
||||
epicsUInt32 val = *(epicsUInt8 *) pbuffer;
|
||||
|
||||
sprintf(pmsg, "%u = 0x%x", val, val);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pbuffer = (char *)pbuffer + sizeof(epicsUInt8);
|
||||
}
|
||||
break;
|
||||
|
||||
case DBR_SHORT:
|
||||
for (i = 0; i < no_elements; i++) {
|
||||
epicsInt16 val = *(epicsInt16 *) pbuffer;
|
||||
|
||||
sprintf(pmsg, "%hd = 0x%hx", val, val);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pbuffer = (char *)pbuffer + sizeof(epicsInt16);
|
||||
}
|
||||
break;
|
||||
|
||||
case DBR_USHORT:
|
||||
for (i = 0; i < no_elements; i++) {
|
||||
epicsUInt16 val = *(epicsUInt16 *) pbuffer;
|
||||
|
||||
sprintf(pmsg, "%hu = 0x%hx", val, val);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pbuffer = (char *)pbuffer + sizeof(epicsUInt16);
|
||||
}
|
||||
break;
|
||||
|
||||
case DBR_LONG:
|
||||
for (i = 0; i < no_elements; i++) {
|
||||
epicsInt32 val = *(epicsInt32 *) pbuffer;
|
||||
|
||||
sprintf(pmsg, "%d = 0x%x", val, val);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pbuffer = (char *)pbuffer + sizeof(epicsInt32);
|
||||
}
|
||||
break;
|
||||
|
||||
case DBR_ULONG:
|
||||
for (i = 0; i < no_elements; i++) {
|
||||
epicsUInt32 val = *(epicsUInt32 *) pbuffer;
|
||||
|
||||
sprintf(pmsg, "%u = 0x%x", val, val);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pbuffer = (char *)pbuffer + sizeof(epicsUInt32);
|
||||
}
|
||||
break;
|
||||
|
||||
case DBR_INT64:
|
||||
for (i = 0; i < no_elements; i++) {
|
||||
epicsInt64 val = *(epicsInt64 *) pbuffer;
|
||||
|
||||
pmsg += cvtInt64ToString(val, pmsg);
|
||||
strcpy(pmsg, " = ");
|
||||
pmsg += 3;
|
||||
cvtInt64ToHexString(val, pmsg);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pmsg = pMsgBuff->message;
|
||||
pbuffer = (char *)pbuffer + sizeof(epicsInt64);
|
||||
}
|
||||
break;
|
||||
|
||||
case DBR_UINT64:
|
||||
for (i = 0; i < no_elements; i++) {
|
||||
epicsUInt64 val = *(epicsUInt64 *) pbuffer;
|
||||
|
||||
pmsg += cvtUInt64ToString(val, pmsg);
|
||||
strcpy(pmsg, " = ");
|
||||
pmsg += 3;
|
||||
cvtUInt64ToHexString(val, pmsg);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pmsg = pMsgBuff->message;
|
||||
pbuffer = (char *)pbuffer + sizeof(epicsUInt64);
|
||||
}
|
||||
break;
|
||||
|
||||
case DBR_FLOAT:
|
||||
for (i = 0; i < no_elements; i++) {
|
||||
sprintf(pmsg, "%.6g", *((epicsFloat32 *) pbuffer));
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pbuffer = (char *)pbuffer + sizeof(epicsFloat32);
|
||||
}
|
||||
break;
|
||||
|
||||
case DBR_DOUBLE:
|
||||
for (i = 0; i < no_elements; i++) {
|
||||
sprintf(pmsg, "%.12g", *((epicsFloat64 *) pbuffer));
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pbuffer = (char *)pbuffer + sizeof(epicsFloat64);
|
||||
}
|
||||
break;
|
||||
|
||||
case DBR_ENUM:
|
||||
for (i = 0; i < no_elements; i++) {
|
||||
sprintf(pmsg, "%u", *((epicsEnum16 *) pbuffer));
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pbuffer = (char *)pbuffer + sizeof(epicsEnum16);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
sprintf(pmsg, "Bad DBR type %d", dbr_type);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < no_elements; i++) {
|
||||
val_u32 = *(epicsUInt8 *) pbuffer;
|
||||
sprintf(pmsg, "%-9u 0x%-9x", val_u32, val_u32);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pbuffer = (char *)pbuffer + sizeof(epicsUInt8);
|
||||
}
|
||||
break;
|
||||
|
||||
case (DBR_SHORT):
|
||||
if (no_elements == 1)
|
||||
sprintf(pmsg, "DBR_SHORT: ");
|
||||
else
|
||||
sprintf(pmsg, "DBR_SHORT[%ld]: ", no_elements);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
if (status != 0) {
|
||||
sprintf(pmsg, " failed.");
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < no_elements; i++) {
|
||||
val_i32 = *(epicsInt16 *) pbuffer;
|
||||
sprintf(pmsg, "%-9d 0x%-9x", val_i32, val_i32);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pbuffer = (char *)pbuffer + sizeof(epicsInt16);
|
||||
}
|
||||
break;
|
||||
|
||||
case (DBR_USHORT):
|
||||
if (no_elements == 1)
|
||||
sprintf(pmsg, "DBR_USHORT: ");
|
||||
else
|
||||
sprintf(pmsg, "DBR_USHORT[%ld]: ", no_elements);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
if (status != 0) {
|
||||
sprintf(pmsg, " failed.");
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < no_elements; i++) {
|
||||
val_u32 = *(epicsUInt16 *) pbuffer;
|
||||
sprintf(pmsg, "%-9u 0x%-9x", val_u32, val_u32);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pbuffer = (char *)pbuffer + sizeof(epicsUInt16);
|
||||
}
|
||||
break;
|
||||
|
||||
case (DBR_LONG):
|
||||
if (no_elements == 1)
|
||||
sprintf(pmsg, "DBR_LONG: ");
|
||||
else
|
||||
sprintf(pmsg, "DBR_LONG[%ld]: ", no_elements);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
if (status != 0) {
|
||||
sprintf(pmsg, " failed.");
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < no_elements; i++) {
|
||||
val_i32 = *(epicsInt32 *) pbuffer;
|
||||
sprintf(pmsg, "%-9d 0x%-9x", val_i32, val_i32);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pbuffer = (char *)pbuffer + sizeof(epicsInt32);
|
||||
}
|
||||
break;
|
||||
|
||||
case (DBR_ULONG):
|
||||
if (no_elements == 1)
|
||||
sprintf(pmsg, "DBR_ULONG: ");
|
||||
else
|
||||
sprintf(pmsg, "DBR_ULONG[%ld]: ", no_elements);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
if (status != 0) {
|
||||
sprintf(pmsg, " failed.");
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < no_elements; i++) {
|
||||
val_u32 = *(epicsUInt32 *) pbuffer;
|
||||
sprintf(pmsg, "%-9u 0x%-9x", val_u32, val_u32);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pbuffer = (char *)pbuffer + sizeof(epicsUInt32);
|
||||
}
|
||||
break;
|
||||
|
||||
case (DBR_FLOAT):
|
||||
if (no_elements == 1)
|
||||
sprintf(pmsg, "DBR_FLOAT: ");
|
||||
else
|
||||
sprintf(pmsg, "DBR_FLOAT[%ld]: ", no_elements);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
if (status != 0) {
|
||||
sprintf(pmsg, " failed.");
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < no_elements; i++) {
|
||||
sprintf(pmsg, "%-13.6g", *((epicsFloat32 *) pbuffer));
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pbuffer = (char *)pbuffer + sizeof(epicsFloat32);
|
||||
}
|
||||
break;
|
||||
|
||||
case (DBR_DOUBLE):
|
||||
if (no_elements == 1)
|
||||
sprintf(pmsg, "DBR_DOUBLE: ");
|
||||
else
|
||||
sprintf(pmsg, "DBR_DOUBLE[%ld]: ", no_elements);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
if (status != 0) {
|
||||
sprintf(pmsg, " failed.");
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < no_elements; i++) {
|
||||
sprintf(pmsg, "%-13.6g", *((epicsFloat64 *) pbuffer));
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pbuffer = (char *)pbuffer + sizeof(epicsFloat64);
|
||||
}
|
||||
break;
|
||||
|
||||
case (DBR_ENUM):
|
||||
if (no_elements == 1)
|
||||
sprintf(pmsg, "DBR_ENUM: ");
|
||||
else
|
||||
sprintf(pmsg, "DBR_ENUM[%ld]: ", no_elements);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
if (status != 0) {
|
||||
sprintf(pmsg, " failed.");
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < no_elements; i++) {
|
||||
sprintf(pmsg, "%-9u", *((epicsEnum16 *) pbuffer));
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
pbuffer = (char *)pbuffer + sizeof(epicsEnum16);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf(" illegal request type.");
|
||||
break;
|
||||
}
|
||||
|
||||
dbpr_msg_flush(pMsgBuff, tab_size);
|
||||
return;
|
||||
}
|
||||
|
||||
static int dbpr_report(
|
||||
@@ -1144,7 +1087,6 @@ static int dbpr_report(
|
||||
pfield = ((char *)paddr->precord) + pdbFldDes->offset;
|
||||
if (pdbFldDes->interest > interest_level )
|
||||
continue;
|
||||
|
||||
switch (pdbFldDes->field_type) {
|
||||
case DBF_STRING:
|
||||
case DBF_USHORT:
|
||||
@@ -1155,6 +1097,8 @@ static int dbpr_report(
|
||||
case DBF_SHORT:
|
||||
case DBF_LONG:
|
||||
case DBF_ULONG:
|
||||
case DBF_INT64:
|
||||
case DBF_UINT64:
|
||||
case DBF_DOUBLE:
|
||||
case DBF_MENU:
|
||||
case DBF_DEVICE:
|
||||
|
||||
@@ -134,6 +134,8 @@ long testdbVPutField(const char* pv, short dbrType, va_list ap)
|
||||
OP(DBR_USHORT, int, uInt16);
|
||||
OP(DBR_LONG, int, int32);
|
||||
OP(DBR_ULONG, unsigned int, uInt32);
|
||||
OP(DBR_INT64, long long, int64);
|
||||
OP(DBR_UINT64, unsigned long long, uInt64);
|
||||
OP(DBR_FLOAT, double, float32);
|
||||
OP(DBR_DOUBLE, double, float64);
|
||||
OP(DBR_ENUM, int, enum16);
|
||||
@@ -220,10 +222,14 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap)
|
||||
OP(DBR_USHORT, int, uInt16, "%d");
|
||||
OP(DBR_LONG, int, int32, "%d");
|
||||
OP(DBR_ULONG, unsigned int, uInt32, "%u");
|
||||
OP(DBR_INT64, long long, int64, "%lld");
|
||||
OP(DBR_UINT64, unsigned long long, uInt64, "%llu");
|
||||
OP(DBR_FLOAT, double, float32, "%e");
|
||||
OP(DBR_DOUBLE, double, float64, "%e");
|
||||
OP(DBR_ENUM, int, enum16, "%d");
|
||||
#undef OP
|
||||
default:
|
||||
testFail("dbGetField(\"%s\", %d) -> unsupported dbf", pv, dbrType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,6 +301,8 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign
|
||||
OP(DBR_USHORT, unsigned short, "%u");
|
||||
OP(DBR_LONG, int, "%d");
|
||||
OP(DBR_ULONG, unsigned int, "%u");
|
||||
OP(DBR_INT64, long long, "%lld");
|
||||
OP(DBR_UINT64, unsigned long long, "%llu");
|
||||
OP(DBR_FLOAT, float, "%e");
|
||||
OP(DBR_DOUBLE, double, "%e");
|
||||
OP(DBR_ENUM, int, "%d");
|
||||
|
||||
@@ -38,6 +38,8 @@ epicsShareFunc void testdbCleanup(void);
|
||||
*
|
||||
* int for DBR_UCHAR, DBR_CHAR, DBR_USHORT, DBR_SHORT, DBR_LONG
|
||||
* unsigned int for DBR_ULONG
|
||||
* long long for DBF_INT64
|
||||
* unsigned long long for DBF_UINT64
|
||||
* double for DBR_FLOAT and DBR_DOUBLE
|
||||
* const char* for DBR_STRING
|
||||
*
|
||||
|
||||
@@ -23,8 +23,8 @@ epicsShareExtern struct dbBase *pdbbase;
|
||||
epicsShareExtern volatile int interruptAccept;
|
||||
|
||||
/*Definitions that allow old database access to use new conversion routines*/
|
||||
#define newDBF_DEVICE 11
|
||||
#define newDBR_ENUM 9
|
||||
#define newDBF_DEVICE 13
|
||||
#define newDBR_ENUM 11
|
||||
epicsShareExtern long (*dbGetConvertRoutine[newDBF_DEVICE+1][newDBR_ENUM+1])
|
||||
(struct dbAddr *paddr, void *pbuffer,long nRequest,
|
||||
long no_elements, long offset);
|
||||
@@ -43,11 +43,11 @@ epicsShareExtern unsigned short dbDBRnewToDBRold[newDBR_ENUM+1];
|
||||
epicsShareDef unsigned short dbDBRoldToDBFnew[DBR_DOUBLE+1] = {
|
||||
0, /*DBR_STRING to DBF_STRING*/
|
||||
3, /*DBR_INT to DBF_SHORT*/
|
||||
7, /*DBR_FLOAT to DBF_FLOAT*/
|
||||
9, /*DBR_ENUM to DBF_ENUM*/
|
||||
9, /*DBR_FLOAT to DBF_FLOAT*/
|
||||
11, /*DBR_ENUM to DBF_ENUM*/
|
||||
1, /*DBR_CHAR to DBF_CHAR*/
|
||||
5, /*DBR_LONG to DBF_LONG*/
|
||||
8 /*DBR_DOUBLE to DBF_DOUBLE*/
|
||||
10 /*DBR_DOUBLE to DBF_DOUBLE*/
|
||||
};
|
||||
epicsShareDef unsigned short dbDBRnewToDBRold[newDBR_ENUM+1] = {
|
||||
0, /*DBR_STRING to DBR_STRING*/
|
||||
@@ -57,6 +57,8 @@ epicsShareDef unsigned short dbDBRnewToDBRold[newDBR_ENUM+1] = {
|
||||
5, /*DBR_USHORT to DBR_LONG*/
|
||||
5, /*DBR_LONG to DBR_LONG*/
|
||||
6, /*DBR_ULONG to DBR_DOUBLE*/
|
||||
6, /*DBR_INT64 to DBR_DOUBLE*/
|
||||
6, /*DBR_UINT64 to DBR_DOUBLE*/
|
||||
2, /*DBR_FLOAT to DBR_FLOAT*/
|
||||
6, /*DBR_DOUBLE to DBR_DOUBLE*/
|
||||
3, /*DBR_ENUM to DBR_ENUM*/
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
# Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# EPICS BASE Versions 3.13.7
|
||||
# and higher are distributed subject to a Software License Agreement found
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
menu(menuFtype) {
|
||||
@@ -15,6 +14,8 @@ menu(menuFtype) {
|
||||
choice(menuFtypeUSHORT,"USHORT")
|
||||
choice(menuFtypeLONG,"LONG")
|
||||
choice(menuFtypeULONG,"ULONG")
|
||||
choice(menuFtypeINT64,"INT64")
|
||||
choice(menuFtypeUINT64,"UINT64")
|
||||
choice(menuFtypeFLOAT,"FLOAT")
|
||||
choice(menuFtypeDOUBLE,"DOUBLE")
|
||||
choice(menuFtypeENUM,"ENUM")
|
||||
|
||||
@@ -120,6 +120,8 @@ void recGblGetPrec(const struct dbAddr *paddr, long *precision)
|
||||
case DBF_USHORT:
|
||||
case DBF_LONG:
|
||||
case DBF_ULONG:
|
||||
case DBF_INT64:
|
||||
case DBF_UINT64:
|
||||
*precision = 0;
|
||||
break;
|
||||
|
||||
@@ -345,7 +347,15 @@ static void getMaxRangeValues(short field_type, double *pupper_limit,
|
||||
*plower_limit = -2147483648.0;
|
||||
break;
|
||||
case DBF_ULONG:
|
||||
*pupper_limit = (double) 0xffffffffU;
|
||||
*pupper_limit = 4294967295.0;
|
||||
*plower_limit = 0.0;
|
||||
break;
|
||||
case DBF_INT64:
|
||||
*pupper_limit = 9223372036854775808.0;
|
||||
*plower_limit = -9223372036854775808.0;
|
||||
break;
|
||||
case DBF_UINT64:
|
||||
*pupper_limit = 18446744073709551615.0;
|
||||
*plower_limit = 0.0;
|
||||
break;
|
||||
case DBF_FLOAT:
|
||||
|
||||
@@ -11,7 +11,7 @@ record(x, "recempty") {
|
||||
|
||||
record(x, "recoverwrite") {
|
||||
# first with non-default values
|
||||
# field(DTYP, "Scan I/O")
|
||||
field(DTYP, "Scan I/O")
|
||||
field(DESC, "hello")
|
||||
field(PHAS, "2")
|
||||
field(TSE , "5")
|
||||
|
||||
@@ -29,6 +29,8 @@ typedef enum {
|
||||
DBF_USHORT,
|
||||
DBF_LONG,
|
||||
DBF_ULONG,
|
||||
DBF_INT64,
|
||||
DBF_UINT64,
|
||||
DBF_FLOAT,
|
||||
DBF_DOUBLE,
|
||||
DBF_ENUM,
|
||||
@@ -56,6 +58,8 @@ epicsShareDef mapdbfType pamapdbfType[DBF_NTYPES] = {
|
||||
{"DBF_USHORT",DBF_USHORT},
|
||||
{"DBF_LONG",DBF_LONG},
|
||||
{"DBF_ULONG",DBF_ULONG},
|
||||
{"DBF_INT64",DBF_INT64},
|
||||
{"DBF_UINT64",DBF_UINT64},
|
||||
{"DBF_FLOAT",DBF_FLOAT},
|
||||
{"DBF_DOUBLE",DBF_DOUBLE},
|
||||
{"DBF_ENUM",DBF_ENUM},
|
||||
@@ -76,6 +80,8 @@ epicsShareDef mapdbfType pamapdbfType[DBF_NTYPES] = {
|
||||
#define DBR_USHORT DBF_USHORT
|
||||
#define DBR_LONG DBF_LONG
|
||||
#define DBR_ULONG DBF_ULONG
|
||||
#define DBR_INT64 DBF_INT64
|
||||
#define DBR_UINT64 DBF_UINT64
|
||||
#define DBR_FLOAT DBF_FLOAT
|
||||
#define DBR_DOUBLE DBF_DOUBLE
|
||||
#define DBR_ENUM DBF_ENUM
|
||||
|
||||
@@ -1086,10 +1086,12 @@ static void dbRecordField(char *name,char *value)
|
||||
dbTranslateEscape(value, value); /* in-place; safe & legal */
|
||||
status = dbPutString(pdbentry,value);
|
||||
if(status) {
|
||||
epicsPrintf("Can't set \"%s.%s\" to \"%s\"\n",
|
||||
dbGetRecordName(pdbentry), name, value);
|
||||
yyerror(NULL);
|
||||
return;
|
||||
char msg[128];
|
||||
errSymLookup(status, msg, sizeof(msg));
|
||||
epicsPrintf("Can't set \"%s.%s\" to \"%s\" %s\n",
|
||||
dbGetRecordName(pdbentry), name, value, msg);
|
||||
yyerror(NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...)
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void ulongToHexString(epicsUInt32 source,char *pdest)
|
||||
static void ulongToHexString(epicsUInt32 source, char *pdest)
|
||||
{
|
||||
static const char hex_digit_to_ascii[16] = "0123456789abcdef";
|
||||
epicsUInt32 val,temp;
|
||||
@@ -1893,6 +1893,8 @@ char * dbGetString(DBENTRY *pdbentry)
|
||||
case DBF_ENUM:
|
||||
case DBF_LONG:
|
||||
case DBF_ULONG:
|
||||
case DBF_INT64:
|
||||
case DBF_UINT64:
|
||||
case DBF_FLOAT:
|
||||
case DBF_DOUBLE:
|
||||
case DBF_MENU:
|
||||
@@ -2056,60 +2058,72 @@ char *dbGetStringNum(DBENTRY *pdbentry)
|
||||
cvttype = pflddes->base;
|
||||
switch (pflddes->field_type) {
|
||||
case DBF_CHAR:
|
||||
if (cvttype==CT_DECIMAL)
|
||||
cvtCharToString(*(char*)pfield, message);
|
||||
if (cvttype == CT_DECIMAL)
|
||||
cvtCharToString(*(char *) pfield, message);
|
||||
else
|
||||
ulongToHexString((epicsUInt32)(*(char*)pfield),message);
|
||||
ulongToHexString(*(char *) pfield, message);
|
||||
break;
|
||||
case DBF_UCHAR:
|
||||
if (cvttype==CT_DECIMAL)
|
||||
cvtUcharToString(*(unsigned char*)pfield, message);
|
||||
cvtUcharToString(*(epicsUInt8 *) pfield, message);
|
||||
else
|
||||
ulongToHexString((epicsUInt32)(*(unsigned char*)pfield),message);
|
||||
ulongToHexString(*(epicsUInt8 *) pfield, message);
|
||||
break;
|
||||
case DBF_SHORT:
|
||||
if (cvttype==CT_DECIMAL)
|
||||
cvtShortToString(*(short*)pfield, message);
|
||||
cvtShortToString(*(epicsInt16 *) pfield, message);
|
||||
else
|
||||
ulongToHexString((epicsUInt32)(*(short*)pfield),message);
|
||||
ulongToHexString(*(epicsInt16 *) pfield, message);
|
||||
break;
|
||||
case DBF_USHORT:
|
||||
case DBF_ENUM:
|
||||
if (cvttype==CT_DECIMAL)
|
||||
cvtUshortToString(*(unsigned short*)pfield, message);
|
||||
cvtUshortToString(*(epicsUInt16 *) pfield, message);
|
||||
else
|
||||
ulongToHexString((epicsUInt32)(*(unsigned short*)pfield),message);
|
||||
ulongToHexString(*(epicsUInt16 *) pfield, message);
|
||||
break;
|
||||
case DBF_LONG:
|
||||
if (cvttype==CT_DECIMAL)
|
||||
cvtLongToString(*(epicsInt32*)pfield, message);
|
||||
cvtLongToString(*(epicsInt32 *) pfield, message);
|
||||
else
|
||||
ulongToHexString((epicsUInt32)(*(epicsInt32*)pfield), message);
|
||||
ulongToHexString(*(epicsInt32 *) pfield, message);
|
||||
break;
|
||||
case DBF_ULONG:
|
||||
if (cvttype==CT_DECIMAL)
|
||||
cvtUlongToString(*(epicsUInt32 *)pfield, message);
|
||||
cvtUlongToString(*(epicsUInt32 *) pfield, message);
|
||||
else
|
||||
ulongToHexString(*(epicsUInt32*)pfield, message);
|
||||
ulongToHexString(*(epicsUInt32 *) pfield, message);
|
||||
break;
|
||||
case DBF_INT64:
|
||||
if (cvttype==CT_DECIMAL)
|
||||
cvtInt64ToString(*(epicsInt64 *) pfield, message);
|
||||
else
|
||||
cvtInt64ToHexString(*(epicsInt64 *) pfield, message);
|
||||
break;
|
||||
case DBF_UINT64:
|
||||
if (cvttype==CT_DECIMAL)
|
||||
cvtUInt64ToString(*(epicsUInt32 *) pfield, message);
|
||||
else
|
||||
cvtUInt64ToHexString(*(epicsUInt32 *) pfield, message);
|
||||
break;
|
||||
case DBF_FLOAT:
|
||||
floatToString(*(float *)pfield,message);
|
||||
floatToString(*(epicsFloat32 *) pfield, message);
|
||||
break;
|
||||
case DBF_DOUBLE:
|
||||
doubleToString(*(double *)pfield,message);
|
||||
doubleToString(*(epicsFloat64 *) pfield, message);
|
||||
break;
|
||||
case DBF_MENU:
|
||||
{
|
||||
dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt;
|
||||
short choice_ind;
|
||||
char *pchoice;
|
||||
dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt;
|
||||
epicsEnum16 choice_ind;
|
||||
char *pchoice;
|
||||
|
||||
if (!pfield) {
|
||||
dbMsgCpy(pdbentry, "Field not found");
|
||||
return message;
|
||||
}
|
||||
choice_ind = *((short *) pdbentry->pfield);
|
||||
if (!pdbMenu || choice_ind<0 || choice_ind>=pdbMenu->nChoice)
|
||||
choice_ind = *((epicsEnum16 *) pdbentry->pfield);
|
||||
if (!pdbMenu || choice_ind < 0 || choice_ind >= pdbMenu->nChoice)
|
||||
return NULL;
|
||||
pchoice = pdbMenu->papChoiceValue[choice_ind];
|
||||
dbMsgCpy(pdbentry, pchoice);
|
||||
@@ -2117,9 +2131,9 @@ char *dbGetStringNum(DBENTRY *pdbentry)
|
||||
break;
|
||||
case DBF_DEVICE:
|
||||
{
|
||||
dbDeviceMenu *pdbDeviceMenu;
|
||||
char *pchoice;
|
||||
short choice_ind;
|
||||
dbDeviceMenu *pdbDeviceMenu;
|
||||
epicsEnum16 choice_ind;
|
||||
char *pchoice;
|
||||
|
||||
if (!pfield) {
|
||||
dbMsgCpy(pdbentry, "Field not found");
|
||||
@@ -2128,7 +2142,7 @@ char *dbGetStringNum(DBENTRY *pdbentry)
|
||||
pdbDeviceMenu = dbGetDeviceMenu(pdbentry);
|
||||
if (!pdbDeviceMenu)
|
||||
return NULL;
|
||||
choice_ind = *((short *) pdbentry->pfield);
|
||||
choice_ind = *((epicsEnum16 *) pdbentry->pfield);
|
||||
if (choice_ind<0 || choice_ind>=pdbDeviceMenu->nChoice)
|
||||
return NULL;
|
||||
pchoice = pdbDeviceMenu->papChoice[choice_ind];
|
||||
@@ -2569,9 +2583,11 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring)
|
||||
case DBF_CHAR:
|
||||
case DBF_SHORT:
|
||||
case DBF_LONG:
|
||||
case DBF_INT64:
|
||||
case DBF_UCHAR:
|
||||
case DBF_USHORT:
|
||||
case DBF_ULONG:
|
||||
case DBF_UINT64:
|
||||
case DBF_ENUM:
|
||||
case DBF_FLOAT:
|
||||
case DBF_DOUBLE:
|
||||
@@ -2635,189 +2651,6 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring)
|
||||
return(status);
|
||||
}
|
||||
|
||||
char * dbVerify(DBENTRY *pdbentry,const char *pstring)
|
||||
{
|
||||
dbFldDes *pflddes = pdbentry->pflddes;
|
||||
char *message;
|
||||
int stringHasMacro=FALSE;
|
||||
|
||||
stringHasMacro = strstr(pstring,"$(") || strstr(pstring,"${");
|
||||
message = getpMessage(pdbentry);
|
||||
if(!pflddes) {strcpy(message,"fldDes not found"); return(message);}
|
||||
if(strstr(pstring,"$(") || strstr(pstring,"${")) return(NULL);
|
||||
switch (pflddes->field_type) {
|
||||
case DBF_STRING: {
|
||||
size_t length;
|
||||
|
||||
length=strlen(pstring);
|
||||
if(length>=pflddes->size) {
|
||||
sprintf(message,"string to big. max=%hd",pflddes->size);
|
||||
return(message);
|
||||
}
|
||||
if((pflddes->special == SPC_CALC) && !stringHasMacro) {
|
||||
char rpcl[RPCL_LEN];
|
||||
short err;
|
||||
long status;
|
||||
|
||||
status = postfix(pstring,rpcl,&err);
|
||||
if(status) {
|
||||
sprintf(message,"%s in CALC expression '%s'",
|
||||
calcErrorStr(err), pstring);
|
||||
return(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
return(NULL);
|
||||
case DBF_CHAR :
|
||||
case DBF_SHORT :
|
||||
case DBF_LONG:{
|
||||
long value;
|
||||
char *endp;
|
||||
|
||||
value = strtol(pstring,&endp,0);
|
||||
if(*endp!=0) {
|
||||
strcpy(message,"not an integer number");
|
||||
return(message);
|
||||
}
|
||||
switch (pflddes->field_type) {
|
||||
case DBF_CHAR :
|
||||
if(value<-128 || value>127) {
|
||||
strcpy(message,"must have -128<=value<=127");
|
||||
return(message);
|
||||
}
|
||||
return(NULL);
|
||||
case DBF_SHORT :
|
||||
if(value<-32768 || value>32767) {
|
||||
strcpy(message,"must have -32768<=value<=32767");
|
||||
return(message);
|
||||
}
|
||||
return(NULL);
|
||||
case DBF_LONG : return(NULL);
|
||||
default:
|
||||
errPrintf(-1,__FILE__, __LINE__,"Logic Error\n");
|
||||
return(NULL);
|
||||
}
|
||||
}
|
||||
case DBF_UCHAR:
|
||||
case DBF_USHORT:
|
||||
case DBF_ULONG:
|
||||
case DBF_ENUM:{
|
||||
unsigned long value;
|
||||
char *endp;
|
||||
|
||||
if(strchr(pstring,'-')) {
|
||||
strcpy(message,"not an unsigned number");
|
||||
return(message);
|
||||
}
|
||||
value = strtoul(pstring,&endp,0);
|
||||
if(*endp!=0) {
|
||||
strcpy(message,"not an integer number");
|
||||
return(message);
|
||||
}
|
||||
switch (pflddes->field_type) {
|
||||
case DBF_UCHAR :
|
||||
if(value>255) {
|
||||
strcpy(message,"must have 0<=value<=255");
|
||||
return(message);
|
||||
}
|
||||
return(NULL);
|
||||
case DBF_ENUM:
|
||||
case DBF_USHORT :
|
||||
if(value>65535) {
|
||||
strcpy(message,"must have 0<=value<=65535");
|
||||
return(message);
|
||||
}
|
||||
return(NULL);
|
||||
case DBF_ULONG : return(NULL);
|
||||
default:
|
||||
errPrintf(-1,__FILE__, __LINE__,"Logic Error\n");
|
||||
return(NULL);
|
||||
}
|
||||
}
|
||||
case DBF_FLOAT:
|
||||
case DBF_DOUBLE: {
|
||||
char *endp;
|
||||
|
||||
(void) epicsStrtod(pstring,&endp);
|
||||
if(*endp!=0) {
|
||||
strcpy(message,"not a number");
|
||||
return(message);
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
case DBF_MENU: {
|
||||
dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt;
|
||||
char *pchoice;
|
||||
int i;
|
||||
|
||||
if(!pdbMenu) return(NULL);
|
||||
for (i = 0; i < pdbMenu->nChoice; i++) {
|
||||
if(!(pchoice = pdbMenu->papChoiceValue[i])) continue;
|
||||
if(strcmp(pchoice, pstring) == 0) {
|
||||
return(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
strcpy(message,"Not a valid menu choice");
|
||||
return (message);
|
||||
case DBF_DEVICE: {
|
||||
dbDeviceMenu *pdbDeviceMenu;
|
||||
char *pchoice;
|
||||
int i;
|
||||
|
||||
pdbDeviceMenu = dbGetDeviceMenu(pdbentry);
|
||||
if(!pdbDeviceMenu) return(NULL);
|
||||
if(pdbDeviceMenu->nChoice == 0) return(NULL);
|
||||
for (i = 0; i < pdbDeviceMenu->nChoice; i++) {
|
||||
if (!(pchoice = pdbDeviceMenu->papChoice[i]))
|
||||
continue;
|
||||
if (strcmp(pchoice, pstring) == 0) {
|
||||
return(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
strcpy(message,"Not a valid menu choice");
|
||||
return (message);
|
||||
case DBF_INLINK:
|
||||
case DBF_OUTLINK:
|
||||
case DBF_FWDLINK:
|
||||
return(NULL);
|
||||
default: break;
|
||||
}
|
||||
strcpy(message,"Not a valid field type");
|
||||
return (message);
|
||||
}
|
||||
|
||||
char *dbGetRange(DBENTRY *pdbentry)
|
||||
{
|
||||
dbFldDes *pflddes = pdbentry->pflddes;
|
||||
char *message;
|
||||
|
||||
message = getpMessage(pdbentry);
|
||||
if(!pflddes) {strcpy(message,"fldDes not found"); return(message);}
|
||||
switch (pflddes->field_type) {
|
||||
case DBF_STRING: {strcpy(message,"STRING"); return(message);}
|
||||
case DBF_CHAR : {strcpy(message,"CHAR"); return(message);}
|
||||
case DBF_SHORT : {strcpy(message,"SHORT");return(message);}
|
||||
case DBF_LONG: {strcpy(message,"LONG"); return(message);}
|
||||
case DBF_UCHAR: {strcpy(message,"UCHAR");return(message);}
|
||||
case DBF_USHORT:{strcpy(message,"USHORT");return(message);}
|
||||
case DBF_ULONG:{strcpy(message,"ULONG:");return(message);}
|
||||
case DBF_ENUM: {strcpy(message,"ENUM");return(message);}
|
||||
case DBF_FLOAT: {strcpy(message,"FLOAT");return(message);}
|
||||
case DBF_DOUBLE: {strcpy(message,"DOUBLE");return(message);}
|
||||
case DBF_MENU: {strcpy(message,"MENU");return(message);}
|
||||
case DBF_DEVICE: {strcpy(message,"DEVICE");return(message);}
|
||||
case DBF_INLINK: {strcpy(message,"INLINK");return(message);}
|
||||
case DBF_OUTLINK: {strcpy(message,"OUTLINK");return(message);}
|
||||
case DBF_FWDLINK: {strcpy(message,"FWDLINK");return(message);}
|
||||
default:
|
||||
errPrintf(-1,__FILE__, __LINE__,"Logic Error\n");
|
||||
}
|
||||
strcpy(message,"Not a valid field type");
|
||||
return (message);
|
||||
}
|
||||
|
||||
long dbFirstInfo(DBENTRY *pdbentry)
|
||||
{
|
||||
dbRecordNode *precnode = pdbentry->precnode;
|
||||
@@ -3270,7 +3103,7 @@ void dbDumpRecordType(DBBASE *pdbbase,const char *recordTypeName)
|
||||
printf("\n");
|
||||
printf("indvalFlddes %d name %s\n",pdbRecordType->indvalFlddes,
|
||||
pdbRecordType->pvalFldDes->name);
|
||||
printf("struct rset * %p rec_size %d\n",
|
||||
printf("rset * %p rec_size %d\n",
|
||||
(void *)pdbRecordType->prset,pdbRecordType->rec_size);
|
||||
if(recordTypeName) break;
|
||||
}
|
||||
|
||||
@@ -263,6 +263,7 @@ epicsShareFunc void dbCatString(char **string, int *stringLength,
|
||||
char *pnew, char *separator);
|
||||
|
||||
extern int dbStaticDebug;
|
||||
extern int dbConvertStrict;
|
||||
|
||||
#define S_dbLib_recordTypeNotFound (M_dbLib|1) /* Record Type does not exist */
|
||||
#define S_dbLib_recExists (M_dbLib|3) /* Record Already exists */
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "epicsTypes.h"
|
||||
#include "errMdef.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "epicsExport.h" /* #define epicsExportSharedSymbols */
|
||||
#include "dbBase.h"
|
||||
#include "dbCommonPvt.h"
|
||||
#include "dbStaticLib.h"
|
||||
@@ -31,6 +31,9 @@
|
||||
#include "devSup.h"
|
||||
#include "special.h"
|
||||
|
||||
epicsShareDef int dbConvertStrict = 0;
|
||||
epicsExportAddress(int, dbConvertStrict);
|
||||
|
||||
static long do_nothing(struct dbCommon *precord) { return 0; }
|
||||
|
||||
/* Dummy DSXT used for soft device supports */
|
||||
@@ -128,6 +131,8 @@ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName)
|
||||
case DBF_USHORT:
|
||||
case DBF_LONG:
|
||||
case DBF_ULONG:
|
||||
case DBF_INT64:
|
||||
case DBF_UINT64:
|
||||
case DBF_FLOAT:
|
||||
case DBF_DOUBLE:
|
||||
case DBF_ENUM:
|
||||
@@ -213,277 +218,341 @@ int dbIsMacroOk(DBENTRY *pdbentry) { return(FALSE); }
|
||||
|
||||
epicsShareFunc int dbIsDefaultValue(DBENTRY *pdbentry)
|
||||
{
|
||||
dbFldDes *pflddes = pdbentry->pflddes;
|
||||
void *pfield = pdbentry->pfield;
|
||||
dbFldDes *pflddes = pdbentry->pflddes;
|
||||
void *pfield = pdbentry->pfield;
|
||||
|
||||
if (!pflddes || !pfield)
|
||||
return FALSE;
|
||||
|
||||
if(!pflddes) return(FALSE);
|
||||
if(!pfield) return(FALSE);
|
||||
switch (pflddes->field_type) {
|
||||
case (DBF_STRING) : {
|
||||
char *p = (char *)pfield;
|
||||
|
||||
if(!pflddes->initial) return((strlen(p)==0) ? TRUE : FALSE);
|
||||
return((strcmp(pflddes->initial,p)==0) ? TRUE : FALSE);
|
||||
}
|
||||
case DBF_CHAR: {
|
||||
char field = *(char *)pfield;
|
||||
long ltemp;
|
||||
if(pflddes->initial) {
|
||||
ltemp = strtol(pflddes->initial,NULL,0);
|
||||
return((field==ltemp));
|
||||
}
|
||||
return((field==0));
|
||||
}
|
||||
case DBF_UCHAR: {
|
||||
unsigned char field = *(unsigned char *)pfield;
|
||||
unsigned long ltemp;
|
||||
case DBF_STRING: {
|
||||
char *p = (char *)pfield;
|
||||
|
||||
if(pflddes->initial) {
|
||||
ltemp = strtoul(pflddes->initial,NULL,0);
|
||||
return((field==ltemp));
|
||||
}
|
||||
return((field==0));
|
||||
}
|
||||
case DBF_SHORT: {
|
||||
short field = *(short *)pfield;
|
||||
long ltemp;
|
||||
return pflddes->initial ? ! strcmp(pflddes->initial, p)
|
||||
: ! strlen(p);
|
||||
}
|
||||
case DBF_CHAR: {
|
||||
epicsInt8 field = *(epicsInt8 *)pfield;
|
||||
epicsInt8 def;
|
||||
|
||||
if(pflddes->initial) {
|
||||
ltemp = strtol(pflddes->initial,NULL,0);
|
||||
return((field==ltemp));
|
||||
}
|
||||
return((field==0));
|
||||
}
|
||||
case DBF_USHORT: {
|
||||
unsigned short field = *(unsigned short *)pfield;
|
||||
unsigned long ltemp;
|
||||
if (!pflddes->initial)
|
||||
return field == 0;
|
||||
|
||||
if(pflddes->initial) {
|
||||
ltemp = strtoul(pflddes->initial,NULL,0);
|
||||
return((field==ltemp));
|
||||
}
|
||||
return((field==0));
|
||||
}
|
||||
case DBF_LONG: {
|
||||
long field = *(long *)pfield;
|
||||
long ltemp;
|
||||
return ! epicsParseInt8(pflddes->initial, &def, 0, NULL)
|
||||
&& field == def;
|
||||
}
|
||||
case DBF_UCHAR: {
|
||||
epicsUInt8 field = *(epicsUInt8 *)pfield;
|
||||
epicsUInt8 def;
|
||||
|
||||
if(pflddes->initial) {
|
||||
ltemp = strtol(pflddes->initial,NULL,0);
|
||||
return((field==ltemp));
|
||||
}
|
||||
return((field==0));
|
||||
}
|
||||
case DBF_ULONG: {
|
||||
unsigned long field = *(unsigned long *)pfield;
|
||||
unsigned long ltemp;
|
||||
if (!pflddes->initial)
|
||||
return field == 0;
|
||||
|
||||
if(pflddes->initial) {
|
||||
ltemp = strtoul(pflddes->initial,NULL,0);
|
||||
return((field==ltemp));
|
||||
}
|
||||
return((field==0));
|
||||
}
|
||||
case DBF_FLOAT: {
|
||||
float field = *(float *)pfield;
|
||||
double dtemp;
|
||||
return ! epicsParseUInt8(pflddes->initial, &def, 0, NULL)
|
||||
&& field == def;
|
||||
}
|
||||
case DBF_SHORT: {
|
||||
epicsInt16 field = *(epicsInt16 *)pfield;
|
||||
epicsInt16 def;
|
||||
|
||||
if(pflddes->initial) {
|
||||
dtemp = epicsStrtod(pflddes->initial,NULL);
|
||||
return((field==dtemp));
|
||||
}
|
||||
return((field==0.0));
|
||||
}
|
||||
case DBF_DOUBLE: {
|
||||
double field = *(double *)pfield;
|
||||
double dtemp;
|
||||
if (!pflddes->initial)
|
||||
return field == 0;
|
||||
|
||||
if(pflddes->initial) {
|
||||
dtemp = epicsStrtod(pflddes->initial,NULL);
|
||||
return((field==dtemp));
|
||||
}
|
||||
return((field==0.0));
|
||||
}
|
||||
case DBF_ENUM: {
|
||||
unsigned short field = *(unsigned short *)pfield;
|
||||
unsigned long ltemp;
|
||||
return ! epicsParseInt16(pflddes->initial, &def, 0, NULL)
|
||||
&& field == def;
|
||||
}
|
||||
case DBF_ENUM:
|
||||
case DBF_USHORT: {
|
||||
epicsUInt16 field = *(epicsUInt16 *)pfield;
|
||||
epicsUInt16 def;
|
||||
|
||||
if(pflddes->initial) {
|
||||
ltemp = strtoul(pflddes->initial,NULL,0);
|
||||
return((field==ltemp));
|
||||
}
|
||||
return((field==0));
|
||||
}
|
||||
case DBF_MENU: {
|
||||
unsigned short field = *(unsigned short *)pfield;
|
||||
long value;
|
||||
char *endp;
|
||||
if (!pflddes->initial)
|
||||
return field == 0;
|
||||
|
||||
if(pflddes->initial) {
|
||||
value = dbGetMenuIndexFromString(pdbentry,pflddes->initial);
|
||||
if(value==-1) {
|
||||
value = strtol(pflddes->initial,&endp,0);
|
||||
if(*endp!='\0') return(FALSE);
|
||||
}
|
||||
} else {
|
||||
value = 0;
|
||||
}
|
||||
if((unsigned short)value == field) return(TRUE);
|
||||
return(FALSE);
|
||||
}
|
||||
case DBF_DEVICE: {
|
||||
dbRecordType *precordType = pdbentry->precordType;
|
||||
return ! epicsParseUInt16(pflddes->initial, &def, 0, NULL)
|
||||
&& field == def;
|
||||
}
|
||||
case DBF_LONG: {
|
||||
epicsInt32 field = *(epicsInt32 *)pfield;
|
||||
epicsInt32 def;
|
||||
|
||||
if(!precordType) {
|
||||
epicsPrintf("dbIsDefaultValue: pdbRecordType is NULL??\n");
|
||||
return(FALSE);
|
||||
}
|
||||
if(ellCount(&precordType->devList)==0) return(TRUE);
|
||||
return(FALSE);
|
||||
}
|
||||
case DBF_INLINK:
|
||||
case DBF_OUTLINK:
|
||||
case DBF_FWDLINK: {
|
||||
struct link *plink = (struct link *)pfield;
|
||||
if (!pflddes->initial)
|
||||
return field == 0;
|
||||
|
||||
if(!plink) return(FALSE);
|
||||
if(plink->type!=CONSTANT) return(FALSE);
|
||||
if(plink->value.constantStr == 0) return(TRUE);
|
||||
if(!pflddes->initial) return(FALSE);
|
||||
if(strcmp(plink->value.constantStr,pflddes->initial)==0)
|
||||
return(TRUE);
|
||||
return(FALSE);
|
||||
}
|
||||
default:
|
||||
return(TRUE);
|
||||
return ! epicsParseInt32(pflddes->initial, &def, 0, NULL)
|
||||
&& field == def;
|
||||
}
|
||||
case DBF_ULONG: {
|
||||
epicsUInt32 field = *(epicsUInt32 *)pfield;
|
||||
epicsUInt32 def;
|
||||
|
||||
if (!pflddes->initial)
|
||||
return field == 0;
|
||||
|
||||
return ! epicsParseUInt32(pflddes->initial, &def, 0, NULL)
|
||||
&& field == def;
|
||||
}
|
||||
case DBF_INT64: {
|
||||
epicsInt64 field = *(epicsInt64 *)pfield;
|
||||
epicsInt64 def;
|
||||
|
||||
if (!pflddes->initial)
|
||||
return field == 0;
|
||||
|
||||
return ! epicsParseInt64(pflddes->initial, &def, 0, NULL)
|
||||
&& field == def;
|
||||
}
|
||||
case DBF_UINT64: {
|
||||
epicsUInt64 field = *(epicsUInt64 *)pfield;
|
||||
epicsUInt64 def;
|
||||
|
||||
if (!pflddes->initial)
|
||||
return field == 0;
|
||||
|
||||
return ! epicsParseUInt64(pflddes->initial, &def, 0, NULL)
|
||||
&& field == def;
|
||||
}
|
||||
case DBF_FLOAT: {
|
||||
epicsFloat32 field = *(epicsFloat32 *)pfield;
|
||||
epicsFloat32 def;
|
||||
|
||||
if (!pflddes->initial)
|
||||
return field == 0;
|
||||
|
||||
return ! epicsParseFloat32(pflddes->initial, &def, NULL)
|
||||
&& field == def;
|
||||
}
|
||||
case DBF_DOUBLE: {
|
||||
epicsFloat64 field = *(epicsFloat64 *)pfield;
|
||||
epicsFloat64 def;
|
||||
|
||||
if (!pflddes->initial)
|
||||
return field == 0;
|
||||
|
||||
return ! epicsParseFloat64(pflddes->initial, &def, NULL)
|
||||
&& field == def;
|
||||
}
|
||||
case DBF_MENU: {
|
||||
epicsEnum16 field = *(epicsEnum16 *)pfield;
|
||||
epicsEnum16 def;
|
||||
int index;
|
||||
|
||||
if (!pflddes->initial)
|
||||
return field == 0;
|
||||
|
||||
index = dbGetMenuIndexFromString(pdbentry, pflddes->initial);
|
||||
if (index < 0) {
|
||||
if (epicsParseUInt16(pflddes->initial, &def, 0, NULL))
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
def = index;
|
||||
return field == def;
|
||||
}
|
||||
case DBF_DEVICE: {
|
||||
dbRecordType *precordType = pdbentry->precordType;
|
||||
|
||||
if (!precordType) {
|
||||
epicsPrintf("dbIsDefaultValue: pdbRecordType is NULL??\n");
|
||||
return FALSE;
|
||||
}
|
||||
return ellCount(&precordType->devList) == 0;
|
||||
}
|
||||
case DBF_INLINK:
|
||||
case DBF_OUTLINK:
|
||||
case DBF_FWDLINK: {
|
||||
struct link *plink = (struct link *)pfield;
|
||||
|
||||
if (!plink || plink->type != CONSTANT)
|
||||
return FALSE;
|
||||
|
||||
/* These conditions don't make a lot of sense... */
|
||||
if (!plink->value.constantStr)
|
||||
return TRUE;
|
||||
|
||||
if (!pflddes->initial) /* Default value for a link field? */
|
||||
return FALSE;
|
||||
|
||||
return !strcmp(plink->value.constantStr, pflddes->initial);
|
||||
}
|
||||
default:
|
||||
return TRUE;
|
||||
}
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
long dbPutStringNum(DBENTRY *pdbentry,const char *pstring)
|
||||
long dbPutStringNum(DBENTRY *pdbentry, const char *pstring)
|
||||
{
|
||||
dbFldDes *pflddes = pdbentry->pflddes;
|
||||
void *pfield = pdbentry->pfield;
|
||||
long status=0;
|
||||
dbFldDes *pflddes = pdbentry->pflddes;
|
||||
void *pfield = pdbentry->pfield;
|
||||
long status;
|
||||
epicsUInt64 u64;
|
||||
epicsInt64 i64;
|
||||
|
||||
if (!pfield)
|
||||
return S_dbLib_fieldNotFound;
|
||||
|
||||
/* empty string is the same as writing numeric zero */
|
||||
if (pstring[0] == '\0')
|
||||
pstring = "0";
|
||||
|
||||
if(!pfield) return(S_dbLib_fieldNotFound);
|
||||
switch (pflddes->field_type) {
|
||||
case DBF_CHAR :
|
||||
case DBF_SHORT :
|
||||
case DBF_LONG:{
|
||||
long value;
|
||||
char *endp;
|
||||
case DBF_CHAR:
|
||||
if (dbConvertStrict)
|
||||
return epicsParseInt8(pstring, pfield, 0, NULL);
|
||||
goto lax_signed;
|
||||
|
||||
case DBF_SHORT:
|
||||
if (dbConvertStrict)
|
||||
return epicsParseInt16(pstring, pfield, 0, NULL);
|
||||
goto lax_signed;
|
||||
|
||||
case DBF_LONG:
|
||||
if (dbConvertStrict)
|
||||
return epicsParseInt32(pstring, pfield, 0, NULL);
|
||||
goto lax_signed;
|
||||
|
||||
case DBF_INT64:
|
||||
if (dbConvertStrict)
|
||||
return epicsParseInt64(pstring, pfield, 0, NULL);
|
||||
|
||||
lax_signed:
|
||||
status = epicsParseInt64(pstring, &i64, 0, NULL);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
switch (pflddes->field_type) {
|
||||
case DBF_CHAR: *(epicsInt8 *)pfield = (epicsInt8) i64; break;
|
||||
case DBF_SHORT: *(epicsInt16*)pfield = (epicsInt16)i64; break;
|
||||
case DBF_LONG: *(epicsInt32*)pfield = (epicsInt32)i64; break;
|
||||
case DBF_INT64: *(epicsInt64*)pfield = (epicsInt64)i64; break;
|
||||
default: break;
|
||||
}
|
||||
return status;
|
||||
|
||||
value = strtol(pstring,&endp,0);
|
||||
if(*endp!=0) status = S_dbLib_badField;
|
||||
switch (pflddes->field_type) {
|
||||
case DBF_CHAR : *(char *)pfield = (char)value; break;
|
||||
case DBF_SHORT : *(short *)pfield = (short)value; break;
|
||||
case DBF_LONG : *(epicsInt32 *)pfield = (epicsInt32)value; break;
|
||||
default: epicsPrintf("Logic error in dbPutStringNum\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DBF_UCHAR:
|
||||
if (dbConvertStrict)
|
||||
return epicsParseUInt8(pstring, pfield, 0, NULL);
|
||||
goto lax_unsigned;
|
||||
|
||||
case DBF_ENUM:
|
||||
case DBF_USHORT:
|
||||
if (dbConvertStrict)
|
||||
return epicsParseUInt16(pstring, pfield, 0, NULL);
|
||||
goto lax_unsigned;
|
||||
|
||||
case DBF_ULONG:
|
||||
case DBF_ENUM:{
|
||||
unsigned long value;
|
||||
char *endp;
|
||||
if (dbConvertStrict)
|
||||
return epicsParseUInt32(pstring, pfield, 0, NULL);
|
||||
goto lax_unsigned;
|
||||
|
||||
case DBF_UINT64:
|
||||
if (dbConvertStrict)
|
||||
return epicsParseUInt64(pstring, pfield, 0, NULL);
|
||||
|
||||
lax_unsigned:
|
||||
status = epicsParseUInt64(pstring, &u64, 0, NULL);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
switch (pflddes->field_type) {
|
||||
case DBF_UCHAR: *(epicsUInt8 *)pfield = (epicsInt8) u64; break;
|
||||
case DBF_ENUM:
|
||||
case DBF_USHORT: *(epicsUInt16*)pfield = (epicsInt16)u64; break;
|
||||
case DBF_ULONG: *(epicsUInt32*)pfield = (epicsInt32)u64; break;
|
||||
case DBF_UINT64: *(epicsUInt64*)pfield = (epicsInt64)u64; break;
|
||||
default: break;
|
||||
}
|
||||
return status;
|
||||
|
||||
value = strtoul(pstring,&endp,0);
|
||||
if(*endp!=0) status = S_dbLib_badField;
|
||||
switch (pflddes->field_type) {
|
||||
case DBF_UCHAR : *(unsigned char *)pfield = (unsigned char)value; break;
|
||||
case DBF_USHORT:
|
||||
case DBF_ENUM: *(unsigned short *)pfield = (unsigned short)value; break;
|
||||
case DBF_ULONG : *(epicsUInt32 *)pfield = (epicsUInt32)value; break;
|
||||
default: epicsPrintf("Logic error in dbPutStringNum\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DBF_FLOAT:
|
||||
case DBF_DOUBLE: {
|
||||
double value;
|
||||
char *endp;
|
||||
return epicsParseFloat32(pstring, pfield, NULL);
|
||||
|
||||
case DBF_DOUBLE:
|
||||
return epicsParseFloat64(pstring, pfield, NULL);
|
||||
|
||||
value = epicsStrtod(pstring,&endp);
|
||||
if(*endp!=0) status = S_dbLib_badField;
|
||||
if(pflddes->field_type == DBF_FLOAT)
|
||||
*(float *)pfield = (float)value;
|
||||
else
|
||||
*(double *)pfield = value;
|
||||
}
|
||||
break;
|
||||
case DBF_MENU:
|
||||
case DBF_DEVICE: {/*Must allow value that is choice or index*/
|
||||
unsigned short *field= (unsigned short*)pfield;
|
||||
int ind;
|
||||
long value;
|
||||
char *endp;
|
||||
case DBF_DEVICE: {
|
||||
epicsEnum16 *field = (epicsEnum16 *) pfield;
|
||||
int index = dbGetMenuIndexFromString(pdbentry, pstring);
|
||||
|
||||
if (index < 0) {
|
||||
epicsEnum16 value;
|
||||
long status = epicsParseUInt16(pstring, &value, 0, NULL);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
index = dbGetNMenuChoices(pdbentry);
|
||||
if (value > index && index > 0)
|
||||
return S_dbLib_badField;
|
||||
|
||||
*field = value;
|
||||
}
|
||||
else
|
||||
*field = index;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ind = dbGetMenuIndexFromString(pdbentry,pstring);
|
||||
if(ind==-1) {
|
||||
value = strtol(pstring,&endp,0);
|
||||
if(*endp!='\0') return(S_dbLib_badField);
|
||||
ind = value;
|
||||
/*Check that ind is withing range*/
|
||||
if(!dbGetMenuStringFromIndex(pdbentry,ind))
|
||||
return(S_dbLib_badField);
|
||||
}
|
||||
*field = (unsigned short)ind;
|
||||
}
|
||||
return (0);
|
||||
default:
|
||||
return (S_dbLib_badField);
|
||||
return S_dbLib_badField;
|
||||
}
|
||||
return(status);
|
||||
}
|
||||
|
||||
|
||||
epicsShareFunc int dbGetMenuIndex(DBENTRY *pdbentry)
|
||||
{
|
||||
dbFldDes *pflddes = pdbentry->pflddes;
|
||||
void *pfield = pdbentry->pfield;
|
||||
dbFldDes *pflddes = pdbentry->pflddes;
|
||||
void *pfield = pdbentry->pfield;
|
||||
|
||||
if (!pflddes || !pfield)
|
||||
return -1;
|
||||
|
||||
if(!pflddes) return(-1);
|
||||
if(!pfield) return(-1);
|
||||
switch (pflddes->field_type) {
|
||||
case (DBF_MENU):
|
||||
case (DBF_DEVICE):
|
||||
return((int)(*(unsigned short *)pfield));
|
||||
default:
|
||||
errPrintf(-1,__FILE__, __LINE__,"Logic Error\n");
|
||||
case DBF_MENU:
|
||||
case DBF_DEVICE:
|
||||
return * (epicsEnum16 *) pfield;
|
||||
default:
|
||||
epicsPrintf("dbGetMenuIndex: Called for field type %d\n",
|
||||
pflddes->field_type);
|
||||
}
|
||||
return(-1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
epicsShareFunc long dbPutMenuIndex(DBENTRY *pdbentry,int index)
|
||||
{
|
||||
dbFldDes *pflddes = pdbentry->pflddes;
|
||||
unsigned short *pfield = pdbentry->pfield;
|
||||
|
||||
if(!pflddes) return(S_dbLib_flddesNotFound);
|
||||
if(!pfield) return(S_dbLib_fieldNotFound);
|
||||
epicsShareFunc long dbPutMenuIndex(DBENTRY *pdbentry, int index)
|
||||
{
|
||||
dbFldDes *pflddes = pdbentry->pflddes;
|
||||
epicsEnum16 *pfield = pdbentry->pfield;
|
||||
|
||||
if (!pflddes)
|
||||
return S_dbLib_flddesNotFound;
|
||||
if (!pfield)
|
||||
return S_dbLib_fieldNotFound;
|
||||
|
||||
switch (pflddes->field_type) {
|
||||
case DBF_MENU: {
|
||||
dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt;
|
||||
dbMenu *pdbMenu = (dbMenu *) pflddes->ftPvt;
|
||||
|
||||
if(!pdbMenu) return(S_dbLib_menuNotFound);
|
||||
if(index<0 || index>=pdbMenu->nChoice) return(S_dbLib_badField);
|
||||
*pfield = (unsigned short)index;
|
||||
return(0);
|
||||
}
|
||||
case DBF_DEVICE: {
|
||||
dbDeviceMenu *pdbDeviceMenu;
|
||||
if (!pdbMenu)
|
||||
return S_dbLib_menuNotFound;
|
||||
if (index < 0 || index >= pdbMenu->nChoice)
|
||||
return S_dbLib_badField;
|
||||
|
||||
pdbDeviceMenu = dbGetDeviceMenu(pdbentry);
|
||||
if(!pdbDeviceMenu) return(S_dbLib_menuNotFound);
|
||||
if(index<0 || index>=pdbDeviceMenu->nChoice)
|
||||
return(S_dbLib_badField);
|
||||
return(dbPutString(pdbentry,pdbDeviceMenu->papChoice[index]));
|
||||
}
|
||||
default:
|
||||
break;
|
||||
*pfield = index;
|
||||
return 0;
|
||||
}
|
||||
return (S_dbLib_badField);
|
||||
|
||||
case DBF_DEVICE: {
|
||||
dbDeviceMenu *pdbDeviceMenu = dbGetDeviceMenu(pdbentry);
|
||||
|
||||
if (!pdbDeviceMenu)
|
||||
return S_dbLib_menuNotFound;
|
||||
if (index < 0 || index >= pdbDeviceMenu->nChoice)
|
||||
return S_dbLib_badField;
|
||||
|
||||
return dbPutString(pdbentry, pdbDeviceMenu->papChoice[index]);
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return S_dbLib_badField;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ variable(dbRecordsOnceOnly,int)
|
||||
variable(dbRecordsAbcSorted,int)
|
||||
variable(dbBptNotMonotonic,int)
|
||||
variable(dbQuietMacroWarnings,int)
|
||||
variable(dbConvertStrict,int)
|
||||
|
||||
# dbLoadTemplate settings
|
||||
variable(dbTemplateMaxVars,int)
|
||||
|
||||
@@ -1,73 +1,69 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* Very efficient routines to convert numbers to strings
|
||||
* Author: Bob Dalesio wrote cvtFloatToString (called FF_TO_STR)
|
||||
* Code is same for cvtDoubleToString
|
||||
* Marty Kraimer wrote cvtCharToString,cvtUcharToString
|
||||
* cvtShortToString,cvtUshortToString,
|
||||
* cvtLongToString, and cvtUlongToString
|
||||
* Mark Anderson wrote cvtLongToHexString, cvtLongToOctalString,
|
||||
* adopted cvt[Float/Double]ExpString and
|
||||
* cvt[Float/Double]CompactString from fToEStr
|
||||
* and fixed calls to gcvt
|
||||
*
|
||||
* Date: 12 January 1993
|
||||
/* Fast numeric to string conversions
|
||||
*
|
||||
* Original Authors:
|
||||
* Bob Dalesio, Mark Anderson and Marty Kraimer
|
||||
* Date: 12 January 1993
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h> /* XPG2/XPG3/POSIX.1/FIPS151-1/ANSI-C */
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "cvtFast.h"
|
||||
#include "epicsMath.h"
|
||||
|
||||
/*
|
||||
* This routine converts numbers less than 10,000,000. It defers to f_to_str for
|
||||
* numbers requiring more than 8 places of precision. There are only eight decimal
|
||||
*/
|
||||
static epicsInt32 frac_multiplier[] =
|
||||
{1,10,100,1000,10000,100000,1000000,10000000,100000000};
|
||||
#include "epicsStdio.h"
|
||||
|
||||
int epicsShareAPI cvtFloatToString(
|
||||
float flt_value,
|
||||
char *pstr_value,
|
||||
unsigned short precision)
|
||||
/*
|
||||
* These routines convert numbers up to +/- 10,000,000.
|
||||
* They defer to sprintf() for numbers requiring more than
|
||||
* 8 places of precision.
|
||||
*/
|
||||
static epicsInt32 frac_multiplier[] =
|
||||
{1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
|
||||
|
||||
int cvtFloatToString(float flt_value, char *pdest,
|
||||
epicsUInt16 precision)
|
||||
{
|
||||
unsigned short got_one,i;
|
||||
epicsInt32 whole,iplace,number,fraction,fplace;
|
||||
int got_one, i;
|
||||
epicsInt32 whole, iplace, number, fraction, fplace;
|
||||
float ftemp;
|
||||
char *startAddr;
|
||||
|
||||
/* can this routine handle this conversion */
|
||||
if (isnan(flt_value) || precision > 8 || flt_value > 10000000.0 || flt_value < -10000000.0) {
|
||||
sprintf(pstr_value,"%12.5e",(double)flt_value);
|
||||
return((int)strlen(pstr_value));
|
||||
if (isnan(flt_value) || precision > 8 ||
|
||||
flt_value > 10000000.0 || flt_value < -10000000.0) {
|
||||
if (precision > 8 || flt_value >= 1e8 || flt_value <= -1e8) {
|
||||
if (precision > 12) precision = 12; /* FIXME */
|
||||
sprintf(pdest, "%*.*e", precision+6, precision, (double) flt_value);
|
||||
} else {
|
||||
if (precision > 3) precision = 3; /* FIXME */
|
||||
sprintf(pdest, "%.*f", precision, (double) flt_value);
|
||||
}
|
||||
return((int)strlen(pdest));
|
||||
}
|
||||
startAddr = pstr_value;
|
||||
startAddr = pdest;
|
||||
|
||||
/* determine the sign */
|
||||
if (flt_value < 0){
|
||||
*pstr_value++ = '-';
|
||||
*pdest++ = '-';
|
||||
flt_value = -flt_value;
|
||||
};
|
||||
|
||||
/* remove the whole number portion */
|
||||
/* remove the whole number portion */
|
||||
whole = (epicsInt32)flt_value;
|
||||
ftemp = flt_value - whole;
|
||||
|
||||
/* multiplier to convert fractional portion to integer */
|
||||
fplace = frac_multiplier[precision];
|
||||
fraction = (epicsInt32)(ftemp * fplace * 10);
|
||||
fraction = (epicsInt32)(ftemp * fplace * 10);
|
||||
fraction = (fraction + 5) / 10; /* round up */
|
||||
|
||||
/* determine rounding into the whole number portion */
|
||||
@@ -83,41 +79,41 @@ int epicsShareAPI cvtFloatToString(
|
||||
got_one = 1;
|
||||
number = whole / iplace;
|
||||
whole = whole - (number * iplace);
|
||||
*pstr_value = number + '0';
|
||||
pstr_value++;
|
||||
*pdest = number + '0';
|
||||
pdest++;
|
||||
}else if (got_one){
|
||||
*pstr_value = '0';
|
||||
pstr_value++;
|
||||
*pdest = '0';
|
||||
pdest++;
|
||||
}
|
||||
}
|
||||
if (!got_one){
|
||||
*pstr_value = '0';
|
||||
pstr_value++;
|
||||
*pdest = '0';
|
||||
pdest++;
|
||||
}
|
||||
|
||||
/* fraction */
|
||||
if (precision > 0){
|
||||
/* convert fractional portional to ASCII */
|
||||
*pstr_value = '.';
|
||||
pstr_value++;
|
||||
*pdest = '.';
|
||||
pdest++;
|
||||
for (fplace /= 10, i = precision; i > 0; fplace /= 10,i--){
|
||||
number = fraction / fplace;
|
||||
fraction -= number * fplace;
|
||||
*pstr_value = number + '0';
|
||||
pstr_value++;
|
||||
*pdest = number + '0';
|
||||
pdest++;
|
||||
}
|
||||
}
|
||||
*pstr_value = 0;
|
||||
*pdest = 0;
|
||||
|
||||
return((int)(pstr_value - startAddr));
|
||||
return((int)(pdest - startAddr));
|
||||
}
|
||||
|
||||
int epicsShareAPI cvtDoubleToString(
|
||||
|
||||
int cvtDoubleToString(
|
||||
double flt_value,
|
||||
char *pstr_value,
|
||||
unsigned short precision)
|
||||
char *pdest,
|
||||
epicsUInt16 precision)
|
||||
{
|
||||
unsigned short got_one,i;
|
||||
epicsUInt16 got_one,i;
|
||||
epicsInt32 whole,iplace,number,fraction,fplace;
|
||||
double ftemp;
|
||||
char *startAddr;
|
||||
@@ -126,23 +122,23 @@ int epicsShareAPI cvtDoubleToString(
|
||||
if (isnan(flt_value) || precision > 8 || flt_value > 10000000.0 || flt_value < -10000000.0) {
|
||||
if (precision > 8 || flt_value > 1e16 || flt_value < -1e16) {
|
||||
if(precision>17) precision=17;
|
||||
sprintf(pstr_value,"%*.*e",precision+7,precision,
|
||||
sprintf(pdest,"%*.*e",precision+7,precision,
|
||||
flt_value);
|
||||
} else {
|
||||
if(precision>3) precision=3;
|
||||
sprintf(pstr_value,"%.*f",precision,flt_value);
|
||||
sprintf(pdest,"%.*f",precision,flt_value);
|
||||
}
|
||||
return((int)strlen(pstr_value));
|
||||
return((int)strlen(pdest));
|
||||
}
|
||||
startAddr = pstr_value;
|
||||
startAddr = pdest;
|
||||
|
||||
/* determine the sign */
|
||||
if (flt_value < 0){
|
||||
*pstr_value++ = '-';
|
||||
*pdest++ = '-';
|
||||
flt_value = -flt_value;
|
||||
};
|
||||
|
||||
/* remove the whole number portion */
|
||||
/* remove the whole number portion */
|
||||
whole = (epicsInt32)flt_value;
|
||||
ftemp = flt_value - whole;
|
||||
|
||||
@@ -164,432 +160,364 @@ int epicsShareAPI cvtDoubleToString(
|
||||
got_one = 1;
|
||||
number = whole / iplace;
|
||||
whole = whole - (number * iplace);
|
||||
*pstr_value = number + '0';
|
||||
pstr_value++;
|
||||
*pdest = number + '0';
|
||||
pdest++;
|
||||
}else if (got_one){
|
||||
*pstr_value = '0';
|
||||
pstr_value++;
|
||||
*pdest = '0';
|
||||
pdest++;
|
||||
}
|
||||
}
|
||||
if (!got_one){
|
||||
*pstr_value = '0';
|
||||
pstr_value++;
|
||||
*pdest = '0';
|
||||
pdest++;
|
||||
}
|
||||
|
||||
/* fraction */
|
||||
if (precision > 0){
|
||||
/* convert fractional portional to ASCII */
|
||||
*pstr_value = '.';
|
||||
pstr_value++;
|
||||
*pdest = '.';
|
||||
pdest++;
|
||||
for (fplace /= 10, i = precision; i > 0; fplace /= 10,i--){
|
||||
number = fraction / fplace;
|
||||
fraction -= number * fplace;
|
||||
*pstr_value = number + '0';
|
||||
pstr_value++;
|
||||
*pdest = number + '0';
|
||||
pdest++;
|
||||
}
|
||||
}
|
||||
*pstr_value = 0;
|
||||
*pdest = 0;
|
||||
|
||||
return((int)(pstr_value - startAddr));
|
||||
return((int)(pdest - startAddr));
|
||||
}
|
||||
|
||||
/*
|
||||
* These routines are provided for backwards compatibility,
|
||||
* extensions such as MEDM, edm and histtool use them.
|
||||
*/
|
||||
|
||||
/*
|
||||
* cvtFloatToExpString
|
||||
*
|
||||
* converts floating point numbers to E-format NULL terminated strings
|
||||
* Converts a float to a %e formatted string
|
||||
*/
|
||||
int epicsShareAPI cvtFloatToExpString(
|
||||
float f_value,
|
||||
char *pstr_value,
|
||||
unsigned short f_precision)
|
||||
int cvtFloatToExpString(float val, char *pdest, epicsUInt16 precision)
|
||||
{
|
||||
/*sunos uses char*sprint as function prototype*/
|
||||
sprintf(pstr_value,"%.*e",(int)f_precision,(double)f_value);
|
||||
return((int)strlen(pstr_value));
|
||||
return epicsSnprintf(pdest, MAX_STRING_SIZE, "%.*e", precision, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* cvtFloatToCompactString
|
||||
*
|
||||
* Converts floating point numbers to %g format NULL terminated strings,
|
||||
* resulting in the most "compact" expression of the value
|
||||
* ("f" notation if 10-4 < |value| < 10+4, otherwise "e" notation)
|
||||
* Converts a float to a %g formatted string.
|
||||
* The result uses %f notation for 10e-4 < |value| < 10e+4,
|
||||
* otherwise %e notation.
|
||||
*/
|
||||
int epicsShareAPI cvtFloatToCompactString(
|
||||
float f_value,
|
||||
char *pstr_value,
|
||||
unsigned short f_precision )
|
||||
int cvtFloatToCompactString(float val, char *pdest, epicsUInt16 precision)
|
||||
{
|
||||
if ((f_value < 1.e4 && f_value > 1.e-4) ||
|
||||
(f_value > -1.e4 && f_value < -1.e-4) || f_value == 0.0) {
|
||||
return(cvtFloatToString(f_value,pstr_value,f_precision));
|
||||
} else {
|
||||
return(cvtFloatToExpString(f_value,pstr_value,f_precision));
|
||||
}
|
||||
}
|
||||
if ((val < 1.e4 && val > 1.e-4) ||
|
||||
(val > -1.e4 && val < -1.e-4) ||
|
||||
val == 0.0)
|
||||
return cvtFloatToString(val, pdest, precision);
|
||||
|
||||
return cvtFloatToExpString(val, pdest, precision);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* cvtDoubleToExpString
|
||||
*
|
||||
* converts double precision floating point numbers to E-format NULL
|
||||
* terminated strings
|
||||
* Converts a double to a %e formatted string
|
||||
*/
|
||||
|
||||
int epicsShareAPI cvtDoubleToExpString(
|
||||
double f_value,
|
||||
char *pstr_value,
|
||||
unsigned short f_precision )
|
||||
int cvtDoubleToExpString(double val, char *pdest, epicsUInt16 precision)
|
||||
{
|
||||
sprintf(pstr_value,"%.*e",(int)f_precision,f_value);
|
||||
return((int)strlen(pstr_value));
|
||||
return epicsSnprintf(pdest, MAX_STRING_SIZE, "%.*e", precision, val);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* cvtDoubleToCompactString
|
||||
*
|
||||
* Converts double precision floating point numbers to %g format NULL
|
||||
* terminated strings, resulting in the most "compact" expression
|
||||
* of the value ("f" notation if 10-4 < |value| < 10+4, otherwise
|
||||
* "e" notation)
|
||||
* Converts a double to %g formatted string.
|
||||
* The result uses %f notation for 10e-4 < |value| < 10e+4,
|
||||
* otherwise %e notation.
|
||||
*/
|
||||
int epicsShareAPI cvtDoubleToCompactString(
|
||||
double f_value,
|
||||
char *pstr_value,
|
||||
unsigned short f_precision )
|
||||
int cvtDoubleToCompactString(double val, char *pdest, epicsUInt16 precision)
|
||||
{
|
||||
if ((f_value < 1.e4 && f_value > 1.e-4) ||
|
||||
(f_value > -1.e4 && f_value < -1.e-4) || f_value == 0.0) {
|
||||
return(cvtDoubleToString(f_value,pstr_value,f_precision));
|
||||
} else {
|
||||
return(cvtDoubleToExpString(f_value,pstr_value,f_precision));
|
||||
}
|
||||
if ((val < 1.e4 && val > 1.e-4) ||
|
||||
(val > -1.e4 && val < -1.e-4) ||
|
||||
val == 0.0)
|
||||
return cvtDoubleToString(val, pdest, precision);
|
||||
|
||||
return cvtDoubleToExpString(val, pdest, precision);
|
||||
}
|
||||
|
||||
/* Convert various integer types to ascii */
|
||||
|
||||
static char digit_to_ascii[10]={'0','1','2','3','4','5','6','7','8','9'};
|
||||
|
||||
int epicsShareAPI cvtCharToString(
|
||||
signed char source,
|
||||
char *pdest)
|
||||
/* Integer conversion primitives */
|
||||
|
||||
static size_t
|
||||
UInt32ToDec(epicsUInt32 val, char *pdest)
|
||||
{
|
||||
unsigned char val,temp;
|
||||
char digit[3];
|
||||
int i,j;
|
||||
char *startAddr = pdest;
|
||||
int i;
|
||||
char digit[10];
|
||||
size_t len;
|
||||
|
||||
if(source==0) {
|
||||
*pdest++ = '0';
|
||||
*pdest = 0;
|
||||
return((int)(pdest-startAddr));
|
||||
}
|
||||
if(source<0) {
|
||||
if(source == CHAR_MIN) {
|
||||
sprintf(pdest,"%d",CHAR_MIN);
|
||||
return((int)strlen(pdest));
|
||||
}
|
||||
*pdest++ = '-';
|
||||
source = -source;
|
||||
}
|
||||
val = source;
|
||||
for(i=0; val!=0; i++) {
|
||||
temp = val/10;
|
||||
digit[i] = digit_to_ascii[val - temp*10];
|
||||
val = temp;
|
||||
}
|
||||
for(j=i-1; j>=0; j--) {
|
||||
*pdest++ = digit[j];
|
||||
for (i = 0; val; i++) {
|
||||
epicsUInt32 tenth = val / 10;
|
||||
|
||||
digit[i] = val - tenth * 10 + '0';
|
||||
val = tenth;
|
||||
}
|
||||
len = i;
|
||||
|
||||
while (i > 0)
|
||||
*pdest++ = digit[--i];
|
||||
|
||||
*pdest = 0;
|
||||
return((int)(pdest-startAddr));
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
int epicsShareAPI cvtUcharToString(
|
||||
unsigned char source,
|
||||
char *pdest)
|
||||
static size_t
|
||||
UInt32ToBase(epicsUInt32 val, char *pdest, int base)
|
||||
{
|
||||
unsigned char val,temp;
|
||||
char digit[3];
|
||||
int i,j;
|
||||
char *startAddr = pdest;
|
||||
int i;
|
||||
char digit, digits[32];
|
||||
size_t len;
|
||||
|
||||
if(source==0) {
|
||||
*pdest++ = '0';
|
||||
*pdest = 0;
|
||||
return((int)(pdest-startAddr));
|
||||
}
|
||||
val = source;
|
||||
for(i=0; val!=0; i++) {
|
||||
temp = val/10;
|
||||
digit[i] = digit_to_ascii[val - temp*10];
|
||||
val = temp;
|
||||
}
|
||||
for(j=i-1; j>=0; j--) {
|
||||
*pdest++ = digit[j];
|
||||
for (i = 0; val; i++) {
|
||||
epicsUInt32 tenth = val / base;
|
||||
|
||||
digit = val - tenth * base;
|
||||
digits[i] = digit < 10 ? digit + '0' : digit - 10 + 'a';
|
||||
val = tenth;
|
||||
}
|
||||
len = i;
|
||||
|
||||
while (i > 0)
|
||||
*pdest++ = digits[--i];
|
||||
|
||||
*pdest = 0;
|
||||
return((int)(pdest-startAddr));
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
int epicsShareAPI cvtShortToString(
|
||||
short source,
|
||||
char *pdest)
|
||||
static size_t
|
||||
UInt64ToDec(epicsUInt64 val, char *pdest)
|
||||
{
|
||||
short val,temp;
|
||||
char digit[6];
|
||||
int i,j;
|
||||
char *startAddr = pdest;
|
||||
int i;
|
||||
char digit[20];
|
||||
size_t len;
|
||||
|
||||
if(source==0) {
|
||||
*pdest++ = '0';
|
||||
*pdest = 0;
|
||||
return((int)(pdest-startAddr));
|
||||
}
|
||||
if(source<0) {
|
||||
if(source == SHRT_MIN) {
|
||||
sprintf(pdest,"%d",SHRT_MIN);
|
||||
return((int)(strlen(pdest)));
|
||||
}
|
||||
*pdest++ = '-';
|
||||
source = -source;
|
||||
}
|
||||
val = source;
|
||||
for(i=0; val!=0; i++) {
|
||||
temp = val/10;
|
||||
digit[i] = digit_to_ascii[val - temp*10];
|
||||
val = temp;
|
||||
}
|
||||
for(j=i-1; j>=0; j--) {
|
||||
*pdest++ = digit[j];
|
||||
for (i = 0; val; i++) {
|
||||
epicsUInt64 tenth = val / 10;
|
||||
|
||||
digit[i] = val - tenth * 10 + '0';
|
||||
val = tenth;
|
||||
}
|
||||
|
||||
len = i;
|
||||
while (i > 0)
|
||||
*pdest++ = digit[--i];
|
||||
|
||||
*pdest = 0;
|
||||
return((int)(pdest-startAddr));
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
int epicsShareAPI cvtUshortToString(
|
||||
unsigned short source,
|
||||
char *pdest)
|
||||
static size_t
|
||||
UInt64ToBase(epicsUInt64 val, char *pdest, int base)
|
||||
{
|
||||
unsigned short val,temp;
|
||||
char digit[5];
|
||||
int i,j;
|
||||
char *startAddr = pdest;
|
||||
int i;
|
||||
char digit, digits[64];
|
||||
size_t len;
|
||||
|
||||
if(source==0) {
|
||||
*pdest++ = '0';
|
||||
*pdest = 0;
|
||||
return((int)(pdest-startAddr));
|
||||
}
|
||||
val = source;
|
||||
for(i=0; val!=0; i++) {
|
||||
temp = val/10;
|
||||
digit[i] = digit_to_ascii[val - temp*10];
|
||||
val = temp;
|
||||
}
|
||||
for(j=i-1; j>=0; j--) {
|
||||
*pdest++ = digit[j];
|
||||
for (i = 0; val; i++) {
|
||||
epicsUInt64 tenth = val / base;
|
||||
|
||||
digit = val - tenth * base;
|
||||
digits[i] = digit < 10 ? digit + '0' : digit - 10 + 'a';
|
||||
val = tenth;
|
||||
}
|
||||
len = i;
|
||||
|
||||
while (i > 0)
|
||||
*pdest++ = digits[--i];
|
||||
|
||||
*pdest = 0;
|
||||
return((int)(pdest-startAddr));
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
int epicsShareAPI cvtLongToString(
|
||||
epicsInt32 source,
|
||||
char *pdest)
|
||||
{
|
||||
epicsInt32 val,temp;
|
||||
char digit[11];
|
||||
int i,j;
|
||||
char *startAddr = pdest;
|
||||
/* Integer conversion routines */
|
||||
|
||||
if(source==0) {
|
||||
*pdest++ = '0';
|
||||
*pdest = 0;
|
||||
return((int)(pdest-startAddr));
|
||||
size_t
|
||||
cvtUInt32ToString(epicsUInt32 val, char *pdest)
|
||||
{
|
||||
if (val == 0) {
|
||||
*pdest++ = '0';
|
||||
*pdest = 0;
|
||||
return 1;
|
||||
}
|
||||
if(source<0) {
|
||||
if(source == INT_MIN) {
|
||||
sprintf(pdest,"%d",source);
|
||||
return((int)strlen(pdest));
|
||||
}
|
||||
*pdest++ = '-';
|
||||
source = -source;
|
||||
|
||||
return UInt32ToDec(val, pdest);
|
||||
}
|
||||
|
||||
size_t
|
||||
cvtInt32ToString(epicsInt32 val, char *pdest)
|
||||
{
|
||||
if (val == 0) {
|
||||
*pdest++ = '0';
|
||||
*pdest = 0;
|
||||
return 1;
|
||||
}
|
||||
val = source;
|
||||
for(i=0; val!=0; i++) {
|
||||
temp = val/10;
|
||||
digit[i] = digit_to_ascii[val - temp*10];
|
||||
val = temp;
|
||||
|
||||
if (val > 0)
|
||||
return UInt32ToDec(val, pdest);
|
||||
|
||||
if (val == -0x80000000) {
|
||||
strcpy(pdest, "-2147483648");
|
||||
return strlen(pdest);
|
||||
}
|
||||
for(j=i-1; j>=0; j--) {
|
||||
*pdest++ = digit[j];
|
||||
}
|
||||
*pdest = 0;
|
||||
return((int)(pdest-startAddr));
|
||||
|
||||
*pdest++ = '-';
|
||||
return 1 + UInt32ToDec(-val, pdest);
|
||||
}
|
||||
|
||||
|
||||
int epicsShareAPI cvtUlongToString(
|
||||
epicsUInt32 source,
|
||||
char *pdest)
|
||||
size_t
|
||||
cvtUInt64ToString(epicsUInt64 val, char *pdest)
|
||||
{
|
||||
epicsUInt32 val,temp;
|
||||
char digit[10];
|
||||
int i,j;
|
||||
char *startAddr = pdest;
|
||||
if (val == 0) {
|
||||
*pdest++ = '0';
|
||||
*pdest = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(source==0) {
|
||||
*pdest++ = '0';
|
||||
*pdest = 0;
|
||||
return((int)(pdest-startAddr));
|
||||
return UInt64ToDec(val, pdest);
|
||||
}
|
||||
|
||||
size_t
|
||||
cvtInt64ToString(epicsInt64 val, char *pdest)
|
||||
{
|
||||
if (val == 0) {
|
||||
*pdest++ = '0';
|
||||
*pdest = 0;
|
||||
return 1;
|
||||
}
|
||||
val = source;
|
||||
for(i=0; val!=0; i++) {
|
||||
temp = val/10;
|
||||
digit[i] = digit_to_ascii[val - temp*10];
|
||||
val = temp;
|
||||
|
||||
if (val > 0)
|
||||
return UInt64ToDec(val, pdest);
|
||||
|
||||
if (val == -0x8000000000000000LL) {
|
||||
strcpy(pdest, "-9223372036854775808");
|
||||
return strlen(pdest);
|
||||
}
|
||||
for(j=i-1; j>=0; j--) {
|
||||
*pdest++ = digit[j];
|
||||
}
|
||||
*pdest = 0;
|
||||
return((int)(pdest-startAddr));
|
||||
|
||||
*pdest++ = '-';
|
||||
return 1 + UInt64ToDec(-val, pdest);
|
||||
}
|
||||
|
||||
|
||||
/* Convert hex digits to ascii */
|
||||
|
||||
static char hex_digit_to_ascii[16]={'0','1','2','3','4','5','6','7','8','9',
|
||||
'a','b','c','d','e','f'};
|
||||
|
||||
|
||||
int epicsShareAPI cvtLongToHexString(
|
||||
epicsInt32 source,
|
||||
char *pdest)
|
||||
size_t
|
||||
cvtInt32ToHexString(epicsInt32 val, char *pdest)
|
||||
{
|
||||
epicsInt32 val,temp;
|
||||
char digit[10];
|
||||
int i,j;
|
||||
char *startAddr = pdest;
|
||||
if (val < 0)
|
||||
*pdest++ = '-';
|
||||
|
||||
if(source==0) {
|
||||
*pdest++ = '0';
|
||||
*pdest = 0;
|
||||
return((int)(pdest-startAddr));
|
||||
}
|
||||
if(source<0) {
|
||||
if(source == INT_MIN) {
|
||||
sprintf(pdest,"-0x%x",source);
|
||||
return((int)strlen(pdest));
|
||||
}
|
||||
*pdest++ = '-';
|
||||
source = -source;
|
||||
}
|
||||
*pdest++ = '0'; *pdest++ = 'x';
|
||||
val = source;
|
||||
for(i=0; val!=0; i++) {
|
||||
temp = val/16;
|
||||
digit[i] = hex_digit_to_ascii[val - temp*16];
|
||||
val = temp;
|
||||
}
|
||||
for(j=i-1; j>=0; j--) {
|
||||
*pdest++ = digit[j];
|
||||
}
|
||||
*pdest = 0;
|
||||
return((int)(pdest-startAddr));
|
||||
}
|
||||
|
||||
|
||||
int epicsShareAPI cvtLongToOctalString(
|
||||
epicsInt32 source,
|
||||
char *pdest)
|
||||
{
|
||||
epicsInt32 val,temp;
|
||||
char digit[16];
|
||||
int i,j;
|
||||
char *startAddr = pdest;
|
||||
|
||||
if(source==0) {
|
||||
*pdest++ = '0';
|
||||
*pdest = 0;
|
||||
return((int)(pdest-startAddr));
|
||||
}
|
||||
if(source<0) {
|
||||
if(source == INT_MIN) {
|
||||
sprintf(pdest,"-0%o",source);
|
||||
return((int)strlen(pdest));
|
||||
}
|
||||
*pdest++ = '-';
|
||||
source = -source;
|
||||
}
|
||||
*pdest++ = '0';
|
||||
val = source;
|
||||
for(i=0; val!=0; i++) {
|
||||
temp = val/8;
|
||||
/* reuse digit_to_ascii since octal is a subset of decimal */
|
||||
digit[i] = digit_to_ascii[val - temp*8];
|
||||
val = temp;
|
||||
*pdest++ = 'x';
|
||||
|
||||
if (val == 0) {
|
||||
*pdest++ = '0';
|
||||
*pdest = 0;
|
||||
return 3;
|
||||
}
|
||||
for(j=i-1; j>=0; j--) {
|
||||
*pdest++ = digit[j];
|
||||
|
||||
if (val > 0)
|
||||
return 2 + UInt32ToBase(val, pdest, 16);
|
||||
|
||||
if (val == -0x80000000) {
|
||||
strcpy(pdest, "80000000");
|
||||
return 11;
|
||||
}
|
||||
*pdest = 0;
|
||||
return((int)(pdest-startAddr));
|
||||
|
||||
return 3 + UInt32ToBase(-val, pdest, 16);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* cvtBitsToUlong()
|
||||
*
|
||||
* extract a bit field from the source epicsUInt32
|
||||
*/
|
||||
epicsUInt32 epicsShareAPI cvtBitsToUlong(
|
||||
epicsUInt32 src,
|
||||
unsigned bitFieldOffset,
|
||||
unsigned bitFieldLength)
|
||||
size_t
|
||||
cvtUInt32ToHexString(epicsUInt32 val, char *pdest)
|
||||
{
|
||||
epicsUInt32 mask;
|
||||
*pdest++ = '0';
|
||||
*pdest++ = 'x';
|
||||
|
||||
src = src >> bitFieldOffset;
|
||||
if (val == 0) {
|
||||
*pdest++ = '0';
|
||||
*pdest = 0;
|
||||
return 3;
|
||||
}
|
||||
|
||||
mask = (1<<bitFieldLength)-1;
|
||||
|
||||
src = src & mask;
|
||||
|
||||
return src;
|
||||
return 2 + UInt32ToBase(val, pdest, 16);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* cvtUlongToBits()
|
||||
*
|
||||
* insert a bit field from the source epicsUInt32
|
||||
* into the destination epicsUInt32
|
||||
*/
|
||||
epicsUInt32 epicsShareAPI cvtUlongToBits(
|
||||
epicsUInt32 src,
|
||||
epicsUInt32 dest,
|
||||
unsigned bitFieldOffset,
|
||||
unsigned bitFieldLength)
|
||||
size_t
|
||||
cvtInt32OctalString(epicsInt32 val, char *pdest)
|
||||
{
|
||||
epicsUInt32 mask;
|
||||
if (val == 0) {
|
||||
*pdest++ = '0';
|
||||
*pdest = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
mask = (1<<bitFieldLength)-1;
|
||||
mask = mask << bitFieldOffset;
|
||||
src = src << bitFieldOffset;
|
||||
dest = (dest & ~mask) | (src & mask);
|
||||
if (val > 0) {
|
||||
*pdest++ = '0';
|
||||
return 1 + UInt32ToBase(val, pdest, 8);
|
||||
}
|
||||
|
||||
return dest;
|
||||
if (val == -0x80000000) {
|
||||
strcpy(pdest, "-020000000000");
|
||||
return strlen(pdest);
|
||||
}
|
||||
|
||||
*pdest++ = '-';
|
||||
*pdest++ = '0';
|
||||
return 2 + UInt32ToBase(-val, pdest, 8);
|
||||
}
|
||||
|
||||
size_t
|
||||
cvtInt64ToHexString(epicsInt64 val, char *pdest)
|
||||
{
|
||||
if (val < 0)
|
||||
*pdest++ = '-';
|
||||
|
||||
*pdest++ = '0';
|
||||
*pdest++ = 'x';
|
||||
|
||||
if (val == 0) {
|
||||
*pdest++ = '0';
|
||||
*pdest = 0;
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (val > 0)
|
||||
return 2 + UInt64ToBase(val, pdest, 16);
|
||||
|
||||
if (val == -0x8000000000000000LL) {
|
||||
strcpy(pdest, "8000000000000000");
|
||||
return 19;
|
||||
}
|
||||
|
||||
return 3 + UInt64ToBase(-val, pdest, 16);
|
||||
}
|
||||
|
||||
size_t
|
||||
cvtUInt64ToHexString(epicsUInt64 val, char *pdest)
|
||||
{
|
||||
*pdest++ = '0';
|
||||
*pdest++ = 'x';
|
||||
|
||||
if (val == 0) {
|
||||
*pdest++ = '0';
|
||||
*pdest = 0;
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 2 + UInt64ToBase(val, pdest, 16);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +1,23 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* Very efficient routines to convert numbers to strings
|
||||
* Fast numeric to string conversions
|
||||
*
|
||||
* Author: Bob Dalesio wrote cvtFloatToString (called FF_TO_STR)
|
||||
* Code is same for cvtDoubleToString
|
||||
* Marty Kraimer wrote cvtCharToString,cvtUcharToString
|
||||
* cvtShortToString,cvtUshortToString,
|
||||
* cvtLongToString, and cvtUlongToString
|
||||
* Mark Anderson wrote cvtLongToHexString, cvtLongToOctalString,
|
||||
* adopted cvt[Float/Double]ExpString and
|
||||
* cvt[Float/Double]CompactString from fToEStr
|
||||
* and fixed calls to gcvt
|
||||
* Date: 12-9-92
|
||||
* Original Authors:
|
||||
* Bob Dalesio, Mark Anderson and Marty Kraimer
|
||||
* Date: 12 January 1993
|
||||
*/
|
||||
|
||||
#ifndef INCcvtFasth
|
||||
#define INCcvtFasth
|
||||
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "epicsTypes.h"
|
||||
#include "shareLib.h"
|
||||
@@ -34,46 +27,54 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* each of these functions return the number of characters "transmitted"
|
||||
* (as in ANSI-C/POSIX.1/XPG3 sprintf() functions)
|
||||
* All functions return the number of characters in the output
|
||||
*/
|
||||
epicsShareFunc int epicsShareAPI
|
||||
cvtFloatToString(float value, char *pstring, unsigned short precision);
|
||||
epicsShareFunc int epicsShareAPI
|
||||
cvtDoubleToString(double value, char *pstring, unsigned short precision);
|
||||
epicsShareFunc int epicsShareAPI
|
||||
cvtFloatToExpString(float value, char *pstring, unsigned short precision);
|
||||
epicsShareFunc int epicsShareAPI
|
||||
cvtDoubleToExpString(double value, char *pstring, unsigned short precision);
|
||||
epicsShareFunc int epicsShareAPI
|
||||
cvtFloatToCompactString(float value, char *pstring, unsigned short precision);
|
||||
epicsShareFunc int epicsShareAPI
|
||||
cvtDoubleToCompactString(double value, char *pstring, unsigned short precision);
|
||||
epicsShareFunc int epicsShareAPI
|
||||
cvtCharToString(signed char value, char *pstring);
|
||||
epicsShareFunc int epicsShareAPI
|
||||
cvtUcharToString(unsigned char value, char *pstring);
|
||||
epicsShareFunc int epicsShareAPI
|
||||
cvtShortToString(short value, char *pstring);
|
||||
epicsShareFunc int epicsShareAPI
|
||||
cvtUshortToString(unsigned short value, char *pstring);
|
||||
epicsShareFunc int epicsShareAPI
|
||||
cvtLongToString(epicsInt32 value, char *pstring);
|
||||
epicsShareFunc int epicsShareAPI
|
||||
cvtUlongToString(epicsUInt32 value, char *pstring);
|
||||
epicsShareFunc int epicsShareAPI
|
||||
cvtLongToHexString(epicsInt32 value, char *pstring);
|
||||
epicsShareFunc int epicsShareAPI
|
||||
cvtLongToOctalString(epicsInt32 value, char *pstring);
|
||||
epicsShareFunc epicsUInt32 epicsShareAPI cvtBitsToUlong(
|
||||
epicsUInt32 src,
|
||||
unsigned bitFieldOffset,
|
||||
unsigned bitFieldLength);
|
||||
epicsShareFunc epicsUInt32 epicsShareAPI cvtUlongToBits(
|
||||
epicsUInt32 src,
|
||||
epicsUInt32 dest,
|
||||
unsigned bitFieldOffset,
|
||||
unsigned bitFieldLength);
|
||||
epicsShareFunc int
|
||||
cvtFloatToString(float val, char *pdest, epicsUInt16 prec);
|
||||
epicsShareFunc int
|
||||
cvtDoubleToString(double val, char *pdest, epicsUInt16 prec);
|
||||
|
||||
epicsShareFunc int
|
||||
cvtFloatToExpString(float val, char *pdest, epicsUInt16 prec);
|
||||
epicsShareFunc int
|
||||
cvtDoubleToExpString(double val, char *pdest, epicsUInt16 prec);
|
||||
epicsShareFunc int
|
||||
cvtFloatToCompactString(float val, char *pdest, epicsUInt16 prec);
|
||||
epicsShareFunc int
|
||||
cvtDoubleToCompactString(double val, char *pdest, epicsUInt16 prec);
|
||||
|
||||
epicsShareFunc size_t
|
||||
cvtInt32ToString(epicsInt32 val, char *pdest);
|
||||
epicsShareFunc size_t
|
||||
cvtUInt32ToString(epicsUInt32 val, char *pdest);
|
||||
epicsShareFunc size_t
|
||||
cvtInt64ToString(epicsInt64 val, char *pdest);
|
||||
epicsShareFunc size_t
|
||||
cvtUInt64ToString(epicsUInt64 val, char *pdest);
|
||||
|
||||
epicsShareFunc size_t
|
||||
cvtInt32ToHexString(epicsInt32 val, char *pdest);
|
||||
epicsShareFunc size_t
|
||||
cvtUInt32ToHexString(epicsUInt32 val, char *pdest);
|
||||
epicsShareFunc size_t
|
||||
cvtInt32ToOctalString(epicsInt32 val, char *pdest);
|
||||
epicsShareFunc size_t
|
||||
cvtInt64ToHexString(epicsInt64 val, char *pdest);
|
||||
epicsShareFunc size_t
|
||||
cvtUInt64ToHexString(epicsUInt64 val, char *pdest);
|
||||
|
||||
/* Support the original names */
|
||||
|
||||
#define cvtCharToString(val, str) cvtInt32ToString(val, str)
|
||||
#define cvtUcharToString(val, str) cvtUInt32ToString(val, str)
|
||||
#define cvtShortToString(val, str) cvtInt32ToString(val, str)
|
||||
#define cvtUshortToString(val, str) cvtUInt32ToString(val, str)
|
||||
#define cvtLongToString(val, str) cvtInt32ToString(val, str)
|
||||
#define cvtUlongToString(val, str) cvtUInt32ToString(val, str)
|
||||
|
||||
#define cvtLongToHexString(val, str) cvtInt32ToHexString(val, str)
|
||||
#define cvtULongToHexString(val, str) cvtUInt32ToHexString(val, str)
|
||||
#define cvtLongToOctalString(val, str) cvtInt32ToOctalString(val, str)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -18,28 +18,18 @@
|
||||
|
||||
epicsShareFunc float epicsConvertDoubleToFloat(double value)
|
||||
{
|
||||
float rtnvalue;
|
||||
double abs;
|
||||
|
||||
if (value == 0) {
|
||||
rtnvalue = 0;
|
||||
} else if (!finite(value)) {
|
||||
rtnvalue = (float)value;
|
||||
} else {
|
||||
double abs = fabs(value);
|
||||
if (value == 0 || !finite(value))
|
||||
return (float) value;
|
||||
|
||||
if (abs >= FLT_MAX) {
|
||||
if (value > 0)
|
||||
rtnvalue = FLT_MAX;
|
||||
else
|
||||
rtnvalue = -FLT_MAX;
|
||||
} else if (abs <= FLT_MIN) {
|
||||
if (value > 0)
|
||||
rtnvalue = FLT_MIN;
|
||||
else
|
||||
rtnvalue = -FLT_MIN;
|
||||
} else {
|
||||
rtnvalue = (float)value;
|
||||
}
|
||||
}
|
||||
return rtnvalue;
|
||||
abs = fabs(value);
|
||||
|
||||
if (abs >= FLT_MAX)
|
||||
return (value > 0) ? FLT_MAX : -FLT_MAX;
|
||||
|
||||
if (abs <= FLT_MIN)
|
||||
return (value > 0) ? FLT_MIN : -FLT_MIN;
|
||||
|
||||
return (float) value;
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ epicsParseInt32(const char *str, epicsInt32 *to, int base, char **units)
|
||||
return S_stdlib_overflow;
|
||||
#endif
|
||||
|
||||
*to = value;
|
||||
*to = (epicsInt32) value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@ epicsParseUInt32(const char *str, epicsUInt32 *to, int base, char **units)
|
||||
return S_stdlib_overflow;
|
||||
#endif
|
||||
|
||||
*to = value;
|
||||
*to = (epicsUInt32) value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ epicsParseInt64(const char *str, epicsInt64 *to, int base, char **units)
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
*to = value;
|
||||
*to = (epicsInt64) value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ epicsParseUInt64(const char *str, epicsUInt64 *to, int base, char **units)
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
*to = value;
|
||||
*to = (epicsUInt64) value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,6 +82,8 @@ typedef union epics_any {
|
||||
epicsEnum16 enum16;
|
||||
epicsInt32 int32;
|
||||
epicsUInt32 uInt32;
|
||||
epicsInt64 int64;
|
||||
epicsUInt64 uInt64;
|
||||
epicsFloat32 float32;
|
||||
epicsFloat64 float64;
|
||||
epicsString string;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
The source files here are vxWorks dependent.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Saskatchewan
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* This header is included as part of epicsString.h and epicsStdlib.h
|
||||
* This header is included by epicsString.h and epicsStdlib.h
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -15,7 +15,7 @@ extern "C" {
|
||||
/*
|
||||
* epicsStrtod() for systems with broken strtod() routine
|
||||
*/
|
||||
epicsShareFunc double epicsStrtod(const char *str, char **endp);
|
||||
double epicsStrtod(const char *str, char **endp);
|
||||
|
||||
/*
|
||||
* VxWorks doesn't provide these routines, so for now we do
|
||||
|
||||
@@ -177,6 +177,11 @@ macDefExpandTest_SRCS += macDefExpandTest.c
|
||||
testHarness_SRCS += macDefExpandTest.c
|
||||
TESTS += macDefExpandTest
|
||||
|
||||
TESTPROD_HOST += cvtFastTest
|
||||
cvtFastTest_SRCS += cvtFastTest.cpp
|
||||
testHarness_SRCS += cvtFastTest.cpp
|
||||
TESTS += cvtFastTest
|
||||
|
||||
TESTPROD_HOST += macLibTest
|
||||
macLibTest_SRCS += macLibTest.c
|
||||
testHarness_SRCS += macLibTest.c
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
|
||||
// Author: Jeff Hill, LANL
|
||||
// Original Author: Jeff Hill, LANL
|
||||
|
||||
#include <cmath>
|
||||
#include <cfloat>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <typeinfo>
|
||||
#include <iostream>
|
||||
|
||||
#include "epicsStdio.h"
|
||||
#include "cvtFast.h"
|
||||
@@ -13,136 +15,337 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef void ( * PTestFunc ) ( const double &, char * pBug, size_t bufSize );
|
||||
|
||||
class Test {
|
||||
class PerfConverter {
|
||||
public:
|
||||
Test ();
|
||||
virtual ~Test ();
|
||||
void execute ();
|
||||
protected:
|
||||
char _pDst[128];
|
||||
double _srcVal;
|
||||
unsigned short _prec;
|
||||
static unsigned const _nUnrolled = 10;
|
||||
static const unsigned _uSecPerSec = 1000000;
|
||||
static unsigned const _nIterations = 10000;
|
||||
virtual void _target () = 0;
|
||||
void _measure ();
|
||||
Test ( const Test & );
|
||||
Test & operator = ( Test & );
|
||||
virtual const char * name(void) const = 0;
|
||||
virtual int maxPrecision(void) const = 0;
|
||||
virtual void target (double srcD, float srcF, char *dst, size_t len, int prec) const = 0;
|
||||
virtual void add(int prec, double elapsed) = 0;
|
||||
virtual double total(int prec) = 0;
|
||||
virtual ~PerfConverter () {};
|
||||
};
|
||||
|
||||
class TestCvtFastDouble : public Test {
|
||||
class Perf {
|
||||
public:
|
||||
Perf ( int maxConverters );
|
||||
virtual ~Perf ();
|
||||
void addConverter( PerfConverter * c );
|
||||
void execute (int count, bool verbose);
|
||||
void report (const char *title, int count);
|
||||
protected:
|
||||
void _target ();
|
||||
static unsigned const nUnrolled = 10;
|
||||
static const unsigned uSecPerSec = 1000000;
|
||||
static unsigned const nIterations = 10000;
|
||||
|
||||
const int maxConverters;
|
||||
PerfConverter **converters;
|
||||
int nConverters;
|
||||
int maxPrecision;
|
||||
bool verbose;
|
||||
|
||||
void measure ( double srcD, float srcF, int prec );
|
||||
|
||||
private:
|
||||
Perf ( const Perf & );
|
||||
Perf & operator = ( Perf & );
|
||||
};
|
||||
|
||||
class TestSNPrintf : public Test {
|
||||
protected:
|
||||
void _target ();
|
||||
};
|
||||
|
||||
Test ::
|
||||
Test () :
|
||||
_srcVal ( 0.0 ), _prec ( 0 )
|
||||
Perf :: Perf ( int maxConverters_ ) :
|
||||
maxConverters ( maxConverters_ ),
|
||||
converters ( new PerfConverter * [ maxConverters_ ] ),
|
||||
nConverters ( 0 ),
|
||||
maxPrecision ( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
Test :: ~Test ()
|
||||
Perf :: ~Perf ()
|
||||
{
|
||||
for ( int j = 0; j < nConverters; j++ )
|
||||
delete converters[ j ];
|
||||
|
||||
delete [] converters;
|
||||
}
|
||||
|
||||
void Test :: execute ()
|
||||
void Perf :: addConverter(PerfConverter *c)
|
||||
{
|
||||
static const unsigned lowPrecision = 2;
|
||||
static const unsigned highPrecision = DBL_MANT_DIG;
|
||||
|
||||
for ( unsigned i = 0; i < 3; i++ ) {
|
||||
if ( nConverters >= maxConverters )
|
||||
throw std :: runtime_error ( "Too many converters" );
|
||||
|
||||
converters[ nConverters++ ] = c;
|
||||
|
||||
int prec = c->maxPrecision();
|
||||
if ( prec > maxPrecision )
|
||||
maxPrecision = prec;
|
||||
}
|
||||
|
||||
void Perf :: execute (const int count, bool verbose_)
|
||||
{
|
||||
verbose = verbose_;
|
||||
|
||||
for ( int i = 0; i < count; i++ ) {
|
||||
double srcDbl = rand ();
|
||||
srcDbl /= (RAND_MAX + 1.0);
|
||||
srcDbl *= 20.0;
|
||||
srcDbl -= 10.0;
|
||||
float srcFlt = (float) srcDbl;
|
||||
|
||||
for ( int prec = 0; prec <= maxPrecision; prec++ ) {
|
||||
measure (srcFlt, srcDbl, prec);
|
||||
}
|
||||
}
|
||||
report ( "Small numbers, -10..+10", count );
|
||||
|
||||
for ( int i = 0; i < count; i++ ) {
|
||||
double mVal = rand ();
|
||||
mVal /= (RAND_MAX + 1.0);
|
||||
double fEVal = rand ();
|
||||
fEVal /= (RAND_MAX + 1.0);
|
||||
fEVal *= DBL_MAX_EXP - DBL_MIN_EXP;
|
||||
fEVal += DBL_MIN_EXP;
|
||||
int eVal = static_cast < int > ( fEVal + 0.5 );
|
||||
_srcVal = ldexp ( mVal, eVal );
|
||||
for ( _prec = lowPrecision;
|
||||
_prec <= highPrecision; _prec += 4u ) {
|
||||
_measure ();
|
||||
double eVal = rand ();
|
||||
eVal /= (RAND_MAX + 1.0);
|
||||
|
||||
double dVal = eVal;
|
||||
dVal *= FLT_MAX_EXP - FLT_MIN_EXP;
|
||||
dVal += FLT_MIN_EXP;
|
||||
int dEVal = static_cast < int > ( dVal + 0.5 );
|
||||
double srcDbl = ldexp ( mVal, dEVal );
|
||||
float srcFlt = (float) srcDbl;
|
||||
|
||||
for ( int prec = 0; prec <= maxPrecision; prec++ ) {
|
||||
measure (srcFlt, srcDbl, prec);
|
||||
}
|
||||
_srcVal = rand ();
|
||||
_srcVal /= (RAND_MAX + 1.0);
|
||||
_srcVal *= 10.0;
|
||||
_srcVal -= 5.0;
|
||||
for ( _prec = lowPrecision;
|
||||
_prec <= highPrecision; _prec += 4u ) {
|
||||
_measure ();
|
||||
}
|
||||
report ( "Random mantissa+exponent", count );
|
||||
}
|
||||
|
||||
void Perf :: report (const char *title, const int count)
|
||||
{
|
||||
printf( "\n%s\n\nprec\t", title );
|
||||
for ( int j = 0; j < nConverters; j++ )
|
||||
printf( "%-16s ", converters[j]->name() );
|
||||
|
||||
for (int prec = 0; prec <= maxPrecision; prec++ ) {
|
||||
printf( "\n %2d\t", prec );
|
||||
for (int j = 0; j < nConverters; j++ ) {
|
||||
PerfConverter *c = converters[j];
|
||||
if (prec > c->maxPrecision())
|
||||
printf( "%11s ", "-" );
|
||||
else {
|
||||
printf( "%11.9f sec ", c->total(prec) / count );
|
||||
}
|
||||
}
|
||||
}
|
||||
printf( "\n\n" );
|
||||
}
|
||||
|
||||
void Perf :: measure (double srcD, float srcF, int prec)
|
||||
{
|
||||
char buf[40];
|
||||
|
||||
for ( int j = 0; j < nConverters; j++ ) {
|
||||
PerfConverter *c = converters[j];
|
||||
|
||||
if (prec > c->maxPrecision())
|
||||
continue;
|
||||
|
||||
std::memset(buf, 0, sizeof(buf));
|
||||
|
||||
epicsTime beg = epicsTime :: getCurrent ();
|
||||
for ( unsigned i = 0; i < nIterations; i++ ) {
|
||||
c->target (srcD, srcF, buf, sizeof(buf) - 1, prec);
|
||||
}
|
||||
epicsTime end = epicsTime :: getCurrent ();
|
||||
|
||||
double elapsed = end - beg;
|
||||
elapsed /= nIterations * nUnrolled;
|
||||
c->add( prec, elapsed );
|
||||
|
||||
if (verbose)
|
||||
printf ( "%17s: %11.9f sec, prec=%2i '%s'\n",
|
||||
c->name (), elapsed, prec, buf );
|
||||
}
|
||||
}
|
||||
|
||||
void Test :: _measure ()
|
||||
{
|
||||
epicsTime beg = epicsTime :: getCurrent ();
|
||||
for ( unsigned i = 0; i < _nIterations; i++ ) {
|
||||
_target ();
|
||||
|
||||
// Conversions to be measured
|
||||
|
||||
class PerfCvtFastFloat : public PerfConverter {
|
||||
static const int digits = 12;
|
||||
public:
|
||||
PerfCvtFastFloat ()
|
||||
{
|
||||
for (int i = 0; i <= digits; i++)
|
||||
measured[i] = 0; // Some targets seem to need this
|
||||
}
|
||||
epicsTime end = epicsTime :: getCurrent ();
|
||||
double elapsed = end - beg;
|
||||
elapsed /= _nIterations * _nUnrolled;
|
||||
elapsed *= _uSecPerSec;
|
||||
printf ( " %4.4f usec, prec=%i, val=%4.4g, for %s\n",
|
||||
elapsed, _prec, _srcVal, typeid ( *this ).name () );
|
||||
}
|
||||
|
||||
void TestCvtFastDouble :: _target ()
|
||||
{
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
int maxPrecision (void) const { return digits; }
|
||||
const char *name (void) const { return "cvtFloatToString"; }
|
||||
void target (double srcD, float srcF, char *dst, size_t len, int prec) const
|
||||
{
|
||||
cvtFloatToString ( srcF, dst, prec );
|
||||
cvtFloatToString ( srcF, dst, prec );
|
||||
cvtFloatToString ( srcF, dst, prec );
|
||||
cvtFloatToString ( srcF, dst, prec );
|
||||
cvtFloatToString ( srcF, dst, prec );
|
||||
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
cvtDoubleToString ( _srcVal, _pDst, _prec );
|
||||
cvtFloatToString ( srcF, dst, prec );
|
||||
cvtFloatToString ( srcF, dst, prec );
|
||||
cvtFloatToString ( srcF, dst, prec );
|
||||
cvtFloatToString ( srcF, dst, prec );
|
||||
cvtFloatToString ( srcF, dst, prec );
|
||||
}
|
||||
void add (int prec, double elapsed) { measured[prec] += elapsed; }
|
||||
double total (int prec) {
|
||||
double total = measured[prec];
|
||||
measured[prec] = 0;
|
||||
return total;
|
||||
}
|
||||
private:
|
||||
double measured[digits+1];
|
||||
};
|
||||
|
||||
|
||||
class PerfCvtFastDouble : public PerfConverter {
|
||||
static const int digits = 17;
|
||||
public:
|
||||
PerfCvtFastDouble ()
|
||||
{
|
||||
for (int i = 0; i <= digits; i++)
|
||||
measured[i] = 0; // Some targets seem to need this
|
||||
}
|
||||
int maxPrecision (void) const { return digits; }
|
||||
const char *name (void) const { return "cvtDoubleToString"; }
|
||||
void target (double srcD, float srcF, char *dst, size_t len, int prec) const
|
||||
{
|
||||
cvtDoubleToString ( srcD, dst, prec );
|
||||
cvtDoubleToString ( srcD, dst, prec );
|
||||
cvtDoubleToString ( srcD, dst, prec );
|
||||
cvtDoubleToString ( srcD, dst, prec );
|
||||
cvtDoubleToString ( srcD, dst, prec );
|
||||
|
||||
cvtDoubleToString ( srcD, dst, prec );
|
||||
cvtDoubleToString ( srcD, dst, prec );
|
||||
cvtDoubleToString ( srcD, dst, prec );
|
||||
cvtDoubleToString ( srcD, dst, prec );
|
||||
cvtDoubleToString ( srcD, dst, prec );
|
||||
}
|
||||
void add(int prec, double elapsed) { measured[prec] += elapsed; }
|
||||
double total (int prec) {
|
||||
double total = measured[prec];
|
||||
measured[prec] = 0;
|
||||
return total;
|
||||
}
|
||||
private:
|
||||
double measured[digits+1];
|
||||
};
|
||||
|
||||
|
||||
class PerfSNPrintf : public PerfConverter {
|
||||
static const int digits = 17;
|
||||
public:
|
||||
PerfSNPrintf ()
|
||||
{
|
||||
for (int i = 0; i <= digits; i++)
|
||||
measured[i] = 0; // Some targets seem to need this
|
||||
}
|
||||
int maxPrecision (void) const { return digits; }
|
||||
const char *name (void) const { return "epicsSnprintf"; }
|
||||
void target (double srcD, float srcF, char *dst, size_t len, int prec) const
|
||||
{
|
||||
epicsSnprintf ( dst, len, "%.*g", prec, srcD );
|
||||
epicsSnprintf ( dst, len, "%.*g", prec, srcD );
|
||||
epicsSnprintf ( dst, len, "%.*g", prec, srcD );
|
||||
epicsSnprintf ( dst, len, "%.*g", prec, srcD );
|
||||
epicsSnprintf ( dst, len, "%.*g", prec, srcD );
|
||||
|
||||
epicsSnprintf ( dst, len, "%.*g", prec, srcD );
|
||||
epicsSnprintf ( dst, len, "%.*g", prec, srcD );
|
||||
epicsSnprintf ( dst, len, "%.*g", prec, srcD );
|
||||
epicsSnprintf ( dst, len, "%.*g", prec, srcD );
|
||||
epicsSnprintf ( dst, len, "%.*g", prec, srcD );
|
||||
}
|
||||
void add(int prec, double elapsed) { measured[prec] += elapsed; }
|
||||
double total (int prec) {
|
||||
double total = measured[prec];
|
||||
measured[prec] = 0;
|
||||
return total;
|
||||
}
|
||||
private:
|
||||
double measured[digits+1];
|
||||
};
|
||||
|
||||
|
||||
// This is a quick-and-dirty std::streambuf converter that writes directly
|
||||
// into the output buffer. Performance is slower than epicsSnprintf().
|
||||
|
||||
struct membuf: public std::streambuf {
|
||||
membuf(char *array, size_t size) {
|
||||
this->setp(array, array + size - 1);
|
||||
}
|
||||
};
|
||||
|
||||
struct omemstream: virtual membuf, std::ostream {
|
||||
omemstream(char *array, size_t size):
|
||||
membuf(array, size),
|
||||
std::ostream(this) {
|
||||
}
|
||||
};
|
||||
|
||||
static void ossConvertD(char *dst, size_t len, int prec, double src) {
|
||||
omemstream oss(dst, len);
|
||||
oss.precision(prec);
|
||||
oss << src << ends;
|
||||
}
|
||||
|
||||
void TestSNPrintf :: _target ()
|
||||
{
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g",
|
||||
static_cast < int > ( _prec ), _srcVal );
|
||||
}
|
||||
class PerfStreamBuf : public PerfConverter {
|
||||
static const int digits = 17;
|
||||
public:
|
||||
PerfStreamBuf ()
|
||||
{
|
||||
for (int i = 0; i <= digits; i++)
|
||||
measured[i] = 0; // Some targets seem to need this
|
||||
}
|
||||
int maxPrecision (void) const { return digits; }
|
||||
const char *name (void) const { return "std::streambuf"; }
|
||||
void target (double srcD, float srcF, char *dst, size_t len, int prec) const
|
||||
{
|
||||
ossConvertD ( dst, len, prec, srcD );
|
||||
ossConvertD ( dst, len, prec, srcD );
|
||||
ossConvertD ( dst, len, prec, srcD );
|
||||
ossConvertD ( dst, len, prec, srcD );
|
||||
ossConvertD ( dst, len, prec, srcD );
|
||||
|
||||
ossConvertD ( dst, len, prec, srcD );
|
||||
ossConvertD ( dst, len, prec, srcD );
|
||||
ossConvertD ( dst, len, prec, srcD );
|
||||
ossConvertD ( dst, len, prec, srcD );
|
||||
ossConvertD ( dst, len, prec, srcD );
|
||||
}
|
||||
void add(int prec, double elapsed) { measured[prec] += elapsed; }
|
||||
double total (int prec) {
|
||||
double total = measured[prec];
|
||||
measured[prec] = 0;
|
||||
return total;
|
||||
}
|
||||
private:
|
||||
double measured[digits+1];
|
||||
};
|
||||
|
||||
|
||||
MAIN(cvtFastPerform)
|
||||
{
|
||||
TestCvtFastDouble testCvtFastDouble;
|
||||
TestSNPrintf testSNPrintf;
|
||||
|
||||
testCvtFastDouble.execute ();
|
||||
testSNPrintf.execute ();
|
||||
Perf t(4);
|
||||
|
||||
t.addConverter( new PerfCvtFastFloat );
|
||||
t.addConverter( new PerfCvtFastDouble );
|
||||
t.addConverter( new PerfSNPrintf );
|
||||
t.addConverter( new PerfStreamBuf );
|
||||
|
||||
// The parameter to execute() below are:
|
||||
// count = number of different random numbers to measure
|
||||
// verbose = whether to display individual measurements
|
||||
|
||||
#ifdef vxWorks
|
||||
t.execute (3, true); // Slow...
|
||||
#else
|
||||
t.execute (5, false);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
457
src/libCom/test/cvtFastTest.c
Normal file
457
src/libCom/test/cvtFastTest.c
Normal file
@@ -0,0 +1,457 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* cvtFastTest.c
|
||||
*
|
||||
* Test the string converters in libCom/cvtFast/cvtFast.c
|
||||
*
|
||||
* To Do: Test Char/Uchar/Short/Ushort/Long/Ulong versions
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
#include "epicsUnitTest.h"
|
||||
#include "cvtFast.h"
|
||||
#include "epicsStdlib.h"
|
||||
#include "testMain.h"
|
||||
|
||||
#define tryIString(typ, lit, siz) \
|
||||
len = cvt##typ##ToString(lit, buf); \
|
||||
if (!testOk(len == siz, "cvt"#typ"ToString(" #lit ") == " #siz)) \
|
||||
testDiag("length returned was %u", (unsigned) len); \
|
||||
status = epicsParse##typ(buf, &val_##typ, 10, NULL); \
|
||||
testOk(!status, "epicsParse"#typ"('%s') OK", buf); \
|
||||
testOk(val_##typ == lit, #lit " => '%s'", buf);
|
||||
|
||||
#define tryFString(typ, lit, prec, siz) \
|
||||
len = cvt##typ##ToString(lit, buf, prec); \
|
||||
if (!testOk(len == siz, "cvt"#typ"ToString(" #lit ", %d) == " #siz, prec)) \
|
||||
testDiag("length returned was %u", (unsigned) len); \
|
||||
status = epicsParse##typ(buf, &val_##typ, NULL); \
|
||||
testOk(!status, "epicsParse"#typ"('%s') OK", buf); \
|
||||
testOk(fabs(val_##typ - lit) < 0.5 * pow(10, -prec), #lit " => '%s'", buf);
|
||||
|
||||
|
||||
MAIN(cvtFastTest)
|
||||
{
|
||||
char buf[80];
|
||||
size_t len;
|
||||
long status;
|
||||
epicsUInt32 val_UInt32;
|
||||
epicsInt32 val_Int32;
|
||||
epicsUInt64 val_UInt64;
|
||||
epicsInt64 val_Int64;
|
||||
epicsFloat32 val_Float;
|
||||
epicsFloat64 val_Double;
|
||||
|
||||
#if defined(WIN32) && (!defined(_MINGW) || __MSVCRT_VERSION__ >= 0x0800)
|
||||
_set_output_format(_TWO_DIGIT_EXPONENT);
|
||||
#endif
|
||||
|
||||
testPlan(1062);
|
||||
|
||||
/* Arguments: type, value, num chars */
|
||||
testDiag("------------------------------------------------------");
|
||||
testDiag("** Positive Int32 **");
|
||||
tryIString(Int32, 0, 1);
|
||||
tryIString(Int32, 1, 1);
|
||||
tryIString(Int32, 10, 2);
|
||||
tryIString(Int32, 100, 3);
|
||||
tryIString(Int32, 254, 3);
|
||||
tryIString(Int32, 255, 3);
|
||||
tryIString(Int32, 256, 3);
|
||||
tryIString(Int32, 257, 3);
|
||||
tryIString(Int32, 1000, 4);
|
||||
tryIString(Int32, 10000, 5);
|
||||
tryIString(Int32, 32766, 5);
|
||||
tryIString(Int32, 32767, 5);
|
||||
tryIString(Int32, 32768, 5);
|
||||
tryIString(Int32, 32769, 5);
|
||||
tryIString(Int32, 65534, 5);
|
||||
tryIString(Int32, 65535, 5);
|
||||
tryIString(Int32, 65536, 5);
|
||||
tryIString(Int32, 65537, 5);
|
||||
tryIString(Int32, 2147483646, 10);
|
||||
tryIString(Int32, 2147483647, 10);
|
||||
|
||||
testDiag("------------------------------------------------------");
|
||||
testDiag("** Negative Int32 **");
|
||||
tryIString(Int32, -1, 2);
|
||||
tryIString(Int32, -10, 3);
|
||||
tryIString(Int32, -100, 4);
|
||||
tryIString(Int32, -254, 4);
|
||||
tryIString(Int32, -255, 4);
|
||||
tryIString(Int32, -256, 4);
|
||||
tryIString(Int32, -257, 4);
|
||||
tryIString(Int32, -1000, 5);
|
||||
tryIString(Int32, -10000, 6);
|
||||
tryIString(Int32, -32766, 6);
|
||||
tryIString(Int32, -32767, 6);
|
||||
tryIString(Int32, -32768, 6);
|
||||
tryIString(Int32, -32769, 6);
|
||||
tryIString(Int32, -65534, 6);
|
||||
tryIString(Int32, -65535, 6);
|
||||
tryIString(Int32, -65536, 6);
|
||||
tryIString(Int32, -65537, 6);
|
||||
tryIString(Int32, -2147483647, 11);
|
||||
tryIString(Int32, -2147483648LL, 11);
|
||||
|
||||
testDiag("------------------------------------------------------");
|
||||
testDiag("** UInt32 **");
|
||||
tryIString(UInt32, 0, 1);
|
||||
tryIString(UInt32, 1, 1);
|
||||
tryIString(UInt32, 10, 2);
|
||||
tryIString(UInt32, 100, 3);
|
||||
tryIString(UInt32, 254, 3);
|
||||
tryIString(UInt32, 255, 3);
|
||||
tryIString(UInt32, 256, 3);
|
||||
tryIString(UInt32, 257, 3);
|
||||
tryIString(UInt32, 1000, 4);
|
||||
tryIString(UInt32, 10000, 5);
|
||||
tryIString(UInt32, 32766, 5);
|
||||
tryIString(UInt32, 32767, 5);
|
||||
tryIString(UInt32, 32768, 5);
|
||||
tryIString(UInt32, 32769, 5);
|
||||
tryIString(UInt32, 65534, 5);
|
||||
tryIString(UInt32, 65535, 5);
|
||||
tryIString(UInt32, 65536, 5);
|
||||
tryIString(UInt32, 65537, 5);
|
||||
tryIString(UInt32, 2147483646ULL, 10);
|
||||
tryIString(UInt32, 2147483647ULL, 10);
|
||||
tryIString(UInt32, 2147483648ULL, 10);
|
||||
tryIString(UInt32, 4294967294ULL, 10);
|
||||
tryIString(UInt32, 4294967295ULL, 10);
|
||||
|
||||
testDiag("------------------------------------------------------");
|
||||
testDiag("** Positive Int64 **");
|
||||
tryIString(Int64, 0, 1);
|
||||
tryIString(Int64, 1, 1);
|
||||
tryIString(Int64, 10, 2);
|
||||
tryIString(Int64, 100, 3);
|
||||
tryIString(Int64, 254, 3);
|
||||
tryIString(Int64, 255, 3);
|
||||
tryIString(Int64, 256, 3);
|
||||
tryIString(Int64, 257, 3);
|
||||
tryIString(Int64, 1000, 4);
|
||||
tryIString(Int64, 10000, 5);
|
||||
tryIString(Int64, 32766, 5);
|
||||
tryIString(Int64, 32767, 5);
|
||||
tryIString(Int64, 32768, 5);
|
||||
tryIString(Int64, 32769, 5);
|
||||
tryIString(Int64, 65534, 5);
|
||||
tryIString(Int64, 65535, 5);
|
||||
tryIString(Int64, 65536, 5);
|
||||
tryIString(Int64, 65537, 5);
|
||||
tryIString(Int64, 2147483646, 10);
|
||||
tryIString(Int64, 2147483647, 10);
|
||||
tryIString(Int64, 2147483648LL, 10);
|
||||
tryIString(Int64, 9223372036854775806LL, 19);
|
||||
tryIString(Int64, 9223372036854775807LL, 19);
|
||||
|
||||
testDiag("------------------------------------------------------");
|
||||
testDiag("** Negative Int64 **");
|
||||
tryIString(Int64, -1, 2);
|
||||
tryIString(Int64, -10, 3);
|
||||
tryIString(Int64, -100, 4);
|
||||
tryIString(Int64, -254, 4);
|
||||
tryIString(Int64, -255, 4);
|
||||
tryIString(Int64, -256, 4);
|
||||
tryIString(Int64, -257, 4);
|
||||
tryIString(Int64, -1000, 5);
|
||||
tryIString(Int64, -10000, 6);
|
||||
tryIString(Int64, -32766, 6);
|
||||
tryIString(Int64, -32767, 6);
|
||||
tryIString(Int64, -32768, 6);
|
||||
tryIString(Int64, -32769, 6);
|
||||
tryIString(Int64, -65534, 6);
|
||||
tryIString(Int64, -65535, 6);
|
||||
tryIString(Int64, -65536, 6);
|
||||
tryIString(Int64, -65537, 6);
|
||||
tryIString(Int64, -2147483647, 11);
|
||||
tryIString(Int64, -2147483648LL, 11);
|
||||
tryIString(Int64, -2147483649LL, 11);
|
||||
tryIString(Int64, -9223372036854775806LL, 20);
|
||||
tryIString(Int64, -9223372036854775807LL, 20);
|
||||
tryIString(Int64, -9223372036854775807LL-1, 20);
|
||||
|
||||
testDiag("------------------------------------------------------");
|
||||
testDiag("** UInt64 **");
|
||||
tryIString(UInt64, 0, 1);
|
||||
tryIString(UInt64, 1, 1);
|
||||
tryIString(UInt64, 10, 2);
|
||||
tryIString(UInt64, 100, 3);
|
||||
tryIString(UInt64, 254, 3);
|
||||
tryIString(UInt64, 255, 3);
|
||||
tryIString(UInt64, 256, 3);
|
||||
tryIString(UInt64, 257, 3);
|
||||
tryIString(UInt64, 1000, 4);
|
||||
tryIString(UInt64, 10000, 5);
|
||||
tryIString(UInt64, 32766, 5);
|
||||
tryIString(UInt64, 32767, 5);
|
||||
tryIString(UInt64, 32768, 5);
|
||||
tryIString(UInt64, 32769, 5);
|
||||
tryIString(UInt64, 65534, 5);
|
||||
tryIString(UInt64, 65535, 5);
|
||||
tryIString(UInt64, 65536, 5);
|
||||
tryIString(UInt64, 65537, 5);
|
||||
tryIString(UInt64, 2147483646, 10);
|
||||
tryIString(UInt64, 2147483647, 10);
|
||||
tryIString(UInt64, 2147483648U, 10);
|
||||
tryIString(UInt64, 2147483649U, 10);
|
||||
tryIString(UInt64, 4294967294U, 10);
|
||||
tryIString(UInt64, 4294967295U, 10);
|
||||
tryIString(UInt64, 4294967296ULL, 10);
|
||||
tryIString(UInt64, 4294967297ULL, 10);
|
||||
tryIString(UInt64, 9223372036854775806ULL, 19);
|
||||
tryIString(UInt64, 9223372036854775807ULL, 19);
|
||||
tryIString(UInt64, 9223372036854775808ULL, 19);
|
||||
tryIString(UInt64, 18446744073709551614ULL, 20);
|
||||
tryIString(UInt64, 18446744073709551615ULL, 20);
|
||||
|
||||
/* Arguments: type, value, precision, num chars */
|
||||
testDiag("------------------------------------------------------");
|
||||
testDiag("** Positive Float fixed-point **");
|
||||
tryFString(Float, 0, 0, 1);
|
||||
tryFString(Float, 0, 1, 3);
|
||||
tryFString(Float, 0, 2, 4);
|
||||
tryFString(Float, 0, 3, 5);
|
||||
tryFString(Float, 0, 4, 6);
|
||||
tryFString(Float, 0, 5, 7);
|
||||
tryFString(Float, 0, 6, 8);
|
||||
tryFString(Float, 0, 7, 9);
|
||||
tryFString(Float, 0, 8, 10);
|
||||
tryFString(Float, FLT_MIN, 0, 1);
|
||||
tryFString(Float, FLT_MIN, 1, 3);
|
||||
tryFString(Float, FLT_MIN, 2, 4);
|
||||
tryFString(Float, FLT_MIN, 3, 5);
|
||||
tryFString(Float, FLT_MIN, 4, 6);
|
||||
tryFString(Float, FLT_MIN, 5, 7);
|
||||
tryFString(Float, FLT_MIN, 6, 8);
|
||||
tryFString(Float, FLT_MIN, 7, 9);
|
||||
tryFString(Float, FLT_MIN, 8, 10);
|
||||
tryFString(Float, 0.000000004999999, 8, 10);
|
||||
tryFString(Float, 0.000000005000001, 8, 10);
|
||||
tryFString(Float, 0.00000004999999, 7, 9);
|
||||
tryFString(Float, 0.00000005000001, 7, 9);
|
||||
tryFString(Float, 0.0000004999999, 6, 8);
|
||||
tryFString(Float, 0.0000005000001, 6, 8);
|
||||
tryFString(Float, 0.000004999999, 5, 7);
|
||||
tryFString(Float, 0.000005000001, 5, 7);
|
||||
tryFString(Float, 0.00004999999, 4, 6);
|
||||
tryFString(Float, 0.00005000001, 4, 6);
|
||||
tryFString(Float, 0.0004999999, 3, 5);
|
||||
tryFString(Float, 0.0005000001, 3, 5);
|
||||
tryFString(Float, 0.004999999, 2, 4);
|
||||
tryFString(Float, 0.005000001, 2, 4);
|
||||
tryFString(Float, 0.04999999, 1, 3);
|
||||
tryFString(Float, 0.05000001, 1, 3);
|
||||
tryFString(Float, 0.4999999, 0, 1);
|
||||
tryFString(Float, 0.5000001, 0, 1);
|
||||
tryFString(Float, 1, 0, 1);
|
||||
tryFString(Float, 1, 1, 3);
|
||||
tryFString(Float, 1, 2, 4);
|
||||
tryFString(Float, 1, 3, 5);
|
||||
tryFString(Float, 1, 4, 6);
|
||||
tryFString(Float, 1, 5, 7);
|
||||
tryFString(Float, 1, 6, 8);
|
||||
tryFString(Float, 1, 7, 9);
|
||||
tryFString(Float, 1, 8, 10);
|
||||
tryFString(Float, 1.0500001, 1, 3);
|
||||
tryFString(Float, 1.1, 1, 3);
|
||||
tryFString(Float, 1.1499999, 1, 3);
|
||||
tryFString(Float, 9.5000001, 0, 2);
|
||||
tryFString(Float, 10, 0, 2);
|
||||
tryFString(Float, 10, 1, 4);
|
||||
tryFString(Float, 10, 8, 11);
|
||||
tryFString(Float, 100, 0, 3);
|
||||
tryFString(Float, 100, 1, 5);
|
||||
tryFString(Float, 100, 8, 12);
|
||||
tryFString(Float, 1000, 0, 4);
|
||||
tryFString(Float, 1000, 1, 6);
|
||||
tryFString(Float, 1000, 8, 13);
|
||||
tryFString(Float, 10000, 0, 5);
|
||||
tryFString(Float, 10000, 1, 7);
|
||||
tryFString(Float, 10000, 8, 14);
|
||||
tryFString(Float, 100000, 0, 6);
|
||||
tryFString(Float, 100000, 1, 8);
|
||||
tryFString(Float, 100000, 8, 15);
|
||||
tryFString(Float, 1000000, 0, 7);
|
||||
tryFString(Float, 1000000, 1, 9);
|
||||
tryFString(Float, 1000000, 8, 16);
|
||||
tryFString(Float, 10000000, 0, 8);
|
||||
tryFString(Float, 10000000, 1, 10);
|
||||
tryFString(Float, 10000000, 8, 17);
|
||||
|
||||
testDiag("------------------------------------------------------");
|
||||
testDiag("** Negative Float fixed-point **");
|
||||
tryFString(Float, -FLT_MIN, 0, 2);
|
||||
tryFString(Float, -FLT_MIN, 1, 4);
|
||||
tryFString(Float, -FLT_MIN, 8, 11);
|
||||
tryFString(Float, -1, 0, 2);
|
||||
tryFString(Float, -1, 1, 4);
|
||||
tryFString(Float, -1, 8, 11);
|
||||
tryFString(Float, -1.0500001, 1, 4);
|
||||
tryFString(Float, -1.1, 1, 4);
|
||||
tryFString(Float, -1.1499999, 1, 4);
|
||||
tryFString(Float, -9.5000001, 0, 3);
|
||||
tryFString(Float, -10, 0, 3);
|
||||
tryFString(Float, -10, 1, 5);
|
||||
tryFString(Float, -10, 8, 12);
|
||||
tryFString(Float, -100, 0, 4);
|
||||
tryFString(Float, -100, 1, 6);
|
||||
tryFString(Float, -100, 8, 13);
|
||||
tryFString(Float, -1000, 0, 5);
|
||||
tryFString(Float, -1000, 1, 7);
|
||||
tryFString(Float, -1000, 8, 14);
|
||||
tryFString(Float, -10000, 0, 6);
|
||||
tryFString(Float, -10000, 1, 8);
|
||||
tryFString(Float, -10000, 8, 15);
|
||||
tryFString(Float, -100000, 0, 7);
|
||||
tryFString(Float, -100000, 1, 9);
|
||||
tryFString(Float, -100000, 8, 16);
|
||||
tryFString(Float, -1000000, 0, 8);
|
||||
tryFString(Float, -1000000, 1, 10);
|
||||
tryFString(Float, -1000000, 8, 17);
|
||||
tryFString(Float, -10000000, 0, 9);
|
||||
tryFString(Float, -10000000, 1, 11);
|
||||
tryFString(Float, -10000000, 8, 18);
|
||||
|
||||
/*
|
||||
* Values > 1e7 trigger the %e format.
|
||||
*/
|
||||
testDiag("------------------------------------------------------");
|
||||
testDiag("** Positive Float scientific **");
|
||||
tryFString(Float, 1e+08, 0, 6);
|
||||
tryFString(Float, 1e+08, 1, 7);
|
||||
tryFString(Float, 1e+08, 2, 8);
|
||||
tryFString(Float, 1e+08, 3, 9);
|
||||
tryFString(Float, 1e+08, 4, 10);
|
||||
tryFString(Float, 1e+08, 5, 11);
|
||||
tryFString(Float, 1e+08, 6, 12);
|
||||
|
||||
testDiag("------------------------------------------------------");
|
||||
testDiag("** Positive Double fixed-point **");
|
||||
tryFString(Double, 0, 0, 1);
|
||||
tryFString(Double, 0, 1, 3);
|
||||
tryFString(Double, 0, 2, 4);
|
||||
tryFString(Double, 0, 3, 5);
|
||||
tryFString(Double, 0, 4, 6);
|
||||
tryFString(Double, 0, 5, 7);
|
||||
tryFString(Double, 0, 6, 8);
|
||||
tryFString(Double, 0, 7, 9);
|
||||
tryFString(Double, 0, 8, 10);
|
||||
tryFString(Double, DBL_MIN, 0, 1);
|
||||
tryFString(Double, DBL_MIN, 1, 3);
|
||||
tryFString(Double, DBL_MIN, 2, 4);
|
||||
tryFString(Double, DBL_MIN, 3, 5);
|
||||
tryFString(Double, DBL_MIN, 4, 6);
|
||||
tryFString(Double, DBL_MIN, 5, 7);
|
||||
tryFString(Double, DBL_MIN, 6, 8);
|
||||
tryFString(Double, DBL_MIN, 7, 9);
|
||||
tryFString(Double, DBL_MIN, 8, 10);
|
||||
tryFString(Double, 0.000000004999999, 8, 10);
|
||||
tryFString(Double, 0.000000005000001, 8, 10);
|
||||
tryFString(Double, 0.00000004999999, 7, 9);
|
||||
tryFString(Double, 0.00000005000001, 7, 9);
|
||||
tryFString(Double, 0.0000004999999, 6, 8);
|
||||
tryFString(Double, 0.0000005000001, 6, 8);
|
||||
tryFString(Double, 0.000004999999, 5, 7);
|
||||
tryFString(Double, 0.000005000001, 5, 7);
|
||||
tryFString(Double, 0.00004999999, 4, 6);
|
||||
tryFString(Double, 0.00005000001, 4, 6);
|
||||
tryFString(Double, 0.0004999999, 3, 5);
|
||||
tryFString(Double, 0.0005000001, 3, 5);
|
||||
tryFString(Double, 0.004999999, 2, 4);
|
||||
tryFString(Double, 0.005000001, 2, 4);
|
||||
tryFString(Double, 0.04999999, 1, 3);
|
||||
tryFString(Double, 0.05000001, 1, 3);
|
||||
tryFString(Double, 0.4999999, 0, 1);
|
||||
tryFString(Double, 0.5000001, 0, 1);
|
||||
tryFString(Double, 1, 0, 1);
|
||||
tryFString(Double, 1, 1, 3);
|
||||
tryFString(Double, 1, 2, 4);
|
||||
tryFString(Double, 1, 3, 5);
|
||||
tryFString(Double, 1, 4, 6);
|
||||
tryFString(Double, 1, 5, 7);
|
||||
tryFString(Double, 1, 6, 8);
|
||||
tryFString(Double, 1, 7, 9);
|
||||
tryFString(Double, 1, 8, 10);
|
||||
tryFString(Double, 1.0500001, 1, 3);
|
||||
tryFString(Double, 1.1, 1, 3);
|
||||
tryFString(Double, 1.1499999, 1, 3);
|
||||
tryFString(Double, 9.5000001, 0, 2);
|
||||
tryFString(Double, 10, 0, 2);
|
||||
tryFString(Double, 10, 1, 4);
|
||||
tryFString(Double, 10, 8, 11);
|
||||
tryFString(Double, 100, 0, 3);
|
||||
tryFString(Double, 100, 1, 5);
|
||||
tryFString(Double, 100, 8, 12);
|
||||
tryFString(Double, 1000, 0, 4);
|
||||
tryFString(Double, 1000, 1, 6);
|
||||
tryFString(Double, 1000, 8, 13);
|
||||
tryFString(Double, 10000, 0, 5);
|
||||
tryFString(Double, 10000, 1, 7);
|
||||
tryFString(Double, 10000, 8, 14);
|
||||
tryFString(Double, 100000, 0, 6);
|
||||
tryFString(Double, 100000, 1, 8);
|
||||
tryFString(Double, 100000, 8, 15);
|
||||
tryFString(Double, 1000000, 0, 7);
|
||||
tryFString(Double, 1000000, 1, 9);
|
||||
tryFString(Double, 1000000, 8, 16);
|
||||
tryFString(Double, 10000000, 0, 8);
|
||||
tryFString(Double, 10000000, 1, 10);
|
||||
tryFString(Double, 10000000, 8, 17);
|
||||
|
||||
testDiag("------------------------------------------------------");
|
||||
testDiag("** Negative Double fixed-point **");
|
||||
tryFString(Double, -DBL_MIN, 0, 2);
|
||||
tryFString(Double, -DBL_MIN, 1, 4);
|
||||
tryFString(Double, -DBL_MIN, 8, 11);
|
||||
tryFString(Double, -1, 0, 2);
|
||||
tryFString(Double, -1, 1, 4);
|
||||
tryFString(Double, -1, 8, 11);
|
||||
tryFString(Double, -1.0500001, 1, 4);
|
||||
tryFString(Double, -1.1, 1, 4);
|
||||
tryFString(Double, -1.1499999, 1, 4);
|
||||
tryFString(Double, -9.5000001, 0, 3);
|
||||
tryFString(Double, -10, 0, 3);
|
||||
tryFString(Double, -10, 1, 5);
|
||||
tryFString(Double, -10, 8, 12);
|
||||
tryFString(Double, -100, 0, 4);
|
||||
tryFString(Double, -100, 1, 6);
|
||||
tryFString(Double, -100, 8, 13);
|
||||
tryFString(Double, -1000, 0, 5);
|
||||
tryFString(Double, -1000, 1, 7);
|
||||
tryFString(Double, -1000, 8, 14);
|
||||
tryFString(Double, -10000, 0, 6);
|
||||
tryFString(Double, -10000, 1, 8);
|
||||
tryFString(Double, -10000, 8, 15);
|
||||
tryFString(Double, -100000, 0, 7);
|
||||
tryFString(Double, -100000, 1, 9);
|
||||
tryFString(Double, -100000, 8, 16);
|
||||
tryFString(Double, -1000000, 0, 8);
|
||||
tryFString(Double, -1000000, 1, 10);
|
||||
tryFString(Double, -1000000, 8, 17);
|
||||
tryFString(Double, -10000000, 0, 9);
|
||||
tryFString(Double, -10000000, 1, 11);
|
||||
tryFString(Double, -10000000, 8, 18);
|
||||
|
||||
/*
|
||||
* Values > 1e7 trigger the %e format.
|
||||
* Windows may print 3 digit exponents...
|
||||
*/
|
||||
testDiag("------------------------------------------------------");
|
||||
testDiag("** Positive Double scientific **");
|
||||
tryFString(Double, 1e+17, 0, 7);
|
||||
tryFString(Double, 1e+17, 1, 8);
|
||||
tryFString(Double, 1e+17, 2, 9);
|
||||
tryFString(Double, 1e+17, 3, 10);
|
||||
tryFString(Double, 1e+17, 4, 11);
|
||||
tryFString(Double, 1e+17, 5, 12);
|
||||
|
||||
return testDone();
|
||||
}
|
||||
@@ -28,6 +28,8 @@ dbRecStd_SRCS += devBoDbState.c
|
||||
dbRecStd_SRCS += devCalcoutSoft.c
|
||||
dbRecStd_SRCS += devEventSoft.c
|
||||
dbRecStd_SRCS += devHistogramSoft.c
|
||||
dbRecStd_SRCS += devI64inSoft.c
|
||||
dbRecStd_SRCS += devI64outSoft.c
|
||||
dbRecStd_SRCS += devLiSoft.c
|
||||
dbRecStd_SRCS += devLoSoft.c
|
||||
dbRecStd_SRCS += devLsiSoft.c
|
||||
@@ -49,6 +51,7 @@ dbRecStd_SRCS += devGeneralTime.c
|
||||
|
||||
dbRecStd_SRCS += devAiSoftCallback.c
|
||||
dbRecStd_SRCS += devBiSoftCallback.c
|
||||
dbRecStd_SRCS += devI64inSoftCallback.c
|
||||
dbRecStd_SRCS += devLiSoftCallback.c
|
||||
dbRecStd_SRCS += devMbbiDirectSoftCallback.c
|
||||
dbRecStd_SRCS += devMbbiSoftCallback.c
|
||||
@@ -57,6 +60,7 @@ dbRecStd_SRCS += devSiSoftCallback.c
|
||||
dbRecStd_SRCS += devAoSoftCallback.c
|
||||
dbRecStd_SRCS += devBoSoftCallback.c
|
||||
dbRecStd_SRCS += devCalcoutSoftCallback.c
|
||||
dbRecStd_SRCS += devI64outSoftCallback.c
|
||||
dbRecStd_SRCS += devLoSoftCallback.c
|
||||
dbRecStd_SRCS += devLsoSoftCallback.c
|
||||
dbRecStd_SRCS += devMbboSoftCallback.c
|
||||
|
||||
78
src/std/dev/devI64inSoft.c
Normal file
78
src/std/dev/devI64inSoft.c
Normal file
@@ -0,0 +1,78 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Original Author: Janet Anderson
|
||||
* Date: 09-23-91
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alarm.h"
|
||||
#include "dbDefs.h"
|
||||
#include "dbAccess.h"
|
||||
#include "recGbl.h"
|
||||
#include "devSup.h"
|
||||
#include "int64inRecord.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
/* Create the dset for devI64inSoft */
|
||||
static long init_record(int64inRecord *prec);
|
||||
static long read_int64in(int64inRecord *prec);
|
||||
|
||||
struct {
|
||||
long number;
|
||||
DEVSUPFUN report;
|
||||
DEVSUPFUN init;
|
||||
DEVSUPFUN init_record;
|
||||
DEVSUPFUN get_ioint_info;
|
||||
DEVSUPFUN read_int64in;
|
||||
} devI64inSoft = {
|
||||
5,
|
||||
NULL,
|
||||
NULL,
|
||||
init_record,
|
||||
NULL,
|
||||
read_int64in
|
||||
};
|
||||
epicsExportAddress(dset, devI64inSoft);
|
||||
|
||||
static long init_record(int64inRecord *prec)
|
||||
{
|
||||
/* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/
|
||||
switch (prec->inp.type) {
|
||||
case CONSTANT:
|
||||
if (recGblInitConstantLink(&prec->inp, DBF_INT64, &prec->val))
|
||||
prec->udf = FALSE;
|
||||
break;
|
||||
case PV_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
break;
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"devI64inSoft (init_record) Illegal INP field");
|
||||
return S_db_badField;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long read_int64in(int64inRecord *prec)
|
||||
{
|
||||
long status;
|
||||
|
||||
status = dbGetLink(&prec->inp, DBR_INT64, &prec->val, 0, 0);
|
||||
if (!status &&
|
||||
prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(&prec->inp, &prec->time);
|
||||
return status;
|
||||
}
|
||||
222
src/std/dev/devI64inSoftCallback.c
Normal file
222
src/std/dev/devI64inSoftCallback.c
Normal file
@@ -0,0 +1,222 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
/* devI64inSoftCallback.c */
|
||||
/*
|
||||
* Authors: Marty Kraimer & Andrew Johnson
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "alarm.h"
|
||||
#include "callback.h"
|
||||
#include "cantProceed.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbDefs.h"
|
||||
#include "dbAccess.h"
|
||||
#include "dbChannel.h"
|
||||
#include "dbNotify.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "recGbl.h"
|
||||
#include "recSup.h"
|
||||
#include "devSup.h"
|
||||
#include "link.h"
|
||||
#include "int64inRecord.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
|
||||
#define GET_OPTIONS (DBR_STATUS | DBR_TIME)
|
||||
|
||||
typedef struct devPvt {
|
||||
processNotify pn;
|
||||
CALLBACK callback;
|
||||
long options;
|
||||
int status;
|
||||
struct {
|
||||
DBRstatus
|
||||
DBRtime
|
||||
epicsInt32 value;
|
||||
} buffer;
|
||||
} devPvt;
|
||||
|
||||
|
||||
static void getCallback(processNotify *ppn, notifyGetType type)
|
||||
{
|
||||
int64inRecord *prec = (int64inRecord *)ppn->usrPvt;
|
||||
devPvt *pdevPvt = (devPvt *)prec->dpvt;
|
||||
long no_elements = 1;
|
||||
|
||||
if (ppn->status == notifyCanceled) {
|
||||
printf("devI64inSoftCallback::getCallback notifyCanceled\n");
|
||||
return;
|
||||
}
|
||||
|
||||
assert(type == getFieldType);
|
||||
pdevPvt->status = dbChannelGetField(ppn->chan, DBR_INT64,
|
||||
&pdevPvt->buffer, &pdevPvt->options, &no_elements, 0);
|
||||
}
|
||||
|
||||
static void doneCallback(processNotify *ppn)
|
||||
{
|
||||
int64inRecord *prec = (int64inRecord *)ppn->usrPvt;
|
||||
devPvt *pdevPvt = (devPvt *)prec->dpvt;
|
||||
|
||||
callbackRequestProcessCallback(&pdevPvt->callback, prec->prio, prec);
|
||||
}
|
||||
|
||||
static long add_record(dbCommon *pcommon)
|
||||
{
|
||||
int64inRecord *prec = (int64inRecord *)pcommon;
|
||||
DBLINK *plink = &prec->inp;
|
||||
dbChannel *chan;
|
||||
devPvt *pdevPvt;
|
||||
processNotify *ppn;
|
||||
|
||||
if (plink->type == CONSTANT) return 0;
|
||||
|
||||
if (plink->type != PV_LINK) {
|
||||
long status = S_db_badField;
|
||||
|
||||
recGblRecordError(status, (void *)prec,
|
||||
"devI64inSoftCallback (add_record) Illegal INP field");
|
||||
return status;
|
||||
}
|
||||
|
||||
chan = dbChannelCreate(plink->value.pv_link.pvname);
|
||||
if (!chan) {
|
||||
long status = S_db_notFound;
|
||||
|
||||
recGblRecordError(status, (void *)prec,
|
||||
"devI64inSoftCallback (init_record) linked record not found");
|
||||
return status;
|
||||
}
|
||||
|
||||
pdevPvt = calloc(1, sizeof(*pdevPvt));
|
||||
if (!pdevPvt) {
|
||||
long status = S_db_noMemory;
|
||||
|
||||
recGblRecordError(status, (void *)prec,
|
||||
"devI64inSoftCallback (add_record) out of memory, calloc() failed");
|
||||
return status;
|
||||
}
|
||||
ppn = &pdevPvt->pn;
|
||||
|
||||
plink->type = PN_LINK;
|
||||
plink->value.pv_link.pvlMask &= pvlOptMsMode; /* Severity flags only */
|
||||
|
||||
ppn->usrPvt = prec;
|
||||
ppn->chan = chan;
|
||||
ppn->getCallback = getCallback;
|
||||
ppn->doneCallback = doneCallback;
|
||||
ppn->requestType = processGetRequest;
|
||||
|
||||
pdevPvt->options = GET_OPTIONS;
|
||||
|
||||
prec->dpvt = pdevPvt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long del_record(dbCommon *pcommon) {
|
||||
int64inRecord *prec = (int64inRecord *)pcommon;
|
||||
DBLINK *plink = &prec->inp;
|
||||
devPvt *pdevPvt = (devPvt *)prec->dpvt;
|
||||
|
||||
if (plink->type == CONSTANT) return 0;
|
||||
assert(plink->type == PN_LINK);
|
||||
|
||||
dbNotifyCancel(&pdevPvt->pn);
|
||||
dbChannelDelete(pdevPvt->pn.chan);
|
||||
free(pdevPvt);
|
||||
|
||||
plink->type = PV_LINK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dsxt dsxtSoftCallback = {
|
||||
add_record, del_record
|
||||
};
|
||||
|
||||
static long init(int pass)
|
||||
{
|
||||
if (pass == 0) devExtend(&dsxtSoftCallback);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long init_record(int64inRecord *prec)
|
||||
{
|
||||
/* INP must be CONSTANT or PN_LINK */
|
||||
switch (prec->inp.type) {
|
||||
case CONSTANT:
|
||||
if (recGblInitConstantLink(&prec->inp, DBR_INT64, &prec->val))
|
||||
prec->udf = FALSE;
|
||||
break;
|
||||
case PN_LINK:
|
||||
/* Handled by add_record */
|
||||
break;
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"devI64inSoftCallback (init_record) Illegal INP field");
|
||||
prec->pact = TRUE;
|
||||
return S_db_badField;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long read_int64in(int64inRecord *prec)
|
||||
{
|
||||
devPvt *pdevPvt = (devPvt *)prec->dpvt;
|
||||
|
||||
if (!prec->dpvt)
|
||||
return 0;
|
||||
|
||||
if (!prec->pact) {
|
||||
dbProcessNotify(&pdevPvt->pn);
|
||||
prec->pact = TRUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pdevPvt->status) {
|
||||
recGblSetSevr(prec, READ_ALARM, INVALID_ALARM);
|
||||
return pdevPvt->status;
|
||||
}
|
||||
|
||||
prec->val = pdevPvt->buffer.value;
|
||||
prec->udf = FALSE;
|
||||
|
||||
switch (prec->inp.value.pv_link.pvlMask & pvlOptMsMode) {
|
||||
case pvlOptNMS:
|
||||
break;
|
||||
case pvlOptMSI:
|
||||
if (pdevPvt->buffer.severity < INVALID_ALARM)
|
||||
break;
|
||||
/* else fall through */
|
||||
case pvlOptMS:
|
||||
recGblSetSevr(prec, LINK_ALARM, pdevPvt->buffer.severity);
|
||||
break;
|
||||
case pvlOptMSS:
|
||||
recGblSetSevr(prec, pdevPvt->buffer.status,
|
||||
pdevPvt->buffer.severity);
|
||||
break;
|
||||
}
|
||||
|
||||
if (prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
prec->time = pdevPvt->buffer.time;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create the dset for devI64inSoftCallback */
|
||||
struct {
|
||||
dset common;
|
||||
DEVSUPFUN read_int64in;
|
||||
} devI64inSoftCallback = {
|
||||
{5, NULL, init, init_record, NULL},
|
||||
read_int64in
|
||||
};
|
||||
epicsExportAddress(dset, devI64inSoftCallback);
|
||||
57
src/std/dev/devI64outSoft.c
Normal file
57
src/std/dev/devI64outSoft.c
Normal file
@@ -0,0 +1,57 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Original Author: Janet Anderson
|
||||
* Date: 09-23-91
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alarm.h"
|
||||
#include "dbDefs.h"
|
||||
#include "dbAccess.h"
|
||||
#include "recGbl.h"
|
||||
#include "recSup.h"
|
||||
#include "devSup.h"
|
||||
#include "int64outRecord.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
/* Create the dset for devI64outSoft */
|
||||
static long init_record(int64outRecord *prec);
|
||||
static long write_int64out(int64outRecord *prec);
|
||||
struct {
|
||||
long number;
|
||||
DEVSUPFUN report;
|
||||
DEVSUPFUN init;
|
||||
DEVSUPFUN init_record;
|
||||
DEVSUPFUN get_ioint_info;
|
||||
DEVSUPFUN write_int64out;
|
||||
} devI64outSoft = {
|
||||
5,
|
||||
NULL,
|
||||
NULL,
|
||||
init_record,
|
||||
NULL,
|
||||
write_int64out
|
||||
};
|
||||
epicsExportAddress(dset, devI64outSoft);
|
||||
|
||||
static long init_record(int64outRecord *prec)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long write_int64out(int64outRecord *prec)
|
||||
{
|
||||
dbPutLink(&prec->out, DBR_INT64, &prec->val,1);
|
||||
return 0;
|
||||
}
|
||||
69
src/std/dev/devI64outSoftCallback.c
Normal file
69
src/std/dev/devI64outSoftCallback.c
Normal file
@@ -0,0 +1,69 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Original Author: Marty Kraimer
|
||||
* Date: 04NOV2003
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alarm.h"
|
||||
#include "dbDefs.h"
|
||||
#include "dbAccess.h"
|
||||
#include "recGbl.h"
|
||||
#include "recSup.h"
|
||||
#include "devSup.h"
|
||||
#include "int64outRecord.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
/* Create the dset for devI64outSoftCallback */
|
||||
static long write_int64out(int64outRecord *prec);
|
||||
struct {
|
||||
long number;
|
||||
DEVSUPFUN report;
|
||||
DEVSUPFUN init;
|
||||
DEVSUPFUN init_record;
|
||||
DEVSUPFUN get_ioint_info;
|
||||
DEVSUPFUN write_int64out;
|
||||
} devI64outSoftCallback = {
|
||||
5,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
write_int64out
|
||||
};
|
||||
epicsExportAddress(dset, devI64outSoftCallback);
|
||||
|
||||
static long write_int64out(int64outRecord *prec)
|
||||
{
|
||||
struct link *plink = &prec->out;
|
||||
long status;
|
||||
|
||||
if (prec->pact)
|
||||
return 0;
|
||||
|
||||
if (plink->type != CA_LINK) {
|
||||
status = dbPutLink(plink, DBR_INT64, &prec->val, 1);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = dbCaPutLinkCallback(plink, DBR_INT64, &prec->val, 1,
|
||||
dbCaCallbackProcess, plink);
|
||||
if (status) {
|
||||
recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM);
|
||||
return status;
|
||||
}
|
||||
|
||||
prec->pact = TRUE;
|
||||
return 0;
|
||||
}
|
||||
@@ -9,6 +9,8 @@ device(bo,CONSTANT,devBoSoft,"Soft Channel")
|
||||
device(calcout,CONSTANT,devCalcoutSoft,"Soft Channel")
|
||||
device(event,CONSTANT,devEventSoft,"Soft Channel")
|
||||
device(histogram,CONSTANT,devHistogramSoft,"Soft Channel")
|
||||
device(int64in,CONSTANT,devI64inSoft,"Soft Channel")
|
||||
device(int64out,CONSTANT,devI64outSoft,"Soft Channel")
|
||||
device(longin,CONSTANT,devLiSoft,"Soft Channel")
|
||||
device(longout,CONSTANT,devLoSoft,"Soft Channel")
|
||||
device(lsi,CONSTANT,devLsiSoft,"Soft Channel")
|
||||
@@ -37,6 +39,8 @@ device(ao,CONSTANT,devAoSoftCallback,"Async Soft Channel")
|
||||
device(bi,CONSTANT,devBiSoftCallback,"Async Soft Channel")
|
||||
device(bo,CONSTANT,devBoSoftCallback,"Async Soft Channel")
|
||||
device(calcout,CONSTANT,devCalcoutSoftCallback,"Async Soft Channel")
|
||||
device(int64in,CONSTANT,devI64inSoftCallback,"Async Soft Channel")
|
||||
device(int64out,CONSTANT,devI64outSoftCallback,"Async Soft Channel")
|
||||
device(longin,CONSTANT,devLiSoftCallback,"Async Soft Channel")
|
||||
device(longout,CONSTANT,devLoSoftCallback,"Async Soft Channel")
|
||||
device(lso,CONSTANT,devLsoSoftCallback,"Async Soft Channel")
|
||||
|
||||
@@ -25,6 +25,8 @@ stdRecords += dfanoutRecord
|
||||
stdRecords += eventRecord
|
||||
stdRecords += fanoutRecord
|
||||
stdRecords += histogramRecord
|
||||
stdRecords += int64inRecord
|
||||
stdRecords += int64outRecord
|
||||
stdRecords += longinRecord
|
||||
stdRecords += longoutRecord
|
||||
stdRecords += lsiRecord
|
||||
|
||||
@@ -97,7 +97,7 @@ recordtype(compress) {
|
||||
}
|
||||
field(BALG,DBF_MENU) {
|
||||
prompt("Buffering Algorithm")
|
||||
promptgroup(GUI_ALARMS)
|
||||
promptgroup("30 - Action")
|
||||
special(SPC_RESET)
|
||||
interest(1)
|
||||
menu(bufferingALG)
|
||||
|
||||
416
src/std/rec/int64inRecord.c
Normal file
416
src/std/rec/int64inRecord.c
Normal file
@@ -0,0 +1,416 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
|
||||
/* int64inRecord.c - Record Support Routines for int64in records */
|
||||
/*
|
||||
* Original Author: Janet Anderson
|
||||
* Date: 9/23/91
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "epicsPrint.h"
|
||||
#include "alarm.h"
|
||||
#include "dbAccess.h"
|
||||
#include "dbEvent.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "devSup.h"
|
||||
#include "errMdef.h"
|
||||
#include "recSup.h"
|
||||
#include "recGbl.h"
|
||||
#include "menuYesNo.h"
|
||||
|
||||
#define GEN_SIZE_OFFSET
|
||||
#include "int64inRecord.h"
|
||||
#undef GEN_SIZE_OFFSET
|
||||
#include "epicsExport.h"
|
||||
|
||||
/* Hysterisis for alarm filtering: 1-1/e */
|
||||
#define THRESHOLD 0.6321
|
||||
/* Create RSET - Record Support Entry Table*/
|
||||
#define report NULL
|
||||
#define initialize NULL
|
||||
static long init_record(dbCommon *, int);
|
||||
static long process(dbCommon *);
|
||||
#define special NULL
|
||||
#define get_value NULL
|
||||
#define cvt_dbaddr NULL
|
||||
#define get_array_info NULL
|
||||
#define put_array_info NULL
|
||||
static long get_units(DBADDR *, char *);
|
||||
#define get_precision NULL
|
||||
#define get_enum_str NULL
|
||||
#define get_enum_strs NULL
|
||||
#define put_enum_str NULL
|
||||
static long get_graphic_double(DBADDR *, struct dbr_grDouble *);
|
||||
static long get_control_double(DBADDR *, struct dbr_ctrlDouble *);
|
||||
static long get_alarm_double(DBADDR *, struct dbr_alDouble *);
|
||||
|
||||
rset int64inRSET={
|
||||
RSETNUMBER,
|
||||
report,
|
||||
initialize,
|
||||
init_record,
|
||||
process,
|
||||
special,
|
||||
get_value,
|
||||
cvt_dbaddr,
|
||||
get_array_info,
|
||||
put_array_info,
|
||||
get_units,
|
||||
get_precision,
|
||||
get_enum_str,
|
||||
get_enum_strs,
|
||||
put_enum_str,
|
||||
get_graphic_double,
|
||||
get_control_double,
|
||||
get_alarm_double
|
||||
};
|
||||
epicsExportAddress(rset,int64inRSET);
|
||||
|
||||
|
||||
struct int64indset { /* int64in input dset */
|
||||
long number;
|
||||
DEVSUPFUN dev_report;
|
||||
DEVSUPFUN init;
|
||||
DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/
|
||||
DEVSUPFUN get_ioint_info;
|
||||
DEVSUPFUN read_int64in; /*returns: (-1,0)=>(failure,success)*/
|
||||
};
|
||||
static void checkAlarms(int64inRecord *prec, epicsTimeStamp *timeLast);
|
||||
static void monitor(int64inRecord *prec);
|
||||
static long readValue(int64inRecord *prec);
|
||||
|
||||
|
||||
static long init_record(dbCommon *pcommon, int pass)
|
||||
{
|
||||
int64inRecord *prec = (int64inRecord*)pcommon;
|
||||
struct int64indset *pdset;
|
||||
long status;
|
||||
|
||||
if (pass==0) return(0);
|
||||
|
||||
/* int64in.siml must be a CONSTANT or a PV_LINK or a DB_LINK */
|
||||
if (prec->siml.type == CONSTANT) {
|
||||
recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
|
||||
}
|
||||
|
||||
/* int64in.siol must be a CONSTANT or a PV_LINK or a DB_LINK */
|
||||
if (prec->siol.type == CONSTANT) {
|
||||
recGblInitConstantLink(&prec->siol,DBF_LONG,&prec->sval);
|
||||
}
|
||||
|
||||
if(!(pdset = (struct int64indset *)(prec->dset))) {
|
||||
recGblRecordError(S_dev_noDSET,(void *)prec,"int64in: init_record");
|
||||
return(S_dev_noDSET);
|
||||
}
|
||||
/* must have read_int64in function defined */
|
||||
if( (pdset->number < 5) || (pdset->read_int64in == NULL) ) {
|
||||
recGblRecordError(S_dev_missingSup,(void *)prec,"int64in: init_record");
|
||||
return(S_dev_missingSup);
|
||||
}
|
||||
if( pdset->init_record ) {
|
||||
if((status=(*pdset->init_record)(prec))) return(status);
|
||||
}
|
||||
prec->mlst = prec->val;
|
||||
prec->alst = prec->val;
|
||||
prec->lalm = prec->val;
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long process(dbCommon *pcommon)
|
||||
{
|
||||
int64inRecord *prec = (int64inRecord*)pcommon;
|
||||
struct int64indset *pdset = (struct int64indset *)(prec->dset);
|
||||
long status;
|
||||
unsigned char pact=prec->pact;
|
||||
epicsTimeStamp timeLast;
|
||||
|
||||
if( (pdset==NULL) || (pdset->read_int64in==NULL) ) {
|
||||
prec->pact=TRUE;
|
||||
recGblRecordError(S_dev_missingSup,(void *)prec,"read_int64in");
|
||||
return(S_dev_missingSup);
|
||||
}
|
||||
timeLast = prec->time;
|
||||
|
||||
status=readValue(prec); /* read the new value */
|
||||
/* check if device support set pact */
|
||||
if ( !pact && prec->pact ) return(0);
|
||||
prec->pact = TRUE;
|
||||
|
||||
recGblGetTimeStamp(prec);
|
||||
if (status==0) prec->udf = FALSE;
|
||||
|
||||
/* check for alarms */
|
||||
checkAlarms(prec, &timeLast);
|
||||
/* check event list */
|
||||
monitor(prec);
|
||||
/* process the forward scan link record */
|
||||
recGblFwdLink(prec);
|
||||
|
||||
prec->pact=FALSE;
|
||||
return(status);
|
||||
}
|
||||
|
||||
#define indexof(field) int64inRecord##field
|
||||
|
||||
static long get_units(DBADDR *paddr,char *units)
|
||||
{
|
||||
int64inRecord *prec=(int64inRecord *)paddr->precord;
|
||||
|
||||
if(paddr->pfldDes->field_type == DBF_LONG) {
|
||||
strncpy(units,prec->egu,DB_UNITS_SIZE);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
|
||||
{
|
||||
int64inRecord *prec=(int64inRecord *)paddr->precord;
|
||||
|
||||
switch (dbGetFieldIndex(paddr)) {
|
||||
case indexof(VAL):
|
||||
case indexof(HIHI):
|
||||
case indexof(HIGH):
|
||||
case indexof(LOW):
|
||||
case indexof(LOLO):
|
||||
case indexof(LALM):
|
||||
case indexof(ALST):
|
||||
case indexof(MLST):
|
||||
case indexof(SVAL):
|
||||
pgd->upper_disp_limit = prec->hopr;
|
||||
pgd->lower_disp_limit = prec->lopr;
|
||||
break;
|
||||
default:
|
||||
recGblGetGraphicDouble(paddr,pgd);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
|
||||
{
|
||||
int64inRecord *prec=(int64inRecord *)paddr->precord;
|
||||
|
||||
switch (dbGetFieldIndex(paddr)) {
|
||||
case indexof(VAL):
|
||||
case indexof(HIHI):
|
||||
case indexof(HIGH):
|
||||
case indexof(LOW):
|
||||
case indexof(LOLO):
|
||||
case indexof(LALM):
|
||||
case indexof(ALST):
|
||||
case indexof(MLST):
|
||||
case indexof(SVAL):
|
||||
pcd->upper_ctrl_limit = prec->hopr;
|
||||
pcd->lower_ctrl_limit = prec->lopr;
|
||||
break;
|
||||
default:
|
||||
recGblGetControlDouble(paddr,pcd);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
|
||||
{
|
||||
int64inRecord *prec=(int64inRecord *)paddr->precord;
|
||||
|
||||
if(dbGetFieldIndex(paddr) == indexof(VAL)){
|
||||
pad->upper_alarm_limit = prec->hihi;
|
||||
pad->upper_warning_limit = prec->high;
|
||||
pad->lower_warning_limit = prec->low;
|
||||
pad->lower_alarm_limit = prec->lolo;
|
||||
} else recGblGetAlarmDouble(paddr,pad);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void checkAlarms(int64inRecord *prec, epicsTimeStamp *timeLast)
|
||||
{
|
||||
enum {
|
||||
range_Lolo = 1,
|
||||
range_Low,
|
||||
range_Normal,
|
||||
range_High,
|
||||
range_Hihi
|
||||
} alarmRange;
|
||||
static const epicsEnum16 range_stat[] = {
|
||||
SOFT_ALARM, LOLO_ALARM, LOW_ALARM,
|
||||
NO_ALARM, HIGH_ALARM, HIHI_ALARM
|
||||
};
|
||||
|
||||
double aftc, afvl;
|
||||
epicsInt64 val, hyst, lalm;
|
||||
epicsInt64 alev;
|
||||
epicsEnum16 asev;
|
||||
|
||||
if (prec->udf) {
|
||||
recGblSetSevr(prec, UDF_ALARM, prec->udfs);
|
||||
prec->afvl = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
val = prec->val;
|
||||
hyst = prec->hyst;
|
||||
lalm = prec->lalm;
|
||||
|
||||
/* check VAL against alarm limits */
|
||||
if ((asev = prec->hhsv) &&
|
||||
(val >= (alev = prec->hihi) ||
|
||||
((lalm == alev) && (val >= alev - hyst))))
|
||||
alarmRange = range_Hihi;
|
||||
else
|
||||
if ((asev = prec->llsv) &&
|
||||
(val <= (alev = prec->lolo) ||
|
||||
((lalm == alev) && (val <= alev + hyst))))
|
||||
alarmRange = range_Lolo;
|
||||
else
|
||||
if ((asev = prec->hsv) &&
|
||||
(val >= (alev = prec->high) ||
|
||||
((lalm == alev) && (val >= alev - hyst))))
|
||||
alarmRange = range_High;
|
||||
else
|
||||
if ((asev = prec->lsv) &&
|
||||
(val <= (alev = prec->low) ||
|
||||
((lalm == alev) && (val <= alev + hyst))))
|
||||
alarmRange = range_Low;
|
||||
else {
|
||||
alev = val;
|
||||
asev = NO_ALARM;
|
||||
alarmRange = range_Normal;
|
||||
}
|
||||
|
||||
aftc = prec->aftc;
|
||||
afvl = 0;
|
||||
|
||||
if (aftc > 0) {
|
||||
/* Apply level filtering */
|
||||
afvl = prec->afvl;
|
||||
if (afvl == 0) {
|
||||
afvl = (double)alarmRange;
|
||||
} else {
|
||||
double t = epicsTimeDiffInSeconds(&prec->time, timeLast);
|
||||
double alpha = aftc / (t + aftc);
|
||||
|
||||
/* The sign of afvl indicates whether the result should be
|
||||
* rounded up or down. This gives the filter hysteresis.
|
||||
* If afvl > 0 the floor() function rounds to a lower alarm
|
||||
* level, otherwise to a higher.
|
||||
*/
|
||||
afvl = alpha * afvl +
|
||||
((afvl > 0) ? (1 - alpha) : (alpha - 1)) * alarmRange;
|
||||
if (afvl - floor(afvl) > THRESHOLD)
|
||||
afvl = -afvl; /* reverse rounding */
|
||||
|
||||
alarmRange = abs((int)floor(afvl));
|
||||
switch (alarmRange) {
|
||||
case range_Hihi:
|
||||
asev = prec->hhsv;
|
||||
alev = prec->hihi;
|
||||
break;
|
||||
case range_High:
|
||||
asev = prec->hsv;
|
||||
alev = prec->high;
|
||||
break;
|
||||
case range_Normal:
|
||||
asev = NO_ALARM;
|
||||
break;
|
||||
case range_Low:
|
||||
asev = prec->lsv;
|
||||
alev = prec->low;
|
||||
break;
|
||||
case range_Lolo:
|
||||
asev = prec->llsv;
|
||||
alev = prec->lolo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
prec->afvl = afvl;
|
||||
|
||||
if (asev) {
|
||||
/* Report alarm condition, store LALM for future HYST calculations */
|
||||
if (recGblSetSevr(prec, range_stat[alarmRange], asev))
|
||||
prec->lalm = alev;
|
||||
} else {
|
||||
/* No alarm condition, reset LALM */
|
||||
prec->lalm = val;
|
||||
}
|
||||
}
|
||||
|
||||
/* DELTA calculates the absolute difference between its arguments
|
||||
* expressed as an unsigned 32-bit integer */
|
||||
#define DELTA(last, val) \
|
||||
((epicsUInt32) ((last) > (val) ? (last) - (val) : (val) - (last)))
|
||||
|
||||
static void monitor(int64inRecord *prec)
|
||||
{
|
||||
unsigned short monitor_mask = recGblResetAlarms(prec);
|
||||
|
||||
if (prec->mdel < 0 ||
|
||||
DELTA(prec->mlst, prec->val) > (epicsUInt32) prec->mdel) {
|
||||
/* post events for value change */
|
||||
monitor_mask |= DBE_VALUE;
|
||||
/* update last value monitored */
|
||||
prec->mlst = prec->val;
|
||||
}
|
||||
|
||||
if (prec->adel < 0 ||
|
||||
DELTA(prec->alst, prec->val) > (epicsUInt32) prec->adel) {
|
||||
/* post events for archive value change */
|
||||
monitor_mask |= DBE_LOG;
|
||||
/* update last archive value monitored */
|
||||
prec->alst = prec->val;
|
||||
}
|
||||
|
||||
/* send out monitors connected to the value field */
|
||||
if (monitor_mask)
|
||||
db_post_events(prec, &prec->val, monitor_mask);
|
||||
}
|
||||
|
||||
static long readValue(int64inRecord *prec)
|
||||
{
|
||||
long status;
|
||||
struct int64indset *pdset = (struct int64indset *) (prec->dset);
|
||||
|
||||
if (prec->pact == TRUE){
|
||||
status=(*pdset->read_int64in)(prec);
|
||||
return(status);
|
||||
}
|
||||
|
||||
status=dbGetLink(&(prec->siml),DBR_USHORT, &(prec->simm),0,0);
|
||||
if (status)
|
||||
return(status);
|
||||
|
||||
if (prec->simm == menuYesNoNO){
|
||||
status=(*pdset->read_int64in)(prec);
|
||||
return(status);
|
||||
}
|
||||
if (prec->simm == menuYesNoYES){
|
||||
status=dbGetLink(&(prec->siol),DBR_LONG,
|
||||
&(prec->sval),0,0);
|
||||
|
||||
if (status==0) {
|
||||
prec->val=prec->sval;
|
||||
prec->udf=FALSE;
|
||||
}
|
||||
} else {
|
||||
status=-1;
|
||||
recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM);
|
||||
return(status);
|
||||
}
|
||||
recGblSetSevr(prec,SIMM_ALARM,prec->sims);
|
||||
|
||||
return(status);
|
||||
}
|
||||
528
src/std/rec/int64inRecord.dbd.pod
Normal file
528
src/std/rec/int64inRecord.dbd.pod
Normal file
@@ -0,0 +1,528 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# 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.
|
||||
#*************************************************************************
|
||||
|
||||
=title 64bit Integer Input Record (int64in)
|
||||
|
||||
This record type is normally used to obtain an integer value of up to 64 bits
|
||||
from a hardware input.
|
||||
The record supports alarm limits, alarm filtering, graphics and control
|
||||
limits.
|
||||
|
||||
=head2 Parameter Fields
|
||||
|
||||
The record-specific fields are described below.
|
||||
|
||||
=recordtype int64in
|
||||
|
||||
=cut
|
||||
|
||||
recordtype(int64in) {
|
||||
|
||||
=head3 Input Specification
|
||||
|
||||
These fields control where the record will read data from when it is processed:
|
||||
|
||||
=fields DTYP, INP
|
||||
|
||||
The DTYP field selects which device support layer should be responsible for
|
||||
providing input data to the record.
|
||||
The int64in device support layers provided by EPICS Base are documented in the
|
||||
L<Device Support> section.
|
||||
External support modules may provide additional device support for this record
|
||||
type.
|
||||
If not set explicitly, the DTYP value defaults to the first device support that
|
||||
is loaded for the record type, which will usually be the C<Soft Channel> support
|
||||
that comes with Base.
|
||||
|
||||
The INP link field contains a database or channel access link or provides
|
||||
hardware address information that the device support uses to determine where the
|
||||
input data should come from.
|
||||
The format for the INP field value depends on the device support layer that is
|
||||
selected by the DTYP field.
|
||||
See L<Address Specification|...> for a description of the various hardware
|
||||
address formats supported.
|
||||
|
||||
=head3 Operator Display Parameters
|
||||
|
||||
These parameters are used to present meaningful data to the operator.
|
||||
They do not affect the functioning of the record.
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
DESC is a string that is usually used to briefly describe the record.
|
||||
|
||||
=item *
|
||||
EGU is a string of up to 16 characters naming the engineering units
|
||||
that the VAL field represents.
|
||||
|
||||
=item *
|
||||
The HOPR and LOPR fields set the upper and lower display limits for the VAL,
|
||||
HIHI, HIGH, LOW, and LOLO fields.
|
||||
|
||||
=back
|
||||
|
||||
=fields DESC, EGU, HOPR, LOPR
|
||||
|
||||
=head3 Alarm Limits
|
||||
|
||||
The user configures limit alarms by putting numerical values into the HIHI,
|
||||
HIGH, LOW and LOLO fields, and by setting the associated alarm severity in the
|
||||
corresponding HHSV, HSV, LSV and LLSV menu fields.
|
||||
|
||||
The HYST field controls hysteresis to prevent alarm chattering from an input
|
||||
signal that is close to one of the limits and suffers from significant readout
|
||||
noise.
|
||||
|
||||
The AFTC field sets the time constant on a low-pass filter that delays the
|
||||
reporting of limit alarms until the signal has been within the alarm range for
|
||||
that number of seconds (the default AFTC value of zero retains the previous
|
||||
behavior).
|
||||
|
||||
The LALM field is used by the record at run-time to implement the alarm limit
|
||||
functionality.
|
||||
|
||||
=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST, AFTC, LALM
|
||||
|
||||
=head3 Monitor Parameters
|
||||
|
||||
These parameters are used to determine when to send monitors placed on the VAL
|
||||
field.
|
||||
The monitors are sent when the current value exceeds the last transmitted value
|
||||
by the appropriate deadband.
|
||||
If these fields are set to zero, a monitor will be triggered every time the
|
||||
value changes; if set to -1, a monitor will be sent every time the record is
|
||||
processed.
|
||||
|
||||
The ADEL field sets the deadband for archive monitors (C<DBE_LOG> events), while
|
||||
the MDEL field controls value monitors (C<DBE_VALUE> events).
|
||||
|
||||
The remaining fields are used by the record at run-time to implement the record
|
||||
monitoring deadband functionality.
|
||||
|
||||
=fields ADEL, MDEL, ALST, MLST
|
||||
|
||||
=cut
|
||||
|
||||
include "dbCommon.dbd"
|
||||
field(VAL,DBF_INT64) {
|
||||
prompt("Current value")
|
||||
promptgroup("40 - Input")
|
||||
asl(ASL0)
|
||||
pp(TRUE)
|
||||
}
|
||||
field(INP,DBF_INLINK) {
|
||||
prompt("Input Specification")
|
||||
promptgroup("40 - Input")
|
||||
interest(1)
|
||||
}
|
||||
field(EGU,DBF_STRING) {
|
||||
prompt("Units name")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
size(16)
|
||||
prop(YES)
|
||||
}
|
||||
field(HOPR,DBF_INT64) {
|
||||
prompt("High Operating Range")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOPR,DBF_INT64) {
|
||||
prompt("Low Operating Range")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HIHI,DBF_INT64) {
|
||||
prompt("Hihi Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOLO,DBF_INT64) {
|
||||
prompt("Lolo Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HIGH,DBF_INT64) {
|
||||
prompt("High Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOW,DBF_INT64) {
|
||||
prompt("Low Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HHSV,DBF_MENU) {
|
||||
prompt("Hihi Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LLSV,DBF_MENU) {
|
||||
prompt("Lolo Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(HSV,DBF_MENU) {
|
||||
prompt("High Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LSV,DBF_MENU) {
|
||||
prompt("Low Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(HYST,DBF_INT64) {
|
||||
prompt("Alarm Deadband")
|
||||
promptgroup("70 - Alarm")
|
||||
interest(1)
|
||||
}
|
||||
field(AFTC, DBF_DOUBLE) {
|
||||
prompt("Alarm Filter Time Constant")
|
||||
promptgroup("70 - Alarm")
|
||||
interest(1)
|
||||
}
|
||||
field(AFVL, DBF_DOUBLE) {
|
||||
prompt("Alarm Filter Value")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(ADEL,DBF_INT64) {
|
||||
prompt("Archive Deadband")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
}
|
||||
field(MDEL,DBF_INT64) {
|
||||
prompt("Monitor Deadband")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
}
|
||||
field(LALM,DBF_INT64) {
|
||||
prompt("Last Value Alarmed")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(ALST,DBF_INT64) {
|
||||
prompt("Last Value Archived")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MLST,DBF_INT64) {
|
||||
prompt("Last Val Monitored")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
|
||||
=head3 Simulation Mode
|
||||
|
||||
The record provides several fields to support simulation of absent hardware.
|
||||
If the SIML field is set it is used to read a value into the SIMM field, which
|
||||
controls whether simulation is used or not:
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
SIMM must be zero (C<NO>) for the record to request a value from the device
|
||||
support.
|
||||
|
||||
=item *
|
||||
If SIMM is C<YES> and the SIOL link field is set, a simulated value in
|
||||
engineering units is read using the link into the SVAL field, from where it will
|
||||
subsequently be copied into the VAL field.
|
||||
|
||||
=back
|
||||
|
||||
The SIMS field can be set to give the record an alarm severity while it is in
|
||||
simulation mode.
|
||||
|
||||
=fields SIML, SIMM, SIOL, SVAL, SIMS
|
||||
|
||||
=cut
|
||||
|
||||
field(SIOL,DBF_INLINK) {
|
||||
prompt("Sim Input Specifctn")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SVAL,DBF_INT64) {
|
||||
prompt("Simulation Value")
|
||||
}
|
||||
field(SIML,DBF_INLINK) {
|
||||
prompt("Sim Mode Location")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SIMM,DBF_MENU) {
|
||||
prompt("Simulation Mode")
|
||||
interest(1)
|
||||
menu(menuYesNo)
|
||||
}
|
||||
field(SIMS,DBF_MENU) {
|
||||
prompt("Sim mode Alarm Svrty")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(2)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
}
|
||||
|
||||
=head2 Record Support
|
||||
|
||||
=head3 Record Support Routines
|
||||
|
||||
The following are the record support routines that would be of interest
|
||||
to an application developer.
|
||||
Other routines are the C<get_units>, C<get_graphic_double>,
|
||||
C<get_alarm_double> and C<get_control_double> routines, which are used to
|
||||
collect properties from the record for the complex DBR data structures.
|
||||
|
||||
=head4 init_record
|
||||
|
||||
This routine first initializes the simulation mode mechanism by setting SIMM
|
||||
if SIML is a constant, and setting SVAL if SIOL is a constant.
|
||||
|
||||
It then checks if the device support and the device support's
|
||||
C<read_int64in> routine are defined.
|
||||
If either one does not exist, an error message is issued
|
||||
and processing is terminated.
|
||||
|
||||
If device support includes C<init_record>, it is called.
|
||||
|
||||
Finally, the deadband mechanisms for monitors and level alarms are
|
||||
initialized.
|
||||
|
||||
=head4 process
|
||||
|
||||
See next section.
|
||||
|
||||
=head3 Record Processing
|
||||
|
||||
Routine C<process> implements the following algorithm:
|
||||
|
||||
=over
|
||||
|
||||
=item 1.
|
||||
|
||||
Check to see that the appropriate device support module and its
|
||||
C<read_int64in> routine are defined.
|
||||
If either one does not exist, an error message is issued and processing is
|
||||
terminated with the PACT field set to TRUE, effectively blocking the record
|
||||
to avoid error storms.
|
||||
|
||||
=item 2.
|
||||
|
||||
Determine the value:
|
||||
|
||||
If PACT is TRUE, call the device support C<read_int64in> routine and return.
|
||||
|
||||
If PACT is FALSE, read the value, honoring simulation mode:
|
||||
|
||||
=over
|
||||
|
||||
=item * Get SIMM by reading the SIML link.
|
||||
|
||||
=item * If SIMM is C<NO>,
|
||||
call the device support C<read_int64in> routine and return.
|
||||
|
||||
=item * If SIMM is C<YES>,
|
||||
read the simulated value into SVAL using the SIOL link,
|
||||
then copy the value into VAL and set UDF to 0 on success.
|
||||
|
||||
=item * Raise an alarm for other values of SIMM.
|
||||
|
||||
=item * Set the record to the severity configured in SIMS.
|
||||
|
||||
=back
|
||||
|
||||
=item 3.
|
||||
|
||||
If PACT has been changed to TRUE, the device support signals asynchronous
|
||||
processing: its C<read_int64in> output routine has started, but not
|
||||
completed reading the new value.
|
||||
In this case, the processing routine merely returns, leaving PACT TRUE.
|
||||
|
||||
=item 4.
|
||||
|
||||
Set PACT to TRUE. Get the processing time stamp. Set UDF to 0 if reading
|
||||
the value was successful.
|
||||
|
||||
=item 5.
|
||||
|
||||
Check UDF and level alarms: This routine checks to see if the record is
|
||||
undefined (UDF is TRUE) or if the new VAL causes the alarm status
|
||||
and severity to change. In the latter case, NSEV, NSTA and LALM are set.
|
||||
It also honors the alarm hysteresis factor (HYST): the value must change
|
||||
by at least HYST between level alarm status and severity changes.
|
||||
If AFTC is set, alarm level filtering is applied.
|
||||
|
||||
=item 6.
|
||||
|
||||
Check to see if monitors should be invoked:
|
||||
|
||||
=over
|
||||
|
||||
=item * Alarm monitors are posted if the alarm status or severity have
|
||||
changed.
|
||||
|
||||
=item * Archive and value change monitors are posted if ADEL and MDEL
|
||||
conditions (see L<Monitor Parameters>) are met.
|
||||
|
||||
=back
|
||||
|
||||
=item 7.
|
||||
|
||||
Scan (process) forward link if necessary, set PACT to FALSE, and return.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Device Support
|
||||
|
||||
=head3 Device Support Interface
|
||||
|
||||
The record requires device support to provide an entry table (dset) which
|
||||
defines the following members:
|
||||
|
||||
typedef struct {
|
||||
long number;
|
||||
long (*report)(int level);
|
||||
long (*init)(int after);
|
||||
long (*init_record)(int64inRecord *prec);
|
||||
long (*get_ioint_info)(int cmd, int64inRecord *prec, IOSCANPVT *piosl);
|
||||
long (*read_int64in)(int64inRecord *prec);
|
||||
} int64indset;
|
||||
|
||||
The module must set C<number> to at least 5, and provide a pointer to its
|
||||
C<read_int64in()> routine; the other function pointers may be C<NULL> if their
|
||||
associated functionality is not required for this support layer.
|
||||
Most device supports also provide an C<init_record()> routine to configure the
|
||||
record instance and connect it to the hardware or driver support layer.
|
||||
|
||||
The individual routines are described below.
|
||||
|
||||
=head3 Device Support Routines
|
||||
|
||||
=head4 long report(int level)
|
||||
|
||||
This optional routine is called by the IOC command C<dbior> and is passed the
|
||||
report level that was requested by the user.
|
||||
It should print a report on the state of the device support to stdout.
|
||||
The C<level> parameter may be used to output increasingly more detailed
|
||||
information at higher levels, or to select different types of information with
|
||||
different levels.
|
||||
Level zero should print no more than a small summary.
|
||||
|
||||
=head4 long init(int after)
|
||||
|
||||
This optional routine is called twice at IOC initialization time.
|
||||
The first call happens before any of the C<init_record()> calls are made, with
|
||||
the integer parameter C<after> set to 0.
|
||||
The second call happens after all of the C<init_record()> calls have been made,
|
||||
with C<after> set to 1.
|
||||
|
||||
=head4 long init_record(int64inRecord *prec)
|
||||
|
||||
This optional routine is called by the record initialization code for each
|
||||
int64in record instance that has its DTYP field set to use this device support.
|
||||
It is normally used to check that the INP address is the expected type and that
|
||||
it points to a valid device; to allocate any record-specific buffer space and
|
||||
other memory; and to connect any communication channels needed for the
|
||||
C<read_int64in()> routine to work properly.
|
||||
|
||||
=head4 long get_ioint_info(int cmd, int64inRecord *prec, IOSCANPVT *piosl)
|
||||
|
||||
This optional routine is called whenever the record's SCAN field is being
|
||||
changed to or from the value C<I/O Intr> to find out which I/O Interrupt Scan
|
||||
list the record should be added to or deleted from.
|
||||
If this routine is not provided, it will not be possible to set the SCAN field
|
||||
to the value C<I/O Intr> at all.
|
||||
|
||||
The C<cmd> parameter is zero when the record is being added to the scan list,
|
||||
and one when it is being removed from the list.
|
||||
The routine must determine which interrupt source the record should be connected
|
||||
to, which it indicates by the scan list that it points the location at C<*piosl>
|
||||
to before returning.
|
||||
It can prevent the SCAN field from being changed at all by returning a non-zero
|
||||
value to its caller.
|
||||
|
||||
In most cases the device support will create the I/O Interrupt Scan lists that
|
||||
it returns for itself, by calling C<void scanIoInit(IOSCANPVT *piosl)> once for
|
||||
each separate interrupt source.
|
||||
That routine allocates memory and inializes the list, then passes back a pointer
|
||||
to the new list in the location at C<*piosl>.
|
||||
|
||||
When the device support receives notification that the interrupt has occurred,
|
||||
it announces that to the IOC by calling C<void scanIoRequest(IOSCANPVT iosl)>
|
||||
which will arrange for the appropriate records to be processed in a suitable
|
||||
thread.
|
||||
The C<scanIoRequest()> routine is safe to call from an interrupt service routine
|
||||
on embedded architectures (vxWorks and RTEMS).
|
||||
|
||||
=head4 long read_int64in(int64inRecord *prec)
|
||||
|
||||
This essential routine is called when the record wants a new value from the
|
||||
addressed device.
|
||||
It is responsible for performing (or at least initiating) a read operation, and
|
||||
(eventually) returning its value to the record.
|
||||
|
||||
If the device may take more than a few microseconds to return the new value,
|
||||
this routine must never block (busy-wait), but use the asynchronous
|
||||
processing mechanism.
|
||||
In that case it signals the asynchronous operation by setting the record's
|
||||
PACT field to TRUE before it returns, having arranged for the record's
|
||||
C<process()> routine to be called later once the read operation is finished.
|
||||
When that happens, the C<read_int64in()> routine will be called again with
|
||||
PACT still set to TRUE; it should then set it to FALSE to indicate the read
|
||||
has completed, and return.
|
||||
|
||||
A return value of zero indicates success, any other value indicates that an
|
||||
error occurred.
|
||||
|
||||
=head3 Extended Device Support
|
||||
|
||||
...
|
||||
|
||||
=cut
|
||||
|
||||
=head2 Device Support For Soft Records
|
||||
|
||||
Two soft device support modules, Soft Channel and Soft Callback Channel, are
|
||||
provided for input records not related to actual hardware devices. The
|
||||
INP link type must be either a CONSTANT, DB_LINK, or CA_LINK.
|
||||
|
||||
=head3 Soft Channel
|
||||
|
||||
This module reads the value using the record's INP link.
|
||||
|
||||
C<read_int64in> calls C<dbGetLink> to read the value.
|
||||
|
||||
=head3 Soft Callback Channel
|
||||
|
||||
This module is like the previous except that it reads the value
|
||||
using asynchronous processing that will not complete until an asynchronous
|
||||
processing of the INP target record has completed.
|
||||
|
||||
=cut
|
||||
394
src/std/rec/int64outRecord.c
Normal file
394
src/std/rec/int64outRecord.c
Normal file
@@ -0,0 +1,394 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Original Author: Janet Anderson
|
||||
* Date: 9/23/91
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "epicsPrint.h"
|
||||
#include "alarm.h"
|
||||
#include "dbAccess.h"
|
||||
#include "dbEvent.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "devSup.h"
|
||||
#include "errMdef.h"
|
||||
#include "recSup.h"
|
||||
#include "recGbl.h"
|
||||
#include "menuYesNo.h"
|
||||
#include "menuIvoa.h"
|
||||
#include "menuOmsl.h"
|
||||
|
||||
#define GEN_SIZE_OFFSET
|
||||
#include "int64outRecord.h"
|
||||
#undef GEN_SIZE_OFFSET
|
||||
#include "epicsExport.h"
|
||||
|
||||
/* Create RSET - Record Support Entry Table*/
|
||||
#define report NULL
|
||||
#define initialize NULL
|
||||
static long init_record(dbCommon *, int);
|
||||
static long process(dbCommon *);
|
||||
#define special NULL
|
||||
#define get_value NULL
|
||||
#define cvt_dbaddr NULL
|
||||
#define get_array_info NULL
|
||||
#define put_array_info NULL
|
||||
static long get_units(DBADDR *, char *);
|
||||
#define get_precision NULL
|
||||
#define get_enum_str NULL
|
||||
#define get_enum_strs NULL
|
||||
#define put_enum_str NULL
|
||||
static long get_graphic_double(DBADDR *, struct dbr_grDouble *);
|
||||
static long get_control_double(DBADDR *, struct dbr_ctrlDouble *);
|
||||
static long get_alarm_double(DBADDR *, struct dbr_alDouble *);
|
||||
|
||||
rset int64outRSET={
|
||||
RSETNUMBER,
|
||||
report,
|
||||
initialize,
|
||||
init_record,
|
||||
process,
|
||||
special,
|
||||
get_value,
|
||||
cvt_dbaddr,
|
||||
get_array_info,
|
||||
put_array_info,
|
||||
get_units,
|
||||
get_precision,
|
||||
get_enum_str,
|
||||
get_enum_strs,
|
||||
put_enum_str,
|
||||
get_graphic_double,
|
||||
get_control_double,
|
||||
get_alarm_double
|
||||
};
|
||||
epicsExportAddress(rset,int64outRSET);
|
||||
|
||||
|
||||
struct int64outdset { /* int64out input dset */
|
||||
long number;
|
||||
DEVSUPFUN dev_report;
|
||||
DEVSUPFUN init;
|
||||
DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/
|
||||
DEVSUPFUN get_ioint_info;
|
||||
DEVSUPFUN write_int64out;/*(-1,0)=>(failure,success*/
|
||||
};
|
||||
static void checkAlarms(int64outRecord *prec);
|
||||
static void monitor(int64outRecord *prec);
|
||||
static long writeValue(int64outRecord *prec);
|
||||
static void convert(int64outRecord *prec, epicsInt64 value);
|
||||
|
||||
|
||||
static long init_record(dbCommon *pcommon, int pass)
|
||||
{
|
||||
int64outRecord *prec = (int64outRecord*)pcommon;
|
||||
struct int64outdset *pdset;
|
||||
long status=0;
|
||||
|
||||
if (pass==0) return(0);
|
||||
if (prec->siml.type == CONSTANT) {
|
||||
recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
|
||||
}
|
||||
if(!(pdset = (struct int64outdset *)(prec->dset))) {
|
||||
recGblRecordError(S_dev_noDSET,(void *)prec,"int64out: init_record");
|
||||
return(S_dev_noDSET);
|
||||
}
|
||||
/* must have write_int64out functions defined */
|
||||
if( (pdset->number < 5) || (pdset->write_int64out == NULL) ) {
|
||||
recGblRecordError(S_dev_missingSup,(void *)prec,"int64out: init_record");
|
||||
return(S_dev_missingSup);
|
||||
}
|
||||
if (prec->dol.type == CONSTANT) {
|
||||
if(recGblInitConstantLink(&prec->dol,DBF_INT64,&prec->val))
|
||||
prec->udf=FALSE;
|
||||
}
|
||||
if( pdset->init_record ) {
|
||||
if((status=(*pdset->init_record)(prec))) return(status);
|
||||
}
|
||||
prec->mlst = prec->val;
|
||||
prec->alst = prec->val;
|
||||
prec->lalm = prec->val;
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long process(dbCommon *pcommon)
|
||||
{
|
||||
int64outRecord *prec = (int64outRecord*)pcommon;
|
||||
struct int64outdset *pdset = (struct int64outdset *)(prec->dset);
|
||||
long status=0;
|
||||
epicsInt64 value;
|
||||
unsigned char pact=prec->pact;
|
||||
|
||||
if( (pdset==NULL) || (pdset->write_int64out==NULL) ) {
|
||||
prec->pact=TRUE;
|
||||
recGblRecordError(S_dev_missingSup,(void *)prec,"write_int64out");
|
||||
return(S_dev_missingSup);
|
||||
}
|
||||
if (!prec->pact) {
|
||||
if((prec->dol.type != CONSTANT)
|
||||
&& (prec->omsl == menuOmslclosed_loop)) {
|
||||
status = dbGetLink(&(prec->dol),DBR_INT64,
|
||||
&value,0,0);
|
||||
if (prec->dol.type!=CONSTANT && RTN_SUCCESS(status))
|
||||
prec->udf=FALSE;
|
||||
}
|
||||
else {
|
||||
value = prec->val;
|
||||
}
|
||||
if (!status) convert(prec,value);
|
||||
}
|
||||
|
||||
/* check for alarms */
|
||||
checkAlarms(prec);
|
||||
|
||||
if (prec->nsev < INVALID_ALARM )
|
||||
status=writeValue(prec); /* write the new value */
|
||||
else {
|
||||
switch (prec->ivoa) {
|
||||
case (menuIvoaContinue_normally) :
|
||||
status=writeValue(prec); /* write the new value */
|
||||
break;
|
||||
case (menuIvoaDon_t_drive_outputs) :
|
||||
break;
|
||||
case (menuIvoaSet_output_to_IVOV) :
|
||||
if(prec->pact == FALSE){
|
||||
prec->val=prec->ivov;
|
||||
}
|
||||
status=writeValue(prec); /* write the new value */
|
||||
break;
|
||||
default :
|
||||
status=-1;
|
||||
recGblRecordError(S_db_badField,(void *)prec,
|
||||
"int64out:process Illegal IVOA field");
|
||||
}
|
||||
}
|
||||
|
||||
/* check if device support set pact */
|
||||
if ( !pact && prec->pact ) return(0);
|
||||
prec->pact = TRUE;
|
||||
|
||||
recGblGetTimeStamp(prec);
|
||||
|
||||
/* check event list */
|
||||
monitor(prec);
|
||||
|
||||
/* process the forward scan link record */
|
||||
recGblFwdLink(prec);
|
||||
|
||||
prec->pact=FALSE;
|
||||
return(status);
|
||||
}
|
||||
|
||||
#define indexof(field) int64outRecord##field
|
||||
|
||||
static long get_units(DBADDR *paddr,char *units)
|
||||
{
|
||||
int64outRecord *prec=(int64outRecord *)paddr->precord;
|
||||
|
||||
if(paddr->pfldDes->field_type == DBF_INT64) {
|
||||
strncpy(units,prec->egu,DB_UNITS_SIZE);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd)
|
||||
{
|
||||
int64outRecord *prec=(int64outRecord *)paddr->precord;
|
||||
|
||||
switch (dbGetFieldIndex(paddr)) {
|
||||
case indexof(VAL):
|
||||
case indexof(HIHI):
|
||||
case indexof(HIGH):
|
||||
case indexof(LOW):
|
||||
case indexof(LOLO):
|
||||
case indexof(LALM):
|
||||
case indexof(ALST):
|
||||
case indexof(MLST):
|
||||
pgd->upper_disp_limit = prec->hopr;
|
||||
pgd->lower_disp_limit = prec->lopr;
|
||||
break;
|
||||
default:
|
||||
recGblGetGraphicDouble(paddr,pgd);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long get_control_double(DBADDR *paddr,struct dbr_ctrlDouble *pcd)
|
||||
{
|
||||
int64outRecord *prec=(int64outRecord *)paddr->precord;
|
||||
|
||||
switch (dbGetFieldIndex(paddr)) {
|
||||
case indexof(VAL):
|
||||
case indexof(HIHI):
|
||||
case indexof(HIGH):
|
||||
case indexof(LOW):
|
||||
case indexof(LOLO):
|
||||
case indexof(LALM):
|
||||
case indexof(ALST):
|
||||
case indexof(MLST):
|
||||
/* do not change pre drvh/drvl behavior */
|
||||
if(prec->drvh > prec->drvl) {
|
||||
pcd->upper_ctrl_limit = prec->drvh;
|
||||
pcd->lower_ctrl_limit = prec->drvl;
|
||||
} else {
|
||||
pcd->upper_ctrl_limit = prec->hopr;
|
||||
pcd->lower_ctrl_limit = prec->lopr;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
recGblGetControlDouble(paddr,pcd);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long get_alarm_double(DBADDR *paddr,struct dbr_alDouble *pad)
|
||||
{
|
||||
int64outRecord *prec=(int64outRecord *)paddr->precord;
|
||||
|
||||
if(dbGetFieldIndex(paddr) == indexof(VAL)) {
|
||||
pad->upper_alarm_limit = prec->hihi;
|
||||
pad->upper_warning_limit = prec->high;
|
||||
pad->lower_warning_limit = prec->low;
|
||||
pad->lower_alarm_limit = prec->lolo;
|
||||
} else recGblGetAlarmDouble(paddr,pad);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void checkAlarms(int64outRecord *prec)
|
||||
{
|
||||
epicsInt64 val, hyst, lalm;
|
||||
epicsInt64 alev;
|
||||
epicsEnum16 asev;
|
||||
|
||||
if (prec->udf) {
|
||||
recGblSetSevr(prec, UDF_ALARM, prec->udfs);
|
||||
return;
|
||||
}
|
||||
|
||||
val = prec->val;
|
||||
hyst = prec->hyst;
|
||||
lalm = prec->lalm;
|
||||
|
||||
/* alarm condition hihi */
|
||||
asev = prec->hhsv;
|
||||
alev = prec->hihi;
|
||||
if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {
|
||||
if (recGblSetSevr(prec, HIHI_ALARM, asev))
|
||||
prec->lalm = alev;
|
||||
return;
|
||||
}
|
||||
|
||||
/* alarm condition lolo */
|
||||
asev = prec->llsv;
|
||||
alev = prec->lolo;
|
||||
if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {
|
||||
if (recGblSetSevr(prec, LOLO_ALARM, asev))
|
||||
prec->lalm = alev;
|
||||
return;
|
||||
}
|
||||
|
||||
/* alarm condition high */
|
||||
asev = prec->hsv;
|
||||
alev = prec->high;
|
||||
if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {
|
||||
if (recGblSetSevr(prec, HIGH_ALARM, asev))
|
||||
prec->lalm = alev;
|
||||
return;
|
||||
}
|
||||
|
||||
/* alarm condition low */
|
||||
asev = prec->lsv;
|
||||
alev = prec->low;
|
||||
if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {
|
||||
if (recGblSetSevr(prec, LOW_ALARM, asev))
|
||||
prec->lalm = alev;
|
||||
return;
|
||||
}
|
||||
|
||||
/* we get here only if val is out of alarm by at least hyst */
|
||||
prec->lalm = val;
|
||||
return;
|
||||
}
|
||||
|
||||
/* DELTA calculates the absolute difference between its arguments
|
||||
* expressed as an unsigned 64-bit integer */
|
||||
#define DELTA(last, val) \
|
||||
((epicsUInt64) ((last) > (val) ? (last) - (val) : (val) - (last)))
|
||||
|
||||
static void monitor(int64outRecord *prec)
|
||||
{
|
||||
unsigned short monitor_mask = recGblResetAlarms(prec);
|
||||
|
||||
if (prec->mdel < 0 ||
|
||||
DELTA(prec->mlst, prec->val) > (epicsUInt64) prec->mdel) {
|
||||
/* post events for value change */
|
||||
monitor_mask |= DBE_VALUE;
|
||||
/* update last value monitored */
|
||||
prec->mlst = prec->val;
|
||||
}
|
||||
|
||||
if (prec->adel < 0 ||
|
||||
DELTA(prec->alst, prec->val) > (epicsUInt64) prec->adel) {
|
||||
/* post events for archive value change */
|
||||
monitor_mask |= DBE_LOG;
|
||||
/* update last archive value monitored */
|
||||
prec->alst = prec->val;
|
||||
}
|
||||
|
||||
/* send out monitors connected to the value field */
|
||||
if (monitor_mask)
|
||||
db_post_events(prec, &prec->val, monitor_mask);
|
||||
}
|
||||
|
||||
static long writeValue(int64outRecord *prec)
|
||||
{
|
||||
long status;
|
||||
struct int64outdset *pdset = (struct int64outdset *) (prec->dset);
|
||||
|
||||
if (prec->pact == TRUE){
|
||||
status=(*pdset->write_int64out)(prec);
|
||||
return(status);
|
||||
}
|
||||
|
||||
status=dbGetLink(&(prec->siml),DBR_USHORT,&(prec->simm),0,0);
|
||||
if (!RTN_SUCCESS(status))
|
||||
return(status);
|
||||
|
||||
if (prec->simm == menuYesNoNO){
|
||||
status=(*pdset->write_int64out)(prec);
|
||||
return(status);
|
||||
}
|
||||
if (prec->simm == menuYesNoYES){
|
||||
status=dbPutLink(&prec->siol,DBR_INT64,&prec->val,1);
|
||||
} else {
|
||||
status=-1;
|
||||
recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM);
|
||||
return(status);
|
||||
}
|
||||
recGblSetSevr(prec,SIMM_ALARM,prec->sims);
|
||||
|
||||
return(status);
|
||||
}
|
||||
|
||||
static void convert(int64outRecord *prec, epicsInt64 value)
|
||||
{
|
||||
/* check drive limits */
|
||||
if(prec->drvh > prec->drvl) {
|
||||
if (value > prec->drvh) value = prec->drvh;
|
||||
else if (value < prec->drvl) value = prec->drvl;
|
||||
}
|
||||
prec->val = value;
|
||||
}
|
||||
596
src/std/rec/int64outRecord.dbd.pod
Normal file
596
src/std/rec/int64outRecord.dbd.pod
Normal file
@@ -0,0 +1,596 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# 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.
|
||||
#*************************************************************************
|
||||
|
||||
=title 64bit Integer Output Record (int64out)
|
||||
|
||||
This record type is normally used to send an integer value of up to 64 bits
|
||||
to an output device.
|
||||
The record supports alarm, drive, graphics and control limits.
|
||||
|
||||
=head2 Parameter Fields
|
||||
|
||||
The record-specific fields are described below.
|
||||
|
||||
=recordtype int64out
|
||||
|
||||
=cut
|
||||
|
||||
recordtype(int64out) {
|
||||
|
||||
=head3 Output Value Determination
|
||||
|
||||
These fields control how the record determines the value to be output when it
|
||||
gets processed:
|
||||
|
||||
=fields OMSL, DOL, DRVH, DRVL, VAL
|
||||
|
||||
The following steps are performed in order during record processing.
|
||||
|
||||
=head4 Fetch Value
|
||||
|
||||
The OMSL menu field is used to determine whether the DOL link field
|
||||
should be used during processing or not:
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
If OMSL is C<supervisory> the DOL link field is not used.
|
||||
The new output value is taken from the VAL field, which may have been set from
|
||||
elsewhere.
|
||||
|
||||
=item *
|
||||
If OMSL is C<closed_loop> the DOL link field is used to obtain a value.
|
||||
|
||||
=back
|
||||
|
||||
=head4 Drive Limits
|
||||
|
||||
The output value is clipped to the range DRVL to DRVH inclusive, provided
|
||||
that DRVH > DRVL.
|
||||
The result is copied into the VAL field.
|
||||
|
||||
=head3 Output Specification
|
||||
|
||||
These fields control where the record will read data from when it is processed:
|
||||
|
||||
=fields DTYP, OUT
|
||||
|
||||
The DTYP field selects which device support layer should be responsible for
|
||||
writing output data.
|
||||
The int64out device support layers provided by EPICS Base are documented in the
|
||||
L<Device Support> section.
|
||||
External support modules may provide additional device support for this record
|
||||
type.
|
||||
If not set explicitly, the DTYP value defaults to the first device support that
|
||||
is loaded for the record type, which will usually be the C<Soft Channel> support
|
||||
that comes with Base.
|
||||
|
||||
The OUT link field contains a database or channel access link or provides
|
||||
hardware address information that the device support uses to determine where the
|
||||
output data should be sent to.
|
||||
The format for the OUT field value depends on the device support layer that is
|
||||
selected by the DTYP field.
|
||||
See L<Address Specification|...> for a description of the various hardware
|
||||
address formats supported.
|
||||
|
||||
=head3 Operator Display Parameters
|
||||
|
||||
These parameters are used to present meaningful data to the operator.
|
||||
They do not affect the functioning of the record.
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
DESC is a string that is usually used to briefly describe the record.
|
||||
|
||||
=item *
|
||||
EGU is a string of up to 16 characters naming the engineering units
|
||||
that the VAL field represents.
|
||||
|
||||
=item *
|
||||
The HOPR and LOPR fields set the upper and lower display limits for the VAL,
|
||||
HIHI, HIGH, LOW, and LOLO fields.
|
||||
|
||||
=back
|
||||
|
||||
=fields DESC, EGU, HOPR, LOPR
|
||||
|
||||
=head3 Alarm Limits
|
||||
|
||||
The user configures limit alarms by putting numerical values into the HIHI,
|
||||
HIGH, LOW and LOLO fields, and by setting the associated alarm severities
|
||||
in the corresponding HHSV, HSV, LSV and LLSV menu fields.
|
||||
|
||||
The HYST field controls hysteresis to prevent alarm chattering from an input
|
||||
signal that is close to one of the limits and suffers from significant readout
|
||||
noise.
|
||||
|
||||
The LALM field is used by the record at run-time to implement the alarm limit
|
||||
hysteresis functionality.
|
||||
|
||||
=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST, LALM
|
||||
|
||||
=head3 Monitor Parameters
|
||||
|
||||
These parameters are used to determine when to send monitors placed on the VAL
|
||||
field.
|
||||
The monitors are sent when the current value exceeds the last transmitted value
|
||||
by the appropriate deadband.
|
||||
If these fields are set to zero, a monitor will be triggered every time the
|
||||
value changes; if set to -1, a monitor will be sent every time the record is
|
||||
processed.
|
||||
|
||||
The ADEL field sets the deadband for archive monitors (C<DBE_LOG> events), while
|
||||
the MDEL field controls value monitors (C<DBE_VALUE> events).
|
||||
|
||||
The remaining fields are used by the record at run-time to implement the record
|
||||
monitoring deadband functionality.
|
||||
|
||||
=fields ADEL, MDEL, ALST, MLST
|
||||
|
||||
=cut
|
||||
|
||||
include "dbCommon.dbd"
|
||||
field(VAL,DBF_INT64) {
|
||||
prompt("Desired Output")
|
||||
promptgroup("50 - Output")
|
||||
asl(ASL0)
|
||||
pp(TRUE)
|
||||
}
|
||||
field(OUT,DBF_OUTLINK) {
|
||||
prompt("Output Specification")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(DOL,DBF_INLINK) {
|
||||
prompt("Desired Output Loc")
|
||||
promptgroup("40 - Input")
|
||||
interest(1)
|
||||
}
|
||||
field(OMSL,DBF_MENU) {
|
||||
prompt("Output Mode Select")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
menu(menuOmsl)
|
||||
}
|
||||
field(EGU,DBF_STRING) {
|
||||
prompt("Units name")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
size(16)
|
||||
prop(YES)
|
||||
}
|
||||
field(DRVH,DBF_INT64) {
|
||||
prompt("Drive High Limit")
|
||||
promptgroup("30 - Action")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(DRVL,DBF_INT64) {
|
||||
prompt("Drive Low Limit")
|
||||
promptgroup("30 - Action")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HOPR,DBF_INT64) {
|
||||
prompt("High Operating Range")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOPR,DBF_INT64) {
|
||||
prompt("Low Operating Range")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HIHI,DBF_INT64) {
|
||||
prompt("Hihi Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOLO,DBF_INT64) {
|
||||
prompt("Lolo Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HIGH,DBF_INT64) {
|
||||
prompt("High Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOW,DBF_INT64) {
|
||||
prompt("Low Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HHSV,DBF_MENU) {
|
||||
prompt("Hihi Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LLSV,DBF_MENU) {
|
||||
prompt("Lolo Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(HSV,DBF_MENU) {
|
||||
prompt("High Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LSV,DBF_MENU) {
|
||||
prompt("Low Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(HYST,DBF_INT64) {
|
||||
prompt("Alarm Deadband")
|
||||
promptgroup("70 - Alarm")
|
||||
interest(1)
|
||||
}
|
||||
field(ADEL,DBF_INT64) {
|
||||
prompt("Archive Deadband")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
}
|
||||
field(MDEL,DBF_INT64) {
|
||||
prompt("Monitor Deadband")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
}
|
||||
field(LALM,DBF_INT64) {
|
||||
prompt("Last Value Alarmed")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(ALST,DBF_INT64) {
|
||||
prompt("Last Value Archived")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MLST,DBF_INT64) {
|
||||
prompt("Last Val Monitored")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
|
||||
=head3 Simulation Mode
|
||||
|
||||
The record provides several fields to support simulation of absent hardware.
|
||||
If the SIML field is set it is used to read a value into the SIMM field,
|
||||
which controls whether simulation is used or not:
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
SIMM must be zero (C<NO>) for the record to write a value to the device
|
||||
support.
|
||||
|
||||
=item *
|
||||
If SIMM is C<YES> and the SIOL link field is set, the value in engineering
|
||||
units is written using the link.
|
||||
|
||||
=back
|
||||
|
||||
The SIMS field can be set to give the record an alarm severity while it is in
|
||||
simulation mode.
|
||||
|
||||
=fields SIML, SIMM, SIOL, SIMS
|
||||
|
||||
=cut
|
||||
|
||||
field(SIOL,DBF_OUTLINK) {
|
||||
prompt("Sim Output Specifctn")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SIML,DBF_INLINK) {
|
||||
prompt("Sim Mode Location")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SIMM,DBF_MENU) {
|
||||
prompt("Simulation Mode")
|
||||
interest(1)
|
||||
menu(menuYesNo)
|
||||
}
|
||||
field(SIMS,DBF_MENU) {
|
||||
prompt("Sim mode Alarm Svrty")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(2)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
|
||||
=head3 Invalid Alarm Output Action
|
||||
|
||||
Whenever an output record is put into INVALID alarm severity, IVOA specifies
|
||||
the action to take.
|
||||
|
||||
=over
|
||||
|
||||
=item C<Continue normally> (default)
|
||||
|
||||
Write the value. Same as if severity is lower than INVALID.
|
||||
|
||||
=item C<Don't drive outputs>
|
||||
|
||||
Do not write value.
|
||||
|
||||
=item C<Set output to IVOV>
|
||||
|
||||
Set VAL to IVOV, then write the value.
|
||||
|
||||
=back
|
||||
|
||||
=fields IVOA, IVOV
|
||||
|
||||
=cut
|
||||
|
||||
field(IVOA,DBF_MENU) {
|
||||
prompt("INVALID output action")
|
||||
promptgroup("50 - Output")
|
||||
interest(2)
|
||||
menu(menuIvoa)
|
||||
}
|
||||
field(IVOV,DBF_INT64) {
|
||||
prompt("INVALID output value")
|
||||
promptgroup("50 - Output")
|
||||
interest(2)
|
||||
}
|
||||
}
|
||||
|
||||
=head2 Record Support
|
||||
|
||||
=head3 Record Support Routines
|
||||
|
||||
The following are the record support routines that would be of interest
|
||||
to an application developer.
|
||||
Other routines are the C<get_units>, C<get_graphic_double>,
|
||||
C<get_alarm_double> and C<get_control_double> routines, which are used to
|
||||
collect properties from the record for the complex DBR data structures.
|
||||
|
||||
=head4 init_record
|
||||
|
||||
This routine first initializes the simulation mode mechanism by setting SIMM
|
||||
if SIML is a constant.
|
||||
|
||||
It then checks if the device support and the device support's
|
||||
C<write_int64out> routine are defined.
|
||||
If either one does not exist, an error message is issued
|
||||
and processing is terminated.
|
||||
|
||||
If DOL is a constant, then VAL is initialized with its value and UDF is
|
||||
set to FALSE.
|
||||
|
||||
If device support includes C<init_record>, it is called.
|
||||
|
||||
Finally, the deadband mechanisms for monitors and level alarms are
|
||||
initialized.
|
||||
|
||||
=head4 process
|
||||
|
||||
See next section.
|
||||
|
||||
=head3 Record Processing
|
||||
|
||||
Routine C<process> implements the following algorithm:
|
||||
|
||||
=over
|
||||
|
||||
=item 1.
|
||||
|
||||
Check to see that the appropriate device support module and its
|
||||
C<write_int64out> routine are defined.
|
||||
If either one does not exist, an error message is issued and processing is
|
||||
terminated with the PACT field set to TRUE, effectively blocking the record
|
||||
to avoid error storms.
|
||||
|
||||
=item 2.
|
||||
|
||||
Check PACT. If PACT is FALSE, do the following:
|
||||
|
||||
=over
|
||||
|
||||
=item * Determine value, honoring closed loop mode:
|
||||
if DOL is not a CONSTANT and OMSL is CLOSED_LOOP then get value from DOL
|
||||
setting UDF to FALSE in case of success, else use the VAL field.
|
||||
|
||||
=item * Call C<convert>:
|
||||
if drive limits are defined then force value to be within those limits.
|
||||
|
||||
=back
|
||||
|
||||
=item 3.
|
||||
|
||||
Check UDF and level alarms: This routine checks to see if the record is
|
||||
undefined (UDF is TRUE) or if the new VAL causes the alarm status
|
||||
and severity to change. In the latter case, NSEV, NSTA and LALM are set.
|
||||
It also honors the alarm hysteresis factor (HYST): the value must change
|
||||
by at least HYST between level alarm status and severity changes.
|
||||
|
||||
=item 4.
|
||||
|
||||
Check severity and write the new value. See L<Invalid Alarm Output Action>
|
||||
for details on how invalid alarms affect output records.
|
||||
|
||||
=item 5.
|
||||
|
||||
If PACT has been changed to TRUE, the device support signals asynchronous
|
||||
processing: its C<write_int64out> output routine has started, but not
|
||||
completed writing the new value.
|
||||
In this case, the processing routine merely returns, leaving PACT TRUE.
|
||||
|
||||
=item 6.
|
||||
|
||||
Check to see if monitors should be invoked:
|
||||
|
||||
=over
|
||||
|
||||
=item * Alarm monitors are posted if the alarm status or severity have
|
||||
changed.
|
||||
|
||||
=item * Archive and value change monitors are posted if ADEL and MDEL
|
||||
conditions (see L<Monitor Parameters>) are met.
|
||||
|
||||
=item * NSEV and NSTA are reset to 0.
|
||||
|
||||
=back
|
||||
|
||||
=item 7.
|
||||
|
||||
Scan (process) forward link if necessary, set PACT to FALSE, and return.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Device Support
|
||||
|
||||
=head3 Device Support Interface
|
||||
|
||||
The record requires device support to provide an entry table (dset) which
|
||||
defines the following members:
|
||||
|
||||
typedef struct {
|
||||
long number;
|
||||
long (*report)(int level);
|
||||
long (*init)(int after);
|
||||
long (*init_record)(int64outRecord *prec);
|
||||
long (*get_ioint_info)(int cmd, int64outRecord *prec, IOSCANPVT *piosl);
|
||||
long (*write_int64out)(int64outRecord *prec);
|
||||
} int64outdset;
|
||||
|
||||
The module must set C<number> to at least 5, and provide a pointer to its
|
||||
C<write_int64out()> routine; the other function pointers may be C<NULL> if their
|
||||
associated functionality is not required for this support layer.
|
||||
Most device supports also provide an C<init_record()> routine to configure the
|
||||
record instance and connect it to the hardware or driver support layer.
|
||||
|
||||
The individual routines are described below.
|
||||
|
||||
=head3 Device Support Routines
|
||||
|
||||
=head4 long report(int level)
|
||||
|
||||
This optional routine is called by the IOC command C<dbior> and is passed the
|
||||
report level that was requested by the user.
|
||||
It should print a report on the state of the device support to stdout.
|
||||
The C<level> parameter may be used to output increasingly more detailed
|
||||
information at higher levels, or to select different types of information with
|
||||
different levels.
|
||||
Level zero should print no more than a small summary.
|
||||
|
||||
=head4 long init(int after)
|
||||
|
||||
This optional routine is called twice at IOC initialization time.
|
||||
The first call happens before any of the C<init_record()> calls are made, with
|
||||
the integer parameter C<after> set to 0.
|
||||
The second call happens after all of the C<init_record()> calls have been made,
|
||||
with C<after> set to 1.
|
||||
|
||||
=head4 long init_record(int64outRecord *prec)
|
||||
|
||||
This optional routine is called by the record initialization code for each
|
||||
int64out record instance that has its DTYP field set to use this device support.
|
||||
It is normally used to check that the OUT address is the expected type and that
|
||||
it points to a valid device, to allocate any record-specific buffer space and
|
||||
other memory, and to connect any communication channels needed for the
|
||||
C<write_int64out()> routine to work properly.
|
||||
|
||||
=head4 long get_ioint_info(int cmd, int64outRecord *prec, IOSCANPVT *piosl)
|
||||
|
||||
This optional routine is called whenever the record's SCAN field is being
|
||||
changed to or from the value C<I/O Intr> to find out which I/O Interrupt Scan
|
||||
list the record should be added to or deleted from.
|
||||
If this routine is not provided, it will not be possible to set the SCAN field
|
||||
to the value C<I/O Intr> at all.
|
||||
|
||||
The C<cmd> parameter is zero when the record is being added to the scan list,
|
||||
and one when it is being removed from the list.
|
||||
The routine must determine which interrupt source the record should be connected
|
||||
to, which it indicates by the scan list that it points the location at C<*piosl>
|
||||
to before returning.
|
||||
It can prevent the SCAN field from being changed at all by returning a non-zero
|
||||
value to its caller.
|
||||
|
||||
In most cases the device support will create the I/O Interrupt Scan lists that
|
||||
it returns for itself, by calling C<void scanIoInit(IOSCANPVT *piosl)> once for
|
||||
each separate interrupt source.
|
||||
That routine allocates memory and inializes the list, then passes back a pointer
|
||||
to the new list in the location at C<*piosl>.
|
||||
|
||||
When the device support receives notification that the interrupt has occurred,
|
||||
it announces that to the IOC by calling C<void scanIoRequest(IOSCANPVT iosl)>
|
||||
which will arrange for the appropriate records to be processed in a suitable
|
||||
thread.
|
||||
The C<scanIoRequest()> routine is safe to call from an interrupt service routine
|
||||
on embedded architectures (vxWorks and RTEMS).
|
||||
|
||||
=head4 long write_int64out(int64outRecord *prec)
|
||||
|
||||
This essential routine is called when the record wants to write a new value
|
||||
to the addressed device.
|
||||
It is responsible for performing (or at least initiating) a write operation,
|
||||
using the value from the record's VAL field.
|
||||
|
||||
If the device may take more than a few microseconds to accept the new value,
|
||||
this routine must never block (busy-wait), but use the asynchronous
|
||||
processing mechanism.
|
||||
In that case it signals the asynchronous operation by setting the record's
|
||||
PACT field to TRUE before it returns, having arranged for the record's
|
||||
C<process()> routine to be called later once the write operation is over.
|
||||
When that happens, the C<write_int64out()> routine will be called again with
|
||||
PACT still set to TRUE; it should then set it to FALSE to indicate the write
|
||||
has completed, and return.
|
||||
|
||||
A return value of zero indicates success, any other value indicates that an
|
||||
error occurred.
|
||||
|
||||
=head3 Extended Device Support
|
||||
|
||||
...
|
||||
|
||||
=cut
|
||||
|
||||
=head2 Device Support For Soft Records
|
||||
|
||||
Two soft device support modules, Soft Channel and Soft Callback Channel, are
|
||||
provided for output records not related to actual hardware devices. The
|
||||
OUT link type must be either a CONSTANT, DB_LINK, or CA_LINK.
|
||||
|
||||
=head3 Soft Channel
|
||||
|
||||
This module writes the current value using the record's VAL field.
|
||||
|
||||
C<write_int64out> calls C<dbPutLink> to write the current value.
|
||||
|
||||
=head3 Soft Callback Channel
|
||||
|
||||
This module is like the previous except that it writes the current value
|
||||
using asynchronous processing that will not complete until an asynchronous
|
||||
processing of the target record has completed.
|
||||
|
||||
=cut
|
||||
@@ -32,6 +32,13 @@ testHarness_SRCS += arrayOpTest.c
|
||||
TESTFILES += ../arrayOpTest.db
|
||||
TESTS += arrayOpTest
|
||||
|
||||
TESTPROD_HOST += recMiscTest
|
||||
recMiscTest_SRCS += recMiscTest.c
|
||||
recMiscTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += recMiscTest.c
|
||||
TESTFILES += ../recMiscTest.db
|
||||
TESTS += recMiscTest
|
||||
|
||||
TESTPROD_HOST += linkRetargetLinkTest
|
||||
linkRetargetLinkTest_SRCS += linkRetargetLinkTest.c
|
||||
linkRetargetLinkTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
int analogMonitorTest(void);
|
||||
int compressTest(void);
|
||||
int recMiscTest(void);
|
||||
int arrayOpTest(void);
|
||||
int asTest(void);
|
||||
int linkRetargetLinkTest(void);
|
||||
@@ -27,6 +28,8 @@ void epicsRunRecordTests(void)
|
||||
|
||||
runTest(compressTest);
|
||||
|
||||
runTest(recMiscTest);
|
||||
|
||||
runTest(arrayOpTest);
|
||||
|
||||
runTest(asTest);
|
||||
|
||||
88
src/std/rec/test/recMiscTest.c
Normal file
88
src/std/rec/test/recMiscTest.c
Normal file
@@ -0,0 +1,88 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2017 Michael Davidsaver
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dbAccess.h"
|
||||
#include "errlog.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
|
||||
static
|
||||
void testint64BeforeInit(void)
|
||||
{
|
||||
const char *S;
|
||||
DBENTRY dbent;
|
||||
|
||||
/* check dbGet/PutString */
|
||||
|
||||
testDiag("In %s", EPICS_FUNCTION);
|
||||
|
||||
dbInitEntryFromRecord(testdbRecordPtr("out64"), &dbent);
|
||||
if(dbFindField(&dbent, "VAL"))
|
||||
testAbort("Failed to find out64.VAL");
|
||||
|
||||
S = dbGetString(&dbent);
|
||||
testOk(S && strcmp(S, "0")==0, "initial value \"%s\"", S);
|
||||
|
||||
testOk1(dbPutString(&dbent, "0x12345678abcdef00")==0);
|
||||
|
||||
S = dbGetString(&dbent);
|
||||
testOk(S && strcmp(S, "1311768467750121216")==0, "1311768467750121216 \"%s\"", S);
|
||||
|
||||
dbFinishEntry(&dbent);
|
||||
}
|
||||
|
||||
static
|
||||
void testint64AfterInit(void)
|
||||
{
|
||||
testDiag("In %s", EPICS_FUNCTION);
|
||||
|
||||
/* check dbGet/PutField and DB links */
|
||||
|
||||
testdbGetFieldEqual("in64", DBF_UINT64, 0llu);
|
||||
testdbGetFieldEqual("out64", DBF_UINT64, 0x12345678abcdef00llu);
|
||||
|
||||
testdbPutFieldOk("out64.PROC", DBF_LONG, 1);
|
||||
|
||||
testdbGetFieldEqual("in64", DBF_UINT64, 0x12345678abcdef00llu);
|
||||
|
||||
testdbPutFieldOk("out64.VAL", DBF_UINT64, 0x22345678abcdef00llu);
|
||||
|
||||
testdbPutFieldOk("in64.PROC", DBF_LONG, 1);
|
||||
|
||||
testdbGetFieldEqual("in64", DBF_UINT64, 0x22345678abcdef00llu);
|
||||
}
|
||||
|
||||
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
MAIN(recMiscTest)
|
||||
{
|
||||
testPlan(0);
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
|
||||
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("recMiscTest.db", NULL, NULL);
|
||||
|
||||
testint64BeforeInit();
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
testint64AfterInit();
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
10
src/std/rec/test/recMiscTest.db
Normal file
10
src/std/rec/test/recMiscTest.db
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
# check int64in/out
|
||||
|
||||
record(int64in, "in64") {
|
||||
field(INP , "out64 NPP")
|
||||
}
|
||||
|
||||
record(int64out, "out64") {
|
||||
field(OUT , "in64 NPP")
|
||||
}
|
||||
@@ -12,6 +12,8 @@ our %field_types = (
|
||||
DBF_USHORT => $RXuintx,
|
||||
DBF_LONG => $RXintx,
|
||||
DBF_ULONG => $RXuintx,
|
||||
DBF_INT64 => $RXintx,
|
||||
DBF_UINT64 => $RXuintx,
|
||||
DBF_FLOAT => $RXnum,
|
||||
DBF_DOUBLE => $RXnum,
|
||||
DBF_ENUM => qr/.*/,
|
||||
@@ -317,6 +319,43 @@ sub toDeclaration {
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
|
||||
package DBD::Recfield::DBF_INT64;
|
||||
|
||||
use DBD::Base;
|
||||
@ISA = qw(DBD::Recfield);
|
||||
|
||||
sub legal_value {
|
||||
my ($this, $value) = @_;
|
||||
$value =~ s/^ ( $RXhex | $RXoct ) $/ oct($1) /xe;
|
||||
return ($value =~ m/^ $RXint $/x);
|
||||
}
|
||||
|
||||
sub toDeclaration {
|
||||
return shift->SUPER::toDeclaration("epicsInt64");
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
|
||||
package DBD::Recfield::DBF_UINT64;
|
||||
|
||||
use DBD::Base;
|
||||
@ISA = qw(DBD::Recfield);
|
||||
|
||||
sub legal_value {
|
||||
my ($this, $value) = @_;
|
||||
$value =~ s/^ ( $RXhex | $RXoct ) $/ oct($1) /xe;
|
||||
return ($value =~ m/^ $RXuint $/x and
|
||||
$value >= 0);
|
||||
}
|
||||
|
||||
sub toDeclaration {
|
||||
return shift->SUPER::toDeclaration("epicsUInt64");
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
|
||||
package DBD::Recfield::DBF_FLOAT;
|
||||
|
||||
Reference in New Issue
Block a user