Merge 3.16 into 7.0
This commit is contained in:
@@ -784,11 +784,16 @@ static void connectionCallback(struct connection_handler_args arg)
|
||||
if (pca->gotFirstConnection) {
|
||||
if (pca->nelements != ca_element_count(arg.chid) ||
|
||||
pca->dbrType != ca_field_type(arg.chid)) {
|
||||
/* BUG: We have no way to clear any old subscription with the
|
||||
* originally chosen data type/size. That will continue
|
||||
* to send us data and will result in an assert() fail.
|
||||
*/
|
||||
/* Let next dbCaGetLink and/or dbCaPutLink determine options */
|
||||
/* Size or type changed, clear everything and let the next call
|
||||
to dbCaGetLink() and/or dbCaPutLink() reset everything */
|
||||
if (pca->evidNative) {
|
||||
ca_clear_event(pca->evidNative);
|
||||
pca->evidNative = 0;
|
||||
}
|
||||
if (pca->evidString) {
|
||||
ca_clear_event(pca->evidString);
|
||||
pca->evidString = 0;
|
||||
}
|
||||
plink->value.pv_link.pvlMask &=
|
||||
~(pvlOptInpNative | pvlOptInpString |
|
||||
pvlOptOutNative | pvlOptOutString);
|
||||
@@ -1149,7 +1154,8 @@ static void dbCaTask(void *arg)
|
||||
status = ca_add_array_event(
|
||||
dbf_type_to_DBR_TIME(ca_field_type(pca->chid)),
|
||||
0, /* dynamic size */
|
||||
pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, 0);
|
||||
pca->chid, eventCallback, pca, 0.0, 0.0, 0.0,
|
||||
&pca->evidNative);
|
||||
if (status != ECA_NORMAL) {
|
||||
errlogPrintf("dbCaTask ca_add_array_event %s\n",
|
||||
ca_message(status));
|
||||
@@ -1161,7 +1167,8 @@ static void dbCaTask(void *arg)
|
||||
pca->pgetString = dbCalloc(1, MAX_STRING_SIZE);
|
||||
epicsMutexUnlock(pca->lock);
|
||||
status = ca_add_array_event(DBR_TIME_STRING, 1,
|
||||
pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, 0);
|
||||
pca->chid, eventCallback, pca, 0.0, 0.0, 0.0,
|
||||
&pca->evidString);
|
||||
if (status != ECA_NORMAL) {
|
||||
errlogPrintf("dbCaTask ca_add_array_event %s\n",
|
||||
ca_message(status));
|
||||
|
||||
@@ -80,6 +80,8 @@ typedef struct caLink
|
||||
char *pgetString;
|
||||
void *pputNative;
|
||||
char *pputString;
|
||||
evid evidNative;
|
||||
evid evidString;
|
||||
char gotInNative;
|
||||
char gotInString;
|
||||
char gotOutNative;
|
||||
|
||||
@@ -42,12 +42,13 @@
|
||||
#include "special.h"
|
||||
|
||||
#define MAXLINE 80
|
||||
#define MAXMESS 128
|
||||
struct msgBuff { /* line output structure */
|
||||
char out_buff[MAXLINE + 1];
|
||||
char *pNext;
|
||||
char *pLast;
|
||||
char *pNexTab;
|
||||
char message[128];
|
||||
char message[MAXMESS];
|
||||
};
|
||||
typedef struct msgBuff TAB_BUFFER;
|
||||
|
||||
@@ -963,7 +964,7 @@ static void printBuffer(
|
||||
int chunk = (len > MAXLINE - 5) ? MAXLINE - 5 : len;
|
||||
|
||||
sprintf(pmsg, "\"%.*s\"", chunk, (char *)pbuffer + i);
|
||||
len -= chunk;
|
||||
len -= chunk; i += chunk;
|
||||
if (len > 0)
|
||||
strcat(pmsg, " +");
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
@@ -1129,7 +1130,7 @@ static int dbpr_report(
|
||||
case DBF_DEVICE:
|
||||
status = dbFindField(pdbentry,pfield_name);
|
||||
pfield_value = dbGetString(pdbentry);
|
||||
sprintf(pmsg, "%s: %s", pfield_name,
|
||||
sprintf(pmsg, "%-4s: %s", pfield_name,
|
||||
(pfield_value ? pfield_value : "<nil>"));
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
break;
|
||||
@@ -1139,19 +1140,18 @@ static int dbpr_report(
|
||||
case DBF_FWDLINK: {
|
||||
DBLINK *plink = (DBLINK *)pfield;
|
||||
int ind;
|
||||
const char *type = "LINK";
|
||||
|
||||
status = dbFindField(pdbentry,pfield_name);
|
||||
for (ind=0; ind<LINK_NTYPES; ind++) {
|
||||
if (pamaplinkType[ind].value == plink->type)
|
||||
break;
|
||||
}
|
||||
if (ind>=LINK_NTYPES) {
|
||||
sprintf(pmsg,"%s: Illegal Link Type", pfield_name);
|
||||
}
|
||||
else {
|
||||
sprintf(pmsg,"%s:%s %s", pfield_name,
|
||||
pamaplinkType[ind].strvalue,dbGetString(pdbentry));
|
||||
}
|
||||
if (!plink->text)
|
||||
for (ind=0; ind<LINK_NTYPES; ind++) {
|
||||
if (pamaplinkType[ind].value == plink->type) {
|
||||
type = pamaplinkType[ind].strvalue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
epicsSnprintf(pmsg, MAXMESS, "%-4s: %s %s", pfield_name,
|
||||
type, dbGetString(pdbentry));
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
}
|
||||
break;
|
||||
@@ -1162,13 +1162,21 @@ static int dbpr_report(
|
||||
char time_buf[40];
|
||||
epicsTimeToStrftime(time_buf, 40, "%Y-%m-%d %H:%M:%S.%09f",
|
||||
&paddr->precord->time);
|
||||
sprintf(pmsg, "%s: %s", pfield_name, time_buf);
|
||||
sprintf(pmsg, "%-4s: %s", pfield_name, time_buf);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
}
|
||||
else if (pdbFldDes->size == sizeof(void *) &&
|
||||
strchr(pdbFldDes->extra, '*')) {
|
||||
/* Special for pointers, needed on little-endian CPUs */
|
||||
sprintf(pmsg, "%s: %p", pfield_name, *(void **)pfield);
|
||||
/* Special for pointers */
|
||||
sprintf(pmsg, "%-4s: PTR %p", pfield_name, *(void **)pfield);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
}
|
||||
else if (pdbFldDes->size == sizeof(ELLLIST) &&
|
||||
!strncmp(pdbFldDes->extra, "ELLLIST", 7)) {
|
||||
/* Special for linked lists */
|
||||
ELLLIST *plist = (ELLLIST *)pfield;
|
||||
sprintf(pmsg, "%-4s: ELL %d [%p .. %p]", pfield_name,
|
||||
ellCount(plist), ellFirst(plist), ellLast(plist));
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
}
|
||||
else { /* just print field as hex bytes */
|
||||
@@ -1184,7 +1192,7 @@ static int dbpr_report(
|
||||
value = (unsigned int)*pchar;
|
||||
sprintf(ptemp_buf, "%02x ", value);
|
||||
}
|
||||
sprintf(pmsg, "%s: %s", pfield_name,temp_buf);
|
||||
sprintf(pmsg, "%-4s: %s", pfield_name,temp_buf);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -35,4 +35,6 @@ PERL_SCRIPTS += dbExpand.pl
|
||||
PERL_SCRIPTS += dbdToHtml.pl
|
||||
PERL_SCRIPTS += registerRecordDeviceDriver.pl
|
||||
|
||||
HTMLS += dbdToHtml.html
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
@@ -44,17 +44,67 @@ BEGIN {
|
||||
}
|
||||
}
|
||||
|
||||
my $tool = 'dbdToHtml';
|
||||
use Pod::Usage;
|
||||
|
||||
our ($opt_D, @opt_I, $opt_o);
|
||||
getopts('DI@o:') or
|
||||
die "Usage: $tool [-D] [-I dir] [-o file.html] file.dbd.pod\n";
|
||||
=head1 NAME
|
||||
|
||||
dbdToHtml.pl - Convert DBD file with POD to HTML
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<dbdToHtml.pl> [B<-h>] [B<-D>] [B<-I> dir] [B<-o> file] file.dbd.pod
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Generates HTML documentation from a B<.dbd.pod> file.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
B<dbdToHtml.pl> understands the following options:
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<-h>
|
||||
|
||||
Help, display usage information.
|
||||
|
||||
=item B<-H>
|
||||
|
||||
Conversion help, display information about converting reference documentation
|
||||
from the EPICS Wiki into a B<.dbd.pod> file for use with this tool.
|
||||
|
||||
=item B<-D>
|
||||
|
||||
Instead of creating the output file as described, read the input file(s) and
|
||||
print a B<Makefile> dependency rule for the output file(s) to stdout.
|
||||
|
||||
=item B<-o> file
|
||||
|
||||
Name of the output file to be created.
|
||||
|
||||
=back
|
||||
|
||||
If no output filename is set, the file created will be named after the input
|
||||
file, removing any directory components in the path and replacing any
|
||||
B<.dbd.pod> file extension with B<.html>.
|
||||
|
||||
=cut
|
||||
|
||||
our ($opt_h, $opt_H, $opt_D, @opt_I, $opt_o);
|
||||
|
||||
my $tool = 'dbdToHtml.pl';
|
||||
|
||||
getopts('hHDI@o:') or
|
||||
pod2usage(2);
|
||||
pod2usage(-verbose => 2) if $opt_H;
|
||||
pod2usage(1) if $opt_h;
|
||||
pod2usage("$tool: No input file given.\n") if @ARGV != 1;
|
||||
|
||||
my $dbd = DBD->new();
|
||||
|
||||
my $infile = shift @ARGV;
|
||||
$infile =~ m/\.dbd.pod$/ or
|
||||
die "$tool: Input file '$infile' must have '.dbd.pod' extension\n";
|
||||
pod2usage("$tool: Input file '$infile' must have '.dbd.pod' extension.\n");
|
||||
|
||||
ParseDBD($dbd, Readfile($infile, 0, \@opt_I));
|
||||
|
||||
@@ -245,3 +295,88 @@ sub DBD::Recfield::writable {
|
||||
if $special eq "SPC_DBADDR";
|
||||
return $fld->dbf_type eq "DBF_NOACCESS" ? 'No' : 'Yes';
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
=head1 Converting Wiki Record Reference to POD
|
||||
|
||||
If you open the src/std/rec/aiRecord.dbd.pod file in your favourite plain text
|
||||
editor you'll see what input was required to generate the aiRecord.html file.
|
||||
The text markup language we're using is a standard called POD (Plain Old
|
||||
Documentation) which is used by Perl developers, but you don't need to know Perl
|
||||
at all to be able to use it.
|
||||
|
||||
When we add POD markup to a record type, we rename its *Record.dbd file to
|
||||
.dbd.pod in the src/std/rec directory; no other changes are needed for the build
|
||||
system to find it by its new name. The POD content is effectively just a new
|
||||
kind of comment that appears in .dbd.pod files, which the formatter knows how to
|
||||
convert into HTML. The build also generates a plain *Record.dbd file from this
|
||||
same input file by stripping out all of the POD markup.
|
||||
|
||||
Documentation for Perl's POD markup standard can be found online at
|
||||
L<https://perldoc.perl.org/perlpod.html> or you may be able to type 'perldoc
|
||||
perlpod' into a Linux command-line to see the same text. We added a few POD
|
||||
keywords of our own to handle the table generation, and I'll cover those briefly
|
||||
below.
|
||||
|
||||
POD text can appear almost anywhere in a dbd.pod file. It always starts with a
|
||||
line "=[keyword] [additional text...]" where [keyword] is "title", "head1"
|
||||
through "head4" etc.. The POD text ends with a line "=cut". There must be a
|
||||
blank line above every POD line, and in many cases below it as well.
|
||||
|
||||
The POD keywords we have added are "title", "recordtype", "menu", "fields",
|
||||
"type", "read" and "write". The last 3 are less common but are used in some of
|
||||
the other record types such as the waveform and aSub records.
|
||||
|
||||
The most interesting of our new keywords is "fields", which takes a list of
|
||||
record field names on the same line after the keyword and generates an HTML
|
||||
Table describing those fields based on the field description found in the DBD
|
||||
parts. In the ai documentation the first such table covers the DTYP and INP
|
||||
fields, so the line
|
||||
|
||||
=fields DTYP, INP
|
||||
|
||||
generates all this in the output:
|
||||
|
||||
<blockquote><table border="1">
|
||||
<tr>
|
||||
<th>Field</th><th>Summary</th><th>Type</th><th>DCT</th>
|
||||
<th>Default</th><th>Read</th><th>Write</th><th>CA PP</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="cell">DTYP</td><td class="cell">Device Type</td>
|
||||
<td class="cell">DEVICE</td>
|
||||
<td class="cell">Yes</td>
|
||||
<td class="cell"> </td>
|
||||
<td class="cell">Yes</td>
|
||||
<td class="cell">Yes</td>
|
||||
<td class="cell">No</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="cell">INP</td>
|
||||
<td class="cell">Input Specification</td>
|
||||
<td class="cell">INLINK</td>
|
||||
<td class="cell">Yes</td>
|
||||
<td class="cell"> </td>
|
||||
<td class="cell">Yes</td>
|
||||
<td class="cell">Yes</td>
|
||||
<td class="cell">No</td>
|
||||
</tr>
|
||||
</table></blockquote>
|
||||
|
||||
Note that the "=fields" line must appear inside the DBD's declaration of the
|
||||
record type, i.e. after the line
|
||||
|
||||
recordtype(ai) {
|
||||
|
||||
The "type", "read" and "write" POD keywords are used inside an individual record
|
||||
field declaration and provide information for the "Type", "Read" and "Write"
|
||||
columns of the field's table output for fields where this information is
|
||||
normally supplied by the record support code. Usage examples for these keywords
|
||||
can be found in the aai and aSub record types.
|
||||
|
||||
If you look at the L<aoRecord.dbd.pod> file you'll see that the POD there starts
|
||||
by documenting a record-specific menu definition. The "menu" keyword generates a
|
||||
table that lists all the choices found in the named menu.
|
||||
|
||||
=cut
|
||||
|
||||
Reference in New Issue
Block a user