diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 3b0058a87..2b8e9337b 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -12,6 +12,33 @@
Dymanic array sizing was developed by Michael Abbott at the 2010 EPICS +Codeathon. It permits a CA client to fetch only the currently valid elements of +an array by specifying a COUNT of zero to either of the ca_array_get_callback() +or ca_create_subscription() routines. It has never before been legal to pass a +COUNT of zero to the ca_array_get_callback() routine, but this development does +introduce a subtle change to the published API of the ca_create_subscription() +routine.
+ +In previous releases a COUNT of zero for a subscription meant use the +ca_element_count() for the channel, but from this release it can return fewer +elements (never more), at the behest of the server. The number of elements can +vary with subsequent array update events, so a client that uses this technique +must use the count field of the event_handler_args in its callback function each +time it is called to obtain the correct element count from the server. Note that +the ca_element_count() value for a channel is only updated at connection time, +and supplies the maximum number of elements that the server array variable can +hold.
+ +Dynamic arrays are currently only supported by the CA client library and IOC +server RSRV, the Perl CA library and the catools programs. The portable CAS does +not understand them, and database links that connect over CA do not attempt to +use them either. CA clients that try to use this functionality with a server +that does not support it will receive the same full-sized zero-filled arrays +that previous releases supported.
+Merged the CA-over-TCP changes developed by Ralph and Jeff at the 2008 EPICS
diff --git a/src/ca/CAref.html b/src/ca/CAref.html
index 13e6cc61e..4f156b25a 100644
--- a/src/ca/CAref.html
+++ b/src/ca/CAref.html
@@ -2297,11 +2297,11 @@ ca_put_callback, ca_get_callback, and ca_add_event all request notification of
asynchronous completion via this mechanism. The event_handler_args
structure is passed by value to the application supplied
callback. In this structure the dbr field is a void pointer to any
-data that might be returned. The status field will be
+data that might be returned. The status field will be
set to one of the CA error codes in caerr.h and will indicate the status of the
operation performed in the IOC. If the status field isn't set to ECA_NORMAL or
data isn't normally returned from the operation (i.e. put call back) then you
-should expect that the dbr field will be set to a nill pointer
+should expect that the dbr field will be set to a nill pointer
(zero). The fields usr, chid, and type
are set to the values specified when the request was made by the application.
The "dbr" pointer, and any data that it points to, are valid only when
@@ -2352,8 +2352,8 @@ library functions that request them.
For routines that require an argument specifying the number of array
elements, no more than the process variable's maximum native element count may
be requested. The process variable's maximum native element count is available
-from ca_element_count() when the channel is connected. If less elements than
-the process variable's native element count are requested the requested values
+from ca_element_count() when the channel is connected. If fewer elements than
+the process variable's native element count are requested, the requested values
will be fetched beginning at element zero. By default CA limits the number of
elements in an array to be no more than approximately 16k divided by the size
of one element in the array. Starting with EPICS R3.14 the maximum array size
@@ -2381,12 +2381,12 @@ href="#ca_create_channel">ca_create_channel. The ca_pend_io approach is best suited to simple
command line programs with short runtime duration, and the connection callback
method is best suited to toolkit components with long runtime duration. Use of
-ca_state is appropriate only in programs
+ca_state is appropriate only in programs
that prefer to poll for connection state changes instead of opting for
asynchronous notification. The ca_pend_io function blocks only for
channels created specifying a nill connection handler callback function. The
user's connection state change function will be run immediately from within
-ca_create_channel if the CA
+ca_create_channel if the CA
client and CA server are both hosted within the same address space (within the
same process).
The following structure is passed by value to the user's
connection connection callback function. The op field will
be set by the CA client library to CA_OP_CONN_UP when the
- channel connects, and to CA_OP_CONN_DOWN when the channel
+ channel connects, and to CA_OP_CONN_DOWN when the channel
disconnects. See ca_puser if the
- PUSER argument is required in your callback
+ PUSER argument is required in your callback
handler.
struct ca_connection_handler_args {
chanId chid; /* channel id */
@@ -2973,7 +2973,8 @@ when a CA get request is initiated.
COUNTCHIDCOUNTCHIDop field can be one of
+ callback function. Currently, the op field can be one of
CA_OP_GET, CA_OP_PUT, CA_OP_CREATE_CHANNEL, CA_OP_ADD_EVENT,
CA_OP_CLEAR_EVENT, or CA_OP_OTHER.
struct exception_handler_args {
diff --git a/src/ca/caProto.h b/src/ca/caProto.h
index 84190bf56..f61f52e08 100644
--- a/src/ca/caProto.h
+++ b/src/ca/caProto.h
@@ -41,6 +41,7 @@
# define CA_V410(MINOR) ((MINOR)>=10u) /* beacon counter */
# define CA_V411(MINOR) ((MINOR)>=11u) /* sequence numbers in UDP version command */
# define CA_V412(MINOR) ((MINOR)>=12u) /* TCP-based search requests */
+# define CA_V413(MINOR) ((MINOR)>=13u) /* Allow zero length in requests. */
#elif CA_MAJOR_PROTOCOL_REVISION > 4u
# define CA_V41(MINOR) ( 1u )
# define CA_V42(MINOR) ( 1u )
@@ -54,6 +55,7 @@
# define CA_V410(MINOR) ( 1u )
# define CA_V411(MINOR) ( 1u )
# define CA_V412(MINOR) ( 1u )
+# define CA_V413(MINOR) ( 1u )
#else
# define CA_V41(MINOR) ( 0u )
# define CA_V42(MINOR) ( 0u )
@@ -67,6 +69,7 @@
# define CA_V410(MINOR) ( 0u )
# define CA_V411(MINOR) ( 0u )
# define CA_V412(MINOR) ( 0u )
+# define CA_V413(MINOR) ( 0u )
#endif
/*
diff --git a/src/ca/db_access.h b/src/ca/db_access.h
index e88a929d0..92aa5d011 100644
--- a/src/ca/db_access.h
+++ b/src/ca/db_access.h
@@ -524,16 +524,16 @@ struct dbr_ctrl_double{
dbr_double_t value; /* current value */
};
-#ifndef db_accessHFORdb_accessC
#define dbr_size_n(TYPE,COUNT)\
((unsigned)((COUNT)<=0?dbr_size[TYPE]:dbr_size[TYPE]+((COUNT)-1)*dbr_value_size[TYPE]))
/* size for each type - array indexed by the DBR_ type code */
-epicsShareExtern const unsigned short dbr_size[LAST_BUFFER_TYPE+1];
+epicsShareExtern const unsigned short dbr_size[];
/* size for each type's value - array indexed by the DBR_ type code */
-epicsShareExtern const unsigned short dbr_value_size[LAST_BUFFER_TYPE+1];
+epicsShareExtern const unsigned short dbr_value_size[];
+#ifndef db_accessHFORdb_accessC
/* class for each type's value */
enum dbr_value_class {
dbr_class_int,
diff --git a/src/ca/nciu.cpp b/src/ca/nciu.cpp
index f61172808..c5ae941b2 100644
--- a/src/ca/nciu.cpp
+++ b/src/ca/nciu.cpp
@@ -291,9 +291,6 @@ cacChannel::ioStatus nciu::read (
if ( countIn > this->count ) {
throw cacChannel::outOfBounds ();
}
- if ( countIn == 0 ) {
- countIn = this->count;
- }
//
// fail out if their arguments are invalid
diff --git a/src/ca/nciu.h b/src/ca/nciu.h
index 5660a8c85..de070dcea 100644
--- a/src/ca/nciu.h
+++ b/src/ca/nciu.h
@@ -41,7 +41,7 @@
# include "shareLib.h"
#endif
-#define CA_MINOR_PROTOCOL_REVISION 12
+#define CA_MINOR_PROTOCOL_REVISION 13
#include "caProto.h"
#include "cacIO.h"
@@ -205,6 +205,7 @@ public:
void disconnectAllIO (
epicsGuard < epicsMutex > &, epicsGuard < epicsMutex > & );
bool connected ( epicsGuard < epicsMutex > & ) const;
+ unsigned getcount() const { return count; }
private:
tsDLList < class baseNMIU > eventq;
diff --git a/src/ca/netIO.h b/src/ca/netIO.h
index b694bc141..57273427a 100644
--- a/src/ca/netIO.h
+++ b/src/ca/netIO.h
@@ -83,7 +83,7 @@ public:
void show (
epicsGuard < epicsMutex > &, unsigned level ) const;
arrayElementCount getCount (
- epicsGuard < epicsMutex > & ) const;
+ epicsGuard < epicsMutex > &, bool allow_zero ) const;
unsigned getType (
epicsGuard < epicsMutex > & ) const;
unsigned getMask (
@@ -242,11 +242,11 @@ inline netSubscription * netSubscription::factory (
}
inline arrayElementCount netSubscription::getCount (
- epicsGuard < epicsMutex > & guard ) const // X aCC 361
+ epicsGuard < epicsMutex > & guard, bool allow_zero ) const // X aCC 361
{
//guard.assertIdenticalMutex ( this->mutex );
arrayElementCount nativeCount = this->privateChanForIO.nativeElementCount ( guard );
- if ( this->count == 0u || this->count > nativeCount ) {
+ if ( (this->count == 0u && !allow_zero) || this->count > nativeCount ) {
return nativeCount;
}
else {
diff --git a/src/ca/oldChannelNotify.cpp b/src/ca/oldChannelNotify.cpp
index 48ddde124..8865f8d63 100644
--- a/src/ca/oldChannelNotify.cpp
+++ b/src/ca/oldChannelNotify.cpp
@@ -280,6 +280,9 @@ int epicsShareAPI ca_array_get ( chtype type,
if ( type < 0 ) {
return ECA_BADTYPE;
}
+ if ( count == 0 )
+ return ECA_BADCOUNT;
+
unsigned tmpType = static_cast < unsigned > ( type );
epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () );
pChan->eliminateExcessiveSendBacklog ( guard );
diff --git a/src/ca/tcpiiu.cpp b/src/ca/tcpiiu.cpp
index 7b775cb50..a38ee7ff3 100644
--- a/src/ca/tcpiiu.cpp
+++ b/src/ca/tcpiiu.cpp
@@ -1464,6 +1464,8 @@ void tcpiiu::readNotifyRequest ( epicsGuard < epicsMutex > & guard, // X aCC 431
if ( nElem > maxElem ) {
throw cacChannel::msgBodyCacheTooSmall ();
}
+ if (nElem == 0 && !CA_V413(this->minorProtocolVersion))
+ nElem = chan.getcount();
comQueSendMsgMinder minder ( this->sendQue, guard );
this->sendQue.insertRequestHeader (
CA_PROTO_READ_NOTIFY, 0u,
@@ -1559,7 +1561,8 @@ void tcpiiu::subscriptionRequest (
if ( mask > 0xffff ) {
throw cacChannel::badEventSelection ();
}
- arrayElementCount nElem = subscr.getCount ( guard );
+ arrayElementCount nElem = subscr.getCount (
+ guard, CA_V413(this->minorProtocolVersion) );
arrayElementCount maxBytes;
if ( CA_V49 ( this->minorProtocolVersion ) ) {
maxBytes = this->cacRef.largeBufferSizeTCP ();
@@ -1605,7 +1608,8 @@ void tcpiiu::subscriptionUpdateRequest (
if ( this->state != iiucs_connected ) {
return;
}
- arrayElementCount nElem = subscr.getCount ( guard );
+ arrayElementCount nElem = subscr.getCount (
+ guard, CA_V413(this->minorProtocolVersion) );
arrayElementCount maxBytes;
if ( CA_V49 ( this->minorProtocolVersion ) ) {
maxBytes = this->cacRef.largeBufferSizeTCP ();
@@ -1643,7 +1647,8 @@ void tcpiiu::subscriptionCancelRequest ( epicsGuard < epicsMutex > & guard, // X
this->sendQue.insertRequestHeader (
CA_PROTO_EVENT_CANCEL, 0u,
static_cast < ca_uint16_t > ( subscr.getType ( guard ) ),
- static_cast < ca_uint16_t > ( subscr.getCount ( guard ) ),
+ static_cast < ca_uint16_t > ( subscr.getCount (
+ guard, CA_V413(this->minorProtocolVersion) ) ),
chan.getSID(guard), subscr.getId(),
CA_V49 ( this->minorProtocolVersion ) );
minder.commit ();
diff --git a/src/cap5/CA.pm b/src/cap5/CA.pm
index 8d5ccd6a6..3aa7c7f51 100644
--- a/src/cap5/CA.pm
+++ b/src/cap5/CA.pm
@@ -210,8 +210,9 @@ data type requested will be the widened form of the channel's native type
fetch all available elements.
The element count can be overridden by providing an integer argument in the
-range 1 .. C. Note that the count argument must be an integer;
-add 0 to it if it is necessary to convert it from a string.
+range 0 .. C, where zero means use the current length from the
+server. Note that the count argument must be an integer; add 0 to it if it is
+necessary to convert it from a string.
The optional data type I should be a string naming
the desired C type; the actual type used will have the C part
widened to one of C, C, C or C. The valid type
diff --git a/src/cap5/Cap5.xs b/src/cap5/Cap5.xs
index d83674855..c3e4d0e50 100644
--- a/src/cap5/Cap5.xs
+++ b/src/cap5/Cap5.xs
@@ -782,7 +782,7 @@ void CA_get(SV *ca_ref) {
New(0, pch->sdata, count + 1, char);
pch->ssize = count;
}
- status = ca_array_get(DBF_CHAR, count, pch->chan, pch->sdata);
+ status = ca_array_get(DBF_CHAR, 0, pch->chan, pch->sdata);
} else {
status = ca_get(best_type(pch), pch->chan, &pch->data);
}
@@ -818,16 +818,16 @@ void CA_get_callback(SV *ca_ref, SV *sub, ...) {
SV *get_sub = newSVsv(sub);
int status;
chtype type = best_type(pch);
- int count = ca_element_count(pch->chan);
+ int count = 0;
int i = 2;
const char *croak_msg;
while (items > i
&& SvOK(ST(i))) {
if (SvIOK(ST(i))) {
- /* Interger => Count arg */
+ /* Interger => Count arg, zero means current size */
count = SvIV(ST(i));
- if (count < 1 || count > ca_element_count(pch->chan)) {
+ if (count < 0 || count > ca_element_count(pch->chan)) {
croak_msg = "Requested array size is out of range";
goto exit_croak;
}
@@ -901,7 +901,7 @@ SV * CA_create_subscription(SV *ca_ref, const char *mask_str, SV *sub, ...) {
while (items > i
&& SvOK(ST(i))) {
if (SvIOK(ST(i))) {
- /* Interger => Count arg, zero means native size */
+ /* Interger => Count arg, zero means current size */
count = SvIV(ST(i));
if (count < 0 || count > ca_element_count(pch->chan)) {
croak_msg = "Requested array size is out of range";
diff --git a/src/cap5/caget.pl b/src/cap5/caget.pl
index f976e0640..7fe60d8c5 100644
--- a/src/cap5/caget.pl
+++ b/src/cap5/caget.pl
@@ -6,10 +6,12 @@ use FindBin qw($Bin);
use lib "$Bin/../../lib/perl";
use Getopt::Std;
+use Scalar::Util qw(looks_like_number);
use CA;
-our ($opt_0, $opt_a, $opt_c, $opt_d, $opt_e, $opt_f, $opt_g, $opt_h, $opt_n);
+our ($opt_0, $opt_a, $opt_d, $opt_e, $opt_f, $opt_g, $opt_h, $opt_n);
our ($opt_s, $opt_S, $opt_t);
+our $opt_c = 0;
our $opt_F = ' ';
our $opt_w = 1;
@@ -18,6 +20,9 @@ $Getopt::Std::OUTPUT_HELP_VERSION = 1;
HELP_MESSAGE() unless getopts('0:ac:d:e:f:F:g:hnsStw:');
HELP_MESSAGE() if $opt_h;
+die "caget: -c option takes a positive number\n"
+ unless looks_like_number($opt_c) && $opt_c >= 0;
+
die "No pv name specified. ('caget -h' gives help.)\n"
unless @ARGV;
@@ -51,9 +56,7 @@ map {
if $opt_a;
}
$rtype{$_} = $type;
- my $count = $_->element_count;
- $count = +$opt_c if $opt_c && $opt_c <= $count;
- $_->get_callback(\&get_callback, $type, $count);
+ $_->get_callback(\&get_callback, $type, 0+$opt_c);
} @chans;
my $incomplete = @chans;
diff --git a/src/cap5/camonitor.pl b/src/cap5/camonitor.pl
index 56c609141..559ec1233 100644
--- a/src/cap5/camonitor.pl
+++ b/src/cap5/camonitor.pl
@@ -6,9 +6,11 @@ use FindBin qw($Bin);
use lib "$Bin/../../lib/perl";
use Getopt::Std;
+use Scalar::Util qw(looks_like_number);
use CA;
-our ($opt_0, $opt_c, $opt_e, $opt_f, $opt_g, $opt_h, $opt_n, $opt_s, $opt_S);
+our ($opt_0, $opt_e, $opt_f, $opt_g, $opt_h, $opt_n, $opt_s, $opt_S);
+our $opt_c = 0;
our $opt_F = ' ';
our $opt_w = 1;
our $opt_m = 'va';
@@ -18,6 +20,9 @@ $Getopt::Std::OUTPUT_HELP_VERSION = 1;
HELP_MESSAGE() unless getopts('0:c:e:f:F:g:hm:nsSw:');
HELP_MESSAGE() if $opt_h;
+die "caget: -c option takes a positive number\n"
+ unless looks_like_number($opt_c) && $opt_c >= 0;
+
die "No pv name specified. ('camonitor -h' gives help.)\n"
unless @ARGV;
@@ -45,11 +50,8 @@ sub conn_callback {
|| (!$opt_S && $type eq 'DBR_CHAR');
$type =~ s/^DBR_/DBR_TIME_/;
- my $count = $chan->element_count;
- $count = +$opt_c if $opt_c && $opt_c <= $count;
-
$monitors{$chan} =
- $chan->create_subscription($opt_m, \&mon_callback, $type, $count);
+ $chan->create_subscription($opt_m, \&mon_callback, $type, 0+$opt_c);
}
}
diff --git a/src/catools/caget.c b/src/catools/caget.c
index bae87e7b5..573ae85a7 100644
--- a/src/catools/caget.c
+++ b/src/catools/caget.c
@@ -127,6 +127,7 @@ static void event_handler (evargs args)
ppv->value = calloc(1, dbr_size_n(args.type, args.count));
memcpy(ppv->value, args.dbr, dbr_size_n(args.type, args.count));
ppv->onceConnected = 1;
+ ppv->nElems = args.count;
nRead++;
}
}
@@ -183,11 +184,9 @@ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format,
}
}
/* Adjust array count */
- if (reqElems == 0 || pvs[n].nElems < reqElems){
- pvs[n].reqElems = pvs[n].nElems; /* Use full number of points */
- } else {
- pvs[n].reqElems = reqElems; /* Limit to specified number */
- }
+ if (reqElems > pvs[n].nElems)
+ reqElems = pvs[n].nElems;
+ pvs[n].reqElems = reqElems;
/* Issue CA request */
/* ---------------- */
@@ -205,13 +204,13 @@ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format,
(void*)&pvs[n]);
} else {
/* Allocate value structure */
- pvs[n].value = calloc(1, dbr_size_n(pvs[n].dbrType, pvs[n].reqElems));
+ pvs[n].value = calloc(1, dbr_size_n(pvs[n].dbrType, pvs[n].nElems));
if(!pvs[n].value) {
fprintf(stderr,"Allocation failed\n");
return 1;
}
result = ca_array_get(pvs[n].dbrType,
- pvs[n].reqElems,
+ pvs[n].nElems,
pvs[n].chid,
pvs[n].value);
}
@@ -253,10 +252,13 @@ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format,
/* -------------- */
for (n = 0; n < nPvs; n++) {
+ /* Truncate the data printed to what was requested. */
+ if (pvs[n].reqElems != 0 && pvs[n].nElems > pvs[n].reqElems)
+ pvs[n].nElems = pvs[n].reqElems;
switch (format) {
case plain: /* Emulate old caget behaviour */
- if (pvs[n].reqElems <= 1 && fieldSeparator == ' ') printf("%-30s", pvs[n].name);
+ if (pvs[n].nElems <= 1 && fieldSeparator == ' ') printf("%-30s", pvs[n].name);
else printf("%s", pvs[n].name);
printf("%c", fieldSeparator);
case terse:
@@ -270,7 +272,7 @@ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format,
printf("*** no data available (timeout)\n");
else
{
- if (charArrAsStr && dbr_type_is_CHAR(pvs[n].dbrType) && (reqElems || pvs[n].reqElems > 1)) {
+ if (charArrAsStr && dbr_type_is_CHAR(pvs[n].dbrType) && (reqElems || pvs[n].nElems > 1)) {
dbr_char_t *s = (dbr_char_t*) dbr_value_ptr(pvs[n].value, pvs[n].dbrType);
int dlen = epicsStrnEscapedFromRawSize((char*)s, strlen((char*)s));
char *d = calloc(dlen+1, sizeof(char));
@@ -282,8 +284,8 @@ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format,
fprintf(stderr,"Failed to allocate space for escaped string\n");
}
} else {
- if (reqElems || pvs[n].nElems > 1) printf("%lu%c", pvs[n].reqElems, fieldSeparator);
- for (i=0; i 1) printf("%lu%c", pvs[n].nElems, fieldSeparator);
+ for (i=0; i 1)) {
+ pvs[n].nElems);
+ if (charArrAsStr && dbr_type_is_CHAR(pvs[n].dbrType) && (reqElems || pvs[n].nElems > 1)) {
dbr_char_t *s = (dbr_char_t*) dbr_value_ptr(pvs[n].value, pvs[n].dbrType);
int dlen = epicsStrnEscapedFromRawSize((char*)s, strlen((char*)s));
char *d = calloc(dlen+1, sizeof(char));
@@ -328,7 +330,7 @@ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format,
fprintf(stderr,"Failed to allocate space for escaped string\n");
}
} else {
- for (i=0; idbrType = args.type;
+ pv->nElems = args.count;
memcpy(pv->value, args.dbr, dbr_size_n(args.type, args.count));
print_time_val_sts(pv, reqElems);
@@ -150,11 +151,9 @@ static void connection_handler ( struct connection_handler_args args )
ppv->dbrType = DBR_TIME_STRING;
}
/* Adjust array count */
- if (reqElems == 0 || ppv->nElems < reqElems){
- ppv->reqElems = ppv->nElems; /* Use full number of points */
- } else {
- ppv->reqElems = reqElems; /* Limit to specified number */
- }
+ if (reqElems > ppv->nElems)
+ reqElems = ppv->nElems;
+ ppv->reqElems = reqElems;
ppv->onceConnected = 1;
nConn++;
@@ -163,7 +162,7 @@ static void connection_handler ( struct connection_handler_args args )
/* install monitor once with first connect */
if ( ! ppv->value ) {
/* Allocate value structure */
- ppv->value = calloc(1, dbr_size_n(ppv->dbrType, ppv->reqElems));
+ ppv->value = calloc(1, dbr_size_n(ppv->dbrType, ppv->nElems));
if ( ppv->value ) {
ppv->status = ca_create_subscription(ppv->dbrType,
ppv->reqElems,
diff --git a/src/catools/tool_lib.c b/src/catools/tool_lib.c
index 0bfcab3a3..13d6330cf 100644
--- a/src/catools/tool_lib.c
+++ b/src/catools/tool_lib.c
@@ -455,8 +455,8 @@ char *dbr2str (const void *value, unsigned type)
printf("Failed to allocate for print_time_val_sts\n"); \
} \
} else { \
- if (reqElems || pv->nElems > 1) printf("%c%lu", fieldSeparator, pv->reqElems); \
- for (i=0; ireqElems; ++i) { \
+ if (reqElems || pv->nElems > 1) printf("%c%lu", fieldSeparator, pv->nElems); \
+ for (i=0; inElems; ++i) { \
printf("%c%s", fieldSeparator, val2str(value, TYPE_ENUM, i)); \
} \
} \
@@ -492,7 +492,7 @@ void print_time_val_sts (pv* pv, unsigned long reqElems)
tsInitS = 1;
}
- if (pv->reqElems <= 1 && fieldSeparator == ' ') printf("%-30s", pv->name);
+ if (pv->nElems <= 1 && fieldSeparator == ' ') printf("%-30s", pv->name);
else printf("%s", pv->name);
printf("%c", fieldSeparator);
if (!pv->onceConnected)
diff --git a/src/catools/tool_lib.h b/src/catools/tool_lib.h
index 5bb6b5ddd..baebf7939 100644
--- a/src/catools/tool_lib.h
+++ b/src/catools/tool_lib.h
@@ -64,8 +64,8 @@ typedef struct
chid chid;
long dbfType;
long dbrType;
- unsigned long nElems;
- unsigned long reqElems;
+ unsigned long nElems; // True length of data in value
+ unsigned long reqElems; // Requested length of data
int status;
void* value;
epicsTimeStamp tsPreviousC;
diff --git a/src/db/db_access.c b/src/db/db_access.c
index 6f389257f..98ff26361 100644
--- a/src/db/db_access.c
+++ b/src/db/db_access.c
@@ -148,95 +148,58 @@ int epicsShareAPI db_name_to_addr(const char *pname, struct dbAddr *paddr)
int epicsShareAPI db_get_field(struct dbAddr *paddr,
int buffer_type, void *pbuffer, int no_elements, void *pfl)
+{
+ long nRequest = no_elements;
+ int result = db_get_field_and_count(
+ paddr, buffer_type, pbuffer, &nRequest, pfl);
+ if (nRequest < no_elements)
+ /* If the database request returned fewer elements than requested then
+ * fill out the remainder of the array with zeros. */
+ memset(
+ (char *)pbuffer + dbr_size_n(buffer_type, nRequest), 0,
+ (no_elements - nRequest) * dbr_value_size[buffer_type]);
+ return result;
+}
+
+/* Performs the work of the public db_get_field API, but also returns the number
+ * of elements actually copied to the buffer. The caller is responsible for
+ * zeroing the remaining part of the buffer. */
+int epicsShareAPI db_get_field_and_count(
+ struct dbAddr *paddr, int buffer_type,
+ void *pbuffer, long *nRequest, void *pfl)
{
long status;
long options;
- long nRequest;
long i;
+ long zero = 0;
- /* The order of the DBR* elements in the "new" structures below is
+ /* The order of the DBR* elements in the "newSt" structures below is
* very important and must correspond to the order of processing
* in the dbAccess.c dbGet() and getOptions() routines.
*/
switch(buffer_type) {
case(oldDBR_STRING):
- {
- DBSTRING *pvalue = (DBSTRING *)pbuffer;
-
- options = 0;
- nRequest = no_elements;
- status = dbGetField(paddr, DBR_STRING, pbuffer, &options, &nRequest,
- pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i][0] = 0;
- }
+ status = dbGetField(paddr, DBR_STRING, pbuffer, &zero, nRequest, pfl);
break;
/* case(oldDBR_INT): */
case(oldDBR_SHORT):
- {
- dbr_short_t *pvalue = (dbr_short_t *)pbuffer;
-
- options = 0;
- nRequest = no_elements;
- status = dbGetField(paddr, DBR_SHORT, pbuffer, &options, &nRequest,
- pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
- }
+ status = dbGetField(paddr, DBR_SHORT, pbuffer, &zero, nRequest, pfl);
break;
case(oldDBR_FLOAT):
- {
- dbr_float_t *pvalue = (dbr_float_t *)pbuffer;
-
- options = 0;
- nRequest = no_elements;
- status = dbGetField(paddr, DBR_FLOAT, pbuffer, &options, &nRequest,
- pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
- }
+ status = dbGetField(paddr, DBR_FLOAT, pbuffer, &zero, nRequest, pfl);
break;
case(oldDBR_ENUM):
- {
- dbr_enum_t *pvalue = (dbr_enum_t *)pbuffer;
-
- options = 0;
- nRequest = no_elements;
- status = dbGetField(paddr, DBR_ENUM, pbuffer, &options, &nRequest,
- pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
- }
+ status = dbGetField(paddr, DBR_ENUM, pbuffer, &zero, nRequest, pfl);
break;
case(oldDBR_CHAR):
- {
- dbr_char_t *pvalue = (dbr_char_t *)pbuffer;
-
- options = 0;
- nRequest = no_elements;
- status = dbGetField(paddr, DBR_CHAR, pbuffer, &options, &nRequest,
- pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
- }
+ status = dbGetField(paddr, DBR_CHAR, pbuffer, &zero, nRequest, pfl);
break;
case(oldDBR_LONG):
- {
- dbr_long_t *pvalue = (dbr_long_t *)pbuffer;
-
- options = 0;
- nRequest = no_elements;
- status = dbGetField(paddr, DBR_LONG, pbuffer, &options, &nRequest,
- pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
- }
+ status = dbGetField(paddr, DBR_LONG, pbuffer, &zero, nRequest, pfl);
break;
case(oldDBR_DOUBLE):
- {
- dbr_double_t *pvalue = (dbr_double_t *)pbuffer;
-
- options = 0;
- nRequest = no_elements;
- status = dbGetField(paddr, DBR_DOUBLE, pbuffer, &options, &nRequest,
- pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
- }
+ status = dbGetField(paddr, DBR_DOUBLE, pbuffer, &zero, nRequest, pfl);
break;
case(oldDBR_STS_STRING):
@@ -247,19 +210,13 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
struct {
DBRstatus
} newSt;
- DBSTRING *pvalue = (DBSTRING *)pold->value;
options = DBR_STATUS;
- nRequest = 0;
- status = dbGetField(paddr, DBR_STRING, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_STRING, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
- options = 0;
- nRequest = no_elements;
- status = dbGetField(paddr, DBR_STRING, pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i][0] = 0;
+ status = dbGetField(paddr, DBR_STRING, pold->value, &zero,
+ nRequest, pfl);
}
break;
/* case(oldDBR_STS_INT): */
@@ -269,19 +226,13 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
struct {
DBRstatus
} newSt;
- dbr_short_t *pvalue = &pold->value;
options = DBR_STATUS;
- nRequest = 0;
- status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
- options = 0;
- nRequest = no_elements;
- status = dbGetField(paddr, DBR_SHORT, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ status = dbGetField(paddr, DBR_SHORT, &pold->value, &zero,
+ nRequest, pfl);
}
break;
case(oldDBR_STS_FLOAT):
@@ -290,19 +241,13 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
struct {
DBRstatus
} newSt;
- dbr_float_t *pvalue = &pold->value;
options = DBR_STATUS;
- nRequest = 0;
- status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
- options = 0;
- nRequest = no_elements;
- status = dbGetField(paddr, DBR_FLOAT, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ status = dbGetField(paddr, DBR_FLOAT, &pold->value, &zero,
+ nRequest, pfl);
}
break;
case(oldDBR_STS_ENUM):
@@ -311,19 +256,13 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
struct {
DBRstatus
} newSt;
- dbr_enum_t *pvalue = &pold->value;
options = DBR_STATUS;
- nRequest = 0;
- status = dbGetField(paddr, DBR_ENUM, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_ENUM, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
- options = 0;
- nRequest = no_elements;
- status = dbGetField(paddr, DBR_ENUM, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ status = dbGetField(paddr, DBR_ENUM, &pold->value, &zero,
+ nRequest, pfl);
}
break;
case(oldDBR_STS_CHAR):
@@ -332,19 +271,13 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
struct {
DBRstatus
} newSt;
- dbr_char_t *pvalue = &pold->value;
options = DBR_STATUS;
- nRequest = 0;
- status = dbGetField(paddr, DBR_UCHAR, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_UCHAR, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
- options = 0;
- nRequest = no_elements;
- status = dbGetField(paddr, DBR_UCHAR, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ status = dbGetField(paddr, DBR_UCHAR, &pold->value, &zero,
+ nRequest, pfl);
}
break;
case(oldDBR_STS_LONG):
@@ -353,19 +286,13 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
struct {
DBRstatus
} newSt;
- dbr_long_t *pvalue = &pold->value;
options = DBR_STATUS;
- nRequest = 0;
- status = dbGetField(paddr, DBR_LONG, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
- options = 0;
- nRequest = no_elements;
- status = dbGetField(paddr, DBR_LONG, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ status = dbGetField(paddr, DBR_LONG, &pold->value, &zero,
+ nRequest, pfl);
}
break;
case(oldDBR_STS_DOUBLE):
@@ -374,19 +301,14 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
struct {
DBRstatus
} newSt;
- dbr_double_t *pvalue = &pold->value;
options = DBR_STATUS;
- nRequest = 0;
- status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ nRequest, pfl);
}
break;
@@ -397,20 +319,15 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
DBRstatus
DBRtime
} newSt;
- DBSTRING *pvalue = (DBSTRING *)(pold->value);
options = DBR_STATUS | DBR_TIME;
- nRequest = 0;
- status = dbGetField(paddr, DBR_STRING, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_STRING, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
- nRequest = no_elements;
- status = dbGetField(paddr, DBR_STRING, pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i][0] = 0;
+ status = dbGetField(paddr, DBR_STRING, pold->value, &options,
+ nRequest, pfl);
}
break;
/* case(oldDBR_TIME_INT): */
@@ -421,20 +338,15 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
DBRstatus
DBRtime
} newSt;
- dbr_short_t *pvalue = &pold->value;
options = DBR_STATUS | DBR_TIME;
- nRequest = 0;
- status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_SHORT, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ nRequest, pfl);
}
break;
case(oldDBR_TIME_FLOAT):
@@ -444,20 +356,15 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
DBRstatus
DBRtime
} newSt;
- dbr_float_t *pvalue = &pold->value;
options = DBR_STATUS | DBR_TIME;
- nRequest = 0;
- status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_FLOAT, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ nRequest, pfl);
}
break;
case(oldDBR_TIME_ENUM):
@@ -467,20 +374,15 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
DBRstatus
DBRtime
} newSt;
- dbr_enum_t *pvalue = &pold->value;
options = DBR_STATUS | DBR_TIME;
- nRequest = 0;
- status = dbGetField(paddr, DBR_ENUM, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_ENUM, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_ENUM, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ nRequest, pfl);
}
break;
case(oldDBR_TIME_CHAR):
@@ -490,20 +392,15 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
DBRstatus
DBRtime
} newSt;
- dbr_char_t *pvalue = &pold->value;
options = DBR_STATUS | DBR_TIME;
- nRequest = 0;
- status = dbGetField(paddr, DBR_CHAR, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_CHAR, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_CHAR, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ nRequest, pfl);
}
break;
case(oldDBR_TIME_LONG):
@@ -513,20 +410,15 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
DBRstatus
DBRtime
} newSt;
- dbr_long_t *pvalue = &pold->value;
options = DBR_STATUS | DBR_TIME;
- nRequest = 0;
- status = dbGetField(paddr, DBR_LONG, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_LONG, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ nRequest, pfl);
}
break;
case(oldDBR_TIME_DOUBLE):
@@ -536,20 +428,15 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
DBRstatus
DBRtime
} newSt;
- dbr_double_t *pvalue = &pold->value;
options = DBR_STATUS | DBR_TIME;
- nRequest = 0;
- status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ nRequest, pfl);
}
break;
@@ -564,12 +451,9 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
DBRgrLong
DBRalLong
} newSt;
- dbr_short_t *pvalue = &pold->value;
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG;
- nRequest = 0;
- status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -581,10 +465,8 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
pold->lower_warning_limit = newSt.lower_warning_limit;
pold->lower_alarm_limit = newSt.lower_alarm_limit;
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_SHORT, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ nRequest, pfl);
}
break;
case(oldDBR_GR_FLOAT):
@@ -597,13 +479,10 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
DBRgrDouble
DBRalDouble
} newSt;
- dbr_float_t *pvalue = &pold->value;
options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
DBR_AL_DOUBLE;
- nRequest = 0;
- status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->precision = newSt.precision.dp;
@@ -616,10 +495,8 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
pold->upper_warning_limit = epicsConvertDoubleToFloat(newSt.upper_warning_limit);
pold->lower_warning_limit = epicsConvertDoubleToFloat(newSt.lower_warning_limit);
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_FLOAT, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ nRequest, pfl);
}
break;
/* case(oldDBR_GR_ENUM): see oldDBR_CTRL_ENUM */
@@ -632,12 +509,9 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
DBRgrLong
DBRalLong
} newSt;
- dbr_char_t *pvalue = &pold->value;
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG;
- nRequest = 0;
- status = dbGetField(paddr, DBR_UCHAR, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_UCHAR, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -649,10 +523,8 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
pold->lower_warning_limit = newSt.lower_warning_limit;
pold->lower_alarm_limit = newSt.lower_alarm_limit;
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_UCHAR, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ nRequest, pfl);
}
break;
case(oldDBR_GR_LONG):
@@ -664,12 +536,9 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
DBRgrLong
DBRalLong
} newSt;
- dbr_long_t *pvalue = &pold->value;
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG;
- nRequest = 0;
- status = dbGetField(paddr, DBR_LONG, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -681,10 +550,8 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
pold->lower_warning_limit = newSt.lower_warning_limit;
pold->lower_alarm_limit = newSt.lower_alarm_limit;
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_LONG, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ nRequest, pfl);
}
break;
case(oldDBR_GR_DOUBLE):
@@ -697,13 +564,10 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
DBRgrDouble
DBRalDouble
} newSt;
- dbr_double_t *pvalue = &pold->value;
options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
DBR_AL_DOUBLE;
- nRequest = 0;
- status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->precision = newSt.precision.dp;
@@ -716,10 +580,8 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
pold->lower_warning_limit = newSt.lower_warning_limit;
pold->lower_alarm_limit = newSt.lower_alarm_limit;
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ nRequest, pfl);
}
break;
@@ -734,13 +596,10 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
DBRctrlLong
DBRalLong
} newSt;
- dbr_short_t *pvalue = &pold->value;
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG |
DBR_AL_LONG;
- nRequest = 0;
- status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -754,10 +613,8 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_SHORT, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ nRequest, pfl);
}
break;
case(oldDBR_CTRL_FLOAT):
@@ -771,13 +628,10 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
DBRctrlDouble
DBRalDouble
} newSt;
- dbr_float_t *pvalue = &pold->value;
options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
DBR_CTRL_DOUBLE | DBR_AL_DOUBLE;
- nRequest = 0;
- status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->precision = newSt.precision.dp;
@@ -792,10 +646,8 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
pold->upper_ctrl_limit = epicsConvertDoubleToFloat(newSt.upper_ctrl_limit);
pold->lower_ctrl_limit = epicsConvertDoubleToFloat(newSt.lower_ctrl_limit);
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_FLOAT, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ nRequest, pfl);
}
break;
case(oldDBR_GR_ENUM):
@@ -807,14 +659,11 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
DBRenumStrs
} newSt;
short no_str;
- dbr_enum_t *pvalue = &pold->value;
memset(pold, '\0', sizeof(struct dbr_ctrl_enum));
/* first get status and severity */
options = DBR_STATUS | DBR_ENUM_STRS;
- nRequest = 0;
- status = dbGetField(paddr, DBR_ENUM, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_ENUM, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
no_str = newSt.no_str;
@@ -824,10 +673,8 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
strncpy(pold->strs[i], newSt.strs[i], sizeof(pold->strs[i]));
/*now get values*/
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_ENUM, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ nRequest, pfl);
}
break;
case(oldDBR_CTRL_CHAR):
@@ -840,13 +687,10 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
DBRctrlLong
DBRalLong
} newSt;
- dbr_char_t *pvalue = &pold->value;
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG |
DBR_AL_LONG;
- nRequest = 0;
- status = dbGetField(paddr, DBR_UCHAR, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_UCHAR, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -860,10 +704,8 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_UCHAR, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ nRequest, pfl);
}
break;
case(oldDBR_CTRL_LONG):
@@ -876,13 +718,10 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
DBRctrlLong
DBRalLong
} newSt;
- dbr_long_t *pvalue = &pold->value;
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG |
DBR_AL_LONG;
- nRequest = 0;
- status = dbGetField(paddr, DBR_LONG, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -896,10 +735,8 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_LONG, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ nRequest, pfl);
}
break;
case(oldDBR_CTRL_DOUBLE):
@@ -913,13 +750,10 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
DBRctrlDouble
DBRalDouble
} newSt;
- dbr_double_t *pvalue = &pold->value;
options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
DBR_CTRL_DOUBLE | DBR_AL_DOUBLE;
- nRequest = 0;
- status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->precision = newSt.precision.dp;
@@ -934,10 +768,8 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options,
- &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i] = 0;
+ nRequest, pfl);
}
break;
@@ -947,21 +779,16 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr,
struct {
DBRstatus
} newSt;
- DBSTRING *pvalue = (DBSTRING *)(pold->value);
options = DBR_STATUS;
- nRequest = 0;
- status = dbGetField(paddr, DBR_STRING, &newSt, &options, &nRequest,
- pfl);
+ status = dbGetField(paddr, DBR_STRING, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->ackt = newSt.ackt;
pold->acks = newSt.acks;
options = 0;
- nRequest = no_elements;
status = dbGetField(paddr, DBR_STRING, pold->value,
- &options, &nRequest, pfl);
- for (i = nRequest; i < no_elements; i++) pvalue[i][0] = 0;
+ &options, nRequest, pfl);
}
break;
diff --git a/src/db/db_access_routines.h b/src/db/db_access_routines.h
index 4dde58fb7..a45c24e29 100644
--- a/src/db/db_access_routines.h
+++ b/src/db/db_access_routines.h
@@ -39,6 +39,9 @@ epicsShareFunc int epicsShareAPI db_put_field(
DBADDR *paddr, int src_type,const void *psrc, int no_elements);
epicsShareFunc int epicsShareAPI db_get_field(
DBADDR *paddr, int dest_type,void *pdest, int no_elements, void *pfl);
+epicsShareFunc int db_get_field_and_count(
+ struct dbAddr *paddr, int buffer_type,
+ void *pbuffer, long *nRequest, void *pfl);
#ifdef __cplusplus
diff --git a/src/rsrv/camessage.c b/src/rsrv/camessage.c
index 984d04cfb..4888a2aa8 100644
--- a/src/rsrv/camessage.c
+++ b/src/rsrv/camessage.c
@@ -507,6 +507,9 @@ static void read_reply ( void *pArg, struct dbAddr *paddr,
const int readAccess = asCheckGet ( pciu->asClientPVT );
int status;
int v41;
+ int autosize;
+ long item_count;
+ ca_uint32_t payload_size;
SEND_LOCK ( pClient );
@@ -528,12 +531,21 @@ static void read_reply ( void *pArg, struct dbAddr *paddr,
cid = pciu->cid;
}
- status = cas_copy_in_header ( pClient, pevext->msg.m_cmmd, pevext->size,
- pevext->msg.m_dataType, pevext->msg.m_count, cid, pevext->msg.m_available,
+ /* If the client has requested a zero element count we interpret this as a
+ * request for all avaiable elements. In this case we initialise the
+ * header with the maximum element size specified by the database. */
+ autosize = pevext->msg.m_count == 0;
+ item_count =
+ autosize ? paddr->no_elements : pevext->msg.m_count;
+ payload_size = dbr_size_n(pevext->msg.m_dataType, item_count);
+ status = cas_copy_in_header(
+ pClient, pevext->msg.m_cmmd, payload_size,
+ pevext->msg.m_dataType, item_count, cid, pevext->msg.m_available,
&pPayload );
if ( status != ECA_NORMAL ) {
send_err ( &pevext->msg, status, pClient,
- "server unable to load read (or subscription update) response into protocol buffer PV=\"%s\" max bytes=%u",
+ "server unable to load read (or subscription update) response "
+ "into protocol buffer PV=\"%s\" max bytes=%u",
RECORD_NAME ( paddr ), rsrvSizeofLargeBufTCP );
if ( ! eventsRemaining )
cas_send_bs_msg ( pClient, FALSE );
@@ -552,8 +564,8 @@ static void read_reply ( void *pArg, struct dbAddr *paddr,
return;
}
- status = db_get_field ( paddr, pevext->msg.m_dataType,
- pPayload, pevext->msg.m_count, pfl);
+ status = db_get_field_and_count(
+ paddr, pevext->msg.m_dataType, pPayload, &item_count, pfl);
if ( status < 0 ) {
/*
* I cant wait to redesign this protocol from scratch!
@@ -567,58 +579,52 @@ static void read_reply ( void *pArg, struct dbAddr *paddr,
send_err ( &pevext->msg, ECA_GETFAIL, pClient, RECORD_NAME ( paddr ) );
}
else {
- /*
- * New clients recv the status of the
- * operation directly to the
+ /* New clients recv the status of the operation directly to the
* event/put/get callback.
*
- * Fetched value is set to zero in case they
- * use it even when the status indicates
- * failure.
+ * Fetched value is set to zero in case they use it even when the
+ * status indicates failure -- unless the client selected autosizing
+ * data, in which case they'd better know what they're doing!
*
- * The m_cid field in the protocol
- * header is abused to carry the status
- */
- memset ( pPayload, 0, pevext->size );
+ * The m_cid field in the protocol header is abused to carry the
+ * status */
+ if (autosize) {
+ payload_size = dbr_size_n(pevext->msg.m_dataType, 0);
+ cas_set_header_count(pClient, 0);
+ }
+ memset ( pPayload, 0, payload_size );
cas_set_header_cid ( pClient, ECA_GETFAIL );
- cas_commit_msg ( pClient, pevext->size );
+ cas_commit_msg ( pClient, payload_size );
}
}
else {
- ca_uint32_t payloadSize = pevext->size;
int cacStatus = caNetConvert (
pevext->msg.m_dataType, pPayload, pPayload,
- TRUE /* host -> net format */, pevext->msg.m_count );
- if ( cacStatus == ECA_NORMAL ) {
- /*
- * force string message size to be the true size rounded to even
- * boundary
- */
- if ( pevext->msg.m_dataType == DBR_STRING
- && pevext->msg.m_count == 1 ) {
- char * pStr = (char *) pPayload;
- size_t strcnt = strlen ( pStr );
- if ( strcnt < payloadSize ) {
- payloadSize = ( ca_uint32_t ) ( strcnt + 1u );
- }
- else {
- pStr[payloadSize-1] = '\0';
- errlogPrintf (
- "caserver: read_reply: detected DBR_STRING w/o nill termination "
- "in response from db_get_field, pPayload = \"%s\"\n",
- pStr );
- }
+ TRUE /* host -> net format */, item_count );
+ if ( cacStatus == ECA_NORMAL ) {
+ ca_uint32_t data_size =
+ dbr_size_n(pevext->msg.m_dataType, item_count);
+ if (autosize) {
+ payload_size = data_size;
+ cas_set_header_count(pClient, item_count);
}
+ else if (payload_size > data_size)
+ memset(
+ (char *) pPayload + data_size, 0, payload_size - data_size);
}
else {
- memset ( pPayload, 0, payloadSize );
+ if (autosize) {
+ payload_size = dbr_size_n(pevext->msg.m_dataType, 0);
+ cas_set_header_count(pClient, 0);
+ }
+ memset ( pPayload, 0, payload_size );
cas_set_header_cid ( pClient, cacStatus );
- }
- cas_commit_msg ( pClient, payloadSize );
+ }
+ cas_commit_msg ( pClient, payload_size );
}
/*
- * Ensures timely response for events, but does que
+ * Ensures timely response for events, but does queue
* them up like db requests when the OPI does not keep up.
*/
if ( ! eventsRemaining )
diff --git a/src/rsrv/caserverio.c b/src/rsrv/caserverio.c
index 34ead95e1..119ad5e3a 100644
--- a/src/rsrv/caserverio.c
+++ b/src/rsrv/caserverio.c
@@ -33,6 +33,10 @@
#define epicsExportSharedSymbols
#include "server.h"
+/* As an optimisation, any message allocated with a large header is resized to
+ * use a small header if the payload size is below this threshold. */
+#define SMALL_MESSAGE_THRESHOLD 65
+
/*
* cas_send_bs_msg()
*
@@ -253,6 +257,7 @@ int cas_copy_in_header (
{
unsigned msgSize;
ca_uint32_t alignedPayloadSize;
+ caHdr *pMsg;
if ( payloadSize > UINT_MAX - sizeof ( caHdr ) - 8u ) {
return ECA_TOLARGE;
@@ -292,32 +297,25 @@ int cas_copy_in_header (
}
}
- if ( alignedPayloadSize < 0xffff && nElem < 0xffff ) {
- caHdr *pMsg = ( caHdr * ) &pclient->send.buf[pclient->send.stk];
- pMsg->m_cmmd = htons ( response );
- pMsg->m_postsize = htons ( ( ( ca_uint16_t ) alignedPayloadSize ) );
- pMsg->m_dataType = htons ( dataType );
- pMsg->m_count = htons ( ( ( ca_uint16_t ) nElem ) );
- pMsg->m_cid = htonl ( cid );
- pMsg->m_available = htonl ( responseSpecific );
- if ( ppPayload ) {
- *ppPayload = ( void * ) ( pMsg + 1 );
- }
+ pMsg = (caHdr *) &pclient->send.buf[pclient->send.stk];
+ pMsg->m_cmmd = htons(response);
+ pMsg->m_dataType = htons(dataType);
+ pMsg->m_cid = htonl(cid);
+ pMsg->m_available = htonl(responseSpecific);
+ if (alignedPayloadSize < 0xffff && nElem < 0xffff) {
+ pMsg->m_postsize = htons(((ca_uint16_t) alignedPayloadSize));
+ pMsg->m_count = htons(((ca_uint16_t) nElem));
+ if (ppPayload)
+ *ppPayload = (void *) (pMsg + 1);
}
else {
- caHdr *pMsg = ( caHdr * ) &pclient->send.buf[pclient->send.stk];
- ca_uint32_t *pW32 = ( ca_uint32_t * ) ( pMsg + 1 );
- pMsg->m_cmmd = htons ( response );
- pMsg->m_postsize = htons ( 0xffff );
- pMsg->m_dataType = htons ( dataType );
- pMsg->m_count = htons ( 0u );
- pMsg->m_cid = htonl ( cid );
- pMsg->m_available = htonl ( responseSpecific );
- pW32[0] = htonl ( alignedPayloadSize );
- pW32[1] = htonl ( nElem );
- if ( ppPayload ) {
- *ppPayload = ( void * ) ( pW32 + 2 );
- }
+ ca_uint32_t *pW32 = (ca_uint32_t *) (pMsg + 1);
+ pMsg->m_postsize = htons(0xffff);
+ pMsg->m_count = htons(0u);
+ pW32[0] = htonl(alignedPayloadSize);
+ pW32[1] = htonl(nElem);
+ if (ppPayload)
+ *ppPayload = (void *) (pW32 + 2);
}
/* zero out pad bytes */
@@ -336,6 +334,22 @@ void cas_set_header_cid ( struct client *pClient, ca_uint32_t cid )
pMsg->m_cid = htonl ( cid );
}
+void cas_set_header_count (struct client *pClient, ca_uint32_t count)
+{
+ caHdr *pMsg = (caHdr *) &pClient->send.buf[pClient->send.stk];
+ if (pMsg->m_postsize == htons(0xffff)) {
+ ca_uint32_t *pLW;
+
+ assert(pMsg->m_count == 0);
+ pLW = (ca_uint32_t *) (pMsg + 1);
+ pLW[1] = htonl(count);
+ }
+ else {
+ assert(count < 65536);
+ pMsg->m_count = htons((ca_uint16_t) count);
+ }
+}
+
void cas_commit_msg ( struct client *pClient, ca_uint32_t size )
{
caHdr * pMsg = ( caHdr * ) &pClient->send.buf[pClient->send.stk];
@@ -343,8 +357,19 @@ void cas_commit_msg ( struct client *pClient, ca_uint32_t size )
if ( pMsg->m_postsize == htons ( 0xffff ) ) {
ca_uint32_t * pLW = ( ca_uint32_t * ) ( pMsg + 1 );
assert ( size <= ntohl ( *pLW ) );
- pLW[0] = htonl ( size );
- size += sizeof ( caHdr ) + 2 * sizeof ( *pLW );
+ if (size < SMALL_MESSAGE_THRESHOLD) {
+ /* If the message is sufficiently small it can be worth converting a
+ * large message header into a small header. This saves us all of 8
+ * bytes over the wire, so it's not such a big deal. */
+ pMsg->m_postsize = htons((ca_uint16_t) size);
+ pMsg->m_count = htons((ca_uint16_t) ntohl(pLW[1]));
+ memmove(pLW, pLW + 2, size);
+ size += sizeof(caHdr);
+ }
+ else {
+ pLW[0] = htonl ( size );
+ size += sizeof ( caHdr ) + 2 * sizeof ( *pLW );
+ }
}
else {
assert ( size <= ntohs ( pMsg->m_postsize ) );
diff --git a/src/rsrv/server.h b/src/rsrv/server.h
index 5fe69c638..6d1dfc38f 100644
--- a/src/rsrv/server.h
+++ b/src/rsrv/server.h
@@ -27,7 +27,7 @@
#include "asLib.h"
#include "dbAddr.h"
#include "dbNotify.h"
-#define CA_MINOR_PROTOCOL_REVISION 12
+#define CA_MINOR_PROTOCOL_REVISION 13
#include "caProto.h"
#include "ellLib.h"
#include "epicsTime.h"
@@ -226,6 +226,7 @@ int cas_copy_in_header (
ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid,
ca_uint32_t responseSpecific, void **pPayload );
void cas_set_header_cid ( struct client *pClient, ca_uint32_t );
+void cas_set_header_count (struct client *pClient, ca_uint32_t count);
void cas_commit_msg ( struct client *pClient, ca_uint32_t size );
#endif /*INCLserverh*/