From 26cb0dcbdcf7253f08aa7c6eae67a8cb552abc77 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Wed, 8 Mar 2023 15:14:13 +0000 Subject: [PATCH 01/99] EpicsHostArch: add -g option to convert a GNU arch tuplet also add Pod-based help and examples --- src/tools/EpicsHostArch.pl | 137 ++++++++++++++++++++++++++++++------- 1 file changed, 111 insertions(+), 26 deletions(-) diff --git a/src/tools/EpicsHostArch.pl b/src/tools/EpicsHostArch.pl index dee1ffe03..bb3a0250c 100644 --- a/src/tools/EpicsHostArch.pl +++ b/src/tools/EpicsHostArch.pl @@ -7,30 +7,103 @@ # in file LICENSE that is included with this distribution. #************************************************************************* -# Returns an architecture name for EPICS_HOST_ARCH that should be -# appropriate for the CPU that this version of Perl was built for. -# Any arguments to the program will be appended with separator '-' -# to allow flags like -gnu -debug and/or -static to be added. - -# Before Base has been built, use a command like this: -# bash$ export EPICS_HOST_ARCH=`perl src/tools/EpicsHostArch.pl` -# -# If Base is already built, use -# tcsh% setenv EPICS_HOST_ARCH `perl base/lib/perl/EpicsHostArch.pl` - -# If your architecture is not recognized by this script, please send -# the output from running 'perl --version' to the EPICS tech-talk -# mailing list to have it added. - use strict; +use warnings; + +use Getopt::Std; +$Getopt::Std::STANDARD_HELP_VERSION = 1; use Config; use POSIX; -print join('-', HostArch(), @ARGV), "\n"; +use FindBin qw($Bin); +use lib ("$Bin/../../lib/perl", $Bin); -sub HostArch { - my $arch = $Config{archname}; +use Pod::Usage; + +=head1 NAME + +EpicsHostArch.pl - Prints the current host architecture + +=head1 SYNOPSIS + +B [extension] + +B -g [extension] + +B -h + +=head1 DESCRIPTION + +Returns an architecture name for EPICS_HOST_ARCH that should be +appropriate for the CPU that this version of Perl was built for. +Any arguments to the program will be appended with separator '-' +to allow flags like -gnu -debug and/or -static to be added. + +Before Base has been built, use a command like this: + +C + +If Base is already built, use: + +C + +If your architecture is not recognized by this script, please send +the output from running C to the EPICS tech-talk +mailing list to have it added. + +If the C<-g> option is provided with an argument, print the EPICS +architecture corresponding to the given GNU architecture tuplet. + +=head1 OPTIONS + +B understands the following options: + +=over 4 + +=item B<-h> + +Help, display this document as text. + +=item B<-g> + +If specified, convert the given GNU architecture tuplet instead. + +=back + +=head1 EXAMPLES + +C + +C + +C + +C + +=cut + +our ($opt_h); +our ($opt_g); +$opt_h = 0; + +sub HELP_MESSAGE { + pod2usage(-exitval => 2, -verbose => $opt_h * 3); +} + +HELP_MESSAGE() if !getopts('hg:') || $opt_h; + +# Convert GNU-like architecture tuples (-) +# to EPICS terminology (-) +# +# Documentation for GNU-like terminology: +# - https://www.gnu.org/software/autoconf/manual/autoconf-2.65/html_node/System-Type.html#System-Type +# - https://git.savannah.gnu.org/cgit/config.git/tree/ +# +# Some examples from the Debian project: +# - https://wiki.debian.org/Multiarch/Tuples +sub toEpicsArch { + my $arch = shift; for ($arch) { return 'linux-x86_64' if m/^x86_64-linux/; return 'linux-x86' if m/^i[3-6]86-linux/; @@ -45,15 +118,9 @@ sub HostArch { return 'solaris-x86' if m/^i86pc-solaris/; return 'freebsd-x86_64' if m/^x86_64-freebsd/; return 'freebsd-x86_64' if m/^amd64-freebsd/; + return 'darwin-x86' if m/^x86(_64)?-darwin/; + return 'darwin-aarch64' if m/^(arm64|aarch64)-darwin/; - my ($kernel, $hostname, $release, $version, $cpu) = uname; - if (m/^darwin/) { - for ($cpu) { - return 'darwin-x86' if m/^x86_64/; - return 'darwin-aarch64' if m/^arm64/; - } - die "$0: macOS CPU type '$cpu' not recognized\n"; - } # mingw64 has 32bit and 64bit build shells which give same arch result if (m/^(x86_64|i686)-msys/) { die "$0: Architecture '$arch' is unclear,\n". @@ -64,3 +131,21 @@ sub HostArch { die "$0: Architecture '$arch' not recognized\n"; } } + +my $arch; + +if ($opt_g) { + $arch = $opt_g; +} else { + $arch = $Config{archname}; + + # On darwin, $Config{archname} returns darwin-2level, which is unusable + # so we use `uname` instead + $_ = $arch; + if (m/^darwin/) { + my ($kernel, $hostname, $release, $version, $cpu) = uname; + $arch = $cpu . "-darwin"; + } +} + +print join('-', toEpicsArch($arch), @ARGV), "\n"; From d724779abf9dbded1b2ae7472e4b827e1ea832f4 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Thu, 9 Mar 2023 09:26:20 +0000 Subject: [PATCH 02/99] EpicsHostArch: also install into bin/ folder --- src/tools/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/Makefile b/src/tools/Makefile index bb47f37a0..c40119ad3 100644 --- a/src/tools/Makefile +++ b/src/tools/Makefile @@ -22,9 +22,10 @@ PERL_MODULES += EPICS/PodXHtml.pm PERL_MODULES += Pod/Markdown.pm PERL_MODULES += URI/Escape.pm -# This goes into lib/perl, not bin/ +# This also goes into lib/perl PERL_MODULES += EpicsHostArch.pl +PERL_SCRIPTS += EpicsHostArch.pl PERL_SCRIPTS += assembleSnippets.pl PERL_SCRIPTS += convertRelease.pl PERL_SCRIPTS += cvsclean.pl From 4dd01aa9b64d5e3da1b8271a94083b89a4c32f62 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 4 Mar 2025 22:11:46 -0600 Subject: [PATCH 03/99] Updated the Event Record reference docs Now covers named events, not numbers. All links to other reference docs now work. --- .../database/src/std/rec/eventRecord.dbd.pod | 135 +++++++++++------- 1 file changed, 86 insertions(+), 49 deletions(-) diff --git a/modules/database/src/std/rec/eventRecord.dbd.pod b/modules/database/src/std/rec/eventRecord.dbd.pod index 964c3973c..131eb7b89 100644 --- a/modules/database/src/std/rec/eventRecord.dbd.pod +++ b/modules/database/src/std/rec/eventRecord.dbd.pod @@ -9,9 +9,12 @@ =title Event Record (event) -The normal use for this record type is to post an event and/or process a -forward link. Device support for this record can provide a hardware interrupt -handler routine for I/O Event-scanned records. +The normal use for this record type is to post a soft-event and/or process a +forward link. +C device support is provided to allow the soft-event name to be +read from an input link. +Hardware device support for this record can provide an interrupt handler routine +to trigger processing of the record when an I/O Intr event occurs. =head2 Parameter Fields @@ -37,18 +40,31 @@ recordtype(event) { =head3 Scan Parameters The event record has the standard fields for specifying under what circumstances -it will be processed. +it should be processed. These fields are described in L. =fields SCAN, PHAS, EVNT, PRIO, PINI -=head3 Event Number Parameters +=head3 Event Name Parameters -The VAL field contains the event number read by the device support routines. It -is this number which is posted. For records that use C device -support, it can be configured before run-time or set via dbPuts. +The VAL field is a string (prior to the Base-3.15.1 release it was a short +integer) providing the name of an IOC soft-event. +This named soft-event gets posted whenever the record is processed. -=fields VAL +When the soft-event name is known at design time, the VAL field should be set to +the name in a loaded database file. +Soft-event names do not have to be registered before use, a handle for the name +is automatically created and stored when a name is first seen and the same +handle returned later if the same name is re-used. + +The EPVT field holds the handle for the soft-event named in the VAL field. +Looking up the handle for a soft-event name is fast and uses a hash table. + +For records that use the default C device support, the soft-event +name can be fetched through the INP field link, written to the VAL field and the +handle looked up during record processing. + +=fields VAL, EPVT =cut @@ -69,16 +85,18 @@ support, it can be configured before run-time or set via dbPuts. =head3 Input Specification -The device support routines use the address in this record to obtain input. For -records that provide an interrupt handler, the INP field should specify the +The device support routines use the address in this record to obtain input. +For records that provide an interrupt handler, the INP field should specify the address of the I/O card, and the DTYP field should specify a valid device -support module. Be aware that the address format differs according to the card -type used. See L
for information on the format of hardware addresses and specifying links. For soft records, the INP field can be a constant, a database link, or a channel -access link. For soft records, the DTYP field should specify C. +access link, and the DTYP field should be empty or set to C. =fields INP, DTYP @@ -93,24 +111,27 @@ access link. For soft records, the DTYP field should specify C. =head3 Operator Display Parameters See L for more on the record name (NAME) and description (DESC) fields. - +Parameters> +for more on the record name (NAME) and description (DESC) fields. =fields NAME, DESC =head3 Alarm Parameters -The Event record has the alarm parameters common to all record types. L lists other fields related to alarms that are common to all record -types. +The Event record has the alarm parameters common to all record types. +L lists other fields related to alarms +that are common to all record types. + +=fields STAT, SEVR, AMSG, NSTA, NSEV, NAMSG, ACKS, ACKT, UDF =head3 Simulation Mode Parameters The following fields are used to operate the event record in the simulation -mode. See L for more information on these -fields. +mode. +See L +for more information on these fields. -=fields SIOL, SVAL, SIML, SIMM, SIMS +=fields SIOL, SVAL, SIML, SIMM, SIMS, SSCN, SDLY =cut @@ -179,38 +200,50 @@ initialized if SIOL is CONSTANT or PV_LINK. If device support includes C, it is called. +The string in VAL is converted to a soft-event handle in EPVT. + =head4 process See next section. +=head4 special + +When the VAL field is set, the new string is converted to a soft-event handle in +EPVT. + =head3 Record Processing -Routine process implements the following algorithm: +Routine C implements the following algorithm: =over =item 1. -readValue is called. See L for more information. +C is called. +See L for more information. =item 2. -If PACT has been changed to TRUE, the device support read routine has started -but has not completed reading a new input value. In this case, the processing -routine merely returns, leaving PACT TRUE. +If PACT has changed to TRUE, the device support read routine has started +but has not completed reading a new soft-event name. +In this case, the processing routine returns immediately, leaving PACT TRUE. =item 3. -If VAL E 0, post event number VAL. +Set PACT to TRUE. =item 4. -Check to see if monitors should be invoked. Alarm monitors are invoked if the -alarm status or severity has chanet to 0. +Post the soft-event whose handle is in EPVT. =item 5. -Scan forward link if necessary, set PACT FALSE, and return. +Check to see if monitors should be invoked. +Alarm monitors are invoked if the new alarm status or severity are non-zero. + +=item 6. + +Scan forward link if set, set PACT to FALSE, and return. =back @@ -221,13 +254,15 @@ Scan forward link if necessary, set PACT FALSE, and return. Each record must have an associated set of device support routines. The device support routines are primarily interested in the following fields: -=fields PACT, DPVT, UDF, NSEV, NSTA, INP, PRIO +=fields PACT, DPVT, UDF, NSEV, NSTA, INP, PRIO, VAL, EPVT =head3 Device Support Routines Device support consists of the following routines: -=head4 long report(int level) +=head4 report + + long report(int level) This optional routine is called by the IOC command C and is passed the report level that was requested by the user. @@ -235,9 +270,11 @@ It should print a report on the state of the device support to stdout. The C 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. +Level zero should print only a 1-line summary. -=head4 long init(int after) +=head4 init + + long init(int after) This optional routine is called twice at IOC initialization time. The first call happens before any of the C calls are made, with @@ -247,23 +284,25 @@ with C set to 1. =head4 init_record - init_record(precord) + long init_record(precord) This routine is optional. If provided, it is called by the record support C routine. =head4 get_ioint_info - get_ioint_info(int cmd, struct dbCommon *precord, IOSCANPVT *ppvt) + long get_ioint_info(int cmd, struct dbCommon *precord, IOSCANPVT *ppvt) -This routine is called by the ioEventScan system each time the record is added -or deleted from an I/O event scan list. C has the value (0,1) if the record is -being (added to, deleted from) an I/O event list. It must be provided for any -device type that can use the ioEvent scanner. +This routine is called by the dbScan system each time the record is added +or deleted from an I/O event scan list. +C has the value (0, 1) if the record is being (added to, deleted from) an +I/O event list. +The C routine is optional, but must be provided by any device +support that implements C scanning for the event record type. =head4 read_event - read_event(precord) + long read_event(precord) This routine returns the following values: @@ -281,15 +320,13 @@ Other: Error. =head3 Device Support For Soft Records -The C device support module is available. The INP link type must -be either CONSTANT, DB_LINK, or CA_LINK. +A C device support module is available. +The INP link field is used to fetch the soft-event name. -If the INP link type is CONSTANT, then the constant value is stored into VAL by -C, and UDF is set to FALSE. If the INP link type is PV_LINK, then -dbCaAddInlink is called by C. +The C routine reads a string through INP and stores it in VAL, +then looks up the named soft-event handle and sets EPVT. -C calls recGblGetLinkValue to read the current value of VAL. See -L for details on soft input. +See L for details on soft input. =cut From dc776b547a9b837dbd053d59657ddef31705075a Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 25 May 2025 23:36:37 -0500 Subject: [PATCH 04/99] Update CALC expression documentation Doxygen changes in postfix.h mostly formatting & adding tags. Rec-ref changes regrouped the operators to match the order in postfix.h, formatting, and added some missing operators. Updated description of calcout.OEVT for named events. --- .../database/src/std/rec/calcRecord.dbd.pod | 341 +++++++++------- .../src/std/rec/calcoutRecord.dbd.pod | 371 ++++++++++-------- modules/libcom/src/calc/postfix.h | 346 ++++++++-------- 3 files changed, 595 insertions(+), 463 deletions(-) diff --git a/modules/database/src/std/rec/calcRecord.dbd.pod b/modules/database/src/std/rec/calcRecord.dbd.pod index 4203555da..d46b7c525 100644 --- a/modules/database/src/std/rec/calcRecord.dbd.pod +++ b/modules/database/src/std/rec/calcRecord.dbd.pod @@ -49,31 +49,35 @@ for information on how to specify database links. =head3 Expression -At the core of the Calc record lies the CALC and RPCL fields. The CALC field -contains the infix expresion which the record routine will use when it -processes the record. The resulting value is placed in the VAL field and -can be accessed from there. The CALC expression is actually converted to -opcode and stored as Reverse Polish Notation in the RPCL field. It is this -expression which is actually used to calculate VAL. The Reverse Polish -expression is evaluated more efficiently during run-time than an infix -expression. CALC can be changed at run-time, and a special record routine -calls a function to convert it to Reverse Polish Notation. +At the core of the Calc record lies the CALC and RPCL fields. +The CALC field holds an infix expression to be evaluated whenever the record is +processed. +The calculated value is placed in the VAL field and can be accessed from there. + +The CALC expression gets compiled into a stream of Reverse Polish Notation (RPN) +opcodes for a stack-based machine, and stored in the RPCL field. +The RPN opcodes are used to calculate VAL at run-time, and are more efficient +than evaluating the infix expression. +The CALC expression can be replaced at run-time, triggering a special record +routine to compile the new expression into Reverse Polish Notation. The infix expressions that can be used are very similar to the C expression -syntax, but with some additions and subtle differences in operator meaning -and precedence. The string may contain a series of expressions separated by -a semi-colon character ";" any one of which may actually provide the -calculation result; however, all of the other expressions included must -assign their result to a variable. All alphabetic elements described below -are case independent, so upper and lower case letters may be used and mixed -in the variable and function names as desired. Spaces may be used anywhere -within an expression except between characters that make up a single -expression element. +syntax, but with some additions and subtle differences in operator meaning and +precedence. +The string may contain a series of expressions separated by a semi-colon +character C<;>, any one of which may provide the calculation result. +All other expressions included in the string must assign their result to a +variable. +All alphabetic elements described below are case independent, so upper and lower +case letters may be used and mixed in the variable and function names as +desired. +Spaces may be used anywhere within an expression except between characters that +make up a single expression element. -The range of expressions supported by the calculation record are separated -into literals, constants, operands, algebraic operators, trigonometric operators, -relational operators, logical operators, the assignment operator, -parentheses and commas, and the question mark or '?:' operator. +The range of expressions supported by the calculation record are separated into +literals, constants, operands, algebraic operators, trigonometric operators, +relational operators, logical operators, the assignment operator, parentheses +and commas, and the question mark colon or C operator. =fields CALC, RPCL @@ -127,112 +131,205 @@ The keyword VAL returns the current contents of the VAL field (which can be written to by a CA put, so it might I be the result from the last time the expression was evaluated). -=head3 Algebraic Operators +=head3 Arithmetic Operators + +Except for unary minus these are infix binary operators. =over 1 =item * -C: Absolute value (unary) +C<+> : Addition =item * -C: Square root (unary) +C<-> : Subtraction =item * -C: Minimum (any number of args) +C<-> : Minus (unary) =item * -C: Maximum (any number of args) +C<*> : Multiplication =item * -C: returns non-zero if none of the arguments are NaN or Inf (any -number of args) +C : Division =item * -C: returns non-zero if any of the arguments is NaN or Inf (any number -of args) +C<%> : Modulo =item * -C: Ceiling (unary) +C<^> : Exponential =item * -C: Floor (unary) - -=item * -C: Floating point modulo (binary) Added in 7.0.8 - -=item * -C: Log base 10 (unary) - -=item * -C: Natural log (unary) - -=item * -C: Natural log (unary) - -=item * -C: Exponential function (unary) - -=item * -C<^> : Exponential (binary) - -=item * -C<**> : Exponential (binary) - -=item * -C<+> : Addition (binary) - -=item * -C<-> : Subtraction (binary) - -=item * -C<*> : Multiplication (binary) - -=item * -C : Division (binary) - -=item * -C<%> : Modulo (binary) - -=item * -C: Negate (unary) +C<**> : Exponential =back -=head3 Trigonometric Operators +=head3 Algebraic Functions + +When functions take more than one argument, a comma separator must appear +between them. =over 1 =item * -C: Sine +C : Absolute value =item * -C: Hyperbolic sine +C : Exponential function =item * -C: Arc sine +C : Floating point modulo. Added in 7.0.8 =item * -C: Cosine +C : Natural log =item * -C: Hyperbolic cosine +C : Log base 10 =item * -C: Arc cosine +C : Natural log =item * -C: Tangent +C : Minimum =item * -C: Hyperbolic tangent +C : Maximum =item * -C: Arc tangent +C : Square root + +=item * +C : Square root + +=back + +=head3 Trigonometric Functions + +=over 1 + +=item * +C : Sine + +=item * +C : Arc sine + +=item * +C : Cosine + +=item * +C : Arc cosine + +=item * +C : Tangent + +=item * +C : Arc tangent + +=item * +C : 2-parameter Arc tangent. Arg's are reversed to ANSI C + +=back + +=head3 Hyperbolic Trigonometry Functions + +=over 1 + +=item * +C : Hyperbolic sine + +=item * +C : Hyperbolic cosine + +=item * +C : Hyperbolic tangent + +=back + +=head3 Numeric Functions + +=over 1 + +=item * +C : Ceiling + +=item * +C : Floor + +=item * +C : Round to nearest integer + +=item * +C : returns non-zero if any argument is Inf + +=item * +C : returns non-zero (true) if any argument is NaN +or Inf + +=item * +C : returns non-zero (true) if none of the +arguments are NaN or Inf + +=back + +=head3 Boolean/Logical Operators + +These operators use their arguments as a true (non-zero) or false (zero) value. + +=over 1 + +=item * +C<&&> : And, infix binary + +=item * +C<||> : Or, infix binary + +=item * +C : Not, unary prefix + +=back + +=head3 Bitwise Operators + +Mostly infix binary, the arguments are converted to a 32-bit integer, the +operator is applied, and the result converted back into a double. + +=over 1 + +=item * +C<&> : Bitwise and + +=item * +C<|> : Bitwise or + +=item * +C<~> : Bitwise not or one's complement, unary prefix + +=item * +C<<< << >>> : Arithmetic shift left + +=item * +C<<< >> >>> : Arithmetic shift right + +=item * +C<<<< >>> >>>> : Logical shift right + +=item * +C : Bitwise and + +=item * +C : Bitwise or + +=item * +C : Bitwise exclusive or + +=item * +C : Bitwise not or one's complement, unary prefix =back =head3 Relational Operators +These are all infix binary operators. + =over 1 =item * @@ -247,59 +344,17 @@ C<<< <= >>> : Less than or equal to =item * C<<< < >>> : Less than +=item * +C<<< != >>> : Not equal to + =item * C<<< # >>> : Not equal to =item * -C<<< = >>> : Equal to - -=back - -=head3 Logical Operators - -=over 1 +C<<< == >>> : Equal to =item * -C<&&> : And - -=item * -C<||> : Or - -=item * -C : Not - -=back - -=head3 Bitwise Operators - -=over 1 - -=item * -C<|> : Bitwise Or - -=item * -C<&> : Bitwise And - -=item * -C : Bitwise Or - -=item * -C : Bitwise And - -=item * -C : Bitwise Exclusive Or - -=item * -C<~> : One's Complement - -=item * -C<<< << >>> : Arithmetic Left Shift - -=item * -C<<< >> >>> : Arithmetic Right Shift - -=item * -C<<<< >>> >>>> : Logical Right Shift +C<<< = >>> : Equal to (not assignment) =back @@ -314,22 +369,24 @@ C<:=> : assigns a value (right hand side) to a variable (i.e. field) =head3 Parantheses, Comma, and Semicolon -The open and close parentheses are supported. Nested parentheses are -supported. +The open C<(> and close parentheses C<)> are supported to override precedence +rules in a sub-expression. +Nested parentheses are supported to significant depth. -The comma is supported when used to separate the arguments of a binary -function. +The comma C<,> is required to separate the arguments of a function. -The semicolon is used to separate expressions. Although only one -traditional calculation expression is allowed, multiple assignment -expressions are allowed. +The semicolon C<;> is used to value separate expressions. +Exactly one value expression must be present, but multiple assignment +expressions may be included before and/or after the value expression. -=head3 Conditional Expression +=head3 Conditional Operator -The C language's question mark operator is supported. The format is: -C +The C language's question mark colon C ternary operator is supported. +The format is: -=head3 Expression Examples +I C I C<:> I + +=head2 Expression Examples =head3 Algebraic @@ -370,8 +427,8 @@ Result is C if C<<< (A + B) >= (C + D) >>> =back -Prior to Base 3.14.9 it was legal to omit the : and the second (else) part -of the conditional, like this: +Prior to Base 3.14.9 it was legal to omit the colon C<:> and the second (else) +part of the conditional, like this: C<<< (A + B)<(C + D) ? E >>> diff --git a/modules/database/src/std/rec/calcoutRecord.dbd.pod b/modules/database/src/std/rec/calcoutRecord.dbd.pod index 3e1dbd4a9..d427d28f2 100644 --- a/modules/database/src/std/rec/calcoutRecord.dbd.pod +++ b/modules/database/src/std/rec/calcoutRecord.dbd.pod @@ -70,38 +70,45 @@ fields. =fields INPA - INPL -=head3 Expression +=head3 Expressions -Like the Calc record, the Calcout record has a CALC field in which the -developer can enter an infix expression which the record routine will -evaluate when it processes the record. The resulting value is placed in the -VAL field. This value can then be used by the OOPT field (see -L) to determine whether or not to write to the output -link or post an output event. It can also be the value that is written to -the output link. The CALC expression is actually converted to opcode and -stored in Reverse Polish Notation in the RPCL field. It is this expression -which is actually used to calculate VAL. The Reverse Polish expression is -evaluated more efficiently during run-time than an infix expression. CALC -can be changes at run-time, and a special record routine will call a -function to convert it to Reverse Polish Notation. +Like the Calc record, the Calcout record's CALC field holds an infix expression +to be evaluated whenever the record is processed. +The resulting value is placed in the VAL field. + +The OOPT field condition is applied to VAL (see L) and +controls whether to write to the output link (or post a named event), and the +DOPT field selects whether VAL should be written, or another expression from +the OCAL field should be evaluated and used instead. + +The CALC and OCAL expressions get compiled into streams of Reverse Polish +Notation (RPN) opcodes for a stack-based machine, and stored in the RPCL and +ORPC fields respectively. + +The RPN opcodes are used to calculate VAL at run-time, and are more efficient +than evaluating the infix expression. +The CALC and OCAL expressions can be replaced at run-time, triggering a special +record routine to compile the new expression into Reverse Polish Notation. The infix expressions that can be used are very similar to the C expression -syntax, but with some additions and subtle differences in operator meaning -and precedence. The string may contain a series of expressions separated by -a semi-colon character ';' any one of which may actually provide the -calculation result; however all of the other expressions included must -assign their result to a variable. All alphabetic elements described below -are case independent, so upper and lower case letters may be used and mixed -in the variable and function names as desired. Spaces may be used anywhere -within an expression except between the characters that make up a single -expression element. +syntax, but with some additions and subtle differences in operator meaning and +precedence. +The string may contain a series of expressions separated by a semi-colon +character C<;>, any one of which may provide the calculation result. +All other expressions included in the string must assign their result to a +variable. +All alphabetic elements described below are case independent, so upper and lower +case letters may be used and mixed in the variable and function names as +desired. +Spaces may be used anywhere within an expression except between characters that +make up a single expression element. The range of expressions supported by the calculation record are separated into literals, constants, operands, algebraic operators, trigonometric operators, -relational operators, logical operator, the assignment operator, -parentheses and commas, and the question mark or '?:' operator. +relational operators, logical operators, the assignment operator, parentheses +and commas, and the question mark colon or C operator. -=fields CALC, VAL, RPCL +=fields CALC, VAL, OVAL, RPCL, ORPC =head3 Literals @@ -153,112 +160,205 @@ field, i.e. the VAL field for the CALC expression and the OVAL field for the OCAL expression. (These fields can be written to by CA put, so it might I be the result from the last time the expression was evaluated). -=head3 Algebraic Operations +=head3 Arithmetic Operators + +Except for unary minus these are infix binary operators. =over 1 =item * -C: Absolute value (unary) +C<+> : Addition =item * -C: Square root (unary) +C<-> : Subtraction =item * -C: Minimum (any number of args) +C<-> : Minus (unary) =item * -C: Maximum (any number of args) +C<*> : Multiplication =item * -C: returns non-zero if none of the arguments are NaN or Inf (any -number of args) +C : Division =item * -C: returns non-zero if any of the arguments is NaN or Inf (any number -of args) +C<%> : Modulo =item * -C: Ceiling (unary) +C<^> : Exponential =item * -C: Floor (unary) - -=item * -C: Floating point modulo (binary) **Added in 7.0.8** - -=item * -C: Log base 10 (unary) - -=item * -C: Natural log (unary) - -=item * -C: Natural log (unary) - -=item * -C: Exponential function (unary) - -=item * -C<^> : Exponential (binary) - -=item * -C<**> : Exponential (binary) - -=item * -C<+> : Addition (binary) - -=item * -C<-> : Subtraction (binary) - -=item * -C<*> : Multiplication (binary) - -=item * -C : Division (binary) - -=item * -C<%> : Modulo (binary) - -=item * -C: Negate (unary) +C<**> : Exponential =back -=head3 Trigonometric Operators +=head3 Algebraic Functions + +When functions take more than one argument, a comma separator must appear +between them. =over 1 =item * -C: Sine +C : Absolute value =item * -C: Hyperbolic sine +C : Exponential function =item * -C: Arc sine +C : Floating point modulo. Added in 7.0.8 =item * -C: Cosine +C : Natural log =item * -C: Hyperbolic cosine +C : Log base 10 =item * -C: Arc cosine +C : Natural log =item * -C: Tangent +C : Minimum =item * -C: Hyperbolic tangent +C : Maximum =item * -C: Arc tangent +C : Square root + +=item * +C : Square root + +=back + +=head3 Trigonometric Functions + +=over 1 + +=item * +C : Sine + +=item * +C : Arc sine + +=item * +C : Cosine + +=item * +C : Arc cosine + +=item * +C : Tangent + +=item * +C : Arc tangent + +=item * +C : 2-parameter Arc tangent. Arg's are reversed to ANSI C + +=back + +=head3 Hyperbolic Trigonometry Functions + +=over 1 + +=item * +C : Hyperbolic sine + +=item * +C : Hyperbolic cosine + +=item * +C : Hyperbolic tangent + +=back + +=head3 Numeric Functions + +=over 1 + +=item * +C : Ceiling + +=item * +C : Floor + +=item * +C : Round to nearest integer + +=item * +C : returns non-zero if any argument is Inf + +=item * +C : returns non-zero (true) if any argument is NaN +or Inf + +=item * +C : returns non-zero (true) if none of the +arguments are NaN or Inf + +=back + +=head3 Boolean/Logical Operators + +These operators use their arguments as a true (non-zero) or false (zero) value. + +=over 1 + +=item * +C<&&> : And, infix binary + +=item * +C<||> : Or, infix binary + +=item * +C : Not, unary prefix + +=back + +=head3 Bitwise Operators + +Mostly infix binary, the arguments are converted to a 32-bit integer, the +operator is applied, and the result converted back into a double. + +=over 1 + +=item * +C<&> : Bitwise and + +=item * +C<|> : Bitwise or + +=item * +C<~> : Bitwise not or one's complement, unary prefix + +=item * +C<<< << >>> : Arithmetic shift left + +=item * +C<<< >> >>> : Arithmetic shift right + +=item * +C<<<< >>> >>>> : Logical shift right + +=item * +C : Bitwise and + +=item * +C : Bitwise or + +=item * +C : Bitwise exclusive or + +=item * +C : Bitwise not or one's complement, unary prefix =back =head3 Relational Operators +These are all infix binary operators. + =over 1 =item * @@ -273,59 +373,17 @@ C<<< <= >>> : Less than or equal to =item * C<<< < >>> : Less than +=item * +C<<< != >>> : Not equal to + =item * C<<< # >>> : Not equal to =item * -C<<< = >>> : Equal to - -=back - -=head3 Logical Operators - -=over 1 +C<<< == >>> : Equal to =item * -C< && >: And - -=item * -C<||> : Or - -=item * -C : Not - -=back - -=head3 Bitwise Operators - -=over 1 - -=item * -C<|> : Bitwise Or - -=item * -C<&> : Bitwise And - -=item * -C : Bitwise Or - -=item * -C : Bitwise And - -=item * -C : Bitwise Exclusive Or - -=item * -C<~> : One's Complement - -=item * -C<<< << >>> : Arithmetic Left Shift - -=item * -C<<< >> >>> : Arithmetic Right Shift - -=item * -C<<<< >>> >>>> : Logical Right Shift +C<<< = >>> : Equal to (not assignment) =back @@ -338,24 +396,26 @@ C<:=> : assigns a value (right hand side) to a variable (i.e. field) =back -=head3 Parentheses, Comma, and Semicolon +=head3 Parantheses, Comma, and Semicolon -The open and close parentheses are supported. Nested parentheses are -supported. +The open C<(> and close parentheses C<)> are supported to override precedence +rules in a sub-expression. +Nested parentheses are supported to significant depth. -The comma is supported when used to separate the arguments of a binary -function. +The comma C<,> is required to separate the arguments of a function. -The semicolon is used to separate expressions. Although only one -traditional calculation expression is allowed, multiple assignment -expressions are allowed. +The semicolon C<;> is used to value separate expressions. +Exactly one value expression must be present, but multiple assignment +expressions may be included before and/or after the value expression. -=head3 Conditional Expression +=head3 Conditional Operator -The C language's question mark operator is supported. The format is: -C +The C language's question mark colon C ternary operator is supported. +The format is: -=head3 Expression Examples +I C I C<:> I + +=head2 Expression Examples =head3 Algebraic @@ -396,8 +456,8 @@ Result is C if C<<< (A + B) >= (C + D) >>> =back -Prior to Base 3.14.9 it was legal to omit the : and the second (else) part -of the conditional, like this: +Prior to Base 3.14.9 it was legal to omit the colon C<:> and the second (else) +part of the conditional, like this: C<<< (A + B)<(C + D) ? E >>> @@ -511,13 +571,14 @@ necessary, the record can use the result of the CALC expression to determine if data should be written and can use the result of the OCAL expression as the data to write. -If the OEVT field specifies a non-zero integer and the condition in the -OOPT field is met, the record will post a corresponding event. If the ODLY -field is non-zero, the record pauses for the specified number of seconds -before executing the OUT link or posting the output event. During this -waiting period the record is "active" and will not be processed again until -the wait is over. The field DLYA is equal to 1 during the delay period. The -resolution of the delay entry system dependent. +If the OEVT field isn't empty and the condition in the OOPT field is met, the +record will post the corresponding named event. +If the ODLY field is non-zero, the record pauses for the specified number of +seconds before executing the OUT link or posting the output event. +During this waiting period the record is "active" and will not be processed +again until the wait is over. +The field DLYA is equal to 1 during the delay period. The resolution of the +delay entry system dependent. The IVOA field specifies what action to take with the OUT link if the Calcout record enters an INVALID alarm status. The options are @@ -1277,8 +1338,8 @@ field IVOA. =item 3. The Alarm Severity is not INVALID or IVOA specifies "Continue Normally", -put the value of OVAL to the OUT link and post the event in OEVT (if -non-zero). +put the value of OVAL to the OUT link and post the event named in OEVT (if +not empty). =item 4. diff --git a/modules/libcom/src/calc/postfix.h b/modules/libcom/src/calc/postfix.h index 5d75a7ddf..07d9d6847 100644 --- a/modules/libcom/src/calc/postfix.h +++ b/modules/libcom/src/calc/postfix.h @@ -41,20 +41,18 @@ * few bytes smaller for some sizes. * * The maximum expansion from infix to postfix is for the sub-expression - \code - .1?.1: -\endcode - * which is 6 characters long and results in 21 bytes of postfix: -\code + * .1?.1: which is 6 characters long and results in 21 bytes of + * postfix: +\verbatim .1 => LITERAL_DOUBLE + 8 byte value ? => COND_IF .1 => LITERAL_DOUBLE + 8 byte value : => COND_ELSE ... => COND_END -\endcode +\endverbatim * For other short expressions the factor 21/6 always gives a big enough - * postfix buffer (proven by hand, look at '1+' and '.1+' as well). + * postfix buffer (proven by hand, look at \c 1+ and .1+ as well). */ #define INFIX_TO_POSTFIX_SIZE(n) ((n)*21/6) @@ -115,193 +113,207 @@ extern "C" { /** \brief Compile an infix expression into postfix byte-code * - * Converts an expression from an infix string to postfix byte-code + * Converts an expression from an infix string to postfix byte-code. * * \param pinfix Pointer to the infix string * \param ppostfix Pointer to the postfix buffer * \param perror Place to return an error code * \return Non-zero value in event of error * - * It is the caller's responsibility to ensure that \c ppostfix points - * to sufficient storage to hold the postfix expression. The macro - * INFIX_TO_POSTFIX_SIZE(n) can be used to calculate an appropriate - * postfix buffer size from the length of the infix buffer. + * It is the caller's responsibility to ensure that \p ppostfix points to + * sufficient storage to hold the postfix expression. + * The macro INFIX_TO_POSTFIX_SIZE(n) can be used to calculate an + * appropriate postfix buffer size from the length of the infix buffer. + * The macro's parameter \p n must count the terminating nil byte too. * - * \note "n" must count the terminating nil byte too. + * -# The **infix expressions** that can be used are very similar to the + * C expression syntax, but with some additions and subtle differences in + * operator meaning and precedence. + * The expression string may contain a series of expressions separated by + * a semi-colon character ; any one of which may actually provide + * the calculation result. + * However all of the other expressions included must assign their result + * to a variable. + * All alphabetic elements described below are case independent, so upper + * and lower case letters may be used and mixed in the variable and + * function names as desired. + * Spaces may be used anywhere within an expression except between the + * characters that make up a single expression element. + + * -# The simplest expression element is a **numeric literal,** any + * (positive) number expressed using the standard floating point syntax + * that can be stored as a double precision value. + * This now includes the values Infinity and NaN (not a number). + * Note that negative numbers will be encoded as a positive literal, to + * which the unary negate operator is applied. * - * -# The **infix expressions** that can be used are very similar - * to the C expression syntax, but with some additions and subtle - * differences in operator meaning and precedence. The string may - * contain a series of expressions separated by a semi-colon character ';' - * any one of which may actually provide the calculation result; however - * all of the other expressions included must assign their result to - * a variable. All alphabetic elements described below are case independent, - * so upper and lower case letters may be used and mixed in the variable - * and function names as desired. Spaces may be used anywhere within an - * expression except between the characters that make up a single expression element. + * Examples: + * - \c 1 + * - \c 2.718281828459 + * - \c Inf * - * -# ***Numeric Literals*** - * The simplest expression element is a numeric literal, any (positive) - * number expressed using the standard floating point syntax that can be stored - * as a double precision value. This now includes the values Infinity and - * NaN (not a number). Note that negative numbers will be encoded as a - * positive literal to which the unary negate operator is applied. - * - * - Examples: - * - 1 - * - 2.718281828459 - * - Inf - * - * -# ***Constants*** - * There are three trigonometric constants available to any expression + * -# There are three **trigonometric constants** available to any expression * which return a value: - * - pi returns the value of the mathematical constant pi. - * - D2R evaluates to pi/180 which, when used as a multiplier, - * converts an angle from degrees to radians. - * - R2D evaluates to 180/pi which as a multiplier converts an angle - * from radians to degrees. + * - \c pi returns the value of the mathematical constant pi. + * - \c D2R evaluates to pi/180 which, when used as a multiplier, + * converts an angle from degrees to radians. + * - \c R2D evaluates to 180/pi which as a multiplier converts an + * angle from radians to degrees. * - * -# ***Variables*** - * Variables are used to provide inputs to an expression, and are named - * using the single letters A through L inclusive or the keyword VAL which - * refers to the previous result of this calculation. The software that - * makes use of the expression evaluation code should document how the - * individual variables are given values; for the calc record type the input - * links INPA through INPL can be used to obtain these from other record fields, - * and VAL refers to the the VAL field (which can be overwritten from outside - * the record via Channel Access or a database link). + * -# **Variables** are used to provide inputs to an expression, and are + * named using the single letters \c A through \c L inclusive or the + * keyword \c VAL which refers to the previous result of this + * calculation. + * The software that makes use of the expression evaluation code should + * document how the individual variables are given values. + * For the calc and calcout record types the input links \c INPA through + * \c INPL can be used to obtain values from other record fields, and \c + * VAL refers to the the VAL field (which can be overwritten from + * outside the record via Channel Access or a database link). * - * -# ***Variable Assignment Operator*** - * Recently added is the ability to assign the result of a sub-expression to - * any of the single letter variables, which can then be used in another - * sub-expression. The variable assignment operator is the character pair - * := and must immediately follow the name of the variable to receive the - * expression value. Since the infix string must return exactly one value, every + * -# The **Variable Assignment Operator** was added in 3.14.9 and + * provides the ability to assign the result of a sub-expression to any + * of the single letter variables, which can then be used in later + * sub-expressions. + * The variable assignment operator is the character pair := and + * must immediately follow the name of the variable to receive the + * expression value. + * Since the infix string must return exactly one value, every * expression string must have exactly one sub-expression that is not an - * assignment, which can appear anywhere in the string. Sub-expressions within - * the string are separated by a semi-colon character. + * assignment, which can appear anywhere in the string. + * Sub-expressions within the string are separated by a semi-colon + * character ; . * - * - Examples: - * - B; B:=A - * - i:=i+1; a*sin(i*D2R) + * Examples: + * - B; B:=A + * - i:=i+1; a*sin(i*D2R) * - * -# ***Arithmetic Operators*** - * The usual binary arithmetic operators are provided: + - * and / with their - * usual relative precedence and left-to-right associativity, and - may also - * be used as a unary negate operator where it has a higher precedence and - * associates from right to left. There is no unary plus operator, so numeric - * literals cannot begin with a + sign. + * -# The standard binary **Arithmetic Operators** are provided: + - + * * and \c / with their usual relative precedence and + * left-to-right associativity. + * A minus sign \c - may also be used as a unary negate operator where + * it has a higher precedence and associates from right to left. + * There is no unary plus operator, so numeric literals cannot begin + * with a plus sign \c + . * - * - Examples: - * - a*b + c - * - a/-4 - b + * Examples: + * - a*b + c + * - a/-4 - b * - * Three other binary operators are also provided: % is the integer modulo operator, - * while the synonymous operators ** and ^ raise their left operand to the power of - * the right operand. % has the same precedence and associativity as * and /, while - * the power operators associate left-to-right and have a precedence in between * and - * unary minus. + * Three other binary operators are also provided: + * \c % is the integer modulo operator, while the synonymous operators + * \c ** and \c ^ raise their left operand to the power of the right + * operand. + * \c % has the same precedence and associativity as \c * and \c /, + * while the power operators associate left-to-right and have a + * precedence in between \c * and unary minus \c - . * - * - Examples: - * - e:=a%10 - * - d:=a/10%10 - * - c:=a/100%10 - * - b:=a/1000%10 - * - b*4096+c*256+d*16+e - * - sqrt(a**2 + b**2) + * Examples: + * - e:=a%10 + * - d:=a/10%10 + * - c:=a/100%10 + * - b:=a/1000%10 + * - b*4096+c*256+d*16+e + * - sqrt(a**2 + b**2) * - * -# ***Algebraic Functions*** - * Various algebraic functions are available which take parameters inside - * parentheses. The parameter separator is a comma. + * -# Various **Algebraic Functions** are available which take parameters + * inside parentheses. + * The parameter separator is a comma , . * - * - Absolute value: abs(a) - * - Exponential ea: exp(a) - * - Logarithm, base 10: log(a) - * - Natural logarithm (base e): ln(a) or loge(a) - * - n parameter maximum value: max(a, b, ...) - * - n parameter minimum value: min(a, b, ...) - * - Square root: sqr(a) or sqrt(a) - * - Floating point modulo: fmod(num, den) - * \since The fmod() function was added in 7.0.8 + * - Absolute value: \c abs(a) + * - Exponential ea: \c exp(a) + * - Logarithm, base 10: \c log(a) + * - Natural logarithm (base e): \c ln(a) or \c loge(a) + * - n parameter maximum value: max(a, b, ...) + * - n parameter minimum value: min(a, b, ...) + * - Square root: \c sqr(a) or \c sqrt(a) + * - Floating point modulo: fmod(num, den) + *
The \c fmod() function was added in 7.0.8 * - * -# ***Trigonometric Functions*** - * Standard circular trigonometric functions, with angles expressed in radians: - * - Sine: sin(a) - * - Cosine: cos(a) - * - Tangent: tan(a) - * - Arcsine: asin(a) - * - Arccosine: acos(a) - * - Arctangent: atan(a) - * - 2 parameter arctangent: atan2(a, b) - * \note Note that these arguments are the reverse of the ANSI C function, - * so while C would return arctan(a/b) the calc expression engine returns arctan(b/a) + * -# Standard circular **Trigonometric Functions** exist with angles + * expressed in radians: * - * -# ***Hyperbolic Trigonometry*** - * The basic hyperbolic functions are provided, but no inverse functions - * (which are not provided by the ANSI C math library either). - * - Hyperbolic sine: sinh(a) - * - Hyperbolic cosine: cosh(a) - * - Hyperbolic tangent: tanh(a) + * - Sine: \c sin(a) + * - Cosine: \c cos(a) + * - Tangent: \c tan(a) + * - Arcsine: \c asin(a) + * - Arccosine: \c acos(a) + * - Arctangent: \c atan(a) + * - 2 parameter arctangent: atan2(a, b) + *
Note that the \c atan2 arguments are the reverse of the ANSI C + * function, so while C would return \c arctan(a/b) the calc + * expression engine returns \c arctan(b/a) * - * -# ***Numeric Functions*** - * The numeric functions perform operations related to the floating point - * numeric representation and truncation or rounding. - * - Round up to next integer: ceil(a) - * - Round down to next integer: floor(a) - * - Round to nearest integer: nint(a) - * - Test for infinite result: isinf(a) - * - Test for any non-numeric values: isnan(a, ...) - * - Test for all finite, numeric values: finite(a, ...) - * - Random number between 0 and 1: rndm + * -# The basic **Hyperbolic Trigonometry** functions are provided, but + * no inverse functions (which aren't provided by the ANSI C math + * library either). * - * -# ***Boolean Operators*** - * These operators regard their arguments as true or false, where 0.0 is - * false and any other value is true. + * - Hyperbolic sine: \c sinh(a) + * - Hyperbolic cosine: \c cosh(a) + * - Hyperbolic tangent: \c tanh(a) * - * - Boolean and: a && b - * - Boolean or: a || b - * - Boolean not: !a + * -# These **Numeric Functions** perform operations related to the + * floating point numeric representation and truncation or rounding. * - * -# ***Bitwise Operators*** - * Most bitwise operators convert their arguments to 32-bit signed integer (by - * truncation), perform the appropriate bitwise operation, then convert back - * to a floating point value. The arithmetic right shift operator >> thus - * retains the sign bit of the left-hand argument. The logical right shift - * operator >>> is performed on an unsigned integer though, so injects zeros - * while shifting. The right-hand shift argument is masked so only the lower - * 5 bits are used. Unlike in C, ^ is not a bitwise exclusive-or operator. + * - Round up to next integer: \c ceil(a) + * - Round down to next integer: \c floor(a) + * - Round to nearest integer: \c nint(a) + * - Test for infinite result: \c isinf(a) + * - Test for any non-numeric values: isnan(a, ...) + * - Test for all finite, numeric values: finite(a, ...) + * - Random number between 0 and 1: \c rndm * - * - Bitwise and: a & b or a and b - * - Bitwise or: a | b or a or b - * - Bitwise exclusive or: a xor b - * - Bitwise not (ones complement): ~a or not a - * - Arithmetic left shift: a << b - * - Arithmetic right shift: a >> b - * - Logical right shift: a >>> b + * -# The **Boolean Operators** evaluate their arguments as true or + * false, where \c 0.0 is false and any other value is true. * - * -# ***Relational Operators*** - * Standard numeric comparisons between two values: + * - Boolean and: a && b + * - Boolean or: a || b + * - Boolean not: \c !a * - * - Less than: a < b - * - Less than or equal to: a <= b - * - Equal to: a = b or a == b - * - Greater than or equal to: a >= b - * - Greater than: a > b - * - Not equal to: a != b or a # b + * -# Most **Bitwise Operators** convert their arguments to 32-bit signed + * integer (by truncation), perform the appropriate bitwise operation, + * then convert back to a floating point value. + * The arithmetic right shift operator \c >> thus retains the sign bit of + * the left-hand argument. + * The logical right shift operator \c >>> is performed on an unsigned + * integer though, so it injects zeros while shifting. + * The right-hand shift argument is masked so only the lower 5 bits are + * used. + * Unlike in C, \c ^ is not a bitwise exclusive-or operator. * - * -# ***Conditional Operator*** - * Expressions can use the C conditional operator, which has a lower - * precedence than all of the other operators except for the assignment operator. + * - Bitwise and: a & b or a and b + * - Bitwise or: a | b or a or b + * - Bitwise exclusive or: a xor b + * - Bitwise not (ones complement): ~a or not a + * - Arithmetic left shift: a << b + * - Arithmetic right shift: a >> b + * - Logical right shift: a >>> b * - * - condition ? true result : false result - * - Example: - * - a < 360 ? a+1 : 0 + * -# The **Relational Operators** perform numeric comparisons between + * two double-precision values: * - * -# ***Parentheses*** - * Sub-expressions can be placed within parentheses to override operator presence rules. - * Parentheses can be nested to any depth, but the intermediate value stack used by - * the expression evaluation engine is limited to 80 results (which require an - * expression at least 321 characters long to reach). + * - Less than: a < b + * - Less than or equal to: a <= b + * - Equal to: a = b or a == b + * - Greater than or equal to: a >= b + * - Greater than: a > b + * - Not equal to: a != b or a # b + * + * -# Expressions can use the C **Conditional Operator**, which has a + * lower precedence than all of the other operators except for the + * assignment operator. + * + * - \a condition ? \a true-expression : + * \a false-expression + * - Example: + * a < 360 ? a+1 : 0 + * + * -# Sub-expressions can be placed within **Parentheses** () + * to override operator presence rules. + * Parentheses can be nested to any depth, but the intermediate value + * stack used by the expression evaluation engine is limited to 80 + * results (which takes an expression at least 321 characters long to + * reach). */ LIBCOM_API long postfix(const char *pinfix, char *ppostfix, short *perror); @@ -310,10 +322,12 @@ LIBCOM_API long * * Evaluates the postfix expression against a set ot input values. * - * \param parg Pointer to an array of double values for the arguments A-L - * that can appear in the expression. Note that the argument values may be - * modified if the expression uses the assignment operator. - * \param presult Where to put the calculated result, which may be a NaN or Infinity. + * \param parg Pointer to an array of double values for the arguments + * \c A-L that can appear in the expression. + * Note that the argument values may be modified if the expression uses + * the assignment operator. + * \param presult Where to put the calculated result, which may be a NaN + * or Infinity. * \param ppostfix The postfix expression created by postfix(). * \return Status value 0 for OK, or non-zero if an error is discovered * during the evaluation process. From 38c11d3afeb442f7ae57733981f1524c128a208b Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 17 May 2025 16:13:22 -0500 Subject: [PATCH 05/99] Spellcheck, and added a Release Note entry --- documentation/new-notes/PR-651.md | 12 ++++++++++++ modules/database/src/std/rec/calcRecord.dbd.pod | 2 +- modules/database/src/std/rec/calcoutRecord.dbd.pod | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 documentation/new-notes/PR-651.md diff --git a/documentation/new-notes/PR-651.md b/documentation/new-notes/PR-651.md new file mode 100644 index 000000000..019dc07d7 --- /dev/null +++ b/documentation/new-notes/PR-651.md @@ -0,0 +1,12 @@ +### Documentation Updates + +The reference documentation for the [event](eventRecord.html) record type +has been updated to cover the use of named events which were added in Base +3.14.12.3 and 3.15.1. + +Documentation for CALC expression evaluation has been updated for format +enhancements and to add some missing operators. +The best documentation for these expressions can be found in the +[postfix.h](postfix_h.html) header in libCom, but both the +[calc](calcRecord.html) and [calcout](calcoutRecord.html) record reference +pages also cover the infix expressions supported. diff --git a/modules/database/src/std/rec/calcRecord.dbd.pod b/modules/database/src/std/rec/calcRecord.dbd.pod index d46b7c525..798b06bb5 100644 --- a/modules/database/src/std/rec/calcRecord.dbd.pod +++ b/modules/database/src/std/rec/calcRecord.dbd.pod @@ -367,7 +367,7 @@ C<:=> : assigns a value (right hand side) to a variable (i.e. field) =back -=head3 Parantheses, Comma, and Semicolon +=head3 Parentheses, Comma, and Semicolon The open C<(> and close parentheses C<)> are supported to override precedence rules in a sub-expression. diff --git a/modules/database/src/std/rec/calcoutRecord.dbd.pod b/modules/database/src/std/rec/calcoutRecord.dbd.pod index d427d28f2..f61d13342 100644 --- a/modules/database/src/std/rec/calcoutRecord.dbd.pod +++ b/modules/database/src/std/rec/calcoutRecord.dbd.pod @@ -396,7 +396,7 @@ C<:=> : assigns a value (right hand side) to a variable (i.e. field) =back -=head3 Parantheses, Comma, and Semicolon +=head3 Parentheses, Comma, and Semicolon The open C<(> and close parentheses C<)> are supported to override precedence rules in a sub-expression. From f29137b98532233c6154bf88defd5f1d203962e9 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 18 Jun 2025 18:49:52 -0500 Subject: [PATCH 06/99] Fixed POD file links into some epics-docs files It seems the guides/*.html docs were moved in epics-docs. A redirect was provided, but links into the middle of those documents no longer pointed to the appropriate section. --- modules/database/src/std/ComponentReference.pod | 4 ++-- modules/database/src/std/rec/aaoRecord.dbd.pod | 2 +- modules/database/src/std/rec/aiRecord.dbd.pod | 4 ++-- modules/database/src/std/rec/aoRecord.dbd.pod | 2 +- modules/database/src/std/rec/biRecord.dbd.pod | 6 +++--- modules/database/src/std/rec/boRecord.dbd.pod | 6 +++--- modules/database/src/std/rec/calcRecord.dbd.pod | 4 ++-- modules/database/src/std/rec/calcoutRecord.dbd.pod | 2 +- modules/database/src/std/rec/compressRecord.dbd.pod | 2 +- modules/database/src/std/rec/dfanoutRecord.dbd.pod | 4 ++-- modules/database/src/std/rec/eventRecord.dbd.pod | 2 +- modules/database/src/std/rec/longoutRecord.dbd.pod | 4 ++-- modules/database/src/std/rec/mbbiRecord.dbd.pod | 2 +- modules/database/src/std/rec/mbboDirectRecord.dbd.pod | 4 ++-- modules/database/src/std/rec/printfRecord.dbd.pod | 2 +- 15 files changed, 25 insertions(+), 25 deletions(-) diff --git a/modules/database/src/std/ComponentReference.pod b/modules/database/src/std/ComponentReference.pod index c43394d50..029b19980 100644 --- a/modules/database/src/std/ComponentReference.pod +++ b/modules/database/src/std/ComponentReference.pod @@ -14,9 +14,9 @@ documentation is now being published. =over -=item * L +=item * L -=item * L +=item * L =back diff --git a/modules/database/src/std/rec/aaoRecord.dbd.pod b/modules/database/src/std/rec/aaoRecord.dbd.pod index cfb70af2b..cbec0c9ba 100644 --- a/modules/database/src/std/rec/aaoRecord.dbd.pod +++ b/modules/database/src/std/rec/aaoRecord.dbd.pod @@ -45,7 +45,7 @@ its output. It can be a hardware address, a channel access or database link, or a constant. Only in records that use soft device support can the OUT field be a channel access link, a database link, or a constant. Otherwise, the OUT field must be a hardware address. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on the format of hardware addresses and database links. =head4 Fields related to array writing diff --git a/modules/database/src/std/rec/aiRecord.dbd.pod b/modules/database/src/std/rec/aiRecord.dbd.pod index 7b051725b..2bedf9058 100644 --- a/modules/database/src/std/rec/aiRecord.dbd.pod +++ b/modules/database/src/std/rec/aiRecord.dbd.pod @@ -46,7 +46,7 @@ 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
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for a description of the various hardware address formats supported. =head3 Units Conversion @@ -212,7 +212,7 @@ positive number of seconds will delay the record going into or out of a minor alarm severity or from minor to major severity until the input signal has been in the alarm range for that number of seconds. -See L +See L for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. diff --git a/modules/database/src/std/rec/aoRecord.dbd.pod b/modules/database/src/std/rec/aoRecord.dbd.pod index 6d596644f..2b838e092 100644 --- a/modules/database/src/std/rec/aoRecord.dbd.pod +++ b/modules/database/src/std/rec/aoRecord.dbd.pod @@ -162,7 +162,7 @@ OUT field. For analog outputs that write their values to devices, the OUT field must specify the address of the I/O card. In addition, the DTYP field must contain the name of the device support module. Be aware that the address format differs according to the I/O bus used. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on the format of hardware addresses. For soft records the output link can be a database link, a channel diff --git a/modules/database/src/std/rec/biRecord.dbd.pod b/modules/database/src/std/rec/biRecord.dbd.pod index 4ef5be036..d2c4672a8 100644 --- a/modules/database/src/std/rec/biRecord.dbd.pod +++ b/modules/database/src/std/rec/biRecord.dbd.pod @@ -47,13 +47,13 @@ field contains the address from where device support retrieves the value. If the binary input record gets its value from hardware, the address of the card must be entered in the INP field, and the name of the device support module must be entered in the DTYP field. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on the format of the hardware address. For records that specify C or C device support routines, the INP field can be a channel or a database link, or a constant. If a constant, VAL can be changed directly by dbPuts. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on the format of database and channel access addresses. Also, see L in this chapter for information on soft device support. @@ -107,7 +107,7 @@ C. The ZSV field holds the severity for the zero state; OSV, for the one state. COSV causes an alarm whenever the state changes between 0 and 1 and the severity is configured as MINOR or MAJOR. -See L +See L for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. diff --git a/modules/database/src/std/rec/boRecord.dbd.pod b/modules/database/src/std/rec/boRecord.dbd.pod index 6bc4e9892..80d0ab609 100644 --- a/modules/database/src/std/rec/boRecord.dbd.pod +++ b/modules/database/src/std/rec/boRecord.dbd.pod @@ -45,7 +45,7 @@ continuous control, a database link to a control algorithm record should be entered in the DOL field. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on hardware addresses and links. =fields DOL, OMSL @@ -105,13 +105,13 @@ It must specify the address of an I/O card if the record sends its output to hardware, and the DTYP field must contain the corresponding device support module. Be aware that the address format differs according to the I/O bus used. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on the format of hardware addresses. Otherwise, if the record is configured to use the soft device support modules, then it can be either a database link, a channel access link, or a constant. Be aware that nothing will be written when OUT is a constant. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on the format of the database and channel access addresses. Also, see L in this chapter for more on output to other records. diff --git a/modules/database/src/std/rec/calcRecord.dbd.pod b/modules/database/src/std/rec/calcRecord.dbd.pod index 798b06bb5..c8758ad53 100644 --- a/modules/database/src/std/rec/calcRecord.dbd.pod +++ b/modules/database/src/std/rec/calcRecord.dbd.pod @@ -42,7 +42,7 @@ the value they are configured with and can be changed via C. They cannot be hardware addresses. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on how to specify database links. =fields INPA - INPL @@ -522,7 +522,7 @@ 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). -See L +See L for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. diff --git a/modules/database/src/std/rec/calcoutRecord.dbd.pod b/modules/database/src/std/rec/calcoutRecord.dbd.pod index f61d13342..994df0f56 100644 --- a/modules/database/src/std/rec/calcoutRecord.dbd.pod +++ b/modules/database/src/std/rec/calcoutRecord.dbd.pod @@ -656,7 +656,7 @@ conditions. The HYST field defines an alarm deadband for each limit. -See L +See L for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. diff --git a/modules/database/src/std/rec/compressRecord.dbd.pod b/modules/database/src/std/rec/compressRecord.dbd.pod index 20c52f496..aa526f89c 100644 --- a/modules/database/src/std/rec/compressRecord.dbd.pod +++ b/modules/database/src/std/rec/compressRecord.dbd.pod @@ -88,7 +88,7 @@ As stated above, the ALG field specifies which algorithm to be performed on the The INP should be a database or channel access link. Though INP can be a constant, the data compression algorithms are supported only when INP is a database link. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on specifying links. diff --git a/modules/database/src/std/rec/dfanoutRecord.dbd.pod b/modules/database/src/std/rec/dfanoutRecord.dbd.pod index 4993accaa..074296450 100644 --- a/modules/database/src/std/rec/dfanoutRecord.dbd.pod +++ b/modules/database/src/std/rec/dfanoutRecord.dbd.pod @@ -60,7 +60,7 @@ undergoes no conversions before it is sent out to the output links. The OUTA-OUTH fields specify where VAL is to be sent. Each field that is to forward data must specify an address to another record. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on specifying links. The SELL, SELM, and SELN fields specify which output links are to be @@ -135,7 +135,7 @@ in the corresponding field (HHSV, LLSV, HSV, LSV) and can be either NO_ALARM, MINOR, or MAJOR. In the hysteresis field (HYST) can be entered a number which serves as the deadband on the limit alarms. -See L +See L for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. diff --git a/modules/database/src/std/rec/eventRecord.dbd.pod b/modules/database/src/std/rec/eventRecord.dbd.pod index 131eb7b89..e7a17c499 100644 --- a/modules/database/src/std/rec/eventRecord.dbd.pod +++ b/modules/database/src/std/rec/eventRecord.dbd.pod @@ -92,7 +92,7 @@ support module. The address format differs according to the card type used. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on the format of hardware addresses and specifying links. For soft records, the INP field can be a constant, a database link, or a channel diff --git a/modules/database/src/std/rec/longoutRecord.dbd.pod b/modules/database/src/std/rec/longoutRecord.dbd.pod index 9fead68d4..03e33ed76 100644 --- a/modules/database/src/std/rec/longoutRecord.dbd.pod +++ b/modules/database/src/std/rec/longoutRecord.dbd.pod @@ -78,7 +78,7 @@ channel access link. If the link is a constant, the result is no output. The DTYP field must then specify the C<<< Soft Channel >>> device support routine. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on the format of hardware addresses and database links. =fields OUT, DTYP, OOPT, OOCH @@ -233,7 +233,7 @@ The HYST field sets an alarm deadband around each limit alarm. For an explanation of the IVOA and IVOV fields, see L. -See L +See L for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. diff --git a/modules/database/src/std/rec/mbbiRecord.dbd.pod b/modules/database/src/std/rec/mbbiRecord.dbd.pod index 56b0d42f2..483316e7f 100644 --- a/modules/database/src/std/rec/mbbiRecord.dbd.pod +++ b/modules/database/src/std/rec/mbbiRecord.dbd.pod @@ -436,7 +436,7 @@ state occurs, if set to MAJOR or MINOR. The other fields, when set to MAJOR or MINOR, trigger an alarm when VAL equals the corresponding state. -See L +See L for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. diff --git a/modules/database/src/std/rec/mbboDirectRecord.dbd.pod b/modules/database/src/std/rec/mbboDirectRecord.dbd.pod index d40cb48c7..84dd5e083 100644 --- a/modules/database/src/std/rec/mbboDirectRecord.dbd.pod +++ b/modules/database/src/std/rec/mbboDirectRecord.dbd.pod @@ -89,7 +89,7 @@ For records that are to write values to hardware devices, the OUT output link must contain the address of the I/O card, and the DTYP field must specify the proper device support module. Be aware that the address format differs according to the I/O bus used. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on the format of hardware addresses. During record processing VAL is converted into RVAL, which is the actual 32-bit @@ -298,7 +298,7 @@ the IVOV field to the output. See L for more information about IVOA and IVOV. -See L +See L for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. diff --git a/modules/database/src/std/rec/printfRecord.dbd.pod b/modules/database/src/std/rec/printfRecord.dbd.pod index a3bef9610..9bb98b0cc 100644 --- a/modules/database/src/std/rec/printfRecord.dbd.pod +++ b/modules/database/src/std/rec/printfRecord.dbd.pod @@ -148,7 +148,7 @@ which type of the data is requested through the appropriate input link. As with C a C<*> character may be used in the format to specify width and/or precision instead of numeric literals, in which case additional input links are used to provide the necessary integer parameter or parameters. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on specifying links. The formatted string is written to the VAL field. The maximum number of From f4e1019ebe40fb0bb32780fbd1b6bcde7512f3dd Mon Sep 17 00:00:00 2001 From: Jerzy Jamroz Date: Fri, 18 Jul 2025 13:13:08 +0200 Subject: [PATCH 07/99] fix: ts{} {} warning: missing initializer for members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - warning: missing initializer for member ‘epicsTimeStamp::secPastEpoch’ [-Wmissing-field-initializers] - warning: missing initializer for member ‘epicsTimeStamp::nsec’ [-Wmissing-field-initializers] --- modules/libcom/src/osi/epicsTime.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/libcom/src/osi/epicsTime.h b/modules/libcom/src/osi/epicsTime.h index 6a4da6dc7..bb75dd56b 100644 --- a/modules/libcom/src/osi/epicsTime.h +++ b/modules/libcom/src/osi/epicsTime.h @@ -329,7 +329,7 @@ public: /** \brief The default constructor sets the time to the EPICS epoch. */ #if __cplusplus>=201103L - constexpr epicsTime() :ts{} {} + constexpr epicsTime() :ts{0, 0} {} #else epicsTime () { ts.secPastEpoch = ts.nsec = 0u; From ff1fb82ef2cb72de6fb53d6102edadde2544c631 Mon Sep 17 00:00:00 2001 From: MarcoMontevechi <90740584+marcomontevechi1@users.noreply.github.com> Date: Wed, 30 Jul 2025 17:12:58 +0200 Subject: [PATCH 08/99] Add dfanout test (#660) Add dfanout record unit tests Authored-by: marcofilho --- modules/database/test/std/rec/Makefile | 7 + modules/database/test/std/rec/dfanoutTest.c | 150 +++++++++++++++++++ modules/database/test/std/rec/dfanoutTest.db | 33 ++++ 3 files changed, 190 insertions(+) create mode 100644 modules/database/test/std/rec/dfanoutTest.c create mode 100644 modules/database/test/std/rec/dfanoutTest.db diff --git a/modules/database/test/std/rec/Makefile b/modules/database/test/std/rec/Makefile index d6aaf44d3..c5eca0dbe 100644 --- a/modules/database/test/std/rec/Makefile +++ b/modules/database/test/std/rec/Makefile @@ -107,6 +107,13 @@ testHarness_SRCS += boTest.c TESTFILES += ../boTest.db TESTS += boTest +TESTPROD_HOST += dfanoutTest +dfanoutTest_SRCS += dfanoutTest.c +dfanoutTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += dfanoutTest.c +TESTFILES += ../dfanoutTest.db +TESTS += dfanoutTest + TESTPROD_HOST += biTest biTest_SRCS += biTest.c biTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp diff --git a/modules/database/test/std/rec/dfanoutTest.c b/modules/database/test/std/rec/dfanoutTest.c new file mode 100644 index 000000000..f033771e0 --- /dev/null +++ b/modules/database/test/std/rec/dfanoutTest.c @@ -0,0 +1,150 @@ +/*************************************************************************\ +* Copyright (c) 2023 Marco Montevechi Filho +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "dbUnitTest.h" +#include "testMain.h" +#include "errlog.h" +#include "dbAccess.h" +#include "menuIvoa.h" +#include "epicsThread.h" +#include "dfanoutRecord.h" + +static const char *dfanout_OUT_pvs[] = {"test_dfanout_record.OUTA", "test_dfanout_record.OUTB", + "test_dfanout_record.OUTC", "test_dfanout_record.OUTD", + "test_dfanout_record.OUTE", "test_dfanout_record.OUTF", + "test_dfanout_record.OUTG", "test_dfanout_record.OUTH"}; + +static const char *dfanout_receivers[] = {"test_dfanout_outa", "test_dfanout_outb", + "test_dfanout_outc", "test_dfanout_outd", + "test_dfanout_oute", "test_dfanout_outf", + "test_dfanout_outg", "test_dfanout_outh"}; + +static testMonitor *monitor; + +void recTestIoc_registerRecordDeviceDriver(struct dbBase *); + +static void test_all(int val, int exception){ + + testMonitorWait(monitor); + // if exception < 0 or > 8 then it tests all. + int i; + for (i = 0; i < NELEMENTS(dfanout_receivers); ++i) { + if ( i == exception) continue; + testdbGetFieldEqual(dfanout_receivers[i], DBF_LONG, val); + } + +} + +static void test_all_output(void){ + + /* set output fields */ + int i; + for (i = 0; i < NELEMENTS(dfanout_OUT_pvs); ++i) { + testdbPutFieldOk(dfanout_OUT_pvs[i], DBF_STRING, dfanout_receivers[i]); + } + + /* set VAL from src to any random number */ + testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 5); + + /* verify that OUT records are updated */ + test_all(5, -1); + +} + +static void test_selm_specified() { + + /* Resetting values */ + testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 0); + test_all(0, -1); + + testdbPutFieldOk("test_dfanout_record.SELM", DBF_STRING, "Specified"); + testdbPutFieldOk("test_dfanout_record.SELN", DBF_LONG, 0); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 10); + test_all(0, -1); + + int val; + for (val = 0; val < NELEMENTS(dfanout_receivers); ++val) { + testdbPutFieldOk("test_dfanout_record.SELN", DBF_LONG, val + 1); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, val + 1); + testMonitorWait(monitor); + + testdbGetFieldEqual(dfanout_receivers[val], DBF_LONG, val + 1); + + int not_seln, not_seln_val; + for (not_seln = 0; not_seln < NELEMENTS(dfanout_receivers); ++not_seln) { + if (not_seln == val) continue; + if (not_seln < val) { // If record has already been tested, expected value is index + 1 + not_seln_val = not_seln + 1; + } else { // If record hasn't been tested yet, expected value is 0 + not_seln_val = 0; + } + testdbGetFieldEqual(dfanout_receivers[not_seln], DBF_LONG, not_seln_val); + } + + } + +} + +static void test_selm_mask() { + + /* Resetting values */ + testdbPutFieldOk("test_dfanout_record.SELM", DBF_STRING, "All"); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 0); + test_all(0, -1); + + /* Resets values. Tests if fields in bitmask have been set */ + int mask; + for (mask = 0; mask <= 255; ++mask) { + + testdbPutFieldOk("test_dfanout_record.SELM", DBF_STRING, "All"); //Setting all values to 1 so we know what to compare with. + testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 1); + testMonitorWait(monitor); + + testdbPutFieldOk("test_dfanout_record.SELM", DBF_STRING, "Mask"); + testdbPutFieldOk("test_dfanout_record.SELN", DBF_LONG, mask); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 9); + testMonitorWait(monitor); + + int item; + for (item = 0; item < NELEMENTS(dfanout_receivers); ++item) { + + if ( mask & (1 << item) ) { // If i represents a set bit in the bitmask + testdbGetFieldEqual(dfanout_receivers[item], DBF_LONG, 9); + } else { + testdbGetFieldEqual(dfanout_receivers[item], DBF_LONG, 1); + } + + } + + } + +} + +MAIN(dfanoutTest) { + + testPlan(3455); + + testdbPrepare(); + testdbReadDatabase("recTestIoc.dbd", NULL, NULL); + recTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("dfanoutTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + monitor = testMonitorCreate("test_dfanout_record", DBE_VALUE, 0); + test_all_output(); + test_selm_specified(); + test_selm_mask(); + testMonitorDestroy(monitor); + + testIocShutdownOk(); + testdbCleanup(); + + return testDone(); +} diff --git a/modules/database/test/std/rec/dfanoutTest.db b/modules/database/test/std/rec/dfanoutTest.db new file mode 100644 index 000000000..b360ead8c --- /dev/null +++ b/modules/database/test/std/rec/dfanoutTest.db @@ -0,0 +1,33 @@ +record(ao, "test_dfanout_src") { + +} + +record(dfanout, "test_dfanout_record") { + field(OMSL, "closed_loop") + field(DOL, "test_dfanout_src CP") +} + +record(ai, "test_dfanout_outa") { +} + +record(ai, "test_dfanout_outb") { +} + +record(ai, "test_dfanout_outc") { +} + +record(ai, "test_dfanout_outd") { +} + +record(ai, "test_dfanout_oute") { +} + +record(ai, "test_dfanout_outf") { +} + +record(ai, "test_dfanout_outg") { +} + +record(ai, "test_dfanout_outh") { +} + From 5ae9cc6536a41337cc8272f3ea8a9bd6123a2742 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 10 Jul 2025 20:10:17 -0700 Subject: [PATCH 09/99] gethostbyname -> getaddrinfo Prefer reentrant DNS API --- modules/libcom/src/osi/os/posix/osdSock.c | 57 ++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/modules/libcom/src/osi/os/posix/osdSock.c b/modules/libcom/src/osi/os/posix/osdSock.c index 0d873e628..41f6580f0 100644 --- a/modules/libcom/src/osi/os/posix/osdSock.c +++ b/modules/libcom/src/osi/os/posix/osdSock.c @@ -41,6 +41,13 @@ # define SOCK_CLOEXEC (0) #endif +#if defined(AI_PASSIVE) && !defined(__rtems__) +# define USE_INFO +#else +# define USE_BY +#endif + +#ifdef USE_BY /* * Protect some routines which are not thread-safe */ @@ -60,7 +67,7 @@ static void unlockInfo (void) { epicsMutexUnlock (infoMutex); } - +#endif static size_t nAttached; @@ -164,6 +171,7 @@ LIBCOM_API void epicsStdCall epicsSocketDestroy ( SOCKET s ) } } +#ifdef USE_BY /* * ipAddrToHostName * On many systems, gethostbyaddr must be protected by a @@ -214,6 +222,53 @@ LIBCOM_API int epicsStdCall hostToIPAddr unlockInfo (); return ret; } +#endif +#ifdef USE_INFO +unsigned epicsStdCall ipAddrToHostName(const struct in_addr *pAddr, char *pBuf, unsigned bufSize) +{ + osiSockAddr query; + if(!bufSize) + return 0; // non-sense + + memset(&query, 0, sizeof(query)); + query.ia.sin_family = AF_INET; + query.ia.sin_addr = *pAddr; + + int ret = getnameinfo(&query.sa, sizeof(query), pBuf, bufSize, NULL, 0, NI_NAMEREQD); + if(ret==0) { + ret = strlen (pBuf); + + } else { // lookup fails + ret = 0; // indicate failure to caller + } + return ret; +} + +int epicsStdCall hostToIPAddr(const char *pHostName, struct in_addr *pIPA) +{ + struct addrinfo hint, *result = NULL; + + memset(&hint, 0, sizeof(hint)); + hint.ai_family = AF_INET; + + int ret = getaddrinfo(pHostName, NULL, &hint, &result); + if(ret==0) { + const struct addrinfo *ai; + ret = -1; + for(ai = result; ai; ai = ai->ai_next) { + assert(ai->ai_family==AF_INET); // ensured by hint + const struct sockaddr_in *answer = (const struct sockaddr_in*)ai->ai_addr; + *pIPA = answer->sin_addr; + ret = 0; + break; + } + } + if(result) { + freeaddrinfo(result); + } + return ret; +} +#endif From 007df69726ae6c7711985a59491f078b7b55dab5 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Wed, 30 Jul 2025 17:23:19 +0200 Subject: [PATCH 10/99] fix EPICS_PRINTF_STYLE so old mingw recognizes %ll and %z (#674) gcc knows __gnu_printf__ since version 4.4.0, not only for MinGW but for all targets. As we would need to check the gcc version anyway to avoid "unrecognized format function type" errors with older MinGW versions, we can simply use it for recent gcc versions and not depend on MinGW at all. --- modules/libcom/src/osi/compiler/gcc/compilerSpecific.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h b/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h index 42723b97c..80b72012f 100644 --- a/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h +++ b/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h @@ -45,7 +45,11 @@ /* * Enable format-string checking if possible */ +#if __GNUC__ * 100 + __GNUC_MINOR__ >= 404 +#define EPICS_PRINTF_STYLE(f,a) __attribute__((format(__gnu_printf__,f,a))) +#else #define EPICS_PRINTF_STYLE(f,a) __attribute__((format(__printf__,f,a))) +#endif /* * Deprecation marker From 15ed1643cdf5eb40d0ef0d19e7a04a25c8673762 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 31 Jul 2025 17:31:33 -0500 Subject: [PATCH 11/99] Convert xxxRecord to use typed dset Fixes #574 --- .../src/template/top/exampleApp/src/Makefile | 2 +- .../template/top/exampleApp/src/devXxxSoft.c | 60 ++--- .../template/top/exampleApp/src/xxxRecord.c | 15 +- .../template/top/exampleApp/src/xxxRecord.dbd | 241 +++++++++--------- 4 files changed, 153 insertions(+), 165 deletions(-) diff --git a/modules/database/src/template/top/exampleApp/src/Makefile b/modules/database/src/template/top/exampleApp/src/Makefile index 07504feac..cf9e8cd8f 100644 --- a/modules/database/src/template/top/exampleApp/src/Makefile +++ b/modules/database/src/template/top/exampleApp/src/Makefile @@ -9,7 +9,7 @@ include $(TOP)/configure/CONFIG # ADD MACRO DEFINITIONS BELOW HERE # Use typed rset structure (see 3.16.1 release notes) -USR_CPPFLAGS += -DUSE_TYPED_RSET +USR_CPPFLAGS += -DUSE_TYPED_RSET -DUSE_TYPED_DSET # xxxRecord.h will be created from xxxRecord.dbd DBDINC += xxxRecord diff --git a/modules/database/src/template/top/exampleApp/src/devXxxSoft.c b/modules/database/src/template/top/exampleApp/src/devXxxSoft.c index b0348213e..f7befadc5 100644 --- a/modules/database/src/template/top/exampleApp/src/devXxxSoft.c +++ b/modules/database/src/template/top/exampleApp/src/devXxxSoft.c @@ -21,42 +21,30 @@ #include "xxxRecord.h" #include "epicsExport.h" +static long init_record(dbCommon *prec) +{ + struct xxxRecord *pxxx = (struct xxxRecord *) prec; + + if (recGblInitConstantLink(&pxxx->inp, DBF_DOUBLE, &pxxx->val)) + pxxx->udf = FALSE; + + return 0; +} + +static long read_xxx(struct xxxRecord *pxxx) +{ + long status = dbGetLink(&(pxxx->inp), DBF_DOUBLE, &(pxxx->val), 0, 0); + + /* If get was successful VAL is now defined */ + if (!status) + pxxx->udf = FALSE; + + return 0; +} + /*Create the dset for devXxxSoft */ -static long init_record(); -static long read_xxx(); -struct { - long number; - DEVSUPFUN report; - DEVSUPFUN init; - DEVSUPFUN init_record; - DEVSUPFUN get_ioint_info; - DEVSUPFUN read_xxx; -}devXxxSoft={ - 5, - NULL, - NULL, - init_record, - NULL, +xxxdset devXxxSoft = { + { 5, NULL, NULL, init_record, NULL }, read_xxx, }; -epicsExportAddress(dset,devXxxSoft); - - -static long init_record(pxxx) - struct xxxRecord *pxxx; -{ - if(recGblInitConstantLink(&pxxx->inp,DBF_DOUBLE,&pxxx->val)) - pxxx->udf = FALSE; - return(0); -} - -static long read_xxx(pxxx) - struct xxxRecord *pxxx; -{ - long status; - - status = dbGetLink(&(pxxx->inp),DBF_DOUBLE, &(pxxx->val),0,0); - /*If return was succesful then set undefined false*/ - if(!status) pxxx->udf = FALSE; - return(0); -} +epicsExportAddress(dset, devXxxSoft); diff --git a/modules/database/src/template/top/exampleApp/src/xxxRecord.c b/modules/database/src/template/top/exampleApp/src/xxxRecord.c index bf8c79982..282c9744c 100644 --- a/modules/database/src/template/top/exampleApp/src/xxxRecord.c +++ b/modules/database/src/template/top/exampleApp/src/xxxRecord.c @@ -67,15 +67,6 @@ rset xxxRSET={ }; epicsExportAddress(rset,xxxRSET); -typedef struct xxxset { /* xxx input dset */ - long number; - DEVSUPFUN dev_report; - DEVSUPFUN init; - DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ - DEVSUPFUN get_ioint_info; - DEVSUPFUN read_xxx; -}xxxdset; - static void checkAlarms(xxxRecord *prec); static void monitor(xxxRecord *prec); @@ -92,13 +83,13 @@ static long init_record(struct dbCommon *pcommon, int pass) return(S_dev_noDSET); } /* must have read_xxx function defined */ - if( (pdset->number < 5) || (pdset->read_xxx == NULL) ) { + if( (pdset->common.number < 5) || (pdset->read_xxx == NULL) ) { recGblRecordError(S_dev_missingSup,(void *)prec,"xxx: init_record"); return(S_dev_missingSup); } - if( pdset->init_record ) { - if((status=(*pdset->init_record)(prec))) return(status); + if( pdset->common.init_record ) { + if((status=(*pdset->common.init_record)(pcommon))) return(status); } return(0); } diff --git a/modules/database/src/template/top/exampleApp/src/xxxRecord.dbd b/modules/database/src/template/top/exampleApp/src/xxxRecord.dbd index af9e01050..088f33f6d 100644 --- a/modules/database/src/template/top/exampleApp/src/xxxRecord.dbd +++ b/modules/database/src/template/top/exampleApp/src/xxxRecord.dbd @@ -3,120 +3,129 @@ # SPDX-License-Identifier: EPICS recordtype(xxx) { - include "dbCommon.dbd" - field(VAL,DBF_DOUBLE) { - prompt("Current EGU Value") - promptgroup("40 - Input") - asl(ASL0) - pp(TRUE) - } - field(INP,DBF_INLINK) { - prompt("Input Specification") - promptgroup("40 - Input") - special(SPC_NOMOD) - interest(1) - } - field(PREC,DBF_SHORT) { - prompt("Display Precision") - promptgroup("80 - Display") - interest(1) - } - field(EGU,DBF_STRING) { - prompt("Engineering Units") - promptgroup("80 - Display") - interest(1) - size(16) - } - field(HOPR,DBF_FLOAT) { - prompt("High Operating Range") - promptgroup("80 - Display") - interest(1) - } - field(LOPR,DBF_FLOAT) { - prompt("Low Operating Range") - promptgroup("80 - Display") - interest(1) - } - field(HIHI,DBF_FLOAT) { - prompt("Hihi Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - } - field(LOLO,DBF_FLOAT) { - prompt("Lolo Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - } - field(HIGH,DBF_FLOAT) { - prompt("High Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - } - field(LOW,DBF_FLOAT) { - prompt("Low Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - } - 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_DOUBLE) { - prompt("Alarm Deadband") - promptgroup("70 - Alarm") - interest(1) - } - field(ADEL,DBF_DOUBLE) { - prompt("Archive Deadband") - promptgroup("80 - Display") - interest(1) - } - field(MDEL,DBF_DOUBLE) { - prompt("Monitor Deadband") - promptgroup("80 - Display") - interest(1) - } - field(LALM,DBF_DOUBLE) { - prompt("Last Value Alarmed") - special(SPC_NOMOD) - interest(3) - } - field(ALST,DBF_DOUBLE) { - prompt("Last Value Archived") - special(SPC_NOMOD) - interest(3) - } - field(MLST,DBF_DOUBLE) { - prompt("Last Val Monitored") - special(SPC_NOMOD) - interest(3) - } + include "dbCommon.dbd" + % + %/* Declare Device Support Entry Table */ + %struct xxxRecord; + %typedef struct xxxdset { + % dset common; + % long (*read_xxx)(struct xxxRecord *prec); + %} xxxdset; + %#define HAS_xxxdset + % + field(VAL,DBF_DOUBLE) { + prompt("Current EGU Value") + promptgroup("40 - Input") + asl(ASL0) + pp(TRUE) + } + field(INP,DBF_INLINK) { + prompt("Input Specification") + promptgroup("40 - Input") + special(SPC_NOMOD) + interest(1) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup("80 - Display") + interest(1) + } + field(EGU,DBF_STRING) { + prompt("Engineering Units") + promptgroup("80 - Display") + interest(1) + size(16) + } + field(HOPR,DBF_FLOAT) { + prompt("High Operating Range") + promptgroup("80 - Display") + interest(1) + } + field(LOPR,DBF_FLOAT) { + prompt("Low Operating Range") + promptgroup("80 - Display") + interest(1) + } + field(HIHI,DBF_FLOAT) { + prompt("Hihi Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + } + field(LOLO,DBF_FLOAT) { + prompt("Lolo Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + } + field(HIGH,DBF_FLOAT) { + prompt("High Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + } + field(LOW,DBF_FLOAT) { + prompt("Low Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + } + 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_DOUBLE) { + prompt("Alarm Deadband") + promptgroup("70 - Alarm") + interest(1) + } + field(ADEL,DBF_DOUBLE) { + prompt("Archive Deadband") + promptgroup("80 - Display") + interest(1) + } + field(MDEL,DBF_DOUBLE) { + prompt("Monitor Deadband") + promptgroup("80 - Display") + interest(1) + } + field(LALM,DBF_DOUBLE) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(ALST,DBF_DOUBLE) { + prompt("Last Value Archived") + special(SPC_NOMOD) + interest(3) + } + field(MLST,DBF_DOUBLE) { + prompt("Last Val Monitored") + special(SPC_NOMOD) + interest(3) + } } From 79624b72e5efbb76d1ac8e763cb25e830b04cbcb Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 22 May 2025 11:14:16 +0200 Subject: [PATCH 12/99] extend calc, calcout and sub records and calc expressions to inputs A - U To accomodate for the increased number of inputs, the size of CALC and OCAL fields has been doubled to 160 characters. --- documentation/new-notes/PR-655.md | 7 + modules/database/src/std/link/links.dbd.pod | 12 +- modules/database/src/std/link/lnkCalc.c | 2 +- modules/database/src/std/rec/calcRecord.c | 4 +- .../database/src/std/rec/calcRecord.dbd.pod | 159 +++++++++++- modules/database/src/std/rec/calcoutRecord.c | 13 +- .../src/std/rec/calcoutRecord.dbd.pod | 238 ++++++++++++++++-- modules/database/src/std/rec/subRecord.c | 6 +- .../database/src/std/rec/subRecord.dbd.pod | 148 ++++++++++- modules/libcom/src/as/asLib_lex.l | 2 +- modules/libcom/src/calc/calcPerform.c | 36 +++ modules/libcom/src/calc/postfix.c | 15 +- modules/libcom/src/calc/postfix.h | 19 +- modules/libcom/src/calc/postfixPvt.h | 6 +- modules/libcom/test/epicsCalcTest.cpp | 102 ++++++-- 15 files changed, 686 insertions(+), 83 deletions(-) create mode 100644 documentation/new-notes/PR-655.md diff --git a/documentation/new-notes/PR-655.md b/documentation/new-notes/PR-655.md new file mode 100644 index 000000000..254ba1356 --- /dev/null +++ b/documentation/new-notes/PR-655.md @@ -0,0 +1,7 @@ +### Records calc, calcout and sub extended + +The record types calc, calcout and sub have been extended from 12 inputs +A - L to 21 inputs A - U. +The macro `CALCPERFORM_NARGS` reflects this change. +The new inputs can be used in calc links and access security as well. +The size of CALC and OCAL fields has been doubled to 160 chars. diff --git a/modules/database/src/std/link/links.dbd.pod b/modules/database/src/std/link/links.dbd.pod index 5247f6950..27689e321 100644 --- a/modules/database/src/std/link/links.dbd.pod +++ b/modules/database/src/std/link/links.dbd.pod @@ -81,11 +81,13 @@ link(calc, lnkCalcIf) =head3 Calculation Link C<"calc"> A calculation link is an input link that can evaluate mathematical expressions -on scalar (double-precision floating-point) values obtained from up to 12 child +on scalar (double-precision floating-point) values obtained from up to 21 child input links, and returns a double-precision floating-point result. The expression is evaluated by the EPICS Calc engine, and the result is returned as the value of the link. +Since UNRELEASED the number of inputs has been increased from 12 to 21. + Two additional expressions may also be provided and are evaluated to determine whether the record owning the link should be placed in alarm state. In both cases the result of the main calculation is available to these expressions as @@ -95,7 +97,7 @@ record will be placed in C alarm. If not and the C expression evaluates to non-zero the record will be placed in C alarm state. A calculation link can also be an output link, with the scalar output value -being converted to a double and provided to the expression as C. Up to 12 +being converted to a double and provided to the expression as C. Up to 21 additional input links can also be read and provided to the expression as above. The result of the calculation is forwarded to a child output link specified in the link's C parameter. @@ -126,8 +128,8 @@ An optional expression that returns non-zero to raise a minor alarm. =item args -A JSON list of up to 12 input arguments for the expression, which are assigned -to the inputs C, C, C, ... C. Each input argument may be either a +A JSON list of up to 24 input arguments for the expression, which are assigned +to the inputs C, C, C, ... C. Each input argument may be either a numeric literal or an embedded JSON link inside C<{}> braces. The same input values are provided to the two alarm expressions as to the primary expression. @@ -149,7 +151,7 @@ result should be displayed. Equivalent to the C field of a record. =item time -An optional string containing a single upper or lower-case letter C ... C +An optional string containing a single upper or lower-case letter C ... C which must correspond to an input provided in the C parameter. When the record containing such a link has C set to -2 (epicsTimeEventDeviceTime) the record's timestamp field C
for information on how to specify database links. -=fields INPA - INPL +=fields INPA - INPU =head3 Expression @@ -117,7 +119,7 @@ radians to degrees The expression uses the values retrieved from the INPx links as operands, though constants can be used as operands too. These values retrieved from -the input links are stored in the A-L fields. The values to be used in the +the input links are stored in the A-U fields. The values to be used in the expression are simply referenced by the field letter. For instance, the value obtained from INPA link is stored in the field A, and the value obtained from INPB is stored in field B. The field names can be included in @@ -125,7 +127,7 @@ the expression which will operate on their respective values, as in A+B. Also, the RNDM nullary function can be included as an operand in the expression in order to generate a random number between 0 and 1. -=fields A - L +=fields A - U The keyword VAL returns the current contents of the VAL field (which can be written to by a CA put, so it might I be the result from the last time @@ -549,12 +551,12 @@ modifiable at run-time. They are used to process the record. The LALM field is used to implement the hysteresis factor for the alarm limits. -The LA-LL fields are used to decide when to trigger monitors for the +The LA-LU fields are used to decide when to trigger monitors for the corresponding fields. For instance, if LA does not equal the value A, monitors for A are triggered. The MLST and ALST fields are used in the same manner for the VAL field. -=fields LALM, ALST, MLST, LA - LL +=fields LALM, ALST, MLST, LA - LU =cut @@ -569,7 +571,7 @@ manner for the VAL field. promptgroup("30 - Action") special(SPC_CALC) pp(TRUE) - size(80) + size(160) initial("0") } field(INPA,DBF_INLINK) { @@ -632,6 +634,60 @@ manner for the VAL field. promptgroup("42 - Input G-L") interest(1) } + field(INPM,DBF_INLINK) { + prompt("Input M") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPN,DBF_INLINK) { + prompt("Input N") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPO,DBF_INLINK) { + prompt("Input O") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPP,DBF_INLINK) { + prompt("Input P") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPQ,DBF_INLINK) { + prompt("Input Q") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPR,DBF_INLINK) { + prompt("Input R") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPS,DBF_INLINK) { + prompt("Input S") + special(SPC_MOD) + promptgroup("44 - Input S-U") + interest(1) + } + field(INPT,DBF_INLINK) { + prompt("Input T") + special(SPC_MOD) + promptgroup("44 - Input S-U") + interest(1) + } + field(INPU,DBF_INLINK) { + prompt("Input U") + special(SPC_MOD) + promptgroup("44 - Input S-U") + interest(1) + } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") @@ -790,6 +846,42 @@ manner for the VAL field. prompt("Value of Input L") pp(TRUE) } + field(M,DBF_DOUBLE) { + prompt("Value of Input M") + pp(TRUE) + } + field(N,DBF_DOUBLE) { + prompt("Value of Input N") + pp(TRUE) + } + field(O,DBF_DOUBLE) { + prompt("Value of Input O") + pp(TRUE) + } + field(P,DBF_DOUBLE) { + prompt("Value of Input P") + pp(TRUE) + } + field(Q,DBF_DOUBLE) { + prompt("Value of Input Q") + pp(TRUE) + } + field(R,DBF_DOUBLE) { + prompt("Value of Input R") + pp(TRUE) + } + field(S,DBF_DOUBLE) { + prompt("Value of Input S") + pp(TRUE) + } + field(T,DBF_DOUBLE) { + prompt("Value of Input T") + pp(TRUE) + } + field(U,DBF_DOUBLE) { + prompt("Value of Input U") + pp(TRUE) + } field(LA,DBF_DOUBLE) { prompt("Prev Value of A") special(SPC_NOMOD) @@ -850,6 +942,51 @@ manner for the VAL field. special(SPC_NOMOD) interest(3) } + field(LM,DBF_DOUBLE) { + prompt("Prev Value of M") + special(SPC_NOMOD) + interest(3) + } + field(LN,DBF_DOUBLE) { + prompt("Prev Value of N") + special(SPC_NOMOD) + interest(3) + } + field(LO,DBF_DOUBLE) { + prompt("Prev Value of O") + special(SPC_NOMOD) + interest(3) + } + field(LP,DBF_DOUBLE) { + prompt("Prev Value of P") + special(SPC_NOMOD) + interest(3) + } + field(LQ,DBF_DOUBLE) { + prompt("Prev Value of Q") + special(SPC_NOMOD) + interest(3) + } + field(LR,DBF_DOUBLE) { + prompt("Prev Value of R") + special(SPC_NOMOD) + interest(3) + } + field(LS,DBF_DOUBLE) { + prompt("Prev Value of S") + special(SPC_NOMOD) + interest(3) + } + field(LT,DBF_DOUBLE) { + prompt("Prev Value of T") + special(SPC_NOMOD) + interest(3) + } + field(LU,DBF_DOUBLE) { + prompt("Prev Value of U") + special(SPC_NOMOD) + interest(3) + } field(LALM,DBF_DOUBLE) { prompt("Last Value Alarmed") special(SPC_NOMOD) @@ -870,7 +1007,7 @@ manner for the VAL field. prompt("Reverse Polish Calc") special(SPC_NOMOD) interest(4) - extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]") + extra("char rpcl[INFIX_TO_POSTFIX_SIZE(160)]") } =head2 Record Support @@ -970,7 +1107,7 @@ Archive and values change monitors are invoked if ADEL and MDEL conditions are met. =item * -Monitors for A-L are checked whenever other monitors are invoked. +Monitors for A-U are checked whenever other monitors are invoked. =item * NSEV and NSTA are reset to 0. diff --git a/modules/database/src/std/rec/calcoutRecord.c b/modules/database/src/std/rec/calcoutRecord.c index 319994596..d6c2a145c 100644 --- a/modules/database/src/std/rec/calcoutRecord.c +++ b/modules/database/src/std/rec/calcoutRecord.c @@ -355,6 +355,15 @@ static long special(DBADDR *paddr, int after) case(calcoutRecordINPJ): case(calcoutRecordINPK): case(calcoutRecordINPL): + case(calcoutRecordINPM): + case(calcoutRecordINPN): + case(calcoutRecordINPO): + case(calcoutRecordINPP): + case(calcoutRecordINPQ): + case(calcoutRecordINPR): + case(calcoutRecordINPS): + case(calcoutRecordINPT): + case(calcoutRecordINPU): case(calcoutRecordOUT): lnkIndex = fieldIndex - calcoutRecordINPA; plink = &prec->inpa + lnkIndex; @@ -406,9 +415,9 @@ static long special(DBADDR *paddr, int after) #define indexof(field) calcoutRecord##field static long get_linkNumber(int fieldIndex) { - if (fieldIndex >= indexof(A) && fieldIndex <= indexof(L)) + if (fieldIndex >= indexof(A) && fieldIndex < indexof(A) + CALCPERFORM_NARGS) return fieldIndex - indexof(A); - if (fieldIndex >= indexof(LA) && fieldIndex <= indexof(LL)) + if (fieldIndex >= indexof(LA) && fieldIndex < indexof(LA) + CALCPERFORM_NARGS) return fieldIndex - indexof(LA); return -1; } diff --git a/modules/database/src/std/rec/calcoutRecord.dbd.pod b/modules/database/src/std/rec/calcoutRecord.dbd.pod index 994df0f56..82a006a5d 100644 --- a/modules/database/src/std/rec/calcoutRecord.dbd.pod +++ b/modules/database/src/std/rec/calcoutRecord.dbd.pod @@ -20,6 +20,8 @@ rather than the DBF_STRING fields used in the Wait record. For new databases, it is recommended that the Calcout record be used instead of the Wait record. +Since UNRELEASED the number of inputs has been increased from 12 to 21. + =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. @@ -57,18 +59,18 @@ These fields are listed in L. =head3 Read Parameters -The read parameters for the Calcout record consists of 12 input links INPA, -INPB, ... INPL. The fields can be database links, channel access links, or -constants. If they are links, they must specify another record's field. If +The read parameters for the Calcout record consists of 21 input links INPA - +INPU. The fields can be database links, channel access links, or constants. +If they are links, they must specify another record's field. If they are constants, they will be initialized with the value they are configured with and can be changed via C. These fields cannot be -hardware addresses. In addition, the Calcout record contains the INAV, -INBV, ... INLV fields which indicate the status of the link fields, for +hardware addresses. In addition, the Calcout record contains the INAV - +INUV fields which indicate the status of the link fields, for example, whether or not the specified PV was found and a link to it established. See L for an explanation of these fields. -=fields INPA - INPL +=fields INPA - INPU =head3 Expressions @@ -146,14 +148,14 @@ angle from radians to degrees The expression can use the values retrieved from the INPx links as operands, though constants can be used as operands too. These values -retrieved from the input links are stored in the A-L fields. The values to +retrieved from the input links are stored in the A-U fields. The values to be used in the expression are simple references by the field letter. For instance, the value obtained from the INPA link is stored in field A, and the values obtained from the INPB link is stored in the field B. The names can be included in the expression will operate on their respective values, as in A+B. -=fields A - L +=fields A - U The keyword VAL returns the current contents of the expression's result field, i.e. the VAL field for the CALC expression and the OVAL field for @@ -604,8 +606,8 @@ LOW, and LOLO fields. PREC controls the precision of the VAL field. =head4 Menu calcoutINAV -The INAV-INLV fields indicate the status of the link to the PVs specified -in the INPA-INPL fields respectively. These fields can have four possible +The INAV-INUV fields indicate the status of the link to the PVs specified +in the INPA-INPU fields respectively. These fields can have four possible values: =menu calcoutINAV @@ -629,7 +631,7 @@ C -- the corresponding link field is a constant. =back The OUTV field indicates the status of the OUT link. If has the same -possible values as the INAV-INLV fields. +possible values as the INAV-INUV fields. The CLCV and OLCV fields indicate the validity of the expression in the CALC and OCAL fields respectively. If the expression in invalid, the field @@ -640,7 +642,7 @@ The DLYA field is set to one during the delay specified in ODLY. See L for more on the record name (NAME) and description (DESC) fields. -=fields EGU, PREC, HOPR, LOPR, INAV - INLV, OUTV, CLCV, OCLV, DLYA, NAME, DESC +=fields EGU, PREC, HOPR, LOPR, INAV - INUV, OUTV, CLCV, OCLV, DLYA, NAME, DESC =head3 Alarm Parameters @@ -684,12 +686,12 @@ modifiable at run-time. They are used to process the record. The LALM field is used to implement the hysteresis factor for the alarm limits. -The LA-LL fields are used to decide when to trigger monitors for the +The LA-LU fields are used to decide when to trigger monitors for the corresponding fields. For instance, if LA does not equal the value for A, monitors for A are triggered. The MLST and ALST fields are used in the same manner for the VAL field. -=fields LALM, ALST, MLST, LA - LL +=fields LALM, ALST, MLST, LA - LU =cut @@ -721,7 +723,7 @@ manner for the VAL field. promptgroup("30 - Action") special(SPC_CALC) pp(TRUE) - size(80) + size(160) initial("0") } field(CLCV,DBF_LONG) { @@ -800,6 +802,60 @@ manner for the VAL field. promptgroup("42 - Input G-L") interest(1) } + field(INPM,DBF_INLINK) { + prompt("Input M") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPN,DBF_INLINK) { + prompt("Input N") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPO,DBF_INLINK) { + prompt("Input O") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPP,DBF_INLINK) { + prompt("Input P") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPQ,DBF_INLINK) { + prompt("Input Q") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPR,DBF_INLINK) { + prompt("Input R") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPS,DBF_INLINK) { + prompt("Input S") + special(SPC_MOD) + promptgroup("44 - Input S-U") + interest(1) + } + field(INPT,DBF_INLINK) { + prompt("Input T") + special(SPC_MOD) + promptgroup("44 - Input S-U") + interest(1) + } + field(INPU,DBF_INLINK) { + prompt("Input U") + special(SPC_MOD) + promptgroup("44 - Input S-U") + interest(1) + } field(OUT,DBF_OUTLINK) { prompt("Output Specification") special(SPC_MOD) @@ -890,6 +946,69 @@ manner for the VAL field. menu(calcoutINAV) initial("1") } + field(INMV,DBF_MENU) { + prompt("INPM PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INNV,DBF_MENU) { + prompt("INPN PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INOV,DBF_MENU) { + prompt("INPO PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INPV,DBF_MENU) { + prompt("INPP PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INQV,DBF_MENU) { + prompt("INPQ PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INRV,DBF_MENU) { + prompt("INPR PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INSV,DBF_MENU) { + prompt("INPS PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INTV,DBF_MENU) { + prompt("INPT PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INUV,DBF_MENU) { + prompt("INPU PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } field(OUTV,DBF_MENU) { prompt("OUT PV Status") special(SPC_NOMOD) @@ -924,7 +1043,7 @@ manner for the VAL field. promptgroup("30 - Action") special(SPC_CALC) pp(TRUE) - size(80) + size(160) initial("0") } field(OCLV,DBF_LONG) { @@ -1104,6 +1223,42 @@ manner for the VAL field. prompt("Value of Input L") pp(TRUE) } + field(M,DBF_DOUBLE) { + prompt("Value of Input M") + pp(TRUE) + } + field(N,DBF_DOUBLE) { + prompt("Value of Input N") + pp(TRUE) + } + field(O,DBF_DOUBLE) { + prompt("Value of Input O") + pp(TRUE) + } + field(P,DBF_DOUBLE) { + prompt("Value of Input P") + pp(TRUE) + } + field(Q,DBF_DOUBLE) { + prompt("Value of Input Q") + pp(TRUE) + } + field(R,DBF_DOUBLE) { + prompt("Value of Input R") + pp(TRUE) + } + field(S,DBF_DOUBLE) { + prompt("Value of Input S") + pp(TRUE) + } + field(T,DBF_DOUBLE) { + prompt("Value of Input T") + pp(TRUE) + } + field(U,DBF_DOUBLE) { + prompt("Value of Input U") + pp(TRUE) + } field(OVAL,DBF_DOUBLE) { prompt("Output Value") asl(ASL0) @@ -1168,6 +1323,51 @@ manner for the VAL field. special(SPC_NOMOD) interest(3) } + field(LM,DBF_DOUBLE) { + prompt("Prev Value of M") + special(SPC_NOMOD) + interest(3) + } + field(LN,DBF_DOUBLE) { + prompt("Prev Value of N") + special(SPC_NOMOD) + interest(3) + } + field(LO,DBF_DOUBLE) { + prompt("Prev Value of O") + special(SPC_NOMOD) + interest(3) + } + field(LP,DBF_DOUBLE) { + prompt("Prev Value of P") + special(SPC_NOMOD) + interest(3) + } + field(LQ,DBF_DOUBLE) { + prompt("Prev Value of Q") + special(SPC_NOMOD) + interest(3) + } + field(LR,DBF_DOUBLE) { + prompt("Prev Value of R") + special(SPC_NOMOD) + interest(3) + } + field(LS,DBF_DOUBLE) { + prompt("Prev Value of S") + special(SPC_NOMOD) + interest(3) + } + field(LT,DBF_DOUBLE) { + prompt("Prev Value of T") + special(SPC_NOMOD) + interest(3) + } + field(LU,DBF_DOUBLE) { + prompt("Prev Value of U") + special(SPC_NOMOD) + interest(3) + } field(POVL,DBF_DOUBLE) { prompt("Prev Value of OVAL") asl(ASL0) @@ -1192,13 +1392,13 @@ manner for the VAL field. prompt("Reverse Polish Calc") special(SPC_NOMOD) interest(4) - extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]") + extra("char rpcl[INFIX_TO_POSTFIX_SIZE(160)]") } field(ORPC,DBF_NOACCESS) { prompt("Reverse Polish OCalc") special(SPC_NOMOD) interest(4) - extra("char orpc[INFIX_TO_POSTFIX_SIZE(80)]") + extra("char orpc[INFIX_TO_POSTFIX_SIZE(160)]") } =head2 Record Support @@ -1307,7 +1507,7 @@ Archive and value change monitors are invoked if ADEL and MDEL conditions are met. =item * -Monitors for A-L are checked whenever other monitors are invoked. +Monitors for A-U are checked whenever other monitors are invoked. =item * NSEV and NSTA are reset to 0 diff --git a/modules/database/src/std/rec/subRecord.c b/modules/database/src/std/rec/subRecord.c index be274d18f..401c6a8eb 100644 --- a/modules/database/src/std/rec/subRecord.c +++ b/modules/database/src/std/rec/subRecord.c @@ -86,7 +86,7 @@ static long do_sub(subRecord *); static long fetch_values(subRecord *); static void monitor(subRecord *); -#define INP_ARG_MAX 12 +#define INP_ARG_MAX 21 static long init_record(struct dbCommon *pcommon, int pass) { @@ -196,9 +196,9 @@ static long special(DBADDR *paddr, int after) #define indexof(field) subRecord##field static long get_linkNumber(int fieldIndex) { - if (fieldIndex >= indexof(A) && fieldIndex <= indexof(L)) + if (fieldIndex >= indexof(A) && fieldIndex < indexof(A) + INP_ARG_MAX) return fieldIndex - indexof(A); - if (fieldIndex >= indexof(LA) && fieldIndex <= indexof(LL)) + if (fieldIndex >= indexof(LA) && fieldIndex < indexof(LA) + INP_ARG_MAX) return fieldIndex - indexof(LA); return -1; } diff --git a/modules/database/src/std/rec/subRecord.dbd.pod b/modules/database/src/std/rec/subRecord.dbd.pod index 6e6edd4bb..2c7cbd291 100644 --- a/modules/database/src/std/rec/subRecord.dbd.pod +++ b/modules/database/src/std/rec/subRecord.dbd.pod @@ -12,6 +12,8 @@ The subroutine record is used to call a C initialization routine and a recurring scan routine. There is no device support for this record. +Since UNRELEASED the number of inputs has been increased from 12 to 21. + =recordtype sub =cut @@ -30,17 +32,17 @@ These fields are described in L. =head3 Read Parameters -The subroutine record has twelve input links (INPA-INPL), each of which has a -corresponding value field (A-L). These fields are used to retrieve and store +The subroutine record has 21 input links INPA - INPU, each of which has a +corresponding value field A - U. These fields are used to retrieve and store values that can be passed to the subroutine that the record calls. The input links can be either channel access or database links, or constants. When constants, the corresponding value field for the link is initialized with the constant value and the field's value can be changed at run-time via dbPuts. -Otherwise, the values for (A-F) are fetched from the input links when the record -is processed. +Otherwise, the values for A - U are fetched from the input links when the +record is processed. -=fields INPA - INPL, A - L +=fields INPA - INPU, A - U =head3 Subroutine Connection @@ -115,11 +117,11 @@ processing routines or the monitors. VAL should be set by the subroutine. SADR holds the subroutine address and is set by the record processing routine. -The rest of these fields--LALM, ALST, MLST, and the LA-LL fields--are used to +The rest of these fields--LALM, ALST, MLST, and the LA-LU fields--are used to implement the monitors. For example, when LA is not equal to A, the value-change monitors are called for that field. -=fields VAL, SADR, LALM, ALST, MLST, LA - LL +=fields VAL, SADR, LALM, ALST, MLST, LA - LU =head2 Record Support @@ -161,7 +163,7 @@ recGblGetPrec() >>>. long (*get_graphic_double)(struct dbAddr *paddr, struct dbr_grDouble *p) Sets the upper display and lower display limits for a field. If the field is -VAL, A-L, LA-LL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, +VAL, A-U, LA-LU, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower maximum values for the field type will be used. @@ -170,7 +172,7 @@ upper and lower maximum values for the field type will be used. long (*get_control_double)(struct dbAddr *paddr, struct dbr_ctrlDouble *p) Sets the upper control and the lower control limits for a field. If the field is -VAL, A-L, LA-LL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, +VAL, A-U, LA-LU, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower maximum values for the field type will be used. @@ -241,7 +243,7 @@ met. =item * -Monitors for A-L are invoked if value has changed. +Monitors for A-U are invoked if value has changed. =item * @@ -500,6 +502,51 @@ processing. promptgroup("42 - Input G-L") interest(1) } + field(INPM,DBF_INLINK) { + prompt("Input M") + promptgroup("43 - Input M-R") + interest(1) + } + field(INPN,DBF_INLINK) { + prompt("Input N") + promptgroup("43 - Input M-R") + interest(1) + } + field(INPO,DBF_INLINK) { + prompt("Input O") + promptgroup("43 - Input M-R") + interest(1) + } + field(INPP,DBF_INLINK) { + prompt("Input P") + promptgroup("43 - Input M-R") + interest(1) + } + field(INPQ,DBF_INLINK) { + prompt("Input Q") + promptgroup("43 - Input M-R") + interest(1) + } + field(INPR,DBF_INLINK) { + prompt("Input R") + promptgroup("43 - Input M-R") + interest(1) + } + field(INPS,DBF_INLINK) { + prompt("Input S") + promptgroup("44 - Input S-U") + interest(1) + } + field(INPT,DBF_INLINK) { + prompt("Input T") + promptgroup("44 - Input S-U") + interest(1) + } + field(INPU,DBF_INLINK) { + prompt("Input U") + promptgroup("44 - Input S-U") + interest(1) + } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") @@ -655,6 +702,42 @@ processing. prompt("Value of Input L") pp(TRUE) } + field(M,DBF_DOUBLE) { + prompt("Value of Input M") + pp(TRUE) + } + field(N,DBF_DOUBLE) { + prompt("Value of Input N") + pp(TRUE) + } + field(O,DBF_DOUBLE) { + prompt("Value of Input O") + pp(TRUE) + } + field(P,DBF_DOUBLE) { + prompt("Value of Input P") + pp(TRUE) + } + field(Q,DBF_DOUBLE) { + prompt("Value of Input Q") + pp(TRUE) + } + field(R,DBF_DOUBLE) { + prompt("Value of Input R") + pp(TRUE) + } + field(S,DBF_DOUBLE) { + prompt("Value of Input S") + pp(TRUE) + } + field(T,DBF_DOUBLE) { + prompt("Value of Input T") + pp(TRUE) + } + field(U,DBF_DOUBLE) { + prompt("Value of Input U") + pp(TRUE) + } field(LA,DBF_DOUBLE) { prompt("Prev Value of A") special(SPC_NOMOD) @@ -715,6 +798,51 @@ processing. special(SPC_NOMOD) interest(3) } + field(LM,DBF_DOUBLE) { + prompt("Prev Value of M") + special(SPC_NOMOD) + interest(3) + } + field(LN,DBF_DOUBLE) { + prompt("Prev Value of N") + special(SPC_NOMOD) + interest(3) + } + field(LO,DBF_DOUBLE) { + prompt("Prev Value of O") + special(SPC_NOMOD) + interest(3) + } + field(LP,DBF_DOUBLE) { + prompt("Prev Value of P") + special(SPC_NOMOD) + interest(3) + } + field(LQ,DBF_DOUBLE) { + prompt("Prev Value of Q") + special(SPC_NOMOD) + interest(3) + } + field(LR,DBF_DOUBLE) { + prompt("Prev Value of R") + special(SPC_NOMOD) + interest(3) + } + field(LS,DBF_DOUBLE) { + prompt("Prev Value of S") + special(SPC_NOMOD) + interest(3) + } + field(LT,DBF_DOUBLE) { + prompt("Prev Value of T") + special(SPC_NOMOD) + interest(3) + } + field(LU,DBF_DOUBLE) { + prompt("Prev Value of U") + special(SPC_NOMOD) + interest(3) + } field(LALM,DBF_DOUBLE) { prompt("Last Value Alarmed") special(SPC_NOMOD) diff --git a/modules/libcom/src/as/asLib_lex.l b/modules/libcom/src/as/asLib_lex.l index df936ba34..b7f01f5c5 100644 --- a/modules/libcom/src/as/asLib_lex.l +++ b/modules/libcom/src/as/asLib_lex.l @@ -18,7 +18,7 @@ stringchar [^"\n\\] name [a-zA-Z0-9_\-+:.\[\]<>;] digit [0-9] punctuation [(){},] -link [A-L] +link [A-U] %{ static ASINPUTFUNCPTR *my_yyinput; diff --git a/modules/libcom/src/calc/calcPerform.c b/modules/libcom/src/calc/calcPerform.c index 81fbdc10c..8678a573d 100644 --- a/modules/libcom/src/calc/calcPerform.c +++ b/modules/libcom/src/calc/calcPerform.c @@ -86,6 +86,15 @@ LIBCOM_API long case FETCH_J: case FETCH_K: case FETCH_L: + case FETCH_M: + case FETCH_N: + case FETCH_O: + case FETCH_P: + case FETCH_Q: + case FETCH_R: + case FETCH_S: + case FETCH_T: + case FETCH_U: *++ptop = parg[op - FETCH_A]; break; @@ -101,6 +110,15 @@ LIBCOM_API long case STORE_J: case STORE_K: case STORE_L: + case STORE_M: + case STORE_N: + case STORE_O: + case STORE_P: + case STORE_Q: + case STORE_R: + case STORE_S: + case STORE_T: + case STORE_U: parg[op - STORE_A] = *ptop--; break; @@ -441,6 +459,15 @@ calcArgUsage(const char *pinst, unsigned long *pinputs, unsigned long *pstores) case FETCH_J: case FETCH_K: case FETCH_L: + case FETCH_M: + case FETCH_N: + case FETCH_O: + case FETCH_P: + case FETCH_Q: + case FETCH_R: + case FETCH_S: + case FETCH_T: + case FETCH_U: /* Don't claim to use an arg we already stored to */ inputs |= (1 << (op - FETCH_A)) & ~stores; break; @@ -457,6 +484,15 @@ calcArgUsage(const char *pinst, unsigned long *pinputs, unsigned long *pstores) case STORE_J: case STORE_K: case STORE_L: + case STORE_M: + case STORE_N: + case STORE_O: + case STORE_P: + case STORE_Q: + case STORE_R: + case STORE_S: + case STORE_T: + case STORE_U: stores |= (1 << (op - STORE_A)); break; diff --git a/modules/libcom/src/calc/postfix.c b/modules/libcom/src/calc/postfix.c index d9dce515d..2bc3e121e 100644 --- a/modules/libcom/src/calc/postfix.c +++ b/modules/libcom/src/calc/postfix.c @@ -117,20 +117,29 @@ static const ELEMENT operands[] = { {"LN", 7, 8, 0, UNARY_OPERATOR, LOG_E}, {"LOG", 7, 8, 0, UNARY_OPERATOR, LOG_10}, {"LOGE", 7, 8, 0, UNARY_OPERATOR, LOG_E}, +{"M", 0, 0, 1, OPERAND, FETCH_M}, {"MAX", 7, 8, 0, VARARG_OPERATOR,MAX}, {"MIN", 7, 8, 0, VARARG_OPERATOR,MIN}, +{"N", 0, 0, 1, OPERAND, FETCH_N}, {"NINT", 7, 8, 0, UNARY_OPERATOR, NINT}, {"NAN", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, {"NOT", 7, 8, 0, UNARY_OPERATOR, BIT_NOT}, +{"O", 0, 0, 1, OPERAND, FETCH_O}, +{"P", 0, 0, 1, OPERAND, FETCH_P}, {"PI", 0, 0, 1, OPERAND, CONST_PI}, +{"Q", 0, 0, 1, OPERAND, FETCH_Q}, +{"R", 0, 0, 1, OPERAND, FETCH_R}, {"R2D", 0, 0, 1, OPERAND, CONST_R2D}, {"RNDM", 0, 0, 1, OPERAND, RANDOM}, +{"S", 0, 0, 1, OPERAND, FETCH_S}, {"SIN", 7, 8, 0, UNARY_OPERATOR, SIN}, {"SINH", 7, 8, 0, UNARY_OPERATOR, SINH}, {"SQR", 7, 8, 0, UNARY_OPERATOR, SQU_RT}, {"SQRT", 7, 8, 0, UNARY_OPERATOR, SQU_RT}, +{"T", 0, 0, 1, OPERAND, FETCH_T}, {"TAN", 7, 8, 0, UNARY_OPERATOR, TAN}, {"TANH", 7, 8, 0, UNARY_OPERATOR, TANH}, +{"U", 0, 0, 1, OPERAND, FETCH_U}, {"VAL", 0, 0, 1, OPERAND, FETCH_VAL}, {"~", 7, 8, 0, UNARY_OPERATOR, BIT_NOT}, }; @@ -286,7 +295,7 @@ LIBCOM_API long case STORE_OPERATOR: if (pout == pdest || pstacktop > stack || - *--pout < FETCH_A || *pout > FETCH_L) { + *--pout < FETCH_A || *pout >= FETCH_A + CALCPERFORM_NARGS) { *perror = CALC_ERR_BAD_ASSIGNMENT; goto bad; } @@ -542,9 +551,13 @@ LIBCOM_API void "LITERAL_DOUBLE", "LITERAL_INT", "VAL", "FETCH_A", "FETCH_B", "FETCH_C", "FETCH_D", "FETCH_E", "FETCH_F", "FETCH_G", "FETCH_H", "FETCH_I", "FETCH_J", "FETCH_K", "FETCH_L", + "FETCH_M", "FETCH_N", "FETCH_O", "FETCH_P", "FETCH_Q", "FETCH_R", + "FETCH_S", "FETCH_T", "FETCH_U", /* Assignment */ "STORE_A", "STORE_B", "STORE_C", "STORE_D", "STORE_E", "STORE_F", "STORE_G", "STORE_H", "STORE_I", "STORE_J", "STORE_K", "STORE_L", + "STORE_M", "STORE_N", "STORE_O", "STORE_P", "STORE_Q", "STORE_R", + "STORE_S", "STORE_T", "STORE_U", /* Trigonometry Constants */ "CONST_PI", "CONST_D2R", diff --git a/modules/libcom/src/calc/postfix.h b/modules/libcom/src/calc/postfix.h index 07d9d6847..1e945252c 100644 --- a/modules/libcom/src/calc/postfix.h +++ b/modules/libcom/src/calc/postfix.h @@ -22,8 +22,11 @@ #include "libComAPI.h" -/** \brief Number of input arguments to a calc expression (A-L) */ -#define CALCPERFORM_NARGS 12 +/** \brief Number of input arguments to a calc expression (A-U) + * + * Since UNRELEASED the number of inputs has been increased from 12 to 21. + */ +#define CALCPERFORM_NARGS 21 /** \brief Size of the internal partial result stack */ #define CALCPERFORM_STACK 80 @@ -61,7 +64,7 @@ * * This is not a hard limit, just the default size for the database */ -#define MAX_INFIX_SIZE 100 +#define MAX_INFIX_SIZE 160 /** * \brief Size of a "standard" postfix buffer. * @@ -161,13 +164,13 @@ extern "C" { * angle from radians to degrees. * * -# **Variables** are used to provide inputs to an expression, and are - * named using the single letters \c A through \c L inclusive or the + * named using the single letters \c A through \c U inclusive or the * keyword \c VAL which refers to the previous result of this * calculation. * The software that makes use of the expression evaluation code should * document how the individual variables are given values. * For the calc and calcout record types the input links \c INPA through - * \c INPL can be used to obtain values from other record fields, and \c + * \c INPU can be used to obtain values from other record fields, and \c * VAL refers to the the VAL field (which can be overwritten from * outside the record via Channel Access or a database link). * @@ -323,7 +326,7 @@ LIBCOM_API long * Evaluates the postfix expression against a set ot input values. * * \param parg Pointer to an array of double values for the arguments - * \c A-L that can appear in the expression. + * \c A-U that can appear in the expression. * Note that the argument values may be modified if the expression uses * the assignment operator. * \param presult Where to put the calculated result, which may be a NaN @@ -345,8 +348,8 @@ LIBCOM_API long * for either of these pointers is legal if only the other is needed. * * The least significant bit (bit 0) of the bitmap at \c *pinputs will be set - * if the expression depends on the argument A, and so on through bit 11 for - * the argument L. An argument that is not used until after a value has been + * if the expression depends on the argument A, and so on through bit 20 for + * the argument U. An argument that is not used until after a value has been * assigned to it will not be set in the pinputs bitmap, thus the bits can * be used to determine whether a value needs to be supplied for their * associated argument or not for the purposes of evaluating the expression. diff --git a/modules/libcom/src/calc/postfixPvt.h b/modules/libcom/src/calc/postfixPvt.h index 3e84e1fb4..23eb35b46 100644 --- a/modules/libcom/src/calc/postfixPvt.h +++ b/modules/libcom/src/calc/postfixPvt.h @@ -13,7 +13,7 @@ */ /* Notes: - * 1. The FETCH_A through FETCH_L and STORE_A through STORE_L opcodes must + * 1. The FETCH_A through FETCH_U and STORE_A through STORE_U opcodes must * be contiguous. * 2. The LITERAL opcodes are followed by a binary representation of their * values, but these are not aligned properly. @@ -34,9 +34,13 @@ typedef enum { LITERAL_DOUBLE, LITERAL_INT, FETCH_VAL, FETCH_A, FETCH_B, FETCH_C, FETCH_D, FETCH_E, FETCH_F, FETCH_G, FETCH_H, FETCH_I, FETCH_J, FETCH_K, FETCH_L, + FETCH_M, FETCH_N, FETCH_O, FETCH_P, FETCH_Q, FETCH_R, + FETCH_S, FETCH_T, FETCH_U, /* Assignment */ STORE_A, STORE_B, STORE_C, STORE_D, STORE_E, STORE_F, STORE_G, STORE_H, STORE_I, STORE_J, STORE_K, STORE_L, + STORE_M, STORE_N, STORE_O, STORE_P, STORE_Q, STORE_R, + STORE_S, STORE_T, STORE_U, /* Trigonometry Constants */ CONST_PI, CONST_D2R, diff --git a/modules/libcom/test/epicsCalcTest.cpp b/modules/libcom/test/epicsCalcTest.cpp index 4f0bfbecb..feae60570 100644 --- a/modules/libcom/test/epicsCalcTest.cpp +++ b/modules/libcom/test/epicsCalcTest.cpp @@ -22,7 +22,8 @@ double doCalc(const char *expr) { /* Evaluate expression, return result */ double args[CALCPERFORM_NARGS] = { - 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, + 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0 }; char *rpn = (char*)malloc(INFIX_TO_POSTFIX_SIZE(strlen(expr)+1)); short err; @@ -48,7 +49,8 @@ void testCalc(const char *expr, double expected) { /* Evaluate expression, test against expected result */ bool pass = false; double args[CALCPERFORM_NARGS] = { - 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, + 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0 }; char *rpn = (char*)malloc(INFIX_TO_POSTFIX_SIZE(strlen(expr)+1)); short err; @@ -85,7 +87,8 @@ void testUInt32Calc(const char *expr, epicsUInt32 expected) { /* Evaluate expression, test against expected result */ bool pass = false; double args[CALCPERFORM_NARGS] = { - 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, + 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0 }; char *rpn = (char*)malloc(INFIX_TO_POSTFIX_SIZE(strlen(expr)+1)); short err; @@ -162,18 +165,27 @@ void testBadExpr(const char *expr, short expected_err) { #define testExpr(expr) testCalc(#expr, expr); /* These are the argument bits for testArgs */ -#define A_A 0x001 -#define A_B 0x002 -#define A_C 0x004 -#define A_D 0x008 -#define A_E 0x010 -#define A_F 0x020 -#define A_G 0x040 -#define A_H 0x080 -#define A_I 0x100 -#define A_J 0x200 -#define A_K 0x400 -#define A_L 0x800 +#define A_A 0x000001 +#define A_B 0x000002 +#define A_C 0x000004 +#define A_D 0x000008 +#define A_E 0x000010 +#define A_F 0x000020 +#define A_G 0x000040 +#define A_H 0x000080 +#define A_I 0x000100 +#define A_J 0x000200 +#define A_K 0x000400 +#define A_L 0x000800 +#define A_M 0x001000 +#define A_N 0x002000 +#define A_O 0x004000 +#define A_P 0x008000 +#define A_Q 0x010000 +#define A_R 0x020000 +#define A_S 0x040000 +#define A_T 0x080000 +#define A_U 0x100000 /* Macros and functions to make some expressions into valid C code */ @@ -296,9 +308,11 @@ MAIN(epicsCalcTest) { int repeat; const double a=1.0, b=2.0, c=3.0, d=4.0, e=5.0, f=6.0, - g=7.0, h=8.0, i=9.0, j=10.0, k=11.0, l=12.0; + g=7.0, h=8.0, i=9.0, j=10.0, k=11.0, l=12.0, + m=13.0, n=14.0, o=15.0, p=16.0, q=17.0, r=18.0, + s=19.0, t=20.0, u=21.0; - testPlan(637); + testPlan(687); /* LITERAL_OPERAND elements */ testExpr(0); @@ -335,6 +349,15 @@ MAIN(epicsCalcTest) testExpr(j); testExpr(k); testExpr(l); + testExpr(m); + testExpr(n); + testExpr(o); + testExpr(p); + testExpr(q); + testExpr(r); + testExpr(s); + testExpr(t); + testExpr(u); testExpr(PI); testExpr(D2R); testExpr(R2D); @@ -832,6 +855,15 @@ MAIN(epicsCalcTest) testCalc("j := 0; j", 0); testCalc("k := 0; k", 0); testCalc("l := 0; l", 0); + testCalc("m := 0; m", 0); + testCalc("n := 0; n", 0); + testCalc("o := 0; o", 0); + testCalc("p := 0; p", 0); + testCalc("q := 0; q", 0); + testCalc("r := 0; r", 0); + testCalc("s := 0; s", 0); + testCalc("t := 0; t", 0); + testCalc("u := 0; u", 0); testCalc("a; a := 0", a); testCalc("b; b := 0", b); @@ -845,6 +877,15 @@ MAIN(epicsCalcTest) testCalc("j; j := 0", j); testCalc("k; k := 0", k); testCalc("l; l := 0", l); + testCalc("m; m := 0", m); + testCalc("n; n := 0", n); + testCalc("o; o := 0", o); + testCalc("p; p := 0", p); + testCalc("q; q := 0", q); + testCalc("r; r := 0", r); + testCalc("s; s := 0", s); + testCalc("t; t := 0", t); + testCalc("u; u := 0", u); // Check relative precedences. testExpr(0 ? 1 : 2 | 4); // 0 1 @@ -921,8 +962,17 @@ MAIN(epicsCalcTest) testArgs("J", A_J, 0); testArgs("K", A_K, 0); testArgs("L", A_L, 0); - testArgs("A+B+C+D+E+F+G+H+I+J+K+L", - A_A|A_B|A_C|A_D|A_E|A_F|A_G|A_H|A_I|A_J|A_K|A_L, 0); + testArgs("M", A_M, 0); + testArgs("N", A_N, 0); + testArgs("O", A_O, 0); + testArgs("P", A_P, 0); + testArgs("Q", A_Q, 0); + testArgs("R", A_R, 0); + testArgs("S", A_S, 0); + testArgs("T", A_T, 0); + testArgs("U", A_U, 0); + testArgs("A+B+C+D+E+F+G+H+I+J+K+L+M+N+O+P+Q+R+S+T+U", + A_A|A_B|A_C|A_D|A_E|A_F|A_G|A_H|A_I|A_J|A_K|A_L|A_M|A_N|A_O|A_P|A_Q|A_R|A_S|A_T|A_U, 0); testArgs("0.1;A:=0", 0, A_A); testArgs("1.1;B:=0", 0, A_B); testArgs("2.1;C:=0", 0, A_C); @@ -935,6 +985,15 @@ MAIN(epicsCalcTest) testArgs("9.1;J:=0", 0, A_J); testArgs("10.1;K:=0", 0, A_K); testArgs("11.1;L:=0", 0, A_L); + testArgs("12.1;M:=0", 0, A_M); + testArgs("13.1;N:=0", 0, A_N); + testArgs("14.1;O:=0", 0, A_O); + testArgs("15.1;P:=0", 0, A_P); + testArgs("16.1;Q:=0", 0, A_Q); + testArgs("17.1;R:=0", 0, A_R); + testArgs("18.1;S:=0", 0, A_S); + testArgs("19.1;T:=0", 0, A_T); + testArgs("20.1;U:=0", 0, A_U); testArgs("12.1;A:=0;B:=A;C:=B;D:=C", 0, A_A|A_B|A_C|A_D); testArgs("13.1;B:=A;A:=B;C:=D;D:=C", A_A|A_D, A_A|A_B|A_C|A_D); @@ -955,6 +1014,11 @@ MAIN(epicsCalcTest) testBadExpr(":1", CALC_ERR_SYNTAX); testBadExpr("0,", CALC_ERR_BAD_SEPERATOR); testBadExpr("0)", CALC_ERR_PAREN_NOT_OPEN); + testBadExpr("V", CALC_ERR_SYNTAX); + testBadExpr("W", CALC_ERR_SYNTAX); + testBadExpr("X", CALC_ERR_SYNTAX); + testBadExpr("Y", CALC_ERR_SYNTAX); + testBadExpr("Z", CALC_ERR_SYNTAX); // Bit manipulations wrt bit 31 (bug lp:1514520) // using integer literals From 5c77c842a43a07d6b9a60e70a23ba06b2f80a0a9 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 31 Jul 2025 17:21:24 +0200 Subject: [PATCH 13/99] test improvements: NAN, DBR strings, floating point format * Allow testdbGetFieldEqual to check for NAN double and float values * Print DBR_xxx names instead of numbers * use %g instead of %e to format floating point values to improve readability --- modules/database/src/ioc/db/dbUnitTest.c | 44 ++++++++++++++---------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/modules/database/src/ioc/db/dbUnitTest.c b/modules/database/src/ioc/db/dbUnitTest.c index 671763636..3af9b39ce 100644 --- a/modules/database/src/ioc/db/dbUnitTest.c +++ b/modules/database/src/ioc/db/dbUnitTest.c @@ -35,6 +35,8 @@ #include "errSymTbl.h" #include "iocshRegisterCommon.h" +#define DBR_NAME(dbrType) (VALID_DB_REQ(dbrType) ? pamapdbfType[dbrType].strvalue+3 : "???") + static dbEventCtx testEvtCtx; static epicsMutexId testEvtLock; static ELLLIST testEvtList; /* holds testMonitor::node */ @@ -153,8 +155,8 @@ long testdbVPutField(const char* pv, short dbrType, va_list ap) OP(DBR_ENUM, int, enum16); #undef OP default: - testFail("invalid DBR: dbPutField(\"%s\", %d, ...)", - dbChannelName(chan), dbrType); + testFail("invalid DBR: dbPutField(\"%s\", DBR%s, ...)", + dbChannelName(chan), DBR_NAME(dbrType)); ret = S_db_badDbrtype; break; } @@ -174,7 +176,8 @@ void testdbPutFieldOk(const char* pv, int dbrType, ...) ret = testdbVPutField(pv, dbrType, ap); va_end(ap); - testOk(ret==0, "dbPutField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, ret, errSymMsg(ret)); + testOk(ret==0, "dbPutField(\"%s\", DBR%s, ...) -> %#lx (%s)", + pv, DBR_NAME(dbrType), ret, errSymMsg(ret)); } void testdbPutFieldFail(long status, const char* pv, int dbrType, ...) @@ -186,8 +189,8 @@ void testdbPutFieldFail(long status, const char* pv, int dbrType, ...) ret = testdbVPutField(pv, dbrType, ap); va_end(ap); - testOk(ret==status, "dbPutField(\"%s\", %d, ...) -> %#lx (%s) == %#lx (%s)", - pv, dbrType, status, errSymMsg(status), ret, errSymMsg(ret)); + testOk(ret==status, "dbPutField(\"%s\", DBR%s, ...) -> %#lx (%s) == %#lx (%s)", + pv, DBR_NAME(dbrType), status, errSymMsg(status), ret, errSymMsg(ret)); } void testdbGetFieldEqual(const char* pv, int dbrType, ...) @@ -213,10 +216,12 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap) status = dbChannelGetField(chan, dbrType, pod.bytes, NULL, &nReq, NULL); if (status) { - testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status)); + testFail("dbGetField(\"%s\", DBR%s, ...) -> %#lx (%s)", + pv, DBR_NAME(dbrType), status, errSymMsg(status)); goto done; } else if(nReq==0) { - testFail("dbGetField(\"%s\", %d, ...) -> zero length", pv, dbrType); + testFail("dbGetField(\"%s\", DBR%s, ...) -> zero length", + pv, DBR_NAME(dbrType)); goto done; } @@ -224,13 +229,14 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap) case DBR_STRING: { const char *expect = va_arg(ap, char*); testOk(strcmp(expect, pod.valStr)==0, - "dbGetField(\"%s\", %d) -> \"%s\" == \"%s\"", - pv, dbrType, expect, pod.valStr); + "dbGetField(\"%s\", DBR%s) -> \"%s\" == \"%s\"", + pv, DBR_NAME(dbrType), expect, pod.valStr); break; } #define OP(DBR,Type,mem,pat) case DBR: {Type expect = va_arg(ap,Type); \ - testOk(expect==pod.val.mem, "dbGetField(\"%s\", %d) -> " pat " == " pat, \ - pv, dbrType, expect, (Type)pod.val.mem); break;} + testOk(expect==pod.val.mem||((expect!=expect)&&(pod.val.mem!=pod.val.mem)), \ + "dbGetField(\"%s\", DBR%s) -> " pat " == " pat, \ + pv, DBR_NAME(dbrType), expect, (Type)pod.val.mem); break;} OP(DBR_CHAR, int, int8, "%d"); OP(DBR_UCHAR, int, uInt8, "%d"); @@ -240,12 +246,12 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap) 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"); + OP(DBR_FLOAT, double, float32, "%g"); + OP(DBR_DOUBLE, double, float64, "%g"); #undef OP default: - testFail("dbGetField(\"%s\", %d) -> unsupported dbf", pv, dbrType); + testFail("dbGetField(\"%s\", DBR%s) -> unsupported dbf", pv, DBR_NAME(dbrType)); } done: @@ -265,7 +271,7 @@ void testdbPutArrFieldOk(const char* pv, short dbrType, unsigned long count, con status = dbChannelPutField(chan, dbrType, pbuf, count); - testOk(status==0, "dbPutField(\"%s\", dbr=%d, count=%lu, ...) -> %ld", pv, dbrType, count, status); + testOk(status==0, "dbPutField(\"%s\", DBR%s, count=%lu, ...) -> %ld", pv, DBR_NAME(dbrType), count, status); done: if(chan) @@ -294,7 +300,7 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign status = dbChannelGetField(chan, dbfType, gbuf, NULL, &nRequest, NULL); if (status) { - testFail("dbGetField(\"%s\", %d, ...) -> %#lx", pv, dbfType, status); + testFail("dbGetField(\"%s\", DBR%s, ...) -> %#lx", pv, DBR_NAME(dbfType), status); } else { unsigned match = nRequest==cnt; @@ -327,14 +333,14 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign 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_FLOAT, float, "%g"); + OP(DBR_DOUBLE, double, "%g"); OP(DBR_ENUM, int, "%d"); #undef OP } } - testOk(match, "dbGetField(\"%s\", dbrType=%d, nRequest=%ld ...) match", pv, dbfType, nRequest); + testOk(match, "dbGetField(\"%s\", DBR%s, nRequest=%ld ...) match", pv, DBR_NAME(dbfType), nRequest); } done: From 640ced41c38a3686f71cfd1ff08fecca57624ed8 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Wed, 23 Jul 2025 09:41:41 +0200 Subject: [PATCH 14/99] increase number of dfanout outputs to 16 and add IVOA/IVOV fields --- documentation/new-notes/PR-688.md | 6 ++ modules/database/src/std/rec/dfanoutRecord.c | 25 ++++- .../src/std/rec/dfanoutRecord.dbd.pod | 95 ++++++++++++++++--- 3 files changed, 108 insertions(+), 18 deletions(-) create mode 100644 documentation/new-notes/PR-688.md diff --git a/documentation/new-notes/PR-688.md b/documentation/new-notes/PR-688.md new file mode 100644 index 000000000..f36ef1de8 --- /dev/null +++ b/documentation/new-notes/PR-688.md @@ -0,0 +1,6 @@ +### `dfanout` improvements + +The dfanout record now has invalid output handling with the usual fields +`IVOA` and `IVOV` just like other output records. + +The number of output links has been increased from 8 to 16. diff --git a/modules/database/src/std/rec/dfanoutRecord.c b/modules/database/src/std/rec/dfanoutRecord.c index 38569c34a..53b36718f 100644 --- a/modules/database/src/std/rec/dfanoutRecord.c +++ b/modules/database/src/std/rec/dfanoutRecord.c @@ -37,6 +37,7 @@ #include "recGbl.h" #include "special.h" #include "menuOmsl.h" +#include "menuIvoa.h" #define GEN_SIZE_OFFSET #include "dfanoutRecord.h" @@ -89,7 +90,7 @@ static void checkAlarms(dfanoutRecord *); static void monitor(dfanoutRecord *); static void push_values(dfanoutRecord *); -#define OUT_ARG_MAX 8 +#define OUT_ARG_MAX 16 static long init_record(struct dbCommon *pcommon, int pass) @@ -124,7 +125,25 @@ static long process(struct dbCommon *pcommon) /* Push out the data to all the forward links */ dbGetLink(&(prec->sell),DBR_USHORT,&(prec->seln),0,0); checkAlarms(prec); - push_values(prec); + if (prec->nsev < INVALID_ALARM) + push_values(prec); + else { + switch (prec->ivoa) { + case menuIvoaContinue_normally: + push_values(prec); + break; + case menuIvoaDon_t_drive_outputs: + break; + case menuIvoaSet_output_to_IVOV: + prec->val=prec->ivov; + push_values(prec); + break; + default : + status=-1; + recGblRecordError(S_db_badField,(void *)prec, + "dfanout:process Illegal IVOA field"); + } + } monitor(prec); recGblFwdLink(prec); prec->pact=FALSE; @@ -284,7 +303,7 @@ static void push_values(dfanoutRecord *prec) struct link *plink; /* structure of the link field */ int i; long status; - unsigned short state; + epicsUInt16 state; switch (prec->selm){ case (dfanoutSELM_All): diff --git a/modules/database/src/std/rec/dfanoutRecord.dbd.pod b/modules/database/src/std/rec/dfanoutRecord.dbd.pod index 074296450..f800dc4d4 100644 --- a/modules/database/src/std/rec/dfanoutRecord.dbd.pod +++ b/modules/database/src/std/rec/dfanoutRecord.dbd.pod @@ -10,10 +10,13 @@ =title Data Fanout Record (dfanout) The Data Fanout or "dfanout" record is used to forward data to up to -eight other records. It's similar to the fanout record except that the -capability to forward data has been added to it. If has no associated +16 other records. It's similar to the fanout record except that the +capability to forward data has been added to it. It has no associated device support. +Since UNRELEASED the number of output links has been increased +from 8 to 16 and IVOA and IVOV fields have been added. + =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. @@ -58,7 +61,7 @@ undergoes no conversions before it is sent out to the output links. =head3 Write Parameters -The OUTA-OUTH fields specify where VAL is to be sent. Each field that is to +The OUTA-OUTP fields specify where VAL is to be sent. Each field that is to forward data must specify an address to another record. See L
for information on specifying links. @@ -88,7 +91,7 @@ If SELM is C, then SELN will be treated as a bit mask. If bit zero OUTB will be written to, and so on. Thus when SELN==5, both OUTC and OUTA will be written to. -=fields SELL, SELM, SELN, OUTA - OUTH +=fields SELL, SELM, SELN, OUTA - OUTP =head3 Operator Display Parameters @@ -128,7 +131,12 @@ Parameters> for more about the record name (NAME) and description (DESC) fields. The possible alarm conditions for data fanouts are the SCAN, READ, INVALID, and limit alarms. The SCAN and READ alarms are called by the record -routines. The limit alarms are configured by the user in the HIHI, LOLO, +routines. The IVOA field specifies the action to take in this case. + +For an explanation of the IVOA and IVOV fields, see +L. + +The limit alarms are configured by the user in the HIHI, LOLO, HIGH, and LOW fields using floating point values. The limit alarms apply only to the VAL field. The severity for each of these limits is specified in the corresponding field (HHSV, LLSV, HSV, LSV) and can be either @@ -140,7 +148,7 @@ for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. -=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST +=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST, IVOA, IVOV =head3 Monitor Parameters @@ -227,6 +235,46 @@ hysteresis factors for monitor callbacks. promptgroup("50 - Output") interest(1) } + field(OUTI,DBF_OUTLINK) { + prompt("Output Spec I") + promptgroup("50 - Output") + interest(1) + } + field(OUTJ,DBF_OUTLINK) { + prompt("Output Spec J") + promptgroup("50 - Output") + interest(1) + } + field(OUTK,DBF_OUTLINK) { + prompt("Output Spec K") + promptgroup("50 - Output") + interest(1) + } + field(OUTL,DBF_OUTLINK) { + prompt("Output Spec L") + promptgroup("50 - Output") + interest(1) + } + field(OUTM,DBF_OUTLINK) { + prompt("Output Spec M") + promptgroup("50 - Output") + interest(1) + } + field(OUTN,DBF_OUTLINK) { + prompt("Output Spec N") + promptgroup("50 - Output") + interest(1) + } + field(OUTO,DBF_OUTLINK) { + prompt("Output Spec O") + promptgroup("50 - Output") + interest(1) + } + field(OUTP,DBF_OUTLINK) { + prompt("Output Spec P") + promptgroup("50 - Output") + interest(1) + } field(DOL,DBF_INLINK) { prompt("Desired Output Link") promptgroup("40 - Input") @@ -353,6 +401,17 @@ hysteresis factors for monitor callbacks. special(SPC_NOMOD) interest(3) } + field(IVOA,DBF_MENU) { + prompt("INVALID output action") + promptgroup("50 - Output") + interest(2) + menu(menuIvoa) + } + field(IVOV,DBF_DOUBLE) { + prompt("INVALID output value") + promptgroup("50 - Output") + interest(2) + } =head2 Record Support @@ -411,32 +470,38 @@ is called. =over =item 1. -The C routine first checks that DOL is not a constant link and -that OMSL is set to "closed_loop". If so, it retrieves a value through DOL -and places it into VAL. If no errors occur, UDF is set to FALSE. + +If OMSL is "closed_loop" and DOL is not a constant link read the new value +of VAL from DOL. If no errors occur, UDF is set to FALSE. =item 2. -PACT is set TRUE, and the record's timestamp is set. + +PACT is set TRUE and the record's timestamp is set. =item 3. + A value is fetched from SELL and placed into SELN. =item 4. + Alarms ranges are checked against the contents of the VAL field. =item 5. -VAL is then sent through the OUTA-OUTH links by calling C for -each link, conditional on the setting of SELM and the value in SELN. + +Check severity and then send the value through the OUTA-OUTP links, depending +on the setting of SELM and the value in SELN. + +See L +for information on how INVALID alarms affect output records. =item 6. + Value and archive monitors are posted on the VAL field if appropriate based on the settings of MDEL and ADEL respectively. =item 7. -The data fanout's forward link FLNK is processed. -=item 6. -PACT is set FALSE, and the C routine returns. +Scan forward link if necessary, set PACT FALSE, and return. =back From 4c40b9f4bb376d4e4709437c5391c6ee6f6af986 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 31 Jul 2025 16:49:32 +0200 Subject: [PATCH 15/99] make dfanoutTest processing synchronous --- modules/database/test/std/rec/dfanoutTest.c | 16 ++++------------ modules/database/test/std/rec/dfanoutTest.db | 4 ++-- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/modules/database/test/std/rec/dfanoutTest.c b/modules/database/test/std/rec/dfanoutTest.c index f033771e0..469d4b1fd 100644 --- a/modules/database/test/std/rec/dfanoutTest.c +++ b/modules/database/test/std/rec/dfanoutTest.c @@ -22,13 +22,10 @@ static const char *dfanout_receivers[] = {"test_dfanout_outa", "test_dfanout_out "test_dfanout_oute", "test_dfanout_outf", "test_dfanout_outg", "test_dfanout_outh"}; -static testMonitor *monitor; - void recTestIoc_registerRecordDeviceDriver(struct dbBase *); static void test_all(int val, int exception){ - testMonitorWait(monitor); // if exception < 0 or > 8 then it tests all. int i; for (i = 0; i < NELEMENTS(dfanout_receivers); ++i) { @@ -39,7 +36,7 @@ static void test_all(int val, int exception){ } static void test_all_output(void){ - + /* set output fields */ int i; for (i = 0; i < NELEMENTS(dfanout_OUT_pvs); ++i) { @@ -69,10 +66,9 @@ static void test_selm_specified() { for (val = 0; val < NELEMENTS(dfanout_receivers); ++val) { testdbPutFieldOk("test_dfanout_record.SELN", DBF_LONG, val + 1); testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, val + 1); - testMonitorWait(monitor); testdbGetFieldEqual(dfanout_receivers[val], DBF_LONG, val + 1); - + int not_seln, not_seln_val; for (not_seln = 0; not_seln < NELEMENTS(dfanout_receivers); ++not_seln) { if (not_seln == val) continue; @@ -101,12 +97,10 @@ static void test_selm_mask() { testdbPutFieldOk("test_dfanout_record.SELM", DBF_STRING, "All"); //Setting all values to 1 so we know what to compare with. testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 1); - testMonitorWait(monitor); testdbPutFieldOk("test_dfanout_record.SELM", DBF_STRING, "Mask"); testdbPutFieldOk("test_dfanout_record.SELN", DBF_LONG, mask); testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 9); - testMonitorWait(monitor); int item; for (item = 0; item < NELEMENTS(dfanout_receivers); ++item) { @@ -127,21 +121,19 @@ MAIN(dfanoutTest) { testPlan(3455); - testdbPrepare(); + testdbPrepare(); testdbReadDatabase("recTestIoc.dbd", NULL, NULL); recTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dfanoutTest.db", NULL, NULL); - + eltc(0); testIocInitOk(); eltc(1); - monitor = testMonitorCreate("test_dfanout_record", DBE_VALUE, 0); test_all_output(); test_selm_specified(); test_selm_mask(); - testMonitorDestroy(monitor); testIocShutdownOk(); testdbCleanup(); diff --git a/modules/database/test/std/rec/dfanoutTest.db b/modules/database/test/std/rec/dfanoutTest.db index b360ead8c..da9e21005 100644 --- a/modules/database/test/std/rec/dfanoutTest.db +++ b/modules/database/test/std/rec/dfanoutTest.db @@ -1,10 +1,10 @@ record(ao, "test_dfanout_src") { - + field(FLNK, "test_dfanout_record") } record(dfanout, "test_dfanout_record") { field(OMSL, "closed_loop") - field(DOL, "test_dfanout_src CP") + field(DOL, "test_dfanout_src") } record(ai, "test_dfanout_outa") { From bff913e4d15e6710437fb94d267e5e90b9a0edbe Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 31 Jul 2025 16:54:30 +0200 Subject: [PATCH 16/99] test new dfanout outputs --- modules/database/test/std/rec/dfanoutTest.c | 36 +++++++++++++------- modules/database/test/std/rec/dfanoutTest.db | 23 +++++++++++++ 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/modules/database/test/std/rec/dfanoutTest.c b/modules/database/test/std/rec/dfanoutTest.c index 469d4b1fd..6fb185220 100644 --- a/modules/database/test/std/rec/dfanoutTest.c +++ b/modules/database/test/std/rec/dfanoutTest.c @@ -12,27 +12,36 @@ #include "epicsThread.h" #include "dfanoutRecord.h" -static const char *dfanout_OUT_pvs[] = {"test_dfanout_record.OUTA", "test_dfanout_record.OUTB", +static const char *dfanout_OUT_pvs[] = { + "test_dfanout_record.OUTA", "test_dfanout_record.OUTB", "test_dfanout_record.OUTC", "test_dfanout_record.OUTD", "test_dfanout_record.OUTE", "test_dfanout_record.OUTF", - "test_dfanout_record.OUTG", "test_dfanout_record.OUTH"}; + "test_dfanout_record.OUTG", "test_dfanout_record.OUTH", + "test_dfanout_record.OUTI", "test_dfanout_record.OUTJ", + "test_dfanout_record.OUTK", "test_dfanout_record.OUTL", + "test_dfanout_record.OUTM", "test_dfanout_record.OUTN", + "test_dfanout_record.OUTO", "test_dfanout_record.OUTP"}; -static const char *dfanout_receivers[] = {"test_dfanout_outa", "test_dfanout_outb", +static const char *dfanout_receivers[] = { + "test_dfanout_outa", "test_dfanout_outb", "test_dfanout_outc", "test_dfanout_outd", "test_dfanout_oute", "test_dfanout_outf", - "test_dfanout_outg", "test_dfanout_outh"}; + "test_dfanout_outg", "test_dfanout_outh", + "test_dfanout_outi", "test_dfanout_outj", + "test_dfanout_outk", "test_dfanout_outl", + "test_dfanout_outm", "test_dfanout_outn", + "test_dfanout_outo", "test_dfanout_outp"}; void recTestIoc_registerRecordDeviceDriver(struct dbBase *); static void test_all(int val, int exception){ - // if exception < 0 or > 8 then it tests all. + // if mask == -1 then it tests all. int i; for (i = 0; i < NELEMENTS(dfanout_receivers); ++i) { if ( i == exception) continue; testdbGetFieldEqual(dfanout_receivers[i], DBF_LONG, val); } - } static void test_all_output(void){ @@ -54,6 +63,7 @@ static void test_all_output(void){ static void test_selm_specified() { /* Resetting values */ + testDiag("Testing Specified"); testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 0); test_all(0, -1); @@ -91,10 +101,11 @@ static void test_selm_mask() { testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 0); test_all(0, -1); - /* Resets values. Tests if fields in bitmask have been set */ - int mask; - for (mask = 0; mask <= 255; ++mask) { - + /* Testing all 65535 possible masks seems a bit excessive -- just test some patterns */ + epicsUInt32 mask = 0x100055aa; + while (1) { + testDiag("Testing mask 0x%04x", mask); + /* Resets values. Tests if fields in bitmask have been set */ testdbPutFieldOk("test_dfanout_record.SELM", DBF_STRING, "All"); //Setting all values to 1 so we know what to compare with. testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 1); @@ -112,14 +123,15 @@ static void test_selm_mask() { } } - + if (!mask) break; + mask >>= 1; } } MAIN(dfanoutTest) { - testPlan(3455); + testPlan(1005); testdbPrepare(); testdbReadDatabase("recTestIoc.dbd", NULL, NULL); diff --git a/modules/database/test/std/rec/dfanoutTest.db b/modules/database/test/std/rec/dfanoutTest.db index da9e21005..ebff7fb25 100644 --- a/modules/database/test/std/rec/dfanoutTest.db +++ b/modules/database/test/std/rec/dfanoutTest.db @@ -31,3 +31,26 @@ record(ai, "test_dfanout_outg") { record(ai, "test_dfanout_outh") { } +record(ai, "test_dfanout_outi") { +} + +record(ai, "test_dfanout_outj") { +} + +record(ai, "test_dfanout_outk") { +} + +record(ai, "test_dfanout_outl") { +} + +record(ai, "test_dfanout_outm") { +} + +record(ai, "test_dfanout_outn") { +} + +record(ai, "test_dfanout_outo") { +} + +record(ai, "test_dfanout_outp") { +} From c3c492d2cd42adc211e9bfec078beeb94b556a20 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 31 Jul 2025 17:53:21 +0200 Subject: [PATCH 17/99] test IVOA in dfanout --- modules/database/test/std/rec/dfanoutTest.c | 34 +++++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/modules/database/test/std/rec/dfanoutTest.c b/modules/database/test/std/rec/dfanoutTest.c index 6fb185220..14597868c 100644 --- a/modules/database/test/std/rec/dfanoutTest.c +++ b/modules/database/test/std/rec/dfanoutTest.c @@ -10,6 +10,7 @@ #include "dbAccess.h" #include "menuIvoa.h" #include "epicsThread.h" +#include "epicsMath.h" #include "dfanoutRecord.h" static const char *dfanout_OUT_pvs[] = { @@ -34,13 +35,13 @@ static const char *dfanout_receivers[] = { void recTestIoc_registerRecordDeviceDriver(struct dbBase *); -static void test_all(int val, int exception){ +static void test_all(double val, int exception){ // if mask == -1 then it tests all. int i; for (i = 0; i < NELEMENTS(dfanout_receivers); ++i) { if ( i == exception) continue; - testdbGetFieldEqual(dfanout_receivers[i], DBF_LONG, val); + testdbGetFieldEqual(dfanout_receivers[i], DBF_DOUBLE, val); } } @@ -126,12 +127,38 @@ static void test_selm_mask() { if (!mask) break; mask >>= 1; } +} +static void test_ivoa() { + + + testDiag("Testing IVOA = Continue normally"); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 1); + testdbPutFieldOk("test_dfanout_record.IVOA", DBF_STRING, "Continue normally"); + testdbPutFieldOk("test_dfanout_record.SELM", DBF_STRING, "All"); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_DOUBLE, epicsNAN); + test_all(epicsNAN, -1); + testdbGetFieldEqual("test_dfanout_record.VAL", DBF_DOUBLE, epicsNAN); + + testDiag("Testing IVOA = Don't drive outputs"); + testdbPutFieldOk("test_dfanout_record.IVOA", DBF_STRING, "Don't drive outputs"); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_DOUBLE, 1.2345); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_DOUBLE, epicsNAN); + test_all(1.2345, -1); + testdbGetFieldEqual("test_dfanout_record.VAL", DBF_DOUBLE, epicsNAN); + + testDiag("Testing IVOA = Set output to IVOV"); + testdbPutFieldOk("test_dfanout_record.IVOA", DBF_STRING, "Set output to IVOV"); + testdbPutFieldOk("test_dfanout_record.IVOV", DBF_DOUBLE, 3.1415); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_DOUBLE, 42); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_DOUBLE, epicsNAN); + test_all(3.1415, -1); + testdbGetFieldEqual("test_dfanout_record.VAL", DBF_DOUBLE, 3.1415); } MAIN(dfanoutTest) { - testPlan(1005); + testPlan(1067); testdbPrepare(); testdbReadDatabase("recTestIoc.dbd", NULL, NULL); @@ -146,6 +173,7 @@ MAIN(dfanoutTest) { test_all_output(); test_selm_specified(); test_selm_mask(); + test_ivoa(); testIocShutdownOk(); testdbCleanup(); From 187801c2e641f4f129ae67c9a07f0d0bbac1cd59 Mon Sep 17 00:00:00 2001 From: Simon Rose Date: Mon, 18 Aug 2025 09:44:16 +0200 Subject: [PATCH 18/99] Fix MSI tests when INSTALL_LOCATION is set --- modules/database/test/ioc/dbtemplate/msi.plt | 4 ++-- modules/database/test/ioc/dbtemplate/t13-substitute.txt | 2 +- modules/database/test/ioc/dbtemplate/t14-template.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/database/test/ioc/dbtemplate/msi.plt b/modules/database/test/ioc/dbtemplate/msi.plt index 09835d3ac..e8b9bb900 100644 --- a/modules/database/test/ioc/dbtemplate/msi.plt +++ b/modules/database/test/ioc/dbtemplate/msi.plt @@ -57,10 +57,10 @@ ok(msi('-I. -I.. -S ../t12-substitute.txt'), slurp('../t12-result.txt')); delete @ENV{ keys %envs }; # Not really needed # Substitution file, relative path includes -ok(msi('-I @TOP@/modules -S ../t13-substitute.txt'), slurp('../t13-result.txt')); +ok(msi('-I ../.. -S ../t13-substitute.txt'), slurp('../t13-result.txt')); # Template file, relative path includes -ok(msi('-I @TOP@/modules ../t14-template.txt'), slurp('../t14-result.txt')); +ok(msi('-I ../.. ../t14-template.txt'), slurp('../t14-result.txt')); # Test support routines diff --git a/modules/database/test/ioc/dbtemplate/t13-substitute.txt b/modules/database/test/ioc/dbtemplate/t13-substitute.txt index 63d3fca1f..5890df7f3 100644 --- a/modules/database/test/ioc/dbtemplate/t13-substitute.txt +++ b/modules/database/test/ioc/dbtemplate/t13-substitute.txt @@ -1,3 +1,3 @@ -file database/test/ioc/dbtemplate/t13-template.txt { +file dbtemplate/t13-template.txt { { a=foo } } diff --git a/modules/database/test/ioc/dbtemplate/t14-template.txt b/modules/database/test/ioc/dbtemplate/t14-template.txt index 276663f7a..b1de6175a 100644 --- a/modules/database/test/ioc/dbtemplate/t14-template.txt +++ b/modules/database/test/ioc/dbtemplate/t14-template.txt @@ -1,5 +1,5 @@ This is t14-template.txt -include "database/test/ioc/dbtemplate/t14-include.txt" +include "dbtemplate/t14-include.txt" End of t14-template.txt From 9ce921f4fdc954205fbafdd2173345c2b8b090a2 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 7 Aug 2025 07:16:36 -0700 Subject: [PATCH 19/99] fdManagerTest: quiet spurious failure One iteration should be enough, but not always, and not for all targets... --- modules/libcom/test/fdManagerTest.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/libcom/test/fdManagerTest.cpp b/modules/libcom/test/fdManagerTest.cpp index 0dc1b330c..ae9f5f79b 100644 --- a/modules/libcom/test/fdManagerTest.cpp +++ b/modules/libcom/test/fdManagerTest.cpp @@ -216,7 +216,8 @@ void testOnlyTimer() epicsTime now(epicsTime::getCurrent()); trig_timer.timer.start(trig, now+0.1); never_timer.timer.start(never, now+9999999.0); - mgr.process(0.2); + for(unsigned i=0; i<10 && !trig.expired; i++) + mgr.process(0.2); testOk1(trig.expired); testOk1(!never.expired); } From 2e26ec09a6304c9a58e143c61fef22e259830bbe Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 1 Sep 2025 17:23:01 -0700 Subject: [PATCH 20/99] posix: initialize pthread_attr for non-epics threads --- modules/libcom/src/osi/os/posix/osdThread.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c index db043dff6..9d46cd204 100644 --- a/modules/libcom/src/osi/os/posix/osdThread.c +++ b/modules/libcom/src/osi/os/posix/osdThread.c @@ -666,6 +666,8 @@ static epicsThreadOSD *createImplicit(void) pthreadInfo->tid = tid; pthreadInfo->osiPriority = 0; pthreadInfo->isOkToBlock = 1; + status = pthread_attr_init(&pthreadInfo->attr); + checkStatusOnce(status,"pthread_attr_init"); #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 if(pthread_getschedparam(tid,&pthreadInfo->schedPolicy,&pthreadInfo->schedParam) == 0) { From e4fbd83c93a68a1d9cb863ff341735388d246347 Mon Sep 17 00:00:00 2001 From: Simon Rose Date: Thu, 28 Aug 2025 16:04:01 +0200 Subject: [PATCH 21/99] Fetch the custom cfg/CONFIG_* files early If you overwrite INSTALL_LOCATION in CONFIG_SITE, then it would be the case that TOP_CFG_CONFIGS would point to those from EPICS base and not from the module that you are loading. Fetching these earlier fixes that issue. --- configure/CONFIG | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/configure/CONFIG b/configure/CONFIG index e72537db1..edf8d4f73 100644 --- a/configure/CONFIG +++ b/configure/CONFIG @@ -50,6 +50,9 @@ endif include $(CONFIG)/CONFIG_COMMON include $(CONFIG)/CONFIG_FILE_TYPE +# Fetch the module-specific CFGS before overwriting INSTALL_LOCATION in order to load them later +TOP_CFG_CONFIGS := $(wildcard $(INSTALL_CFG)/CONFIG*) + # Base-specific build options # include $(CONFIG)/CONFIG_BASE @@ -117,7 +120,6 @@ endif # Include $(INSTALL_CFG)/CONFIG* definitions # -TOP_CFG_CONFIGS = $(wildcard $(INSTALL_CFG)/CONFIG*) ifneq ($(TOP_CFG_CONFIGS),) include $(TOP_CFG_CONFIGS) endif From edd99a903bc14eb0caa28fd156e70adf3dd27a97 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 20 Sep 2025 09:36:09 -0500 Subject: [PATCH 22/99] doc --- modules/libcom/src/env/envDefs.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/libcom/src/env/envDefs.h b/modules/libcom/src/env/envDefs.h index c54a781c7..c6421374f 100644 --- a/modules/libcom/src/env/envDefs.h +++ b/modules/libcom/src/env/envDefs.h @@ -77,6 +77,12 @@ LIBCOM_API extern const ENV_PARAM IOCSH_PS1; LIBCOM_API extern const ENV_PARAM IOCSH_HISTSIZE; LIBCOM_API extern const ENV_PARAM IOCSH_HISTEDIT_DISABLE; LIBCOM_API extern const ENV_PARAM EPICS_ABORT_ON_ASSERT; +/** @brief List of all parameters. + * + * A NULL terminated array of all ENV_PARAM known to EPICS Base. + * This array is assembled during the EPICS Base build, and + * contains at least the preceeding parameters. + */ LIBCOM_API extern const ENV_PARAM *env_param_list[]; struct in_addr; From 7444936a22c00a8776f3bba9bed84acf91801b1c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 20 Sep 2025 09:36:20 -0500 Subject: [PATCH 23/99] colorize more errors/warnings --- modules/libcom/src/osi/os/default/gnuReadline.c | 5 +++-- modules/libcom/src/osi/os/posix/osdThread.c | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/libcom/src/osi/os/default/gnuReadline.c b/modules/libcom/src/osi/os/default/gnuReadline.c index a4a322569..78b16f6cc 100644 --- a/modules/libcom/src/osi/os/default/gnuReadline.c +++ b/modules/libcom/src/osi/os/default/gnuReadline.c @@ -16,6 +16,7 @@ #include #include +#include "errlog.h" #include "epicsExit.h" #include "envDefs.h" #include "epicsReadlinePvt.h" @@ -78,7 +79,7 @@ osdReadline (const char *prompt, struct readlineContext *context) line = malloc(linesize); if (line == NULL) { - printf("Out of memory!\n"); + fprintf(stderr, ERL_ERROR " osdReadline() Out of memory!\n"); return NULL; } if (prompt) { @@ -98,7 +99,7 @@ osdReadline (const char *prompt, struct readlineContext *context) linesize = linelen + 50; cp = (char *)realloc(line, linesize); if (cp == NULL) { - printf ("Out of memory!\n"); + fprintf(stderr, ERL_ERROR " osdReadline() Out of memory!\n"); free(line); line = NULL; break; diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c index 9d46cd204..8c3d8c848 100644 --- a/modules/libcom/src/osi/os/posix/osdThread.c +++ b/modules/libcom/src/osi/os/posix/osdThread.c @@ -473,16 +473,16 @@ void epicsThreadRealtimeLock(void) #ifdef __linux__ case ENOMEM: fprintf(stderr, "epicsThreadRealtimeLock " - "Warning: unable to lock memory. RLIMIT_MEMLOCK is too small or missing CAP_IPC_LOCK\n"); + ERL_WARNING ": unable to lock memory. RLIMIT_MEMLOCK is too small or missing CAP_IPC_LOCK\n"); break; case EPERM: fprintf(stderr, "epicsThreadRealtimeLock " - "Warning: unable to lock memory. missing CAP_IPC_LOCK\n"); + ERL_WARNING ": unable to lock memory. missing CAP_IPC_LOCK\n"); break; #endif default: fprintf(stderr, "epicsThreadRealtimeLock " - "Warning: Unable to lock the virtual address space.\n" + ERL_WARNING ": Unable to lock the virtual address space.\n" "VM page faults may harm real-time performance. errno=%d\n", err); } From 1cea3f1eed7210d7ff35019c35dea39db318e46e Mon Sep 17 00:00:00 2001 From: Madeline Park <137943653+madelinespark@users.noreply.github.com> Date: Wed, 8 Oct 2025 07:35:29 -0700 Subject: [PATCH 24/99] added prop(YES) annotations (#706) All Base record types annotated with `prop(YES)` as appropriate. --- .../database/src/std/rec/aSubRecord.dbd.pod | 2 +- .../database/src/std/rec/aaiRecord.dbd.pod | 9 +++--- .../database/src/std/rec/aaoRecord.dbd.pod | 9 +++--- modules/database/src/std/rec/aiRecord.dbd.pod | 24 +++++++------- modules/database/src/std/rec/aoRecord.dbd.pod | 28 ++++++++-------- modules/database/src/std/rec/biRecord.dbd.pod | 4 +-- modules/database/src/std/rec/boRecord.dbd.pod | 4 +-- .../database/src/std/rec/calcRecord.dbd.pod | 24 +++++++------- .../src/std/rec/calcoutRecord.dbd.pod | 24 +++++++------- .../src/std/rec/compressRecord.dbd.pod | 8 ++--- .../src/std/rec/dfanoutRecord.dbd.pod | 24 +++++++------- .../src/std/rec/int64inRecord.dbd.pod | 14 ++++---- .../src/std/rec/int64outRecord.dbd.pod | 18 +++++------ .../database/src/std/rec/longinRecord.dbd.pod | 18 +++++++---- .../src/std/rec/longoutRecord.dbd.pod | 22 +++++++------ .../src/std/rec/mbbiDirectRecord.dbd.pod | 1 + .../database/src/std/rec/mbbiRecord.dbd.pod | 32 +++++++++---------- .../src/std/rec/mbboDirectRecord.dbd.pod | 1 + .../database/src/std/rec/mbboRecord.dbd.pod | 32 +++++++++---------- .../database/src/std/rec/selRecord.dbd.pod | 23 ++++++------- .../src/std/rec/subArrayRecord.dbd.pod | 11 ++++--- .../database/src/std/rec/subRecord.dbd.pod | 24 +++++++------- .../src/std/rec/waveformRecord.dbd.pod | 8 ++--- 23 files changed, 189 insertions(+), 175 deletions(-) diff --git a/modules/database/src/std/rec/aSubRecord.dbd.pod b/modules/database/src/std/rec/aSubRecord.dbd.pod index 5131af881..86fd9e70d 100644 --- a/modules/database/src/std/rec/aSubRecord.dbd.pod +++ b/modules/database/src/std/rec/aSubRecord.dbd.pod @@ -198,7 +198,7 @@ Except when it doesn't. prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } =head3 Output Event Flag diff --git a/modules/database/src/std/rec/aaiRecord.dbd.pod b/modules/database/src/std/rec/aaiRecord.dbd.pod index 969f5800f..e77832585 100644 --- a/modules/database/src/std/rec/aaiRecord.dbd.pod +++ b/modules/database/src/std/rec/aaiRecord.dbd.pod @@ -319,7 +319,7 @@ Scan forward link if necessary, set PACT FALSE, and return. prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(INP,DBF_INLINK) { prompt("Input Specification") @@ -331,19 +331,19 @@ Scan forward link if necessary, set PACT FALSE, and return. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(NELM,DBF_ULONG) { prompt("Number of Elements") @@ -351,6 +351,7 @@ Scan forward link if necessary, set PACT FALSE, and return. special(SPC_NOMOD) interest(1) initial("1") + prop(YES) # get_graphic_double, get_control_double } field(FTVL,DBF_MENU) { prompt("Field Type of Value") diff --git a/modules/database/src/std/rec/aaoRecord.dbd.pod b/modules/database/src/std/rec/aaoRecord.dbd.pod index cbec0c9ba..c35b7454e 100644 --- a/modules/database/src/std/rec/aaoRecord.dbd.pod +++ b/modules/database/src/std/rec/aaoRecord.dbd.pod @@ -341,7 +341,7 @@ Scan forward link if necessary, set PACT FALSE, and return. prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(OUT,DBF_OUTLINK) { prompt("Output Specification") @@ -364,19 +364,19 @@ Scan forward link if necessary, set PACT FALSE, and return. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(NELM,DBF_ULONG) { prompt("Number of Elements") @@ -384,6 +384,7 @@ Scan forward link if necessary, set PACT FALSE, and return. special(SPC_NOMOD) interest(1) initial("1") + prop(YES) # get_graphic_double, get_control_double } field(FTVL,DBF_MENU) { prompt("Field Type of Value") diff --git a/modules/database/src/std/rec/aiRecord.dbd.pod b/modules/database/src/std/rec/aiRecord.dbd.pod index 2bedf9058..6cdad47eb 100644 --- a/modules/database/src/std/rec/aiRecord.dbd.pod +++ b/modules/database/src/std/rec/aiRecord.dbd.pod @@ -265,7 +265,7 @@ monitoring functionality. prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(LINR,DBF_MENU) { prompt("Linearization") @@ -294,19 +294,19 @@ monitoring functionality. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(AOFF,DBF_DOUBLE) { prompt("Adjustment Offset") @@ -331,35 +331,35 @@ monitoring functionality. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { @@ -367,7 +367,7 @@ monitoring functionality. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HSV,DBF_MENU) { @@ -375,7 +375,7 @@ monitoring functionality. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LSV,DBF_MENU) { @@ -383,7 +383,7 @@ monitoring functionality. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { diff --git a/modules/database/src/std/rec/aoRecord.dbd.pod b/modules/database/src/std/rec/aoRecord.dbd.pod index 2b838e092..34519ab52 100644 --- a/modules/database/src/std/rec/aoRecord.dbd.pod +++ b/modules/database/src/std/rec/aoRecord.dbd.pod @@ -328,7 +328,7 @@ for more information on simulation mode and its fields. prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(LINR,DBF_MENU) { prompt("Linearization") @@ -357,7 +357,7 @@ for more information on simulation mode and its fields. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(ROFF,DBF_ULONG) { prompt("Raw Offset") @@ -382,26 +382,26 @@ for more information on simulation mode and its fields. promptgroup("30 - Action") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_control_double } field(DRVL,DBF_DOUBLE) { prompt("Drive Low Limit") promptgroup("30 - Action") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_control_double } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double } field(AOFF,DBF_DOUBLE) { prompt("Adjustment Offset") @@ -420,35 +420,35 @@ for more information on simulation mode and its fields. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) #get_alarm_double } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) #get_alarm_double } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) #get_alarm_double } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) #get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) #get_alarm_double menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { @@ -456,7 +456,7 @@ for more information on simulation mode and its fields. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) #get_alarm_double menu(menuAlarmSevr) } field(HSV,DBF_MENU) { @@ -464,7 +464,7 @@ for more information on simulation mode and its fields. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) #get_alarm_double menu(menuAlarmSevr) } field(LSV,DBF_MENU) { @@ -472,7 +472,7 @@ for more information on simulation mode and its fields. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) #get_alarm_double menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { diff --git a/modules/database/src/std/rec/biRecord.dbd.pod b/modules/database/src/std/rec/biRecord.dbd.pod index d2c4672a8..b46c21f65 100644 --- a/modules/database/src/std/rec/biRecord.dbd.pod +++ b/modules/database/src/std/rec/biRecord.dbd.pod @@ -186,7 +186,7 @@ is not equal to VAL. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(ONAM,DBF_STRING) { prompt("One Name") @@ -194,7 +194,7 @@ is not equal to VAL. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_Str } field(RVAL,DBF_ULONG) { prompt("Raw Value") diff --git a/modules/database/src/std/rec/boRecord.dbd.pod b/modules/database/src/std/rec/boRecord.dbd.pod index 80d0ab609..84f22024f 100644 --- a/modules/database/src/std/rec/boRecord.dbd.pod +++ b/modules/database/src/std/rec/boRecord.dbd.pod @@ -239,7 +239,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(ONAM,DBF_STRING) { prompt("One Name") @@ -247,7 +247,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(RVAL,DBF_ULONG) { prompt("Raw Value") diff --git a/modules/database/src/std/rec/calcRecord.dbd.pod b/modules/database/src/std/rec/calcRecord.dbd.pod index 001f34efd..d51f1408b 100644 --- a/modules/database/src/std/rec/calcRecord.dbd.pod +++ b/modules/database/src/std/rec/calcRecord.dbd.pod @@ -693,60 +693,60 @@ manner for the VAL field. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(HOPR,DBF_DOUBLE) { prompt("High Operating Rng") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_dpuble, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_dpuble, get_control_double } field(HIHI,DBF_DOUBLE) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { @@ -754,7 +754,7 @@ manner for the VAL field. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HSV,DBF_MENU) { @@ -762,7 +762,7 @@ manner for the VAL field. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LSV,DBF_MENU) { @@ -770,7 +770,7 @@ manner for the VAL field. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(AFTC, DBF_DOUBLE) { diff --git a/modules/database/src/std/rec/calcoutRecord.dbd.pod b/modules/database/src/std/rec/calcoutRecord.dbd.pod index 82a006a5d..368ecc854 100644 --- a/modules/database/src/std/rec/calcoutRecord.dbd.pod +++ b/modules/database/src/std/rec/calcoutRecord.dbd.pod @@ -1080,60 +1080,60 @@ manner for the VAL field. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(HOPR,DBF_DOUBLE) { prompt("High Operating Rng") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(HIHI,DBF_DOUBLE) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { @@ -1141,7 +1141,7 @@ manner for the VAL field. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HSV,DBF_MENU) { @@ -1149,7 +1149,7 @@ manner for the VAL field. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LSV,DBF_MENU) { @@ -1157,7 +1157,7 @@ manner for the VAL field. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { diff --git a/modules/database/src/std/rec/compressRecord.dbd.pod b/modules/database/src/std/rec/compressRecord.dbd.pod index aa526f89c..f2ea5100c 100644 --- a/modules/database/src/std/rec/compressRecord.dbd.pod +++ b/modules/database/src/std/rec/compressRecord.dbd.pod @@ -449,26 +449,26 @@ Scan forward link if necessary, set PACT FALSE, and return. prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(OFF,DBF_ULONG) { prompt("Offset") diff --git a/modules/database/src/std/rec/dfanoutRecord.dbd.pod b/modules/database/src/std/rec/dfanoutRecord.dbd.pod index f800dc4d4..7d92d4c75 100644 --- a/modules/database/src/std/rec/dfanoutRecord.dbd.pod +++ b/modules/database/src/std/rec/dfanoutRecord.dbd.pod @@ -291,60 +291,60 @@ hysteresis factors for monitor callbacks. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(HIHI,DBF_DOUBLE) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { @@ -352,7 +352,7 @@ hysteresis factors for monitor callbacks. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HSV,DBF_MENU) { @@ -360,7 +360,7 @@ hysteresis factors for monitor callbacks. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LSV,DBF_MENU) { @@ -368,7 +368,7 @@ hysteresis factors for monitor callbacks. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { diff --git a/modules/database/src/std/rec/int64inRecord.dbd.pod b/modules/database/src/std/rec/int64inRecord.dbd.pod index a6e793d8d..51d0f3c40 100644 --- a/modules/database/src/std/rec/int64inRecord.dbd.pod +++ b/modules/database/src/std/rec/int64inRecord.dbd.pod @@ -135,47 +135,47 @@ monitoring deadband functionality. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(HOPR,DBF_INT64) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_INT64) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(HIHI,DBF_INT64) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_INT64) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_INT64) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_INT64) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") diff --git a/modules/database/src/std/rec/int64outRecord.dbd.pod b/modules/database/src/std/rec/int64outRecord.dbd.pod index 07e929a12..b9e8aacd5 100644 --- a/modules/database/src/std/rec/int64outRecord.dbd.pod +++ b/modules/database/src/std/rec/int64outRecord.dbd.pod @@ -171,61 +171,61 @@ monitoring deadband functionality. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(DRVH,DBF_INT64) { prompt("Drive High Limit") promptgroup("30 - Action") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_control_double } field(DRVL,DBF_INT64) { prompt("Drive Low Limit") promptgroup("30 - Action") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_control_double } field(HOPR,DBF_INT64) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_INT64) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(HIHI,DBF_INT64) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_INT64) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_INT64) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_INT64) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") diff --git a/modules/database/src/std/rec/longinRecord.dbd.pod b/modules/database/src/std/rec/longinRecord.dbd.pod index 94b8cf7f5..1cf425ab0 100644 --- a/modules/database/src/std/rec/longinRecord.dbd.pod +++ b/modules/database/src/std/rec/longinRecord.dbd.pod @@ -345,53 +345,54 @@ sets UDF to FALSE. read_longin returns the status of C. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(HOPR,DBF_LONG) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_LONG) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(HIHI,DBF_LONG) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_LONG) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_LONG) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_LONG) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { @@ -399,6 +400,7 @@ sets UDF to FALSE. read_longin returns the status of C. promptgroup("70 - Alarm") pp(TRUE) interest(1) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HSV,DBF_MENU) { @@ -406,6 +408,7 @@ sets UDF to FALSE. read_longin returns the status of C. promptgroup("70 - Alarm") pp(TRUE) interest(1) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LSV,DBF_MENU) { @@ -413,6 +416,7 @@ sets UDF to FALSE. read_longin returns the status of C. promptgroup("70 - Alarm") pp(TRUE) interest(1) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HYST,DBF_LONG) { diff --git a/modules/database/src/std/rec/longoutRecord.dbd.pod b/modules/database/src/std/rec/longoutRecord.dbd.pod index 03e33ed76..d9a2c6e3c 100644 --- a/modules/database/src/std/rec/longoutRecord.dbd.pod +++ b/modules/database/src/std/rec/longoutRecord.dbd.pod @@ -187,33 +187,33 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(DRVH,DBF_LONG) { prompt("Drive High Limit") promptgroup("30 - Action") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_control_double } field(DRVL,DBF_LONG) { prompt("Drive Low Limit") promptgroup("30 - Action") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_control_double } field(HOPR,DBF_LONG) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_LONG) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } =head3 Alarm Parameters @@ -247,34 +247,35 @@ to alarms that are common to all record types. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_LONG) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_LONG) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_LONG) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { @@ -282,6 +283,7 @@ to alarms that are common to all record types. promptgroup("70 - Alarm") pp(TRUE) interest(1) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HSV,DBF_MENU) { @@ -289,6 +291,7 @@ to alarms that are common to all record types. promptgroup("70 - Alarm") pp(TRUE) interest(1) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LSV,DBF_MENU) { @@ -296,6 +299,7 @@ to alarms that are common to all record types. promptgroup("70 - Alarm") pp(TRUE) interest(1) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HYST,DBF_LONG) { diff --git a/modules/database/src/std/rec/mbbiDirectRecord.dbd.pod b/modules/database/src/std/rec/mbbiDirectRecord.dbd.pod index 40e1fd103..43d45352c 100644 --- a/modules/database/src/std/rec/mbbiDirectRecord.dbd.pod +++ b/modules/database/src/std/rec/mbbiDirectRecord.dbd.pod @@ -102,6 +102,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. promptgroup("40 - Input") special(SPC_NOMOD) interest(1) + prop(YES) # get_precision } field(INP,DBF_INLINK) { prompt("Input Specification") diff --git a/modules/database/src/std/rec/mbbiRecord.dbd.pod b/modules/database/src/std/rec/mbbiRecord.dbd.pod index 483316e7f..8e208b064 100644 --- a/modules/database/src/std/rec/mbbiRecord.dbd.pod +++ b/modules/database/src/std/rec/mbbiRecord.dbd.pod @@ -276,7 +276,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(ONST,DBF_STRING) { prompt("One String") @@ -285,7 +285,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(TWST,DBF_STRING) { prompt("Two String") @@ -294,7 +294,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(THST,DBF_STRING) { prompt("Three String") @@ -303,7 +303,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(FRST,DBF_STRING) { prompt("Four String") @@ -312,7 +312,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(FVST,DBF_STRING) { prompt("Five String") @@ -321,7 +321,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(SXST,DBF_STRING) { prompt("Six String") @@ -330,7 +330,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(SVST,DBF_STRING) { prompt("Seven String") @@ -339,7 +339,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(EIST,DBF_STRING) { prompt("Eight String") @@ -348,7 +348,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(NIST,DBF_STRING) { prompt("Nine String") @@ -357,7 +357,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(TEST,DBF_STRING) { prompt("Ten String") @@ -366,7 +366,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(ELST,DBF_STRING) { prompt("Eleven String") @@ -375,7 +375,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(TVST,DBF_STRING) { prompt("Twelve String") @@ -384,7 +384,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(TTST,DBF_STRING) { prompt("Thirteen String") @@ -393,7 +393,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(FTST,DBF_STRING) { prompt("Fourteen String") @@ -402,7 +402,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(FFST,DBF_STRING) { prompt("Fifteen String") @@ -411,7 +411,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } =head3 Alarm Parameters diff --git a/modules/database/src/std/rec/mbboDirectRecord.dbd.pod b/modules/database/src/std/rec/mbboDirectRecord.dbd.pod index 84dd5e083..f0ae3d545 100644 --- a/modules/database/src/std/rec/mbboDirectRecord.dbd.pod +++ b/modules/database/src/std/rec/mbboDirectRecord.dbd.pod @@ -152,6 +152,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. promptgroup("50 - Output") special(SPC_NOMOD) interest(1) + prop(YES) # get_precision } field(DOL,DBF_INLINK) { prompt("Desired Output Link") diff --git a/modules/database/src/std/rec/mbboRecord.dbd.pod b/modules/database/src/std/rec/mbboRecord.dbd.pod index 3c3652a39..61189e2d6 100644 --- a/modules/database/src/std/rec/mbboRecord.dbd.pod +++ b/modules/database/src/std/rec/mbboRecord.dbd.pod @@ -354,7 +354,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(ONST,DBF_STRING) { prompt("One String") @@ -363,7 +363,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(TWST,DBF_STRING) { prompt("Two String") @@ -372,7 +372,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(THST,DBF_STRING) { prompt("Three String") @@ -381,7 +381,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(FRST,DBF_STRING) { prompt("Four String") @@ -390,7 +390,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(FVST,DBF_STRING) { prompt("Five String") @@ -399,7 +399,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(SXST,DBF_STRING) { prompt("Six String") @@ -408,7 +408,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(SVST,DBF_STRING) { prompt("Seven String") @@ -417,7 +417,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(EIST,DBF_STRING) { prompt("Eight String") @@ -426,7 +426,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(NIST,DBF_STRING) { prompt("Nine String") @@ -435,7 +435,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(TEST,DBF_STRING) { prompt("Ten String") @@ -444,7 +444,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(ELST,DBF_STRING) { prompt("Eleven String") @@ -453,7 +453,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(TVST,DBF_STRING) { prompt("Twelve String") @@ -462,7 +462,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(TTST,DBF_STRING) { prompt("Thirteen String") @@ -471,7 +471,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(FTST,DBF_STRING) { prompt("Fourteen String") @@ -480,7 +480,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(FFST,DBF_STRING) { prompt("Fifteen String") @@ -489,7 +489,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(ZRSV,DBF_MENU) { prompt("State Zero Severity") diff --git a/modules/database/src/std/rec/selRecord.dbd.pod b/modules/database/src/std/rec/selRecord.dbd.pod index 56d650366..0988d721b 100644 --- a/modules/database/src/std/rec/selRecord.dbd.pod +++ b/modules/database/src/std/rec/selRecord.dbd.pod @@ -299,6 +299,7 @@ Scan forward link if necessary, set PACT FALSE, and return. prompt("Display Precision") promptgroup("80 - Display") interest(1) + prop(YES) # get_precision } field(NVL,DBF_INLINK) { prompt("Index Value Location") @@ -370,54 +371,54 @@ Scan forward link if necessary, set PACT FALSE, and return. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(HOPR,DBF_DOUBLE) { prompt("High Operating Rng") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(HIHI,DBF_DOUBLE) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { @@ -425,7 +426,7 @@ Scan forward link if necessary, set PACT FALSE, and return. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HSV,DBF_MENU) { @@ -433,7 +434,7 @@ Scan forward link if necessary, set PACT FALSE, and return. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LSV,DBF_MENU) { @@ -441,7 +442,7 @@ Scan forward link if necessary, set PACT FALSE, and return. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { diff --git a/modules/database/src/std/rec/subArrayRecord.dbd.pod b/modules/database/src/std/rec/subArrayRecord.dbd.pod index 8a95b142e..e3054a938 100644 --- a/modules/database/src/std/rec/subArrayRecord.dbd.pod +++ b/modules/database/src/std/rec/subArrayRecord.dbd.pod @@ -334,7 +334,7 @@ INP is expected to point to an array field of a waveform record or similar. prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(FTVL,DBF_MENU) { prompt("Field Type of Value") @@ -353,26 +353,27 @@ INP is expected to point to an array field of a waveform record or similar. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(MALM,DBF_ULONG) { prompt("Maximum Elements") promptgroup("30 - Action") special(SPC_NOMOD) interest(1) - initial("1") + initial("1") + prop(YES) # get_graphic_double, get_control_double } field(NELM,DBF_ULONG) { prompt("Number of Elements") diff --git a/modules/database/src/std/rec/subRecord.dbd.pod b/modules/database/src/std/rec/subRecord.dbd.pod index 2c7cbd291..774664684 100644 --- a/modules/database/src/std/rec/subRecord.dbd.pod +++ b/modules/database/src/std/rec/subRecord.dbd.pod @@ -552,53 +552,53 @@ processing. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(HIHI,DBF_DOUBLE) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(BRSV,DBF_MENU) { prompt("Bad Return Severity") @@ -612,7 +612,7 @@ processing. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { @@ -620,7 +620,7 @@ processing. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HSV,DBF_MENU) { @@ -628,7 +628,7 @@ processing. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LSV,DBF_MENU) { @@ -636,7 +636,7 @@ processing. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { diff --git a/modules/database/src/std/rec/waveformRecord.dbd.pod b/modules/database/src/std/rec/waveformRecord.dbd.pod index db4ca362f..c47721d4b 100644 --- a/modules/database/src/std/rec/waveformRecord.dbd.pod +++ b/modules/database/src/std/rec/waveformRecord.dbd.pod @@ -417,7 +417,7 @@ routine and NORD is also set at that time. prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(INP,DBF_INLINK) { prompt("Input Specification") @@ -429,19 +429,19 @@ routine and NORD is also set at that time. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(NELM,DBF_ULONG) { prompt("Number of Elements") From 4c4f2f075f58b91f1b711d248607cb67ff0a38eb Mon Sep 17 00:00:00 2001 From: Sky Brewer Date: Wed, 23 Jul 2025 16:14:35 +0200 Subject: [PATCH 25/99] Update README with quick start Adds a quick install and run a softIoc documentation to the README Also adds some up to date links at the top of the page. --- README | 10 +-------- README.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 README.md diff --git a/README b/README index 942d00cb3..da2492989 100644 --- a/README +++ b/README @@ -13,15 +13,7 @@ this distribution. --------------------------------------------------------- -Installation and release information can be found in the -various files in the documentation subdirectory. - -Additional information about EPICS including mailing list -archives and subscription instructions, documentation and -training materials, additional components, links to other -websites etc. is available on the EPICS home page at - https://epics.anl.gov/ +For more information, see the README.md file. $Format:%cD$ $Format:%H$ -https://code.launchpad.net/epics-base diff --git a/README.md b/README.md new file mode 100644 index 000000000..cc7c15370 --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ + +# EPICS Base + +EPICS (Experimental Physics and Industrial Control System) is a set of software +tools and applications which provide a software infrastructure for use in +building distributed control systems to operate devices such as Particle +Accelerators, Large Experiments and major Telescopes. EPICS Base is the central +core of the control system toolkit. More details can be found at the +[About page of the official website](https://epics-controls.org/about-epics/) + +## Links + +- [Official Website](https://epics-controls.org/) +- [Original Website](https://epics.anl.gov/) +- [Repository](https://github.com/epics-base/epics-base) + +### Documentation + +- [Documentation](https://docs.epics-controls.org/en/latest/) +- [Documentation Repository](https://github.com/epics-docs/epics-docs) + +### Community Communication + +- [Tech-Talk Mailing List](https://epics.anl.gov/tech-talk/) +- [Matrix Rooms](https://matrix.to/#/#epics:epics-controls.org) +- [News](https://epics-controls.org/news-and-events/) + +## Quick Install + +Download a release from the +[Downloads page](https://epics-controls.org/resources-and-support/base/downloads) +and unpack it. Inside the unpacked folder run: + +```bash +make +``` + +For more information on how to install on your system see the +[Installation page](https://docs.epics-controls.org/en/latest/getting-started/installation.html) +of the documentation. + +### Quick run a softIOC + +After building, you can run an example soft-IOC (Input/Output Controller) +which uses the pvAccess network protocol. + +```bash +./bin/*/softIoc -x first +``` + +You can then run `dbl` to get: + +```bash +epics> dbl +first:BaseVersion +first:exit +epics> +``` + +## License + +EPICS Base is distributed subject to a Software License +Agreement found in the file [LICENSE](./LICENSE) that is included with +this distribution. From d00ce1bc0ab6f14ee8bbfb9c2b4d20bfd701a988 Mon Sep 17 00:00:00 2001 From: Jeremy Lorelli Date: Wed, 8 Oct 2025 07:30:41 -0700 Subject: [PATCH 26/99] gitignore cleanup + comments --- .gitignore | 11 +++++------ src/template/base/top/.gitignore | 8 +++----- src/template/ext/top/.gitignore | 8 +++----- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 046630981..2f54c20c5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +# Before adding patterns here, please read the gitignore +# documentation at https://git-scm.com/docs/gitignore /cfg/ /bin/ /lib/ @@ -11,11 +13,8 @@ /modules/RELEASE.*.local /modules/Makefile.local O.*/ -/QtC-* -/.qtc_* -/.vscode/ -*.orig *.log -.*.swp -.DS_Store .iocsh_history + +# Common files generated by other tools +.DS_Store diff --git a/src/template/base/top/.gitignore b/src/template/base/top/.gitignore index a3960f2d2..8b9a1c9c0 100644 --- a/src/template/base/top/.gitignore +++ b/src/template/base/top/.gitignore @@ -2,6 +2,9 @@ # # SPDX-License-Identifier: EPICS +# Before adding patterns here, please read the gitignore +# documentation at https://git-scm.com/docs/gitignore + # Install directories /bin/ /cfg/ @@ -28,9 +31,4 @@ O.*/ # Common files created by other tools -/QtC-* -/.vscode/ -*.orig -*.log -.*.swp .DS_Store diff --git a/src/template/ext/top/.gitignore b/src/template/ext/top/.gitignore index 51edcf53e..febbdf4d6 100644 --- a/src/template/ext/top/.gitignore +++ b/src/template/ext/top/.gitignore @@ -2,6 +2,9 @@ # # SPDX-License-Identifier: EPICS +# Before adding patterns here, please read the gitignore +# documentation at https://git-scm.com/docs/gitignore + # Install directories /bin/ /cfg/ @@ -19,9 +22,4 @@ O.*/ # Common files created by other tools -/QtC-* -/.vscode/ -*.orig -*.log -.*.swp .DS_Store From acd1aef2a02f9934ca091ffb7e54ebfe9388a36a Mon Sep 17 00:00:00 2001 From: Evan Daykin Date: Wed, 8 Oct 2025 10:44:27 -0400 Subject: [PATCH 27/99] Warn to stderr when discarding CPP modifier for outlink (#608) * warn to stderr when un-setting inapplicable CPP flag on output links * Warn and unset CP flag for output links * use errlogprintf instead of fprintf for dbParseLink CP/CPP warning * Pass source record name to dbParseLink for debug information when modifier is discarded * make CP/CPP discard warning less verbose * Include field name of affected record when warning of CP/CPP discard --- modules/database/src/ioc/db/dbAccess.c | 2 +- modules/database/src/ioc/dbStatic/dbStaticLib.c | 16 +++++++++++----- modules/database/src/ioc/dbStatic/dbStaticPvt.h | 2 +- modules/database/test/ioc/db/dbPutLinkTest.c | 16 +++++++++++++--- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index a6c040d45..ca6627287 100644 --- a/modules/database/src/ioc/db/dbAccess.c +++ b/modules/database/src/ioc/db/dbAccess.c @@ -1095,7 +1095,7 @@ static long dbPutFieldLink(DBADDR *paddr, return S_db_badDbrtype; } - status = dbParseLink(pstring, pfldDes->field_type, &link_info); + status = dbParseLink(pstring, pfldDes->field_type, &link_info, precord->name, pfldDes->name); if (status) return status; diff --git a/modules/database/src/ioc/dbStatic/dbStaticLib.c b/modules/database/src/ioc/dbStatic/dbStaticLib.c index 3e892b11f..08225c5c8 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticLib.c +++ b/modules/database/src/ioc/dbStatic/dbStaticLib.c @@ -2214,7 +2214,7 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec) if(!plink->text) continue; - if(dbParseLink(plink->text, pflddes->field_type, &link_info)!=0) { + if(dbParseLink(plink->text, pflddes->field_type, &link_info, prec->name, pflddes->name)!=0) { /* This was already parsed once when ->text was set. * Any syntax error messages were printed at that time. */ @@ -2243,7 +2243,7 @@ void dbFreeLinkInfo(dbLinkInfo *pinfo) pinfo->target = NULL; } -long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo) +long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, const char *recname, const char *fieldname) { char *pstr; size_t len; @@ -2380,7 +2380,13 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo) /* filter modifiers based on link type */ switch(ftype) { case DBF_INLINK: /* accept all */ break; - case DBF_OUTLINK: pinfo->modifiers &= ~pvlOptCPP; break; + case DBF_OUTLINK: + if(pinfo->modifiers & (pvlOptCPP|pvlOptCP)){ + errlogPrintf(ERL_WARNING ": Discarding CP/CPP modifier in CA output link from %s.%s to %s.\n", + recname, fieldname, pinfo->target); + } + pinfo->modifiers &= ~(pvlOptCPP|pvlOptCP); + break; case DBF_FWDLINK: pinfo->modifiers &= pvlOptCA; break; } } @@ -2618,7 +2624,7 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring) dbLinkInfo link_info; DBLINK *plink = (DBLINK *)pfield; - status = dbParseLink(pstring, pflddes->field_type, &link_info); + status = dbParseLink(pstring, pflddes->field_type, &link_info, dbGetRecordName(pdbentry), dbGetFieldName(pdbentry)); if (status) break; if (plink->type==CONSTANT && plink->value.constantStr==NULL) { @@ -3602,7 +3608,7 @@ void dbReportDeviceConfig(dbBase *pdbbase, FILE *report) if (plink->text) { /* Not yet parsed */ dbLinkInfo linfo; - if (dbParseLink(plink->text, pdbentry->pflddes->field_type, &linfo)) + if (dbParseLink(plink->text, pdbentry->pflddes->field_type, &linfo, dbGetRecordName(pdbentry), dbGetFieldName(pdbentry))) continue; linkType = linfo.ltype; diff --git a/modules/database/src/ioc/dbStatic/dbStaticPvt.h b/modules/database/src/ioc/dbStatic/dbStaticPvt.h index 033fde40c..32287a149 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticPvt.h +++ b/modules/database/src/ioc/dbStatic/dbStaticPvt.h @@ -74,7 +74,7 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec); /* Parse link string. no record locks needed. * on success caller must free pinfo->target */ -DBCORE_API long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo); +DBCORE_API long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, const char *recordname, const char *fieldname); /* Check if link type allow the parsed link value pinfo * to be assigned to the given link. * Record containing plink must be locked. diff --git a/modules/database/test/ioc/db/dbPutLinkTest.c b/modules/database/test/ioc/db/dbPutLinkTest.c index 2b7d3a518..a53214f6d 100644 --- a/modules/database/test/ioc/db/dbPutLinkTest.c +++ b/modules/database/test/ioc/db/dbPutLinkTest.c @@ -92,7 +92,7 @@ static void testLinkParse(void) for (;td->str; td++) { int i, N; testDiag("Parsing \"%s\"", td->str); - testOk(dbParseLink(td->str, DBF_INLINK, &info) == 0, "Parser returned OK"); + testOk(dbParseLink(td->str, DBF_INLINK, &info, "dummy","INP") == 0, "Parser returned OK"); if (!testOk(info.ltype == td->info.ltype, "Link type value")) testDiag("Expected %d, got %d", td->info.ltype, info.ltype); if (td->info.target && info.target) @@ -121,6 +121,16 @@ static void testLinkParse(void) dbFreeLinkInfo(&info); } + info.modifiers |= pvlOptCPP; + dbParseLink("something CPP", DBF_OUTLINK, &info, "dummy","OUT"); + testOk(info.modifiers == 0, "CPP modifier was discarded"); + dbFreeLinkInfo(&info); + + info.modifiers |= pvlOptCP; + dbParseLink("something CP", DBF_OUTLINK, &info, "dummy","OUT"); + testOk(info.modifiers == 0, "CP modifier was discarded"); + dbFreeLinkInfo(&info); + testIocShutdownOk(); testdbCleanup(); @@ -155,7 +165,7 @@ static void testLinkFailParse(void) eltc(1); for(;*td; td++) { - testOk(dbParseLink(*td, DBF_INLINK, &info) == S_dbLib_badField, + testOk(dbParseLink(*td, DBF_INLINK, &info, "dummy","INP") == S_dbLib_badField, "dbParseLink correctly rejected \"%s\"", *td); } @@ -705,7 +715,7 @@ void testTSEL(void) MAIN(dbPutLinkTest) { - testPlan(352); + testPlan(354); testLinkParse(); testLinkFailParse(); testCADBSet(); From bd8c35d8e7bea4a69799e11ab13d65deb16fed49 Mon Sep 17 00:00:00 2001 From: Jeremy Lorelli Date: Tue, 23 Sep 2025 09:16:29 -0700 Subject: [PATCH 28/99] Implement epicsMessageQueueSend/epicsMessageQueueReceive to resolve link error These were defined as macros in osdMessageQueue.h, but declared as prototypes in epcisMessageQueue.h public header. We need to implement them as C functions for these to actually be usable. --- .../src/osi/os/RTEMS-posix/osdMessageQueue.c | 16 ++++++++++++++++ .../src/osi/os/RTEMS-posix/osdMessageQueue.h | 2 -- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.c b/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.c index 7ccdc878d..fd55fa005 100644 --- a/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.c +++ b/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.c @@ -74,6 +74,22 @@ LIBCOM_API void epicsStdCall epicsMessageQueueDestroy( free(id); } +LIBCOM_API int epicsStdCall epicsMessageQueueSend( + epicsMessageQueueId id, + void *message, + unsigned int messageSize) +{ + return mq_send(id->id, (const char*)message, messageSize, 0); +} + +LIBCOM_API int epicsStdCall epicsMessageQueueReceive( + epicsMessageQueueId id, + void *message, + unsigned int messageSize) +{ + return mq_receive(id->id, (char*)message, messageSize, NULL); +} + LIBCOM_API int epicsStdCall epicsMessageQueueTrySend( epicsMessageQueueId id, diff --git a/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.h b/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.h index 95e66e73e..a3fc30bd4 100644 --- a/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.h +++ b/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.h @@ -23,6 +23,4 @@ struct epicsMessageQueueOSD { mqd_t id; char name[24]; }; -#define epicsMessageQueueSend(q,m,l) (mq_send((q)->id, (const char*)(m), (l), 0)) -#define epicsMessageQueueReceive(q,m,s) (mq_receive((q)->id, (char*)(m), (s), NULL)) From eff502217c3dad1b403c4a56ac240fd39210b32c Mon Sep 17 00:00:00 2001 From: Jeremy Lorelli Date: Fri, 19 Sep 2025 21:42:11 -0700 Subject: [PATCH 29/99] Use OP_SYS_LDFLAGS for -Wl,--gc-sections OP_SYS_LDLIBS ends up on the GeSys object link command line, which ends up breaking things. When linking GeSys objects, a symbol root is not defined using -u or -e, which is required for --gc-sections to work. --- configure/os/CONFIG.Common.RTEMS-beagleboneblack | 2 +- configure/os/CONFIG.Common.RTEMS-pc686 | 3 +-- configure/os/CONFIG.Common.RTEMS-qoriq_e500 | 2 +- configure/os/CONFIG.Common.RTEMS-xilinx_zynq_a9_qemu | 2 +- configure/os/CONFIG.Common.RTEMS-xilinx_zynq_zedboard | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/configure/os/CONFIG.Common.RTEMS-beagleboneblack b/configure/os/CONFIG.Common.RTEMS-beagleboneblack index 4b6107b05..edda91b9d 100644 --- a/configure/os/CONFIG.Common.RTEMS-beagleboneblack +++ b/configure/os/CONFIG.Common.RTEMS-beagleboneblack @@ -9,7 +9,7 @@ RTEMS_BSP = beagleboneblack RTEMS_TARGET_CPU = arm GNU_TARGET = arm-rtems -OP_SYS_LDLIBS += -Wl,--gc-sections +OP_SYS_LDFLAGS += -Wl,--gc-sections ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/beagleboneblack/lib/ include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-pc686 b/configure/os/CONFIG.Common.RTEMS-pc686 index bfe4c26e4..5d30b17d8 100644 --- a/configure/os/CONFIG.Common.RTEMS-pc686 +++ b/configure/os/CONFIG.Common.RTEMS-pc686 @@ -20,7 +20,6 @@ define MUNCH_CMD $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< $@ endef -OP_SYS_LDLIBS += -Wl,--gc-sections ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/pc686/lib/ include $(CONFIG)/os/CONFIG.Common.RTEMS @@ -28,7 +27,7 @@ include $(CONFIG)/os/CONFIG.Common.RTEMS # # Put text segment where it will work with etherboot # -OP_SYS_LDFLAGS += -Wl,-Ttext,0x100000 +OP_SYS_LDFLAGS += -Wl,-Ttext,0x100000 -Wl,--gc-sections # This check must appear after the above include diff --git a/configure/os/CONFIG.Common.RTEMS-qoriq_e500 b/configure/os/CONFIG.Common.RTEMS-qoriq_e500 index 94c7e825e..234c2e4f4 100644 --- a/configure/os/CONFIG.Common.RTEMS-qoriq_e500 +++ b/configure/os/CONFIG.Common.RTEMS-qoriq_e500 @@ -20,7 +20,7 @@ ARCH_DEP_CFLAGS += -DRTEMS_HAS_ALTIVEC #ARCH_DEP_CFLAGS += -I$(RTEMS_BASE)/powerpc-rtems5/qoriq_e500/lib/include #OP_SYS_LDLIBS += -lbspExt #does not use posix stuff ... want to ignore -OP_SYS_LDLIBS += -Wl,--gc-sections +OP_SYS_LDFLAGS += -Wl,--gc-sections #ARCH_DEP_LDFLAGS = -mcpu=8540 -meabi -msdata=sysv -mstrict-align -mspe -mabi=spe -mfloat-gprs=double ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/$(RTEMS_BSP)/lib diff --git a/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_a9_qemu b/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_a9_qemu index 83b518b69..51cd286ae 100644 --- a/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_a9_qemu +++ b/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_a9_qemu @@ -12,7 +12,7 @@ GNU_TARGET = arm-rtems #use dhcp/bootp ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL -OP_SYS_LDLIBS += -Wl,--gc-sections +OP_SYS_LDFLAGS += -Wl,--gc-sections ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/xilinx_zynq_a9_qemu/lib/ diff --git a/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_zedboard b/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_zedboard index bac33eb91..f745b15d6 100644 --- a/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_zedboard +++ b/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_zedboard @@ -9,7 +9,7 @@ RTEMS_BSP = xilinx_zynq_zedboard RTEMS_TARGET_CPU = arm GNU_TARGET = arm-rtems -OP_SYS_LDLIBS += -Wl,--gc-sections +OP_SYS_LDFLAGS += -Wl,--gc-sections ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/xilinx_zynq_zedboard/lib/ From d7635413410761a6866c6b39b5bbe8642a6f5643 Mon Sep 17 00:00:00 2001 From: bsbevins <145619550+bsbevins@users.noreply.github.com> Date: Wed, 8 Oct 2025 09:57:30 -0500 Subject: [PATCH 30/99] Allow CA clients to determine the server protocol version (#711) * Allow CA clients to determine the server protocol version Adds a call to the CA client API that allows a client to determine the server's protocol minor version number. This is needed to allow the ca-nameserver to report a server's protocol version correctly to a client. * ca_host_minor_protocol return for disconnected channels ca_host_minor_protocol now explicitly returns CA_UKN_MINOR_VERSION for a disconnected channel. --- modules/ca/src/client/cac.h | 7 +++++++ modules/ca/src/client/cacChannel.cpp | 7 +++++++ modules/ca/src/client/cacIO.h | 3 ++- modules/ca/src/client/cadef.h | 11 +++++++++++ modules/ca/src/client/nciu.cpp | 7 +++++++ modules/ca/src/client/nciu.h | 2 ++ modules/ca/src/client/netiiu.cpp | 6 ++++++ modules/ca/src/client/netiiu.h | 2 ++ modules/ca/src/client/oldAccess.h | 2 ++ modules/ca/src/client/oldChannelNotify.cpp | 10 ++++++++++ modules/ca/src/client/tcpiiu.cpp | 7 +++++++ modules/ca/src/client/udpiiu.cpp | 6 ++++++ modules/ca/src/client/udpiiu.h | 4 +++- modules/ca/src/client/virtualCircuit.h | 2 ++ 14 files changed, 74 insertions(+), 2 deletions(-) diff --git a/modules/ca/src/client/cac.h b/modules/ca/src/client/cac.h index 79dcbaa9b..8fcb7c563 100644 --- a/modules/ca/src/client/cac.h +++ b/modules/ca/src/client/cac.h @@ -203,6 +203,7 @@ public: void destroyIIU ( tcpiiu & iiu ); const char * pLocalHostName (); + const resTable < tcpiiu, caServerID > & getServerTable(); private: epicsSingleton < localHostName > :: reference _refLocalHostName; @@ -424,4 +425,10 @@ inline double cac :: return this->connTMO; } +inline const resTable < tcpiiu, caServerID > & cac :: + getServerTable() +{ + return this->serverTable; +} + #endif // ifndef INC_cac_H diff --git a/modules/ca/src/client/cacChannel.cpp b/modules/ca/src/client/cacChannel.cpp index 33345967c..a76d6e957 100644 --- a/modules/ca/src/client/cacChannel.cpp +++ b/modules/ca/src/client/cacChannel.cpp @@ -129,6 +129,13 @@ unsigned cacChannel::getHostName ( return 0u; } +unsigned cacChannel::getHostMinorProtocol ( + epicsGuard < epicsMutex > &) const throw () +{ + epicsThreadOnce ( & cacChannelIdOnce, cacChannelSetup, 0); + return 0u; +} + // the default is to assume that it is a locally hosted channel const char * cacChannel::pHostName ( epicsGuard < epicsMutex > & ) const throw () diff --git a/modules/ca/src/client/cacIO.h b/modules/ca/src/client/cacIO.h index 760d8dd72..777155047 100644 --- a/modules/ca/src/client/cacIO.h +++ b/modules/ca/src/client/cacIO.h @@ -246,7 +246,8 @@ public: // !! deprecated, avoid use !! virtual const char * pHostName ( epicsGuard < epicsMutex > & guard ) const throw (); - + virtual unsigned getHostMinorProtocol ( + epicsGuard < epicsMutex > &) const throw () ; // exceptions class badString {}; class badType {}; diff --git a/modules/ca/src/client/cadef.h b/modules/ca/src/client/cadef.h index e7ab45cbe..5c60a06f7 100644 --- a/modules/ca/src/client/cadef.h +++ b/modules/ca/src/client/cadef.h @@ -1460,6 +1460,17 @@ LIBCA_API const char * epicsStdCall ca_host_name (chid channel); LIBCA_API unsigned epicsStdCall ca_get_host_name ( chid pChan, char *pBuf, unsigned bufLength ); +/** \brief Return the minor protocol version number used by the host to + * which a channel is cuurently connected. + * + * \param[in] pChan channel identifier + * \returns The minor protocol version number. + * If the channel is disconnected CA_UKN_MINOR_VERSION is returned. + */ +LIBCA_API unsigned epicsStdCall ca_host_minor_protocol (chid pChan); + +#define HAS_CA_HOST_MINOR_PROTOCOL + /** \brief Call their function with their argument whenever * a new fd is added or removed. * diff --git a/modules/ca/src/client/nciu.cpp b/modules/ca/src/client/nciu.cpp index a862cb228..fc5d3cb4c 100644 --- a/modules/ca/src/client/nciu.cpp +++ b/modules/ca/src/client/nciu.cpp @@ -410,6 +410,13 @@ const char * nciu::pHostName ( return this->piiu->pHostName ( guard ); } +unsigned nciu::getHostMinorProtocol ( + epicsGuard < epicsMutex > & guard) const throw () +{ + return this->piiu->getHostMinorProtocol ( + guard ); +} + bool nciu::ca_v42_ok ( epicsGuard < epicsMutex > & guard ) const { diff --git a/modules/ca/src/client/nciu.h b/modules/ca/src/client/nciu.h index d909aa917..7e94fe1b8 100644 --- a/modules/ca/src/client/nciu.h +++ b/modules/ca/src/client/nciu.h @@ -183,6 +183,8 @@ public: unsigned getHostName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLen ) const throw (); + unsigned getHostMinorProtocol ( + epicsGuard < epicsMutex > &) const throw (); void writeException ( epicsGuard < epicsMutex > &, epicsGuard < epicsMutex > &, int status, const char *pContext, unsigned type, arrayElementCount count ); diff --git a/modules/ca/src/client/netiiu.cpp b/modules/ca/src/client/netiiu.cpp index c73732af6..04a5fb524 100644 --- a/modules/ca/src/client/netiiu.cpp +++ b/modules/ca/src/client/netiiu.cpp @@ -116,6 +116,12 @@ const char * netiiu::pHostName ( return pHostNameNetIIU; } +unsigned netiiu::getHostMinorProtocol ( + epicsGuard < epicsMutex > & ) const throw () +{ + return CA_UKN_MINOR_VERSION; +} + osiSockAddr netiiu::getNetworkAddress ( epicsGuard < epicsMutex > & ) const { diff --git a/modules/ca/src/client/netiiu.h b/modules/ca/src/client/netiiu.h index 298337bd3..d21ed8428 100644 --- a/modules/ca/src/client/netiiu.h +++ b/modules/ca/src/client/netiiu.h @@ -43,6 +43,8 @@ public: unsigned bufLength ) const throw () = 0; virtual const char * pHostName ( epicsGuard < epicsMutex > & ) const throw () = 0; + virtual unsigned getHostMinorProtocol ( + epicsGuard < epicsMutex > & ) const throw (); virtual bool ca_v41_ok ( epicsGuard < epicsMutex > & ) const = 0; virtual bool ca_v42_ok ( diff --git a/modules/ca/src/client/oldAccess.h b/modules/ca/src/client/oldAccess.h index 2c39d2768..3e8edb914 100644 --- a/modules/ca/src/client/oldAccess.h +++ b/modules/ca/src/client/oldAccess.h @@ -64,6 +64,8 @@ public: chid pChan, char * pBuf, unsigned bufLength ); friend const char * epicsStdCall ca_host_name ( chid pChan ); + friend unsigned epicsStdCall ca_host_minor_protocol ( + chid pChan ); friend const char * epicsStdCall ca_name ( chid pChan ); friend void epicsStdCall ca_set_puser ( diff --git a/modules/ca/src/client/oldChannelNotify.cpp b/modules/ca/src/client/oldChannelNotify.cpp index 71688c94c..0a3512827 100644 --- a/modules/ca/src/client/oldChannelNotify.cpp +++ b/modules/ca/src/client/oldChannelNotify.cpp @@ -193,6 +193,16 @@ const char * epicsStdCall ca_host_name ( return pChan->io.pHostName ( guard ); } +/* + * ca_host_minorProtocol () + */ +unsigned epicsStdCall ca_host_minor_protocol ( + chid pChan ) +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + return pChan->io.getHostMinorProtocol( guard ); +} + /* * ca_set_puser () */ diff --git a/modules/ca/src/client/tcpiiu.cpp b/modules/ca/src/client/tcpiiu.cpp index 30f6fad17..b8ebaac32 100644 --- a/modules/ca/src/client/tcpiiu.cpp +++ b/modules/ca/src/client/tcpiiu.cpp @@ -1804,6 +1804,13 @@ const char * tcpiiu::pHostName ( return this->hostNameCacheInstance.pointer (); } +unsigned tcpiiu::getHostMinorProtocol ( + epicsGuard < epicsMutex > & guard) const throw () +{ + guard.assertIdenticalMutex ( this->mutex ); + return this->minorProtocolVersion; +} + void tcpiiu::disconnectAllChannels ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard, diff --git a/modules/ca/src/client/udpiiu.cpp b/modules/ca/src/client/udpiiu.cpp index d36e2c5b4..bfe3d628e 100644 --- a/modules/ca/src/client/udpiiu.cpp +++ b/modules/ca/src/client/udpiiu.cpp @@ -1342,6 +1342,12 @@ const char * udpiiu::pHostName ( return netiiu::pHostName ( cacGuard ); } +unsigned udpiiu::getHostMinorProtocol ( + epicsGuard < epicsMutex > & cacGuard ) const throw () +{ + return netiiu::getHostMinorProtocol ( cacGuard ); +} + bool udpiiu::ca_v42_ok ( epicsGuard < epicsMutex > & cacGuard ) const { diff --git a/modules/ca/src/client/udpiiu.h b/modules/ca/src/client/udpiiu.h index aba79be5d..abc3fa757 100644 --- a/modules/ca/src/client/udpiiu.h +++ b/modules/ca/src/client/udpiiu.h @@ -239,7 +239,9 @@ private: unsigned bufLength ) const throw (); const char * pHostName ( epicsGuard < epicsMutex > & ) const throw (); - bool ca_v41_ok ( + unsigned getHostMinorProtocol ( + epicsGuard < epicsMutex > & ) const throw (); + bool ca_v41_ok ( epicsGuard < epicsMutex > & ) const; bool ca_v42_ok ( epicsGuard < epicsMutex > & ) const; diff --git a/modules/ca/src/client/virtualCircuit.h b/modules/ca/src/client/virtualCircuit.h index 42af12f5f..0f43ea95e 100644 --- a/modules/ca/src/client/virtualCircuit.h +++ b/modules/ca/src/client/virtualCircuit.h @@ -168,6 +168,8 @@ public: unsigned getHostName ( epicsGuard < epicsMutex > &, char *pBuf, unsigned bufLength ) const throw (); + unsigned getHostMinorProtocol ( + epicsGuard < epicsMutex > &) const throw (); bool alive ( epicsGuard < epicsMutex > & ) const; bool connecting ( From 271f20faa05a3fe1e79ede7532de98db6776de0a Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 27 Aug 2025 09:16:49 -0700 Subject: [PATCH 31/99] expand dbEvent synchronization Changes method of waiting in db_flush_extra_labor_event(), which also blocks if labor is pending. Adds testMonitorSync() --- modules/database/src/ioc/db/dbEvent.c | 79 +++++++++++++----------- modules/database/src/ioc/db/dbUnitTest.c | 11 ++++ modules/database/src/ioc/db/dbUnitTest.h | 28 ++++++++- 3 files changed, 81 insertions(+), 37 deletions(-) diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c index c910d4692..a60f2572b 100644 --- a/modules/database/src/ioc/db/dbEvent.c +++ b/modules/database/src/ioc/db/dbEvent.c @@ -557,6 +557,40 @@ static void event_remove ( struct event_que *ev_que, pevent->npend--; } +/* synchronize with worker thread. + * + * On return, any previously pending events or extra labor have been handled. + * + * caller must lock evUser->lock + */ +static +void db_sync_event (struct event_user * const evUser) +{ + /* grab current cycle counter, then wait for it to change */ + epicsUInt32 curSeq = evUser->pflush_seq; + event_waiter wait; + wait.wake = epicsEventCreate(epicsEventEmpty); /* failure allowed */ + + ellAdd(&evUser->waiters, &wait.node); + do { + epicsMutexUnlock( evUser->lock ); + /* ensure worker will cycle at least once */ + epicsEventMustTrigger(evUser->ppendsem); + + if(wait.wake) { + epicsEventMustWait(wait.wake); + } else { + epicsThreadSleep(0.01); /* ick. but better than cantProceed() */ + } + + epicsMutexMustLock ( evUser->lock ); + } while(curSeq == evUser->pflush_seq); + ellDelete(&evUser->waiters, &wait.node); + /* destroy under lock to ensure epicsEventMustTrigger() has returned */ + if(wait.wake) + epicsEventDestroy(wait.wake); +} + /* * DB_CANCEL_EVENT() * @@ -594,34 +628,9 @@ void db_cancel_event (dbEventSubscription event) UNLOCKEVQUE (que); if(sync) { - /* cycle through worker */ - struct event_user *evUser = que->evUser; - epicsUInt32 curSeq; - event_waiter wait; - wait.wake = epicsEventCreate(epicsEventEmpty); /* may fail */ - - epicsMutexMustLock ( evUser->lock ); - ellAdd(&evUser->waiters, &wait.node); - /* grab current cycle counter, then wait for it to change */ - curSeq = evUser->pflush_seq; - do { - epicsMutexUnlock( evUser->lock ); - /* ensure worker will cycle at least once */ - epicsEventMustTrigger(evUser->ppendsem); - - if(wait.wake) { - epicsEventMustWait(wait.wake); - } else { - epicsThreadSleep(0.01); /* ick. but better than cantProceed() */ - } - - epicsMutexMustLock ( evUser->lock ); - } while(curSeq == evUser->pflush_seq); - ellDelete(&evUser->waiters, &wait.node); - /* destroy under lock to ensure epicsEventMustTrigger() has returned */ - if(wait.wake) - epicsEventDestroy(wait.wake); - epicsMutexUnlock( evUser->lock ); + epicsMutexMustLock ( que->evUser->lock ); + db_sync_event(que->evUser); + epicsMutexUnlock( que->evUser->lock ); } } @@ -635,10 +644,10 @@ void db_flush_extra_labor_event (dbEventCtx ctx) struct event_user * const evUser = (struct event_user *) ctx; epicsMutexMustLock ( evUser->lock ); - while ( evUser->extraLaborBusy ) { - epicsMutexUnlock ( evUser->lock ); - epicsThreadSleep(0.1); - epicsMutexMustLock ( evUser->lock ); + if ( evUser->extraLaborBusy || (evUser->extra_labor && evUser->extralabor_sub) ) { + db_sync_event(evUser); + // At this point, original labor completed. + // Do not wait for any additional labor queued afterwards. } epicsMutexUnlock ( evUser->lock ); } @@ -1027,9 +1036,7 @@ static void event_task (void *pParm) * labor to this task */ epicsMutexMustLock ( evUser->lock ); - evUser->extraLaborBusy = TRUE; if ( evUser->extra_labor && evUser->extralabor_sub ) { - evUser->extra_labor = FALSE; pExtraLaborSub = evUser->extralabor_sub; pExtraLaborArg = evUser->extralabor_arg; } @@ -1037,12 +1044,14 @@ static void event_task (void *pParm) pExtraLaborSub = NULL; pExtraLaborArg = NULL; } + evUser->extra_labor = FALSE; if ( pExtraLaborSub ) { + evUser->extraLaborBusy = TRUE; epicsMutexUnlock ( evUser->lock ); (*pExtraLaborSub)(pExtraLaborArg); epicsMutexMustLock ( evUser->lock ); + evUser->extraLaborBusy = FALSE; } - evUser->extraLaborBusy = FALSE; for ( ev_que = &evUser->firstque; ev_que; ev_que = ev_que->nextque ) { /* unlock during iteration is safe as event_que will not be free'd */ diff --git a/modules/database/src/ioc/db/dbUnitTest.c b/modules/database/src/ioc/db/dbUnitTest.c index 3af9b39ce..a11afee3a 100644 --- a/modules/database/src/ioc/db/dbUnitTest.c +++ b/modules/database/src/ioc/db/dbUnitTest.c @@ -434,6 +434,17 @@ void testMonitorWait(testMonitor *mon) } } +static void dummylabor(void* unused) {(void)unused;} + +void testMonitorSync(testMonitor *mon) +{ + // db_flush_extra_labor_event() only blocks if there is actual labor pending + (void)db_add_extra_labor_event(testEvtCtx, dummylabor, NULL); + (void)db_post_extra_labor(testEvtCtx); + db_flush_extra_labor_event(testEvtCtx); + (void)db_add_extra_labor_event(testEvtCtx, NULL, NULL); +} + unsigned testMonitorCount(testMonitor *mon, unsigned reset) { unsigned count; diff --git a/modules/database/src/ioc/db/dbUnitTest.h b/modules/database/src/ioc/db/dbUnitTest.h index f697b07fa..fbacb65aa 100644 --- a/modules/database/src/ioc/db/dbUnitTest.h +++ b/modules/database/src/ioc/db/dbUnitTest.h @@ -139,21 +139,45 @@ DBCORE_API dbCommon* testdbRecordPtr(const char* pv); typedef struct testMonitor testMonitor; -/** Setup monitoring the named PV for changes */ +/** Setup monitoring the named PV for changes + * + * @param[in] pvname Requested PV name. Must be valid for dbChannelCreate(). + * @param[in] dbe_mask A bitwise or of DBE_VALUE and friends. + * @param[in] opt Currently unused. Set to zero. + * @returns Newly allocated testMonitor object, which caller must testMonitorDestroy() + * + * Calls testAbort() on failure. Will never return NULL. + * + * @since 3.16.0.1 + */ DBCORE_API testMonitor* testMonitorCreate(const char* pvname, unsigned dbe_mask, unsigned opt); -/** Stop monitoring */ +/** Stop monitoring + * + * @since 3.16.0.1 + */ DBCORE_API void testMonitorDestroy(testMonitor*); /** Return immediately if it has been updated since create, last wait, * or reset (count w/ reset=1). * Otherwise, block until the value of the target PV is updated. + * + * @since 3.16.0.1 */ DBCORE_API void testMonitorWait(testMonitor*); +/** Synchronize with dbEvent working for subscription. + * + * On return, any updates previously posted for this subscriptions have been delivered. + * + * @since UNRELEASED + */ +DBCORE_API void testMonitorSync(testMonitor*); /** Return the number of monitor events which have occured since create, * or a previous reset (called reset=1). * Calling w/ reset=0 only returns the count. * Calling w/ reset=1 resets the count to zero and ensures that the next * wait will block unless subsequent events occur. Returns the previous * count. + * + * @since 3.16.0.1 */ DBCORE_API unsigned testMonitorCount(testMonitor*, unsigned reset); From 232d9bec10d60b88e4f1f6b5d1d68a5aaa9ccbd0 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 23 Sep 2025 17:06:29 +0200 Subject: [PATCH 32/99] MSVC does not support designated initializers before C++20 --- modules/libcom/src/fdmgr/fdManager.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/modules/libcom/src/fdmgr/fdManager.cpp b/modules/libcom/src/fdmgr/fdManager.cpp index 9e133d2ad..e25a0f787 100644 --- a/modules/libcom/src/fdmgr/fdManager.cpp +++ b/modules/libcom/src/fdmgr/fdManager.cpp @@ -187,19 +187,12 @@ LIBCOM_API void fdManager::process(double delay) ++ioPending; #ifdef FDMGR_USE_POLL -#if __cplusplus >= 201100L - priv->pollfds.emplace_back(pollfd{ - .fd = iter->getFD(), - .events = WIN_POLLEVENT_FILTER(PollEvents[iter->getType()]) - }); -#else struct pollfd pollfd; pollfd.fd = iter->getFD(); pollfd.events = WIN_POLLEVENT_FILTER(PollEvents[iter->getType()]); pollfd.revents = 0; priv->pollfds.push_back(pollfd); #endif -#endif #ifdef FDMGR_USE_SELECT FD_SET(iter->getFD(), &priv->fdSets[iter->getType()]); From 3600924f9989a07e71e82f199cb775f272ea1969 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Wed, 23 Jul 2025 11:22:05 +0200 Subject: [PATCH 33/99] better guesses for wrong field names --- modules/database/src/ioc/dbStatic/dbLexRoutines.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c index bbd5bac93..117f214ab 100644 --- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c +++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c @@ -1209,7 +1209,15 @@ static void dbRecordField(char *name,char *value) double bestSim = -1.0; const dbFldDes *bestFld = NULL; dbCopyEntryContents(pdbentry, &temp); + const char* guess = + strcmp(name, "OUT") == 0 ? "INP" : + strcmp(name, "INP") == 0 ? "OUT" : + NULL; for(status = dbFirstField(&temp, 0); !status; status = dbNextField(&temp, 0)) { + if (guess && strcmp(temp.pflddes->name, guess) == 0) { + bestFld = temp.pflddes; + break; + } double sim = epicsStrSimilarity(name, temp.pflddes->name); if(!bestFld || sim > bestSim) { bestSim = sim; From a781896e283dd8e6d3de35747ded2641eec53833 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Fri, 25 Jul 2025 11:39:44 +0200 Subject: [PATCH 34/99] map for fields and more --- .../database/src/ioc/dbStatic/dbLexRoutines.c | 115 ++++++++++++++++-- 1 file changed, 104 insertions(+), 11 deletions(-) diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c index 117f214ab..b96aa17fd 100644 --- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c +++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c @@ -1190,6 +1190,17 @@ static void dbRecordHead(char *recordType, char *name, int visible) dbVisibleRecord(pdbentry); } +static const char* dbFieldConfusionMap [] = { + "INP","OUT", + "DOL","INP", + "ZNAM","ZRST", + "ONAM","ONST", + "INPA-J","DOL0-9", + "INPK-P","DOLA-F", + "INP0-9","INPA-J", + NULL +}; + static void dbRecordField(char *name,char *value) { DBENTRY *pdbentry; @@ -1208,24 +1219,106 @@ static void dbRecordField(char *name,char *value) DBENTRY temp; double bestSim = -1.0; const dbFldDes *bestFld = NULL; + int i; + const char* fld; dbCopyEntryContents(pdbentry, &temp); - const char* guess = - strcmp(name, "OUT") == 0 ? "INP" : - strcmp(name, "INP") == 0 ? "OUT" : - NULL; - for(status = dbFirstField(&temp, 0); !status; status = dbNextField(&temp, 0)) { - if (guess && strcmp(temp.pflddes->name, guess) == 0) { + for(i = 0; (fld = dbFieldConfusionMap[i]) != NULL; i++) { + char buf[10]; + const char* guess = NULL; + size_t l = strlen(fld); + if (l >= 3 && fld[l-2] == '-' && + strncmp(name, fld, l-3) == 0 && + name[l-3] >= fld[l-3] && + name[l-3] <= fld[l-1]) + { + /* range map */ + size_t l2 = strlen(dbFieldConfusionMap[i^1]); + memset(buf, 0, sizeof(buf)); + strncpy(buf, dbFieldConfusionMap[i^1], sizeof(buf)-1); + buf[l2-3] += name[l-3] - fld[l-3]; + buf[l2-2] = 0; + guess = buf; + } else if (strcmp(name, fld) == 0) { + /* simple map */ + guess = dbFieldConfusionMap[i^1]; + } + if (guess && dbFindFieldPart(&temp, &guess) == 0) { + /* guessed field exists */ bestFld = temp.pflddes; break; } - double sim = epicsStrSimilarity(name, temp.pflddes->name); - if(!bestFld || sim > bestSim) { - bestSim = sim; - bestFld = temp.pflddes; + } + if (!bestFld) { + /* no map found, use weighted lexical similarity */ + char quote = 0; + if (*value == '"' || *value == '\'') { + quote = *value++; + } + for (status = dbFirstField(&temp, 0); !status; status = dbNextField(&temp, 0)) { + if (temp.pflddes->special == SPC_NOMOD || + temp.pflddes->special == SPC_DBADDR) /* cannot be configured */ + continue; + double sim = epicsStrSimilarity(name, temp.pflddes->name); + if (!temp.pflddes->promptgroup) + sim *= 0.5; /* no prompt: unlikely */ + if (temp.pflddes->interest) + sim *= 1.0 - 0.1 * temp.pflddes->interest; /* 10% less likely per interest level */ + + long status = 0; + char* end = "e; + if (*value != quote) { + /* value given, check match to field type */ + switch (temp.pflddes->field_type) { + epicsAny dummy; + case DBF_CHAR: + status = epicsParseInt8(value, &dummy.int8, 0, &end); + break; + case DBF_UCHAR: + status = epicsParseUInt8(value, &dummy.uInt8, 0, &end); + break; + case DBF_SHORT: + status = epicsParseInt16(value, &dummy.int16, 0, &end); + break; + case DBF_USHORT: + status = epicsParseUInt16(value, &dummy.uInt16, 0, &end); + break; + case DBF_LONG: + status = epicsParseInt32(value, &dummy.int32, 0, &end); + break; + case DBF_ULONG: + status = epicsParseUInt32(value, &dummy.uInt32, 0, &end); + break; + case DBF_INT64: + status = epicsParseInt64(value, &dummy.int64, 0, &end); + break; + case DBF_UINT64: + status = epicsParseUInt64(value, &dummy.uInt64, 0, &end); + break; + case DBF_FLOAT: + status = epicsParseFloat(value, &dummy.float32, &end); + break; + case DBF_DOUBLE: + status = epicsParseDouble(value, &dummy.float64, &end); + break; + case DBF_ENUM: + case DBF_MENU: + case DBF_DEVICE: + /* TODO */ + default: + break; + } + if (status || *end != quote) { + sim *= 0.1; /* value type does not match field type: unlikely */ + } + } + if (sim > bestSim) { + bestSim = sim; + bestFld = temp.pflddes; + } } } dbFinishEntry(&temp); - if(bestSim>0.0) { + if (bestFld) { fprintf(stderr, " Did you mean \"%s\"?", bestFld->name); if(bestFld->prompt) fprintf(stderr, " (%s)", bestFld->prompt); From 47c263112ff62bc968c3eb9fe46cb8a239dada76 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Fri, 25 Jul 2025 11:55:01 +0200 Subject: [PATCH 35/99] calm down codacy --- modules/database/src/ioc/dbStatic/dbLexRoutines.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c index b96aa17fd..048fe8757 100644 --- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c +++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c @@ -1233,7 +1233,6 @@ static void dbRecordField(char *name,char *value) { /* range map */ size_t l2 = strlen(dbFieldConfusionMap[i^1]); - memset(buf, 0, sizeof(buf)); strncpy(buf, dbFieldConfusionMap[i^1], sizeof(buf)-1); buf[l2-3] += name[l-3] - fld[l-3]; buf[l2-2] = 0; @@ -1264,10 +1263,10 @@ static void dbRecordField(char *name,char *value) if (temp.pflddes->interest) sim *= 1.0 - 0.1 * temp.pflddes->interest; /* 10% less likely per interest level */ - long status = 0; - char* end = "e; if (*value != quote) { /* value given, check match to field type */ + long status = 0; + char* end = "e; switch (temp.pflddes->field_type) { epicsAny dummy; case DBF_CHAR: From f5a5e7c5f7087ea13a577ae424f0294afb61a0d6 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 1 Sep 2025 17:02:59 +0200 Subject: [PATCH 36/99] more in-code documentation and STATIC_ASSERT check --- .../database/src/ioc/dbStatic/dbLexRoutines.c | 59 +++++++++++++------ 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c index 048fe8757..9982543fe 100644 --- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c +++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c @@ -34,6 +34,7 @@ #include "dbStaticLib.h" #include "dbStaticPvt.h" #include "epicsExport.h" +#include "epicsAssert.h" #include "link.h" #include "special.h" #include "iocInit.h" @@ -1190,16 +1191,36 @@ static void dbRecordHead(char *recordType, char *name, int visible) dbVisibleRecord(pdbentry); } -static const char* dbFieldConfusionMap [] = { +/* For better suggestions for wrong field names + the following array contains pairs of often + confused fields. Thus, the number of elements + must be even. + For the last character, ranges like A-F are + allowed as a shortcut. Pairs must have matching + range size. + If extending this map, please add only field names + found in record types from base. + Each array element (i.e. both sides of a pair) + is tested against the faulty field name. + The first match (considering ranges) where the + other side of the pair is an existing field name + (after adjusting for ranges) will be suggested + as a replacement. + If no such match is found, the suggestion falls + back to weighted lexical similarity with existing + field names. +*/ + +static const char* const dbFieldConfusionMap [] = { "INP","OUT", "DOL","INP", "ZNAM","ZRST", "ONAM","ONST", "INPA-J","DOL0-9", "INPK-P","DOLA-F", - "INP0-9","INPA-J", - NULL + "INP0-9","INPA-J" }; +STATIC_ASSERT(NELEMENTS(dbFieldConfusionMap)%2==0); static void dbRecordField(char *name,char *value) { @@ -1220,26 +1241,27 @@ static void dbRecordField(char *name,char *value) double bestSim = -1.0; const dbFldDes *bestFld = NULL; int i; - const char* fld; dbCopyEntryContents(pdbentry, &temp); - for(i = 0; (fld = dbFieldConfusionMap[i]) != NULL; i++) { - char buf[10]; + for(i = 0; i < NELEMENTS(dbFieldConfusionMap); i++) { + const char* fieldname = dbFieldConfusionMap[i]; + const char* replacement = dbFieldConfusionMap[i^1]; /* swap even with odd indices */ const char* guess = NULL; - size_t l = strlen(fld); - if (l >= 3 && fld[l-2] == '-' && - strncmp(name, fld, l-3) == 0 && - name[l-3] >= fld[l-3] && - name[l-3] <= fld[l-1]) + char buf[8]; /* no field name is so long */ + size_t l = strlen(fieldname); + if (l >= 3 && fieldname[l-2] == '-' && + strncmp(name, fieldname, l-3) == 0 && + name[l-3] >= fieldname[l-3] && + name[l-3] <= fieldname[l-1]) { - /* range map */ - size_t l2 = strlen(dbFieldConfusionMap[i^1]); - strncpy(buf, dbFieldConfusionMap[i^1], sizeof(buf)-1); - buf[l2-3] += name[l-3] - fld[l-3]; + /* range map (like XXXA-Z) */ + size_t l2 = strlen(replacement); + strncpy(buf, replacement, sizeof(buf)-1); + buf[l2-3] += name[l-3] - fieldname[l-3]; buf[l2-2] = 0; guess = buf; - } else if (strcmp(name, fld) == 0) { + } else if (strcmp(name, fieldname) == 0) { /* simple map */ - guess = dbFieldConfusionMap[i^1]; + guess = replacement; } if (guess && dbFindFieldPart(&temp, &guess) == 0) { /* guessed field exists */ @@ -1248,7 +1270,8 @@ static void dbRecordField(char *name,char *value) } } if (!bestFld) { - /* no map found, use weighted lexical similarity */ + /* no map found, use weighted lexical similarity + the weights are a bit arbitrary */ char quote = 0; if (*value == '"' || *value == '\'') { quote = *value++; From 54ca2cb595ef736c2f8f958bff72fb13efc82f64 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 22 Sep 2025 16:45:06 +0200 Subject: [PATCH 37/99] support menus and devsups --- .../database/src/ioc/dbStatic/dbLexRoutines.c | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c index 9982543fe..6b62a7ee1 100644 --- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c +++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c @@ -1238,7 +1238,6 @@ static void dbRecordField(char *name,char *value) dbGetRecordTypeName(pdbentry), dbGetRecordName(pdbentry), name); if(dbGetRecordName(pdbentry)) { DBENTRY temp; - double bestSim = -1.0; const dbFldDes *bestFld = NULL; int i; dbCopyEntryContents(pdbentry, &temp); @@ -1272,10 +1271,10 @@ static void dbRecordField(char *name,char *value) if (!bestFld) { /* no map found, use weighted lexical similarity the weights are a bit arbitrary */ + double bestSim = -1.0; char quote = 0; - if (*value == '"' || *value == '\'') { + if (*value == '"' || *value == '\'') quote = *value++; - } for (status = dbFirstField(&temp, 0); !status; status = dbNextField(&temp, 0)) { if (temp.pflddes->special == SPC_NOMOD || temp.pflddes->special == SPC_DBADDR) /* cannot be configured */ @@ -1285,11 +1284,13 @@ static void dbRecordField(char *name,char *value) sim *= 0.5; /* no prompt: unlikely */ if (temp.pflddes->interest) sim *= 1.0 - 0.1 * temp.pflddes->interest; /* 10% less likely per interest level */ - + if (sim == 0) + continue; if (*value != quote) { /* value given, check match to field type */ long status = 0; char* end = "e; + switch (temp.pflddes->field_type) { epicsAny dummy; case DBF_CHAR: @@ -1302,6 +1303,7 @@ static void dbRecordField(char *name,char *value) status = epicsParseInt16(value, &dummy.int16, 0, &end); break; case DBF_USHORT: + case DBF_ENUM: status = epicsParseUInt16(value, &dummy.uInt16, 0, &end); break; case DBF_LONG: @@ -1322,16 +1324,45 @@ static void dbRecordField(char *name,char *value) case DBF_DOUBLE: status = epicsParseDouble(value, &dummy.float64, &end); break; - case DBF_ENUM: case DBF_MENU: - case DBF_DEVICE: - /* TODO */ + case DBF_DEVICE: { + char** choices; + int nChoice; + int choice; + + if (temp.pflddes->field_type == DBF_MENU) { + dbMenu* menu = (dbMenu*)temp.pflddes->ftPvt; + choices = menu->papChoiceValue; + nChoice = menu->nChoice; + } else { + dbDeviceMenu* menu = (dbDeviceMenu*)temp.pflddes->ftPvt; + choices = menu->papChoice; + nChoice = menu->nChoice; + } + status = epicsParseUInt16(value, &dummy.uInt16, 0, &end); + if (!status && *end == quote && dummy.uInt16 < nChoice) { + if (temp.pflddes->field_type == DBF_DEVICE) + sim *= 0.5; /* numeric device type index is uncommon */ + break; + } + for (choice = 0; choice < nChoice; choice++) { + size_t len = strlen(choices[choice]); + end = value + len; + if (strncmp(value, choices[choice], len) == 0 && *end == quote) { + sim *= 1.5; /* boost for matching choice string */ + status = 0; + break; + } + } + if (choice == nChoice) + status = S_stdlib_noConversion; + break; + } default: break; } - if (status || *end != quote) { + if (status || *end != quote) sim *= 0.1; /* value type does not match field type: unlikely */ - } } if (sim > bestSim) { bestSim = sim; From cb0688c850ea95d5f57136cf5209f0a2bb5213ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Nogueira?= Date: Fri, 20 Sep 2024 10:26:23 -0300 Subject: [PATCH 38/99] Check if test was killed by signal in makeTestFile. Tests killed by a signal should cause the .t script to exit with an error code, otherwise its exit code will be 0, and the test harness won't be aware of the unsuccessful exit. This change also makes the test runner more robust, so attaching to a running test (e.g. with gdb or strace) won't cause the .t script to exit. --- src/tools/makeTestfile.pl | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/tools/makeTestfile.pl b/src/tools/makeTestfile.pl index 31c64bd66..22d4fad10 100644 --- a/src/tools/makeTestfile.pl +++ b/src/tools/makeTestfile.pl @@ -140,6 +140,8 @@ else { ######################################## Code for Unix run-hosts print $OUT <<__UNIX__; +use POSIX qw(WIFEXITED WIFSIGNALED WEXITSTATUS); + my \$pid = fork(); die "\$tool: Can't fork for '$error': \$!\\n" unless defined \$pid; @@ -154,9 +156,19 @@ if (\$pid) { }; alarm \$timeout; - waitpid \$pid, 0; - alarm 0; - exit \$? >> 8; + while (1) { + waitpid \$pid, 0; + if (WIFEXITED(\$?)) { + # normal exit + alarm 0; + exit WEXITSTATUS(\$?); + } elsif (WIFSIGNALED(\$?)) { + # terminated by signal + alarm 0; + die "\$tool: Test was terminated by signal '\$?'\\n"; + } + # non-terminal change of status, continue waiting + } } else { # Child process From aba63096042118b60a39c518bd855a07c0a6ace0 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 8 Oct 2025 16:06:03 -0500 Subject: [PATCH 39/99] Fix README.md, CA not PVA --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cc7c15370..66c719c93 100644 --- a/README.md +++ b/README.md @@ -42,13 +42,13 @@ of the documentation. ### Quick run a softIOC After building, you can run an example soft-IOC (Input/Output Controller) -which uses the pvAccess network protocol. +which runs a Channel Access server. ```bash ./bin/*/softIoc -x first ``` -You can then run `dbl` to get: +Run the `dbl` command to list the records it provides: ```bash epics> dbl From 9af7ff3b3f09ca38185c655443c11eae7f161c13 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 8 Oct 2025 16:10:00 -0500 Subject: [PATCH 40/99] Don't duplicate dbLoadRecords() error message --- modules/database/src/ioc/db/dbAccess.c | 2 +- modules/database/src/std/softIoc/softMain.cpp | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index ca6627287..e3901a757 100644 --- a/modules/database/src/ioc/db/dbAccess.c +++ b/modules/database/src/ioc/db/dbAccess.c @@ -805,7 +805,7 @@ int dbLoadRecords(const char* file, const char* subs) if(dbLoadRecordsHook) dbLoadRecordsHook(file, subs); } else { - fprintf(stderr, ERL_ERROR " failed to load '%s'\n", file); + fprintf(stderr, ERL_ERROR ": Failed to load '%s'\n", file); if(status==-2) fprintf(stderr, " Records cannot be loaded after iocInit!\n"); } diff --git a/modules/database/src/std/softIoc/softMain.cpp b/modules/database/src/std/softIoc/softMain.cpp index 384a002fa..0c45975b0 100644 --- a/modules/database/src/std/softIoc/softMain.cpp +++ b/modules/database/src/std/softIoc/softMain.cpp @@ -185,7 +185,7 @@ int main(int argc, char *argv[]) break; case 'D': if(lazy_dbd_loaded) { - throw std::runtime_error("-D specified too late. softIoc.dbd already loaded.\n"); + throw std::runtime_error("-D specified too late, softIoc.dbd already loaded.\n"); } dbd_file = optarg; break; @@ -195,8 +195,7 @@ int main(int argc, char *argv[]) + optarg + "\"" + ( !macros.empty() ? (std::string(", \"") + macros + "\"") : std::string() ) + ")"); - errIf(dbLoadRecords(optarg, macros.c_str()), - std::string("Failed to load: ")+optarg); + errIf(dbLoadRecords(optarg, macros.c_str()), ""); loadedDb = true; break; case 'm': @@ -216,8 +215,7 @@ int main(int argc, char *argv[]) xmacro += optarg; verbose_out(CMD, std::string("dbLoadRecords(\"") + exit_file + "\", \"" + xmacro + "\")"); - errIf(dbLoadRecords(exit_file.c_str(), xmacro.c_str()), - std::string("Failed to load: ")+exit_file); + errIf(dbLoadRecords(exit_file.c_str(), xmacro.c_str()), ""); loadedDb = true; break; } @@ -274,7 +272,8 @@ int main(int argc, char *argv[]) }catch(std::exception& e){ errlogFlush(); - std::cerr< Date: Wed, 8 Oct 2025 16:11:15 -0500 Subject: [PATCH 41/99] Initialize error strings before any database errors occur --- .../database/src/ioc/dbStatic/dbLexRoutines.c | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c index 6b62a7ee1..7ea975986 100644 --- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c +++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c @@ -238,6 +238,8 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, goto cleanup; } + errlogInit(0); /* Initialize the errSymTable */ + if(*ppdbbase == 0) *ppdbbase = dbAllocBase(); savedPdbbase = *ppdbbase; if(path && strlen(path)>0) { @@ -352,14 +354,18 @@ cleanup: return(status); } -long dbReadDatabase(DBBASE **ppdbbase,const char *filename, - const char *path,const char *substitutions) -{return (dbReadCOM(ppdbbase,filename,0,path,substitutions));} +long dbReadDatabase(DBBASE **ppdbbase, const char *filename, + const char *path, const char *substitutions) +{ + return dbReadCOM(ppdbbase, filename, 0, path, substitutions); +} + +long dbReadDatabaseFP(DBBASE **ppdbbase, FILE *fp, + const char *path, const char *substitutions) +{ + return dbReadCOM(ppdbbase, 0, fp, path, substitutions); +} -long dbReadDatabaseFP(DBBASE **ppdbbase,FILE *fp, - const char *path,const char *substitutions) -{return (dbReadCOM(ppdbbase,0,fp,path,substitutions));} - static int db_yyinput(char *buf, int max_size) { size_t l,n; From a2e01c29294c95e608af2607dc59bbe05d23636c Mon Sep 17 00:00:00 2001 From: DW Date: Fri, 18 Apr 2025 09:24:43 +0900 Subject: [PATCH 42/99] fix: stdint.h is not available before vw6.9 --- .../src/ioc/misc/registerAllRecordDeviceDrivers.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/database/src/ioc/misc/registerAllRecordDeviceDrivers.cpp b/modules/database/src/ioc/misc/registerAllRecordDeviceDrivers.cpp index 7d1a1ed61..d5860d9fe 100644 --- a/modules/database/src/ioc/misc/registerAllRecordDeviceDrivers.cpp +++ b/modules/database/src/ioc/misc/registerAllRecordDeviceDrivers.cpp @@ -9,7 +9,14 @@ #include #include +#if defined(vxWorks) && \ + (_WRS_VXWORKS_MAJOR+0 <= 6) && (_WRS_VXWORKS_MINOR+0 < 9) +typedef int intptr_t; +typedef unsigned int uintptr_t; +#else #include +#endif + #include #define EPICS_PRIVATE_API From 0ab956b123ee28e3ecfb8404a2291f0032829cf5 Mon Sep 17 00:00:00 2001 From: Gustavo de Souza dos Reis <52132909+gustavosr8@users.noreply.github.com> Date: Tue, 14 Oct 2025 12:47:59 -0300 Subject: [PATCH 43/99] Fix doc. comments in epicsEvent.h * Fix typo in epicsEventWaitWithTimeout. * Reword epicsEvent timeout documentation to reflect the actual behavior. Negative values of timeout are handled the same way as zero values. Change the documentation to reflect this behavior. --- modules/libcom/src/osi/epicsEvent.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/libcom/src/osi/epicsEvent.h b/modules/libcom/src/osi/epicsEvent.h index 4fd2d338a..d69c496f9 100644 --- a/modules/libcom/src/osi/epicsEvent.h +++ b/modules/libcom/src/osi/epicsEvent.h @@ -99,7 +99,7 @@ public: **/ void wait (); /**\brief Wait for the event or until the specified timeout. - * \param timeout The timeout delay in seconds. A timeout of zero is + * \param timeout The timeout delay in seconds. A timeout of zero or less is * equivalent to calling tryWait(); NaN or any value too large to be * represented to the target OS is equivalent to no timeout. * \return True if the event was triggered, False if it timed out. @@ -189,10 +189,10 @@ LIBCOM_API epicsEventStatus epicsEventWait( */ LIBCOM_API void epicsEventMustWait(epicsEventId id); -/**\brief Wait an the event or until the specified timeout period is over. +/**\brief Wait for the event or until the specified timeout period is over. * \note Blocks until full or timeout. * \param id The event identifier. - * \param timeout The timeout delay in seconds. A timeout of zero is + * \param timeout The timeout delay in seconds. A timeout of zero or less is * equivalent to calling epicsEventTryWait(); NaN or any value too large * to be represented to the target OS is equivalent to no timeout. * \return Status indicator. From 098b3968d64f1f00731bdde7391e3035a405db9e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 14 Oct 2025 17:39:32 -0700 Subject: [PATCH 44/99] doc --- modules/libcom/src/ellLib/ellLib.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/libcom/src/ellLib/ellLib.h b/modules/libcom/src/ellLib/ellLib.h index 31f54fbf4..e56760ffa 100644 --- a/modules/libcom/src/ellLib/ellLib.h +++ b/modules/libcom/src/ellLib/ellLib.h @@ -146,6 +146,8 @@ LIBCOM_API ELLNODE * ellGet (ELLLIST *pList); * \brief Deletes and returns the last node from a list. * \param pList Pointer to list from which to get node * \return Pointer to the last node from the list, or NULL if the list is empty + * + * \since 3.15.0.1 */ LIBCOM_API ELLNODE * ellPop (ELLLIST *pList); /** @@ -192,6 +194,8 @@ typedef int (*pListCmp)(const ELLNODE* A, const ELLNODE* B); * * \note Use of mergesort algorithm based on analysis by * http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html + * + * \since 3.15.5 */ LIBCOM_API void ellSortStable(ELLLIST *pList, pListCmp pListCmp); /** From a46bd5ae88087bbadab0d547141146ebf4d46dc8 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 20 Sep 2025 14:18:12 -0500 Subject: [PATCH 45/99] dbCa: iocInit wait for local CA links to connect --- documentation/new-notes/PR-713.md | 8 +++++ modules/database/src/ioc/db/dbCa.c | 35 +++++++++++++++++---- modules/database/src/ioc/db/dbCaPvt.h | 21 +++++++++++++ modules/database/src/ioc/db/dbLink.c | 6 ++-- modules/database/test/ioc/db/dbCaLinkTest.c | 7 ----- 5 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 documentation/new-notes/PR-713.md diff --git a/documentation/new-notes/PR-713.md b/documentation/new-notes/PR-713.md new file mode 100644 index 000000000..c647c39bc --- /dev/null +++ b/documentation/new-notes/PR-713.md @@ -0,0 +1,8 @@ +### iocInit wait for local CA links to connect + +PR [713](https://github.com/epics-base/epics-base/pull/713) + +During iocInit(), wait for local CA links to connect. +With this change, database authors can be certain that +any local CA link is connected prior to `initHookAfterIocRunning` +and `field(PINI, RUNNING)`. diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c index 660fc5223..0a0da33df 100644 --- a/modules/database/src/ioc/db/dbCa.c +++ b/modules/database/src/ioc/db/dbCa.c @@ -40,6 +40,7 @@ /* We can't include dbStaticLib.h here */ #define dbCalloc(nobj,size) callocMustSucceed(nobj,size,"dbCalloc") +#include #include "db_access_routines.h" #include "dbCa.h" #include "dbCaPvt.h" @@ -64,6 +65,7 @@ extern int dbServiceIsolate; static ELLLIST workList = ELLLIST_INIT; /* Work list for dbCaTask */ static epicsMutexId workListLock; /*Mutual exclusions semaphores for workList*/ static epicsEventId workListEvent; /*wakeup event for dbCaTask*/ +static size_t initOutstanding; static int removesOutstanding = 0; #define removesOutstandingWarning 10000 @@ -340,7 +342,7 @@ static void dbCaLinkInitImpl(int isolate) dbCaCtl = ctlPause; dbCaWorker = epicsThreadCreateOpt("dbCaLink", dbCaTask, NULL, &opts); - /* wait for worker to startup and initialize dbCaClientContext */ + /* wait for worker to startup, initialize dbCaClientContext, and connect local CA */ epicsEventMustWait(startStopEvent); } @@ -369,11 +371,12 @@ void dbCaPause(void) epicsEventSignal(workListEvent); } } - -void dbCaAddLinkCallback(struct link *plink, - dbCaCallback connect, dbCaCallback monitor, void *userPvt) +void dbCaAddLinkCallbackOpt(struct dbLocker *locker, struct link *plink, + dbCaCallback connect, dbCaCallback monitor, void *userPvt, + unsigned flags) { caLink *pca; + (void)locker; /* Passed for symmetry with dbDbAddLink(). So far unused. */ assert(!plink->value.pv_link.pvt); @@ -385,6 +388,10 @@ void dbCaAddLinkCallback(struct link *plink, pca->connect = connect; pca->monitor = monitor; pca->userPvt = userPvt; + pca->flags = flags; + + if(flags & DBCA_CALLBACK_INIT_WAIT) + epicsAtomicIncrSizeT(&initOutstanding); epicsMutexMustLock(pca->lock); plink->lset = &dbCa_lset; @@ -394,15 +401,22 @@ void dbCaAddLinkCallback(struct link *plink, epicsMutexUnlock(pca->lock); } +void dbCaAddLinkCallback(struct link *plink, + dbCaCallback connect, dbCaCallback monitor, void *userPvt) +{ + dbCaAddLinkCallbackOpt(NULL, plink, connect, monitor, userPvt, 0); +} + long dbCaAddLink(struct dbLocker *locker, struct link *plink, short dbfType) { - dbCaAddLinkCallback(plink, 0, 0, NULL); + dbCaAddLinkCallbackOpt(locker, plink, 0, 0, NULL, 0); return 0; } void dbCaRemoveLink(struct dbLocker *locker, struct link *plink) { caLink *pca = (caLink *)plink->value.pv_link.pvt; + (void)locker; /* Passed for symmetry with dbDbRemoveLink(). So far unused. */ if (!pca) return; epicsMutexMustLock(pca->lock); @@ -962,6 +976,10 @@ static void eventCallback(struct event_handler_args arg) } } done: + if(pca->flags & DBCA_CALLBACK_INIT_WAIT) { + pca->flags &= ~DBCA_CALLBACK_INIT_WAIT; + addAction(pca, CA_INIT_WAIT); + } epicsMutexUnlock(pca->lock); if (monitor) monitor(userPvt); } @@ -1099,7 +1117,9 @@ static void dbCaTask(void *arg) dbCaClientContext = ca_current_context (); SEVCHK(ca_add_exception_event(exceptionCallback,NULL), "ca_add_exception_event"); - epicsEventSignal(startStopEvent); + if(epicsAtomicGetSizeT(&initOutstanding)==0) + epicsEventSignal(startStopEvent); + // else: defer to CA_INIT_WAIT /* channel access event loop */ while (TRUE){ @@ -1255,6 +1275,9 @@ static void dbCaTask(void *arg) db_process(prec); dbScanUnlock(prec); } + if ((link_action & CA_INIT_WAIT) && epicsAtomicDecrSizeT(&initOutstanding)==0) { + epicsEventSignal(startStopEvent); + } } SEVCHK(ca_flush_io(), "dbCaTask"); } diff --git a/modules/database/src/ioc/db/dbCaPvt.h b/modules/database/src/ioc/db/dbCaPvt.h index 1ee0745e0..185b6104f 100644 --- a/modules/database/src/ioc/db/dbCaPvt.h +++ b/modules/database/src/ioc/db/dbCaPvt.h @@ -21,6 +21,19 @@ #include "epicsMutex.h" #include "epicsTypes.h" #include "link.h" +#include "shareLib.h" +#include "libCaAPI.h" + +#ifndef INC_cadef_H +/* Copy some definitions so this header to be included from + * places where cadef.h and db_access.h can not. + */ +typedef void * chid; +typedef void * evid; +LIBCA_API extern const unsigned short dbr_value_size[]; +LIBCA_API short epicsShareAPI ca_field_type (chid chan); +#define MAX_UNITS_SIZE 8 +#endif /* link_action mask */ #define CA_CLEAR_CHANNEL 0x1 @@ -32,6 +45,7 @@ #define CA_GET_ATTRIBUTES 0x40 #define CA_SYNC 0x1000 #define CA_DBPROCESS 0x2000 +#define CA_INIT_WAIT 0x4000 /* write type */ #define CA_PUT 0x1 #define CA_PUT_CALLBACK 0x2 @@ -63,6 +77,7 @@ typedef struct caLink dbCaCallback connect; dbCaCallback monitor; void *userPvt; + unsigned flags; /* The following are for write request */ short putType; dbCaCallback putCallback; @@ -97,4 +112,10 @@ typedef struct caLink unsigned long nUpdate; }caLink; +#define DBCA_CALLBACK_INIT_WAIT (1) + +void dbCaAddLinkCallbackOpt(struct dbLocker *locker, struct link *plink, + dbCaCallback connect, dbCaCallback monitor, + void *userPvt, unsigned flags); + #endif /* INC_dbCaPvt_H */ diff --git a/modules/database/src/ioc/db/dbLink.c b/modules/database/src/ioc/db/dbLink.c index a2819d553..e03650eab 100644 --- a/modules/database/src/ioc/db/dbLink.c +++ b/modules/database/src/ioc/db/dbLink.c @@ -31,7 +31,7 @@ #include "dbAccessDefs.h" #include "dbAddr.h" #include "dbBase.h" -#include "dbCa.h" +#include "dbCaPvt.h" #include "dbCommon.h" #include "dbConstLink.h" #include "dbDbLink.h" @@ -125,7 +125,9 @@ void dbInitLink(struct link *plink, short dbfType) if (dbfType == DBF_INLINK) plink->value.pv_link.pvlMask |= pvlOptInpNative; - dbCaAddLink(NULL, plink, dbfType); + int isLocal = dbChannelTest(plink->value.pv_link.pvname)==0; + + dbCaAddLinkCallbackOpt(NULL, plink, NULL, NULL, NULL, isLocal ? DBCA_CALLBACK_INIT_WAIT : 0); if (dbfType == DBF_FWDLINK) { char *pperiod = strrchr(plink->value.pv_link.pvname, '.'); diff --git a/modules/database/test/ioc/db/dbCaLinkTest.c b/modules/database/test/ioc/db/dbCaLinkTest.c index 8243a6c82..c50649910 100644 --- a/modules/database/test/ioc/db/dbCaLinkTest.c +++ b/modules/database/test/ioc/db/dbCaLinkTest.c @@ -29,13 +29,6 @@ #include "dbEvent.h" #include "shareLib.h" -/* Declarations from cadef.h and db_access.h which we can't include here */ -typedef void * chid; -typedef void * evid; -epicsShareExtern const unsigned short dbr_value_size[]; -epicsShareExtern short epicsShareAPI ca_field_type (chid chan); -#define MAX_UNITS_SIZE 8 - #include "dbCaPvt.h" #include "errlog.h" #include "testMain.h" From 3f382f6b68521c686b078aad6782fb0c693fbc09 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 17 Oct 2025 14:51:46 -0700 Subject: [PATCH 46/99] Revert "dbCa: iocInit wait for local CA links to connect" This reverts commit a46bd5ae88087bbadab0d547141146ebf4d46dc8. --- documentation/new-notes/PR-713.md | 8 ----- modules/database/src/ioc/db/dbCa.c | 35 ++++----------------- modules/database/src/ioc/db/dbCaPvt.h | 21 ------------- modules/database/src/ioc/db/dbLink.c | 6 ++-- modules/database/test/ioc/db/dbCaLinkTest.c | 7 +++++ 5 files changed, 15 insertions(+), 62 deletions(-) delete mode 100644 documentation/new-notes/PR-713.md diff --git a/documentation/new-notes/PR-713.md b/documentation/new-notes/PR-713.md deleted file mode 100644 index c647c39bc..000000000 --- a/documentation/new-notes/PR-713.md +++ /dev/null @@ -1,8 +0,0 @@ -### iocInit wait for local CA links to connect - -PR [713](https://github.com/epics-base/epics-base/pull/713) - -During iocInit(), wait for local CA links to connect. -With this change, database authors can be certain that -any local CA link is connected prior to `initHookAfterIocRunning` -and `field(PINI, RUNNING)`. diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c index 0a0da33df..660fc5223 100644 --- a/modules/database/src/ioc/db/dbCa.c +++ b/modules/database/src/ioc/db/dbCa.c @@ -40,7 +40,6 @@ /* We can't include dbStaticLib.h here */ #define dbCalloc(nobj,size) callocMustSucceed(nobj,size,"dbCalloc") -#include #include "db_access_routines.h" #include "dbCa.h" #include "dbCaPvt.h" @@ -65,7 +64,6 @@ extern int dbServiceIsolate; static ELLLIST workList = ELLLIST_INIT; /* Work list for dbCaTask */ static epicsMutexId workListLock; /*Mutual exclusions semaphores for workList*/ static epicsEventId workListEvent; /*wakeup event for dbCaTask*/ -static size_t initOutstanding; static int removesOutstanding = 0; #define removesOutstandingWarning 10000 @@ -342,7 +340,7 @@ static void dbCaLinkInitImpl(int isolate) dbCaCtl = ctlPause; dbCaWorker = epicsThreadCreateOpt("dbCaLink", dbCaTask, NULL, &opts); - /* wait for worker to startup, initialize dbCaClientContext, and connect local CA */ + /* wait for worker to startup and initialize dbCaClientContext */ epicsEventMustWait(startStopEvent); } @@ -371,12 +369,11 @@ void dbCaPause(void) epicsEventSignal(workListEvent); } } -void dbCaAddLinkCallbackOpt(struct dbLocker *locker, struct link *plink, - dbCaCallback connect, dbCaCallback monitor, void *userPvt, - unsigned flags) + +void dbCaAddLinkCallback(struct link *plink, + dbCaCallback connect, dbCaCallback monitor, void *userPvt) { caLink *pca; - (void)locker; /* Passed for symmetry with dbDbAddLink(). So far unused. */ assert(!plink->value.pv_link.pvt); @@ -388,10 +385,6 @@ void dbCaAddLinkCallbackOpt(struct dbLocker *locker, struct link *plink, pca->connect = connect; pca->monitor = monitor; pca->userPvt = userPvt; - pca->flags = flags; - - if(flags & DBCA_CALLBACK_INIT_WAIT) - epicsAtomicIncrSizeT(&initOutstanding); epicsMutexMustLock(pca->lock); plink->lset = &dbCa_lset; @@ -401,22 +394,15 @@ void dbCaAddLinkCallbackOpt(struct dbLocker *locker, struct link *plink, epicsMutexUnlock(pca->lock); } -void dbCaAddLinkCallback(struct link *plink, - dbCaCallback connect, dbCaCallback monitor, void *userPvt) -{ - dbCaAddLinkCallbackOpt(NULL, plink, connect, monitor, userPvt, 0); -} - long dbCaAddLink(struct dbLocker *locker, struct link *plink, short dbfType) { - dbCaAddLinkCallbackOpt(locker, plink, 0, 0, NULL, 0); + dbCaAddLinkCallback(plink, 0, 0, NULL); return 0; } void dbCaRemoveLink(struct dbLocker *locker, struct link *plink) { caLink *pca = (caLink *)plink->value.pv_link.pvt; - (void)locker; /* Passed for symmetry with dbDbRemoveLink(). So far unused. */ if (!pca) return; epicsMutexMustLock(pca->lock); @@ -976,10 +962,6 @@ static void eventCallback(struct event_handler_args arg) } } done: - if(pca->flags & DBCA_CALLBACK_INIT_WAIT) { - pca->flags &= ~DBCA_CALLBACK_INIT_WAIT; - addAction(pca, CA_INIT_WAIT); - } epicsMutexUnlock(pca->lock); if (monitor) monitor(userPvt); } @@ -1117,9 +1099,7 @@ static void dbCaTask(void *arg) dbCaClientContext = ca_current_context (); SEVCHK(ca_add_exception_event(exceptionCallback,NULL), "ca_add_exception_event"); - if(epicsAtomicGetSizeT(&initOutstanding)==0) - epicsEventSignal(startStopEvent); - // else: defer to CA_INIT_WAIT + epicsEventSignal(startStopEvent); /* channel access event loop */ while (TRUE){ @@ -1275,9 +1255,6 @@ static void dbCaTask(void *arg) db_process(prec); dbScanUnlock(prec); } - if ((link_action & CA_INIT_WAIT) && epicsAtomicDecrSizeT(&initOutstanding)==0) { - epicsEventSignal(startStopEvent); - } } SEVCHK(ca_flush_io(), "dbCaTask"); } diff --git a/modules/database/src/ioc/db/dbCaPvt.h b/modules/database/src/ioc/db/dbCaPvt.h index 185b6104f..1ee0745e0 100644 --- a/modules/database/src/ioc/db/dbCaPvt.h +++ b/modules/database/src/ioc/db/dbCaPvt.h @@ -21,19 +21,6 @@ #include "epicsMutex.h" #include "epicsTypes.h" #include "link.h" -#include "shareLib.h" -#include "libCaAPI.h" - -#ifndef INC_cadef_H -/* Copy some definitions so this header to be included from - * places where cadef.h and db_access.h can not. - */ -typedef void * chid; -typedef void * evid; -LIBCA_API extern const unsigned short dbr_value_size[]; -LIBCA_API short epicsShareAPI ca_field_type (chid chan); -#define MAX_UNITS_SIZE 8 -#endif /* link_action mask */ #define CA_CLEAR_CHANNEL 0x1 @@ -45,7 +32,6 @@ LIBCA_API short epicsShareAPI ca_field_type (chid chan); #define CA_GET_ATTRIBUTES 0x40 #define CA_SYNC 0x1000 #define CA_DBPROCESS 0x2000 -#define CA_INIT_WAIT 0x4000 /* write type */ #define CA_PUT 0x1 #define CA_PUT_CALLBACK 0x2 @@ -77,7 +63,6 @@ typedef struct caLink dbCaCallback connect; dbCaCallback monitor; void *userPvt; - unsigned flags; /* The following are for write request */ short putType; dbCaCallback putCallback; @@ -112,10 +97,4 @@ typedef struct caLink unsigned long nUpdate; }caLink; -#define DBCA_CALLBACK_INIT_WAIT (1) - -void dbCaAddLinkCallbackOpt(struct dbLocker *locker, struct link *plink, - dbCaCallback connect, dbCaCallback monitor, - void *userPvt, unsigned flags); - #endif /* INC_dbCaPvt_H */ diff --git a/modules/database/src/ioc/db/dbLink.c b/modules/database/src/ioc/db/dbLink.c index e03650eab..a2819d553 100644 --- a/modules/database/src/ioc/db/dbLink.c +++ b/modules/database/src/ioc/db/dbLink.c @@ -31,7 +31,7 @@ #include "dbAccessDefs.h" #include "dbAddr.h" #include "dbBase.h" -#include "dbCaPvt.h" +#include "dbCa.h" #include "dbCommon.h" #include "dbConstLink.h" #include "dbDbLink.h" @@ -125,9 +125,7 @@ void dbInitLink(struct link *plink, short dbfType) if (dbfType == DBF_INLINK) plink->value.pv_link.pvlMask |= pvlOptInpNative; - int isLocal = dbChannelTest(plink->value.pv_link.pvname)==0; - - dbCaAddLinkCallbackOpt(NULL, plink, NULL, NULL, NULL, isLocal ? DBCA_CALLBACK_INIT_WAIT : 0); + dbCaAddLink(NULL, plink, dbfType); if (dbfType == DBF_FWDLINK) { char *pperiod = strrchr(plink->value.pv_link.pvname, '.'); diff --git a/modules/database/test/ioc/db/dbCaLinkTest.c b/modules/database/test/ioc/db/dbCaLinkTest.c index c50649910..8243a6c82 100644 --- a/modules/database/test/ioc/db/dbCaLinkTest.c +++ b/modules/database/test/ioc/db/dbCaLinkTest.c @@ -29,6 +29,13 @@ #include "dbEvent.h" #include "shareLib.h" +/* Declarations from cadef.h and db_access.h which we can't include here */ +typedef void * chid; +typedef void * evid; +epicsShareExtern const unsigned short dbr_value_size[]; +epicsShareExtern short epicsShareAPI ca_field_type (chid chan); +#define MAX_UNITS_SIZE 8 + #include "dbCaPvt.h" #include "errlog.h" #include "testMain.h" From 6e6cac09e1cb7a5ecdb04cd5b11296ee0f97f5f1 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 19 Oct 2025 16:25:36 -0400 Subject: [PATCH 47/99] Add module version numbers to Perl modules that might need them --- src/tools/EPICS/PodHtml.pm | 2 ++ src/tools/EPICS/PodMD.pm | 2 ++ src/tools/EPICS/PodXHtml.pm | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/tools/EPICS/PodHtml.pm b/src/tools/EPICS/PodHtml.pm index a02fc9cc9..ed586156d 100644 --- a/src/tools/EPICS/PodHtml.pm +++ b/src/tools/EPICS/PodHtml.pm @@ -5,6 +5,8 @@ use warnings; use base 'Pod::Simple::HTML'; +our $VERSION = '1.2.1'; + sub encode_entities { my ($self, $str) = @_; my %entities = ( diff --git a/src/tools/EPICS/PodMD.pm b/src/tools/EPICS/PodMD.pm index fb6a47ec3..eadf5fce6 100644 --- a/src/tools/EPICS/PodMD.pm +++ b/src/tools/EPICS/PodMD.pm @@ -7,6 +7,8 @@ use warnings; use base 'Pod::Markdown'; +our $VERSION = '1.1.1'; + # Translate L # into link text# # This is for Sphinx processing on Readthedocs. Sphinx converts diff --git a/src/tools/EPICS/PodXHtml.pm b/src/tools/EPICS/PodXHtml.pm index 3a5e64502..e5ee78e26 100644 --- a/src/tools/EPICS/PodXHtml.pm +++ b/src/tools/EPICS/PodXHtml.pm @@ -5,6 +5,8 @@ use warnings; use base 'Pod::Simple::XHTML'; +our $VERSION = '1.2.1'; + BEGIN { if ($Pod::Simple::XHTML::VERSION < '3.16') { # Add encode_entities() as a method From 8311a5b36047b3fb769e923da2784fb8386ced0a Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 24 Oct 2025 10:39:24 -0500 Subject: [PATCH 48/99] Disable epicsStdio.h redefinitions readline.h uses printf in an attribute --- modules/libcom/src/iocsh/iocsh.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp index 82551c7d2..46194464e 100644 --- a/modules/libcom/src/iocsh/iocsh.cpp +++ b/modules/libcom/src/iocsh/iocsh.cpp @@ -26,6 +26,10 @@ #define EPICS_PRIVATE_API +// Recent readline.h uses printf in an attribute +#define epicsStdioStdStreams +#define epicsStdioStdPrintfEtc + #include "epicsMath.h" #include "errlog.h" #include "macLib.h" @@ -41,7 +45,6 @@ #include "iocsh.h" #include "epicsReadlinePvt.h" - #if EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_READLINE # include # include @@ -639,8 +642,9 @@ struct ReadlineContext { if(!hist_file.empty()) { if(int err = read_history(hist_file.c_str())) { if(err!=ENOENT) - fprintf(stderr, ERL_ERROR " %s (%d) loading '%s'\n", - strerror(err), err, hist_file.c_str()); + fprintf(epicsGetStderr(), + ERL_ERROR " %s (%d) loading '%s'\n", + strerror(err), err, hist_file.c_str()); } stifle_history(1024); // some limit... } @@ -654,8 +658,9 @@ struct ReadlineContext { #ifdef USE_READLINE if(!hist_file.empty()) { if(int err = write_history(hist_file.c_str())) { - fprintf(stderr, ERL_ERROR " %s (%d) writing '%s'\n", - strerror(err), err, hist_file.c_str()); + fprintf(epicsGetStderr(), + ERL_ERROR " %s (%d) writing '%s'\n", + strerror(err), err, hist_file.c_str()); } } rl_readline_name = prev_rl_readline_name; @@ -1167,7 +1172,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) if (c == '#') { if ((prompt == NULL) && (commandLine == NULL)) if (raw[icin + 1] != '-') { - printf(ANSI_BLUE("%s") "\n", raw); + fprintf(epicsGetStdout(), ANSI_BLUE("%s") "\n", raw); } continue; } @@ -1194,7 +1199,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) */ if ((prompt == NULL) && *line && (commandLine == NULL)) { if ((c != '#') || (line[icin + 1] != '-')) { - printf(ANSI_BOLD("%s") "\n", line); + fprintf(epicsGetStdout(), ANSI_BOLD("%s") "\n", line); } } From e12269c302020fcf5b220b1b2cd01d2f01c06114 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 24 Oct 2025 13:13:03 -0500 Subject: [PATCH 49/99] Include readline.h before EPICS headers --- modules/libcom/src/iocsh/iocsh.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp index 46194464e..39bc493bb 100644 --- a/modules/libcom/src/iocsh/iocsh.cpp +++ b/modules/libcom/src/iocsh/iocsh.cpp @@ -30,20 +30,7 @@ #define epicsStdioStdStreams #define epicsStdioStdPrintfEtc -#include "epicsMath.h" -#include "errlog.h" -#include "macLib.h" -#include "epicsStdio.h" -#include "epicsString.h" -#include "epicsStdlib.h" -#include "epicsThread.h" -#include "epicsMutex.h" -#include "envDefs.h" -#include "registry.h" #include "epicsReadline.h" -#include "cantProceed.h" -#include "iocsh.h" - #include "epicsReadlinePvt.h" #if EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_READLINE # include @@ -65,6 +52,19 @@ static const char *rl_basic_quote_characters; # endif #endif +#include "epicsMath.h" +#include "errlog.h" +#include "macLib.h" +#include "epicsStdio.h" +#include "epicsString.h" +#include "epicsStdlib.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "envDefs.h" +#include "registry.h" +#include "cantProceed.h" +#include "iocsh.h" + extern "C" { /* From d58d1420b8b65d44048637e6ca3b9c9d3de1de7a Mon Sep 17 00:00:00 2001 From: Jerzy Jamroz Date: Tue, 28 Oct 2025 13:53:01 +0100 Subject: [PATCH 50/99] docs: clarify Drive Limits behavior in aoRecord --- modules/database/src/std/rec/aoRecord.dbd.pod | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/modules/database/src/std/rec/aoRecord.dbd.pod b/modules/database/src/std/rec/aoRecord.dbd.pod index 34519ab52..6ae436d67 100644 --- a/modules/database/src/std/rec/aoRecord.dbd.pod +++ b/modules/database/src/std/rec/aoRecord.dbd.pod @@ -69,9 +69,13 @@ output value PVAL is added to it. =head4 Drive Limits -The output value is now clipped to the range DRVL to DRVH inclusive, provided -that DRVH E DRVL. -The result is copied into both the VAL and PVAL fields. +The VAL field's value will be clipped within limits specified in the fields DRVH +and DRVL if these have been configured by the database designer: + + DRVL <= VAL <= DRVH + +Note: These limits are only enforced as long as DRVH E DRVL. If they are not +set or DRVH E= DRVL they will not be used. =head4 Limit Rate of Change @@ -689,7 +693,7 @@ Routine process implements the following algorithm: =over -=item 1. +=item 1. Check to see that the appropriate device support module exists. If it doesn't, an error message is issued and processing is @@ -697,7 +701,7 @@ terminated with the PACT field set to TRUE. This ensures that processes will no longer be called for this record. Thus error storms will not occur. -=item 2. +=item 2. Check PACT: If PACT is FALSE call fetch_values and convert which perform the following steps: @@ -746,7 +750,7 @@ calculated in, using the formula RVAL = (RVAL -AOFF) / ASLO - ROFF. =back -=item 3. +=item 3. Check alarms: This routine checks to see if the new VAL causes the alarm status and severity to change. If so, NSEV, NSTA and y are @@ -754,18 +758,18 @@ set. It also honors the alarm hysteresis factor (HYST). Thus the value must change by at least HYST before the alarm status and severity is reduced. -=item 4. +=item 4. Check severity and write the new value. See Invalid Alarm Output Action for details on how invalid alarms affect output records. -=item 5. +=item 5. If PACT has been changed to TRUE, the device support write output routine has started but has not completed writing the new value. In this case, the processing routine merely returns, leaving PACT TRUE. -=item 6. +=item 6. Check to see if monitors should be invoked: @@ -784,7 +788,7 @@ monitors are invoked. =back -=item 7. +=item 7. Scan forward link if necessary, set PACT and INIT FALSE, and return. From e08d7310b97d1cdf887d4cc920a93c09cfcb8bac Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 5 Sep 2025 20:00:28 -0700 Subject: [PATCH 51/99] posix: epicsThreadShowAll include OSD priority range and mlockall state --- modules/libcom/src/osi/os/posix/osdThread.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c index 8c3d8c848..49754c0fd 100644 --- a/modules/libcom/src/osi/os/posix/osdThread.c +++ b/modules/libcom/src/osi/os/posix/osdThread.c @@ -396,11 +396,6 @@ static void once(void) pcommonAttr->maxPriority); } - if (errVerbose) { - fprintf(stderr, "LRT: min priority: %d max priority %d\n", - pcommonAttr->minPriority, pcommonAttr->maxPriority); - } - #else if(errVerbose) fprintf(stderr,"task priorities are not implemented\n"); #endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ @@ -459,9 +454,13 @@ static void epicsThreadInit(void) } } +static +unsigned char mlocked; + LIBCOM_API void epicsThreadRealtimeLock(void) { + mlocked = 0; #if USE_MEMLOCK #ifndef RTEMS_LEGACY_STACK // seems to be part of libbsd? if (pcommonAttr->maxPriority > pcommonAttr->minPriority) { @@ -486,6 +485,8 @@ void epicsThreadRealtimeLock(void) "VM page faults may harm real-time performance. errno=%d\n", err); } + } else { + mlocked = 1; } } #endif // LEGACY STACK @@ -982,6 +983,11 @@ LIBCOM_API void epicsStdCall epicsThreadShowAll(unsigned int level) } status = pthread_mutex_unlock(&listLock); checkStatus(status,"pthread_mutex_unlock epicsThreadShowAll"); + + fprintf(stderr, + "OSD priority range min: %d max %d, memory %slocked\n", + pcommonAttr->minPriority, pcommonAttr->maxPriority, + mlocked ? "" : "not "); } LIBCOM_API void epicsStdCall epicsThreadShow(epicsThreadId showThread, unsigned int level) From 88bfd6f378dac38bf2751621d2c8633379aa092c Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Wed, 5 Nov 2025 17:11:06 +0100 Subject: [PATCH 52/99] Allow hex and octal strings in dbPut and dbGet Setting EPICS_DB_CONVERT_DECIMAL_ONLY to YES/yes change the dbPut() and dbGet() string to integer conversions to the original decimal only policy. --- documentation/new-notes/PR-678.md | 12 +++++++ modules/database/src/ioc/db/dbConvert.c | 34 ++++++++++---------- modules/database/src/ioc/db/dbFastLinkConv.c | 20 ++++++------ modules/database/src/ioc/misc/iocInit.c | 12 +++++++ modules/libcom/src/misc/epicsConvert.c | 2 ++ modules/libcom/src/misc/epicsConvert.h | 6 ++++ 6 files changed, 59 insertions(+), 27 deletions(-) create mode 100644 documentation/new-notes/PR-678.md diff --git a/documentation/new-notes/PR-678.md b/documentation/new-notes/PR-678.md new file mode 100644 index 000000000..26b237186 --- /dev/null +++ b/documentation/new-notes/PR-678.md @@ -0,0 +1,12 @@ +### Allow hex and octal strings in dbPut and dbGet + +It is now possible to convert hex and octal strings to integer fields +with `dbPut()`, `dbGet()` and related functions like the iocsh command +`dbpf` or through database links. + +Possible incompatibility: Up to now, leading `0`s have been ignored, +now they switch to octal mode. + +For backward compatibility, this behavior can be switched off, returning +to the old decimal only conversions, by setting the environment variable +`EPICS_DB_CONVERT_DECIMAL_ONLY` to `YES` (case insensitive) before `iocInit`. diff --git a/modules/database/src/ioc/db/dbConvert.c b/modules/database/src/ioc/db/dbConvert.c index ee1b5cbeb..2b17358c4 100644 --- a/modules/database/src/ioc/db/dbConvert.c +++ b/modules/database/src/ioc/db/dbConvert.c @@ -171,7 +171,7 @@ static long getStringChar(const dbAddr *paddr, *pdst++ = 0; else { char *end; - long status = epicsParseInt8(psrc, pdst++, 10, &end); + long status = epicsParseInt8(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -195,7 +195,7 @@ static long getStringUchar(const dbAddr *paddr, *pdst++ = 0; else { char *end; - long status = epicsParseUInt8(psrc, pdst++, 10, &end); + long status = epicsParseUInt8(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -219,7 +219,7 @@ static long getStringShort(const dbAddr *paddr, *pdst++ = 0; else { char *end; - long status = epicsParseInt16(psrc, pdst++, 10, &end); + long status = epicsParseInt16(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -243,7 +243,7 @@ static long getStringUshort(const dbAddr *paddr, *pdst++ = 0; else { char *end; - long status = epicsParseUInt16(psrc, pdst++, 10, &end); + long status = epicsParseUInt16(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -267,7 +267,7 @@ static long getStringLong(const dbAddr *paddr, *pdst++ = 0; else { char *end; - long status = epicsParseInt32(psrc, pdst++, 10, &end); + long status = epicsParseInt32(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -291,7 +291,7 @@ static long getStringUlong(const dbAddr *paddr, *pdst++ = 0; else { char *end; - long status = epicsParseUInt32(psrc, pdst, 10, &end); + long status = epicsParseUInt32(psrc, pdst, dbConvertBase, &end); if (status == S_stdlib_noConversion || (!status && (*end == '.' || *end == 'e' || *end == 'E'))) { @@ -328,7 +328,7 @@ static long getStringInt64(const dbAddr *paddr, *pdst++ = 0; else { char *end; - long status = epicsParseInt64(psrc, pdst++, 10, &end); + long status = epicsParseInt64(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -946,7 +946,7 @@ static long putStringChar(dbAddr *paddr, while (nRequest--) { char *end; - long status = epicsParseInt8(psrc, pdst++, 10, &end); + long status = epicsParseInt8(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -965,7 +965,7 @@ static long putStringUchar(dbAddr *paddr, while (nRequest--) { char *end; - long status = epicsParseUInt8(psrc, pdst++, 10, &end); + long status = epicsParseUInt8(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -984,7 +984,7 @@ static long putStringShort(dbAddr *paddr, while (nRequest--) { char *end; - long status = epicsParseInt16(psrc, pdst++, 10, &end); + long status = epicsParseInt16(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -1003,7 +1003,7 @@ static long putStringUshort(dbAddr *paddr, while (nRequest--) { char *end; - long status = epicsParseUInt16(psrc, pdst++, 10, &end); + long status = epicsParseUInt16(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -1022,7 +1022,7 @@ static long putStringLong(dbAddr *paddr, while (nRequest--) { char *end; - long status = epicsParseInt32(psrc, pdst++, 10, &end); + long status = epicsParseInt32(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -1041,7 +1041,7 @@ static long putStringUlong(dbAddr *paddr, while (nRequest--) { char *end; - long status = epicsParseUInt32(psrc, pdst, 10, &end); + long status = epicsParseUInt32(psrc, pdst, dbConvertBase, &end); if (status == S_stdlib_noConversion || (!status && (*end == '.' || *end == 'e' || *end == 'E'))) { @@ -1074,7 +1074,7 @@ static long putStringInt64(dbAddr *paddr, while (nRequest--) { char *end; - long status = epicsParseInt64(psrc, pdst++, 10, &end); + long status = epicsParseInt64(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -1178,7 +1178,7 @@ static long putStringEnum(dbAddr *paddr, epicsEnum16 val; char *end; - status = epicsParseUInt16(pfrom, &val, 10, &end); + status = epicsParseUInt16(pfrom, &val, dbConvertBase, &end); if (!status && val < enumStrs.no_str) { *pfield = val; return 0; @@ -1219,7 +1219,7 @@ static long putStringMenu(dbAddr *paddr, } } - if (!epicsParseUInt16(pfrom, &val, 10, NULL) + if (!epicsParseUInt16(pfrom, &val, dbConvertBase, NULL) && val < nChoice) { *pfield = val; return 0; @@ -1258,7 +1258,7 @@ static long putStringDevice(dbAddr *paddr, } } - if (!epicsParseUInt16(pfrom, &val, 10, NULL) && val < nChoice) { + if (!epicsParseUInt16(pfrom, &val, dbConvertBase, NULL) && val < nChoice) { *pfield = val; return 0; } diff --git a/modules/database/src/ioc/db/dbFastLinkConv.c b/modules/database/src/ioc/db/dbFastLinkConv.c index 48339f549..890c6732f 100644 --- a/modules/database/src/ioc/db/dbFastLinkConv.c +++ b/modules/database/src/ioc/db/dbFastLinkConv.c @@ -98,7 +98,7 @@ static long cvt_st_c(const void *f, void *t, const dbAddr *paddr) *to = 0; return 0; } - return epicsParseInt8(from, to, 10, &end); + return epicsParseInt8(from, to, dbConvertBase, &end); } /* Convert String to Unsigned Char */ @@ -112,7 +112,7 @@ static long cvt_st_uc(const void *f, void *t, const dbAddr *paddr) *to = 0; return 0; } - return epicsParseUInt8(from, to, 10, &end); + return epicsParseUInt8(from, to, dbConvertBase, &end); } /* Convert String to Short */ @@ -126,7 +126,7 @@ static long cvt_st_s(const void *f, void *t, const dbAddr *paddr) *to = 0; return 0; } - return epicsParseInt16(from, to, 10, &end); + return epicsParseInt16(from, to, dbConvertBase, &end); } /* Convert String to Unsigned Short */ @@ -140,7 +140,7 @@ static long cvt_st_us(const void *f, void *t, const dbAddr *paddr) *to = 0; return 0; } - return epicsParseUInt16(from, to, 10, &end); + return epicsParseUInt16(from, to, dbConvertBase, &end); } /* Convert String to Long */ @@ -154,7 +154,7 @@ static long cvt_st_l(const void *f, void *t, const dbAddr *paddr) *to = 0; return 0; } - return epicsParseInt32(from, to, 10, &end); + return epicsParseInt32(from, to, dbConvertBase, &end); } /* Convert String to Unsigned Long */ @@ -169,7 +169,7 @@ static long cvt_st_ul(const void *f, void *t, const dbAddr *paddr) *to = 0; return 0; } - status = epicsParseUInt32(from, to, 10, &end); + status = epicsParseUInt32(from, to, dbConvertBase, &end); if (status == S_stdlib_noConversion || (!status && (*end == '.' || *end == 'e' || *end == 'E'))) { /* @@ -198,7 +198,7 @@ static long cvt_st_q(const void *f, void *t, const dbAddr *paddr) *to = 0; return 0; } - return epicsParseInt64(from, to, 10, &end); + return epicsParseInt64(from, to, dbConvertBase, &end); } /* Convert String to UInt64 */ @@ -269,7 +269,7 @@ static long cvt_st_e(const void *f, void *t, const dbAddr *paddr) if (!status) { epicsEnum16 val; - status = epicsParseUInt16(from, &val, 10, NULL); + status = epicsParseUInt16(from, &val, dbConvertBase, NULL); if (!status && val < enumStrs.no_str) { *to = val; return 0; @@ -306,7 +306,7 @@ static long cvt_st_menu(const void *f, void *t, const dbAddr *paddr) } } - if (!epicsParseUInt16(from, &val, 10, NULL) && val < nChoice) { + if (!epicsParseUInt16(from, &val, dbConvertBase, NULL) && val < nChoice) { *to = val; return 0; } @@ -339,7 +339,7 @@ static long cvt_st_device(const void *f, void *t, const dbAddr *paddr) } } - if (!epicsParseUInt16(from, &val, 10, NULL) && val < nChoice) { + if (!epicsParseUInt16(from, &val, dbConvertBase, NULL) && val < nChoice) { *to = val; return 0; } diff --git a/modules/database/src/ioc/misc/iocInit.c b/modules/database/src/ioc/misc/iocInit.c index ac89495f0..57f141014 100644 --- a/modules/database/src/ioc/misc/iocInit.c +++ b/modules/database/src/ioc/misc/iocInit.c @@ -31,6 +31,7 @@ #include "epicsPrint.h" #include "epicsSignal.h" #include "epicsThread.h" +#include "epicsString.h" #include "errMdef.h" #include "iocsh.h" #include "taskwd.h" @@ -59,6 +60,7 @@ #include "devSup.h" #include "drvSup.h" #include "epicsRelease.h" +#include "epicsConvert.h" #include "initHooks.h" #include "iocInit.h" #include "link.h" @@ -129,6 +131,16 @@ static int iocBuild_1(void) errlogPrintf("iocBuild: " ERL_ERROR " Aborting, bad database definition (DBD)!\n"); return -1; } + + { + /* fall back to "old" decimal-only conversion if + EPICS_DB_CONVERT_DECIMAL_ONLY is YES (case insensitive). */ + const char* dec_only = getenv("EPICS_DB_CONVERT_DECIMAL_ONLY"); + if (dec_only && epicsStrCaseCmp(dec_only, "YES") == 0) { + dbConvertBase = 10; + } + } + epicsSignalInstallSigHupIgnore(); initHookAnnounce(initHookAtBeginning); diff --git a/modules/libcom/src/misc/epicsConvert.c b/modules/libcom/src/misc/epicsConvert.c index be4290932..380c04f76 100644 --- a/modules/libcom/src/misc/epicsConvert.c +++ b/modules/libcom/src/misc/epicsConvert.c @@ -33,3 +33,5 @@ LIBCOM_API float epicsConvertDoubleToFloat(double value) return (float) value; } + +int dbConvertBase = 0; diff --git a/modules/libcom/src/misc/epicsConvert.h b/modules/libcom/src/misc/epicsConvert.h index 52876dfd8..6be4b5eb6 100644 --- a/modules/libcom/src/misc/epicsConvert.h +++ b/modules/libcom/src/misc/epicsConvert.h @@ -20,6 +20,12 @@ extern "C" { LIBCOM_API float epicsConvertDoubleToFloat(double value); +/* dbConvertBase is used in dbPut and dbGet string to integer conversions. + It defaults to 0 but is set to 10 if the EPICS_DB_CONVERT_DECIMAL_ONLY + environment variable is YES (case insensitive). +*/ +LIBCOM_API extern int dbConvertBase; + #ifdef __cplusplus } #endif From bef86189d55f1d5ac3e44cec4b9154ac0a6f56c1 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Wed, 5 Nov 2025 17:13:18 +0100 Subject: [PATCH 53/99] CI fix windows cplusplus * fix misspelled MSVC command line flag * add windows builds with updated __cplusplus macro --- .github/workflows/ci-scripts-build.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-scripts-build.yml b/.github/workflows/ci-scripts-build.yml index 3ac88a6c0..1639653b1 100644 --- a/.github/workflows/ci-scripts-build.yml +++ b/.github/workflows/ci-scripts-build.yml @@ -157,13 +157,25 @@ jobs: cmp: vs2022 configuration: debug name: "Win-22 MSC-22" - extra: "CMD_CXXFLAGS=-analysis" + extra: "CMD_CXXFLAGS=-analyze" + + - os: windows-2022 + cmp: vs2022 + configuration: debug + name: "Win-22 MSC-22 c++14" + extra: "CMD_CXXFLAGS='-analyze -Zc:__cplusplus -std:c++17'" + + - os: windows-2022 + cmp: vs2022 + configuration: debug + name: "Win-22 MSC-22 c++17" + extra: "CMD_CXXFLAGS='-analyze -Zc:__cplusplus -std:c++17'" - os: windows-2022 cmp: vs2022 configuration: static-debug name: "Win-22 MSC-22, static" - extra: "CMD_CXXFLAGS=-analysis" + extra: "CMD_CXXFLAGS=-analyze" - os: windows-2022 cmp: vs2022 From 6c2cbe010184bc408c9fa7f90367cce3bb74ad20 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Wed, 5 Nov 2025 21:17:31 +0100 Subject: [PATCH 54/99] Update normativeTypes to fix NTNDArray::getValueSize (#732) * Update normativeTypes to fix NTNDArray::getValueSize * Update normativeTypes for Release Notes --------- Co-authored-by: Andrew Johnson --- modules/normativeTypes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/normativeTypes b/modules/normativeTypes index 7a2d264f2..65c31491d 160000 --- a/modules/normativeTypes +++ b/modules/normativeTypes @@ -1 +1 @@ -Subproject commit 7a2d264f2cb107bfd10adb23bc2b73d8323a79e4 +Subproject commit 65c31491d9a87aa793afefa0f4d5d4eb862bd921 From b308be3273fef5dc3b3e6af21bde91e409454d8f Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Mon, 19 May 2025 15:28:07 +1000 Subject: [PATCH 55/99] rtems: Add arm/xilinx-microzed configuration - This BSP works with SMP enabled and with LibBSD. --- .../os/CONFIG.Common.RTEMS-xilinx_zynq_microzed | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 configure/os/CONFIG.Common.RTEMS-xilinx_zynq_microzed diff --git a/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_microzed b/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_microzed new file mode 100644 index 000000000..212b1b16f --- /dev/null +++ b/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_microzed @@ -0,0 +1,16 @@ +# +# CONFIG.Common.RTEMS-xilinx_zynq_microzed +# Author: Chris Johns +# +# All RTEMS targets use the same Makefile fragment +# +#EXE = .elf +RTEMS_BSP = xilinx_zynq_microzed +RTEMS_TARGET_CPU = arm +GNU_TARGET = arm-rtems + +OP_SYS_LDLIBS += -Wl,--gc-sections +ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/xilinx_zynq_microzed/lib/ + + +include $(CONFIG)/os/CONFIG.Common.RTEMS From 862272d6665b174c178b472236214c45e8bbb0bc Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Mon, 10 Nov 2025 20:18:56 +0100 Subject: [PATCH 56/99] libCom/posix: add env var to opt out of FIFO scheduling - add EPICS_ALLOW_POSIX_THREAD_PRIORITY_SCHEDULING (default: YES) - start EPICS threads with default policy when set to NO --- configure/CONFIG_ENV | 2 ++ modules/libcom/src/env/envDefs.h | 1 + modules/libcom/src/osi/os/posix/osdThread.c | 9 +++++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/configure/CONFIG_ENV b/configure/CONFIG_ENV index 6d0d52a79..3ec50c020 100644 --- a/configure/CONFIG_ENV +++ b/configure/CONFIG_ENV @@ -53,3 +53,5 @@ EPICS_IOC_IGNORE_SERVERS="" # EPICS_IOC_LOG_PORT Log server port number etc. EPICS_IOC_LOG_PORT=7004 +# Posix priority scheduling +EPICS_ALLOW_POSIX_THREAD_PRIORITY_SCHEDULING=YES diff --git a/modules/libcom/src/env/envDefs.h b/modules/libcom/src/env/envDefs.h index c6421374f..bd0212a6f 100644 --- a/modules/libcom/src/env/envDefs.h +++ b/modules/libcom/src/env/envDefs.h @@ -77,6 +77,7 @@ LIBCOM_API extern const ENV_PARAM IOCSH_PS1; LIBCOM_API extern const ENV_PARAM IOCSH_HISTSIZE; LIBCOM_API extern const ENV_PARAM IOCSH_HISTEDIT_DISABLE; LIBCOM_API extern const ENV_PARAM EPICS_ABORT_ON_ASSERT; +LIBCOM_API extern const ENV_PARAM EPICS_ALLOW_POSIX_THREAD_PRIORITY_SCHEDULING; /** @brief List of all parameters. * * A NULL terminated array of all ENV_PARAM known to EPICS Base. diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c index 49754c0fd..4839c8b19 100644 --- a/modules/libcom/src/osi/os/posix/osdThread.c +++ b/modules/libcom/src/osi/os/posix/osdThread.c @@ -50,6 +50,7 @@ #include "epicsAssert.h" #include "epicsExit.h" #include "epicsAtomic.h" +#include "envDefs.h" LIBCOM_API void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level); LIBCOM_API void osdThreadHooksRun(epicsThreadId id); @@ -93,6 +94,7 @@ static pthread_mutex_t listLock; static ELLLIST pthreadList = ELLLIST_INIT; static commonAttr *pcommonAttr = 0; static int epicsThreadOnceCalled = 0; +static int wantPrioScheduling = 0; static epicsThreadOSD *createImplicit(void); @@ -384,6 +386,7 @@ static void once(void) checkStatusOnce(status,"pthread_attr_getschedparam"); findPriorityRange(pcommonAttr); + envGetBoolConfigParam(&EPICS_ALLOW_POSIX_THREAD_PRIORITY_SCHEDULING, &wantPrioScheduling); if(pcommonAttr->maxPriority == -1) { pcommonAttr->maxPriority = pcommonAttr->schedParam.sched_priority; @@ -604,8 +607,10 @@ epicsThreadCreateOpt(const char * name, return 0; pthreadInfo->isEpicsThread = 1; - setSchedulingPolicy(pthreadInfo, SCHED_FIFO); - pthreadInfo->isRealTimeScheduled = 1; + if (wantPrioScheduling) { + setSchedulingPolicy(pthreadInfo, SCHED_FIFO); + pthreadInfo->isRealTimeScheduled = 1; + } if (pthreadInfo->joinable) { /* extra ref for epicsThreadMustJoin() */ From 0916cf985c20a35ed7accfb732ebe904b9ddcd33 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Mon, 10 Nov 2025 14:56:41 +0100 Subject: [PATCH 57/99] libCom/posix: don't memlock() if opted out of FIFO scheduling --- modules/libcom/src/osi/os/posix/osdThread.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c index 4839c8b19..50eb911e1 100644 --- a/modules/libcom/src/osi/os/posix/osdThread.c +++ b/modules/libcom/src/osi/os/posix/osdThread.c @@ -466,7 +466,7 @@ void epicsThreadRealtimeLock(void) mlocked = 0; #if USE_MEMLOCK #ifndef RTEMS_LEGACY_STACK // seems to be part of libbsd? - if (pcommonAttr->maxPriority > pcommonAttr->minPriority) { + if (pcommonAttr->maxPriority > pcommonAttr->minPriority && wantPrioScheduling) { int status = mlockall(MCL_CURRENT | MCL_FUTURE); if (status) { From 4acae4c602e7f04f8dc131f79f9e873bbd4863ae Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Mon, 10 Nov 2025 15:15:08 +0100 Subject: [PATCH 58/99] PR #737: add Release Note snippet --- documentation/new-notes/PR-737.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 documentation/new-notes/PR-737.md diff --git a/documentation/new-notes/PR-737.md b/documentation/new-notes/PR-737.md new file mode 100644 index 000000000..57f770649 --- /dev/null +++ b/documentation/new-notes/PR-737.md @@ -0,0 +1,5 @@ +### Add environment variable to opt out of POSIX Real-Time scheduling + +On POSIX systems, processes with real-time capabilities can opt out of using +Posix thread priority scheduling and memory locking. +Set `EPICS_ALLOW_POSIX_THREAD_PRIORITY_SCHEDULING=NO` to achieve this. From 84b4a5f63c17dc14b4fc2871a36c921ddce21077 Mon Sep 17 00:00:00 2001 From: Jerzy Jamroz Date: Wed, 19 Nov 2025 17:47:23 +0100 Subject: [PATCH 59/99] Doc: Fix array field descriptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(aao, aai): change VAL field type from DOUBLE[] to FTVL[] * fix(aSub): change 'Input Value Fields' type from FT[A–U] to FT[A–U][NOA–NOU] * fix(aSub): change 'Output Value Fields' type from FTV[A–U] to FTV[A–U][NOVA–NOVU] * fix(waveform): change VAL field type from FTVL to FTVL[NELM] * fix(aai): remove 'analog' from the record name * fix(aao): remove 'analog' from the record name * fix(aai,aao): remove 'Analog' from the record names --- .../database/src/std/rec/aSubRecord.dbd.pod | 86 +++++++++---------- .../database/src/std/rec/aaiRecord.dbd.pod | 20 ++--- .../database/src/std/rec/aaoRecord.dbd.pod | 20 ++--- .../src/std/rec/waveformRecord.dbd.pod | 2 +- 4 files changed, 64 insertions(+), 64 deletions(-) diff --git a/modules/database/src/std/rec/aSubRecord.dbd.pod b/modules/database/src/std/rec/aSubRecord.dbd.pod index 86fd9e70d..14676b996 100644 --- a/modules/database/src/std/rec/aSubRecord.dbd.pod +++ b/modules/database/src/std/rec/aSubRecord.dbd.pod @@ -353,7 +353,7 @@ INPA,...,INPU. extra("void *a") #=read Yes #=write Yes - #=type Set by FTA + #=type Set by FTA[NOA] } field(B,DBF_NOACCESS) { prompt("Input value B") @@ -363,7 +363,7 @@ INPA,...,INPU. extra("void *b") #=read Yes #=write Yes - #=type Set by FTB + #=type Set by FTB[NOB] } field(C,DBF_NOACCESS) { prompt("Input value C") @@ -373,7 +373,7 @@ INPA,...,INPU. extra("void *c") #=read Yes #=write Yes - #=type Set by FTC + #=type Set by FTC[NOC] } field(D,DBF_NOACCESS) { prompt("Input value D") @@ -383,7 +383,7 @@ INPA,...,INPU. extra("void *d") #=read Yes #=write Yes - #=type Set by FTD + #=type Set by FTD[NOD] } field(E,DBF_NOACCESS) { prompt("Input value E") @@ -393,7 +393,7 @@ INPA,...,INPU. extra("void *e") #=read Yes #=write Yes - #=type Set by FTE + #=type Set by FTE[NOE] } field(F,DBF_NOACCESS) { prompt("Input value F") @@ -403,7 +403,7 @@ INPA,...,INPU. extra("void *f") #=read Yes #=write Yes - #=type Set by FTF + #=type Set by FTF[NOF] } field(G,DBF_NOACCESS) { prompt("Input value G") @@ -413,7 +413,7 @@ INPA,...,INPU. extra("void *g") #=read Yes #=write Yes - #=type Set by FTG + #=type Set by FTG[NOG] } field(H,DBF_NOACCESS) { prompt("Input value H") @@ -423,7 +423,7 @@ INPA,...,INPU. extra("void *h") #=read Yes #=write Yes - #=type Set by FTH + #=type Set by FTH[NOH] } field(I,DBF_NOACCESS) { prompt("Input value I") @@ -433,7 +433,7 @@ INPA,...,INPU. extra("void *i") #=read Yes #=write Yes - #=type Set by FTI + #=type Set by FTI[NOI] } field(J,DBF_NOACCESS) { prompt("Input value J") @@ -443,7 +443,7 @@ INPA,...,INPU. extra("void *j") #=read Yes #=write Yes - #=type Set by FTJ + #=type Set by FTJ[NOJ] } field(K,DBF_NOACCESS) { prompt("Input value K") @@ -453,7 +453,7 @@ INPA,...,INPU. extra("void *k") #=read Yes #=write Yes - #=type Set by FTK + #=type Set by FTK[NOK] } field(L,DBF_NOACCESS) { prompt("Input value L") @@ -463,7 +463,7 @@ INPA,...,INPU. extra("void *l") #=read Yes #=write Yes - #=type Set by FTL + #=type Set by FTL[NOL] } field(M,DBF_NOACCESS) { prompt("Input value M") @@ -473,7 +473,7 @@ INPA,...,INPU. extra("void *m") #=read Yes #=write Yes - #=type Set by FTM + #=type Set by FTM[NOM] } field(N,DBF_NOACCESS) { prompt("Input value N") @@ -483,7 +483,7 @@ INPA,...,INPU. extra("void *n") #=read Yes #=write Yes - #=type Set by FTN + #=type Set by FTN[NON] } field(O,DBF_NOACCESS) { prompt("Input value O") @@ -493,7 +493,7 @@ INPA,...,INPU. extra("void *o") #=read Yes #=write Yes - #=type Set by FTO + #=type Set by FTO[NOO] } field(P,DBF_NOACCESS) { prompt("Input value P") @@ -503,7 +503,7 @@ INPA,...,INPU. extra("void *p") #=read Yes #=write Yes - #=type Set by FTP + #=type Set by FTP[NOP] } field(Q,DBF_NOACCESS) { prompt("Input value Q") @@ -513,7 +513,7 @@ INPA,...,INPU. extra("void *q") #=read Yes #=write Yes - #=type Set by FTQ + #=type Set by FTQ[NOQ] } field(R,DBF_NOACCESS) { prompt("Input value R") @@ -523,7 +523,7 @@ INPA,...,INPU. extra("void *r") #=read Yes #=write Yes - #=type Set by FTR + #=type Set by FTR[NOR] } field(S,DBF_NOACCESS) { prompt("Input value S") @@ -533,7 +533,7 @@ INPA,...,INPU. extra("void *s") #=read Yes #=write Yes - #=type Set by FTS + #=type Set by FTS[NOS] } field(T,DBF_NOACCESS) { prompt("Input value T") @@ -543,7 +543,7 @@ INPA,...,INPU. extra("void *t") #=read Yes #=write Yes - #=type Set by FTT + #=type Set by FTT[NOT] } field(U,DBF_NOACCESS) { prompt("Input value U") @@ -553,7 +553,7 @@ INPA,...,INPU. extra("void *u") #=read Yes #=write Yes - #=type Set by FTU + #=type Set by FTU[NOU] } =head3 Input Value Data Types @@ -1161,7 +1161,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *vala") #=read Yes #=write Yes - #=type Set by FTVA + #=type Set by FTVA[NOVA] } field(VALB,DBF_NOACCESS) { prompt("Output value B") @@ -1171,7 +1171,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *valb") #=read Yes #=write Yes - #=type Set by FTVB + #=type Set by FTVB[NOVB] } field(VALC,DBF_NOACCESS) { prompt("Output value C") @@ -1181,7 +1181,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *valc") #=read Yes #=write Yes - #=type Set by FTVC + #=type Set by FTVC[NOVC] } field(VALD,DBF_NOACCESS) { prompt("Output value D") @@ -1191,7 +1191,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *vald") #=read Yes #=write Yes - #=type Set by FTVD + #=type Set by FTVD[NOVD] } field(VALE,DBF_NOACCESS) { prompt("Output value E") @@ -1201,7 +1201,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *vale") #=read Yes #=write Yes - #=type Set by FTVE + #=type Set by FTVE[NOVE] } field(VALF,DBF_NOACCESS) { prompt("Output value F") @@ -1211,7 +1211,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *valf") #=read Yes #=write Yes - #=type Set by FTVF + #=type Set by FTVF[NOVF] } field(VALG,DBF_NOACCESS) { prompt("Output value G") @@ -1221,7 +1221,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *valg") #=read Yes #=write Yes - #=type Set by FTVG + #=type Set by FTVG[NOVG] } field(VALH,DBF_NOACCESS) { prompt("Output value H") @@ -1231,7 +1231,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *valh") #=read Yes #=write Yes - #=type Set by FTVH + #=type Set by FTVH[NOVH] } field(VALI,DBF_NOACCESS) { prompt("Output value I") @@ -1241,7 +1241,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *vali") #=read Yes #=write Yes - #=type Set by FTVI + #=type Set by FTVI[NOVI] } field(VALJ,DBF_NOACCESS) { prompt("Output value J") @@ -1251,7 +1251,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *valj") #=read Yes #=write Yes - #=type Set by FTVJ + #=type Set by FTVJ[NOVJ] } field(VALK,DBF_NOACCESS) { prompt("Output value K") @@ -1261,7 +1261,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *valk") #=read Yes #=write Yes - #=type Set by FTVK + #=type Set by FTVK[NOVK] } field(VALL,DBF_NOACCESS) { prompt("Output value L") @@ -1271,7 +1271,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *vall") #=read Yes #=write Yes - #=type Set by FTVL + #=type Set by FTVL[NOVL] } field(VALM,DBF_NOACCESS) { prompt("Output value M") @@ -1281,7 +1281,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *valm") #=read Yes #=write Yes - #=type Set by FTVM + #=type Set by FTVM[NOVM] } field(VALN,DBF_NOACCESS) { prompt("Output value N") @@ -1291,7 +1291,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *valn") #=read Yes #=write Yes - #=type Set by FTVN + #=type Set by FTVN[NOVN] } field(VALO,DBF_NOACCESS) { prompt("Output value O") @@ -1301,7 +1301,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *valo") #=read Yes #=write Yes - #=type Set by FTVO + #=type Set by FTVO[NOVO] } field(VALP,DBF_NOACCESS) { prompt("Output value P") @@ -1311,7 +1311,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *valp") #=read Yes #=write Yes - #=type Set by FTVP + #=type Set by FTVP[NOVP] } field(VALQ,DBF_NOACCESS) { prompt("Output value Q") @@ -1321,7 +1321,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *valq") #=read Yes #=write Yes - #=type Set by FTVQ + #=type Set by FTVQ[NOVQ] } field(VALR,DBF_NOACCESS) { prompt("Output value R") @@ -1331,7 +1331,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *valr") #=read Yes #=write Yes - #=type Set by FTVR + #=type Set by FTVR[NOVR] } field(VALS,DBF_NOACCESS) { prompt("Output value S") @@ -1341,7 +1341,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *vals") #=read Yes #=write Yes - #=type Set by FTVS + #=type Set by FTVS[NOVS] } field(VALT,DBF_NOACCESS) { prompt("Output value T") @@ -1351,7 +1351,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *valt") #=read Yes #=write Yes - #=type Set by FTVT + #=type Set by FTVT[NOVT] } field(VALU,DBF_NOACCESS) { prompt("Output value U") @@ -1361,7 +1361,7 @@ be sent through the OUTA ... OUTU links during record processing. extra("void *valu") #=read Yes #=write Yes - #=type Set by FTVU + #=type Set by FTVU[NOVU] } =head3 Old Value Fields @@ -2378,7 +2378,7 @@ record can locate them. The simplest way is as follows: #include #include - + static long my_asub_routine(aSubRecord *prec) { ... } diff --git a/modules/database/src/std/rec/aaiRecord.dbd.pod b/modules/database/src/std/rec/aaiRecord.dbd.pod index e77832585..c27a225d0 100644 --- a/modules/database/src/std/rec/aaiRecord.dbd.pod +++ b/modules/database/src/std/rec/aaiRecord.dbd.pod @@ -7,9 +7,9 @@ # in file LICENSE that is included with this distribution. #************************************************************************* -=title Array Analog Input (aai) +=title Array Input (aai) -The array analog input record type is used to read array data. The array data can +The array input record type is used to read array data. The array data can contain any of the supported data types. The record is in many ways similar to the waveform record. It allows, however, the device support to allocate the array storage. @@ -31,14 +31,14 @@ The record-specific fields are described below, grouped by functionality. =head3 Scan Parameters -The array analog input record has the standard fields for specifying under what +The array input record has the standard fields for specifying under what circumstances the record will be processed. These fields are described in L. =head3 Read Parameters These fields are configurable by the user to specify how and from where the record -reads its data. The INP field determines from where the array analog input gets +reads its data. The INP field determines from where the array input gets its input. It can be a hardware address, a channel access or database link, or a constant. Only in records that use soft device support can the INP field be a channel access link, a database link, or a constant. Otherwise, the INP field must @@ -83,7 +83,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. =head3 Alarm Parameters -The array analog input record has the alarm parameters common to all record types. +The array input record has the alarm parameters common to all record types. =head3 Monitor Parameters @@ -108,11 +108,11 @@ These are the possible choices for the C and C fields: =head3 Run-time Parameters -These parameters are used by the run-time code for processing the array analog +These parameters are used by the run-time code for processing the array input record. They are not configured using a configuration tool. Only the VAL field is modifiable at run-time. -VAL references the array where the array analog input record stores its data. The +VAL references the array where the array input record stores its data. The BPTR field holds the address of the array. The NORD field holds a counter of the number of elements that have been read @@ -294,7 +294,7 @@ Scan forward link if necessary, set PACT FALSE, and return. =cut - include "dbCommon.dbd" + include "dbCommon.dbd" % %/* Declare Device Support Entry Table */ %struct aaiRecord; @@ -311,7 +311,7 @@ Scan forward link if necessary, set PACT FALSE, and return. special(SPC_DBADDR) pp(TRUE) extra("void * val") - #=type DOUBLE[NELM] + #=type Set by FTVL[NELM] #=read Yes #=write Yes } @@ -439,7 +439,7 @@ Scan forward link if necessary, set PACT FALSE, and return. =head3 Fields Of Interest To Device Support -Each array analog input record record must have an associated set of device +Each array input record record must have an associated set of device support routines. The primary responsibility of the device support routines is to obtain a new array value whenever C is called. The device support routines are primarily interested in the following fields: diff --git a/modules/database/src/std/rec/aaoRecord.dbd.pod b/modules/database/src/std/rec/aaoRecord.dbd.pod index c35b7454e..4ad29a0bb 100644 --- a/modules/database/src/std/rec/aaoRecord.dbd.pod +++ b/modules/database/src/std/rec/aaoRecord.dbd.pod @@ -7,9 +7,9 @@ # in file LICENSE that is included with this distribution. #************************************************************************* -=title Array Analog Output (aao) +=title Array Output (aao) -The array analog output record type is used to write array data. The array data +The array output record type is used to write array data. The array data can contain any of the supported data types. The record is in many ways similar to the waveform record but outputs arrays instead of reading them. It also allows the device support to allocate the array storage. @@ -31,7 +31,7 @@ The record-specific fields are described below, grouped by functionality. =head3 Scan Parameters -The array analog output record has the standard fields for specifying under what +The array output record has the standard fields for specifying under what circumstances the record will be processed. These fields are described in L. @@ -40,7 +40,7 @@ These fields are described in L. =head3 Write Parameters These fields are configurable by the user to specify how and where to the record -writes its data. The OUT field determines where the array analog output writes +writes its data. The OUT field determines where the array output writes its output. It can be a hardware address, a channel access or database link, or a constant. Only in records that use soft device support can the OUT field be a channel access link, a database link, or a constant. Otherwise, the OUT field @@ -87,7 +87,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. =head3 Alarm Parameters -The array analog output record has the alarm parameters common to all record +The array output record has the alarm parameters common to all record types. =head3 Monitor Parameters @@ -115,11 +115,11 @@ These are the choices available for the C and C fields =head3 Run-time Parameters -These parameters are used by the run-time code for processing the array analog +These parameters are used by the run-time code for processing the array output record. They are not configured using a configuration tool. Only the VAL field is modifiable at run-time. -VAL references the array where the array analog output record stores its data. +VAL references the array where the array output record stores its data. The BPTR field holds the address of the array. The NORD field holds a counter of the number of elements that have been written @@ -317,7 +317,7 @@ Scan forward link if necessary, set PACT FALSE, and return. =cut - include "dbCommon.dbd" + include "dbCommon.dbd" % %/* Declare Device Support Entry Table */ %struct aaoRecord; @@ -333,7 +333,7 @@ Scan forward link if necessary, set PACT FALSE, and return. special(SPC_DBADDR) pp(TRUE) extra("void * val") - #=type DOUBLE[] + #=type Set by FTVL[NELM] #=read Yes #=write Yes } @@ -472,7 +472,7 @@ Scan forward link if necessary, set PACT FALSE, and return. =head3 Fields Of Interest To Device Support -Each array analog output record record must have an associated set of device +Each array output record record must have an associated set of device support routines. The primary responsibility of the device support routines is to write the array data value whenever C is called. The device support routines are primarily interested in the following fields: diff --git a/modules/database/src/std/rec/waveformRecord.dbd.pod b/modules/database/src/std/rec/waveformRecord.dbd.pod index c47721d4b..329ceee35 100644 --- a/modules/database/src/std/rec/waveformRecord.dbd.pod +++ b/modules/database/src/std/rec/waveformRecord.dbd.pod @@ -403,7 +403,7 @@ routine and NORD is also set at that time. special(SPC_DBADDR) pp(TRUE) extra("void * val") - #=type Set by FTVL + #=type Set by FTVL[NELM] #=read Yes #=write Yes } From b893ae080e56639762538f749b3fed4c1841f985 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 20 Nov 2025 14:50:06 -0800 Subject: [PATCH 60/99] update GHA --- .ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci b/.ci index 0e93b7085..261f218e0 160000 --- a/.ci +++ b/.ci @@ -1 +1 @@ -Subproject commit 0e93b708551cb7bb212fda7029eccdf872dabc81 +Subproject commit 261f218e094e39550e3a7c54b98e34adcf42ad9b From 46ecf5d25c0f939010e7c18fc8ef7df33c9b062e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 13 Nov 2025 07:49:07 -0800 Subject: [PATCH 61/99] Revert "Fetch the custom cfg/CONFIG_* files early" This reverts commit e4fbd83c93a68a1d9cb863ff341735388d246347. --- configure/CONFIG | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/configure/CONFIG b/configure/CONFIG index edf8d4f73..e72537db1 100644 --- a/configure/CONFIG +++ b/configure/CONFIG @@ -50,9 +50,6 @@ endif include $(CONFIG)/CONFIG_COMMON include $(CONFIG)/CONFIG_FILE_TYPE -# Fetch the module-specific CFGS before overwriting INSTALL_LOCATION in order to load them later -TOP_CFG_CONFIGS := $(wildcard $(INSTALL_CFG)/CONFIG*) - # Base-specific build options # include $(CONFIG)/CONFIG_BASE @@ -120,6 +117,7 @@ endif # Include $(INSTALL_CFG)/CONFIG* definitions # +TOP_CFG_CONFIGS = $(wildcard $(INSTALL_CFG)/CONFIG*) ifneq ($(TOP_CFG_CONFIGS),) include $(TOP_CFG_CONFIGS) endif From 9b532540bf22112963edf3c9fb4ee5a9b59b0e8c Mon Sep 17 00:00:00 2001 From: george-mcintyre Date: Fri, 4 Apr 2025 04:59:17 +0100 Subject: [PATCH 62/99] Introduce Future Proofing to ACF parsing. - Allow ACF files to contain sections that are unsupported - introduce a pragma disable-strict-parsing to disable strict parsing and allow files to be accepted Add tests for future proof parser functionaluty Expanded tests for Access Security configurations to validate parsing behaviors for unsupported elements. Added detailed cases covering invalid formats, unsupported modifications, and acceptable configurations while maintaining future-proofing and backward compatibility assurances. Refactor parser to handle generic items and floating-point tokens Replaced "unsupported elements" with a more flexible "generic items" mechanism to enhance parser extensibility. Added support for floating-point numbers in parsing rules. Updated tests and rules to reflect these changes, ensuring better handling of nested and unknown configurations. Add warnings for unsupported ACF elements and update tests Introduced `yywarn` to log warnings for unsupported ACF elements, rule permissions, and conditions in `asLib.y`. Updated test configuration to replace generic placeholders with more descriptive names. Adjusted test cases and reduced the test plan count to reflect the changes. Remove unused strictParsing logic and improve error handling. Removed strictParsing variable and related code as it was no longer in use. Enhanced error messaging and parsing logic for handling unsupported elements. Updated test cases for better clarity and expanded coverage of edge cases. Enhance ASLib parsing with Int64/Float64 support. Updated ASLib to utilize 64-bit integers and floats for improved parsing precision. Replaced legacy parsing methods with epics-friendly functions, improved error handling, and adjusted tests and grammar accordingly. Refactor AS parser to separate non-rule keywords Introduced a new `non_rule_keyword` type and moved `tokenASG` and `tokenRULE` under it for better logical grouping. This improves parser clarity and maintains a clean separation of keyword types. Add rule-specific parsing for generic block elements Introduce new rules for parsing rule-specific generic block elements and their names. This enhances the grammar to handle additional cases while maintaining memory management and error handling. Adjust references to use the new rule where applicable. Refactor grammar rules in asLib.y to remove remaining shift/reduce conflict Reorganize and refine grammar rules for handling generic elements, blocks, and lists. Simplifies the structure while introducing clearer distinctions between different constructs. This enhances maintainability and removes ambiguities in parsing. --- modules/libcom/src/as/asLib.h | 1 + modules/libcom/src/as/asLib.y | 207 ++++++-- modules/libcom/src/as/asLibRoutines.c | 41 +- modules/libcom/src/as/asLib_lex.l | 29 +- modules/libcom/test/aslibtest.c | 651 +++++++++++++++++++++++++- 5 files changed, 867 insertions(+), 62 deletions(-) diff --git a/modules/libcom/src/as/asLib.h b/modules/libcom/src/as/asLib.h index 415d98d1a..b2361c531 100644 --- a/modules/libcom/src/as/asLib.h +++ b/modules/libcom/src/as/asLib.h @@ -202,6 +202,7 @@ typedef struct{ ELLLIST uagList; /*List of ASGUAG*/ ELLLIST hagList; /*List of ASGHAG*/ int trapMask; + int ignore; // 1 if rule to be ignored because of unknown elements } ASGRULE; typedef struct{ ELLNODE node; diff --git a/modules/libcom/src/as/asLib.y b/modules/libcom/src/as/asLib.y index 1e7397123..b947223b4 100644 --- a/modules/libcom/src/as/asLib.y +++ b/modules/libcom/src/as/asLib.y @@ -12,6 +12,7 @@ static int yyerror(char *); static int yy_start; #include "asLibRoutines.c" static int yyFailed = FALSE; +static int yyWarned = FALSE; static int line_num=1; static UAG *yyUag=NULL; static HAG *yyHag=NULL; @@ -21,17 +22,24 @@ static ASGRULE *yyAsgRule=NULL; %start asconfig -%token tokenUAG tokenHAG tokenASG tokenRULE tokenCALC -%token tokenINP -%token tokenINTEGER -%token tokenSTRING +%token tokenUAG tokenHAG tokenASG tokenRULE tokenCALC tokenINP tokenSTRING +%token tokenINT64 +%token tokenFLOAT64 %union { - int Int; + epicsInt64 Int64; + epicsFloat64 Float64; char *Str; } +%type non_rule_keyword +%type generic_block_elem_name +%type generic_block_elem +%type rule_generic_block_elem +%type rule_generic_block_elem_name +%type keyword + %% asconfig: asconfig asconfig_item @@ -43,13 +51,121 @@ asconfig_item: tokenUAG uag_head uag_body | tokenHAG hag_head | tokenASG asg_head asg_body | tokenASG asg_head + | generic_item + ; + +keyword: tokenUAG + | tokenHAG + | tokenCALC + | non_rule_keyword + ; + +non_rule_keyword: tokenASG + | tokenRULE + | tokenINP + ; + +generic_item: tokenSTRING generic_head generic_list_block + { + yywarn("Ignoring unsupported TOP LEVEL Access Control Definition", $1); + free($1); + } + | tokenSTRING generic_head generic_block + { + yywarn("Ignoring unsupported TOP LEVEL Access Control Definition", $1); + free($1); + } + | tokenSTRING generic_head + { + yywarn("Ignoring unsupported TOP LEVEL Access Control Definition", $1); + free($1); + } + ; + +generic_head: '(' ')' + | '(' generic_element ')' + | '(' generic_list ')' + ; + +generic_list_block: '{' generic_element '}' + '{' generic_list '}' + ; + +generic_list: generic_list ',' generic_element + | generic_element ',' generic_element + ; + +generic_element: keyword + | tokenSTRING + { + free($1); + } + | tokenINT64 + | tokenFLOAT64 + ; + +generic_block: '{' generic_element '}' + | '{' generic_list '}' + | '{' generic_block_list '}' + ; + +generic_block_list: generic_block_list generic_block_elem + { + free($2); + } + | generic_block_elem + { + free($1); + } + ; + +generic_block_elem: generic_block_elem_name generic_head generic_block + { + $$ = $1; + } + | generic_block_elem_name generic_head + { + $$ = $1; + } + ; + +generic_block_elem_name: keyword + { + $$ = strdup($1); + if (!$$) yyerror("Out of memory"); + } + | tokenSTRING + { + $$ = $1; + } + ; + +rule_generic_block_elem: rule_generic_block_elem_name generic_head generic_block + { + $$ = $1; + } + | rule_generic_block_elem_name generic_head + { + $$ = $1; + } + ; + +rule_generic_block_elem_name: non_rule_keyword + { + $$ = strdup($1); + if (!$$) yyerror("Out of memory"); + } + | tokenSTRING + { + $$ = $1; + } ; uag_head: '(' tokenSTRING ')' { yyUag = asUagAdd($2); if(!yyUag) yyerror(""); - free((void *)$2); + free($2); } ; @@ -67,7 +183,7 @@ uag_user_list_name: tokenSTRING { if (asUagAddUser(yyUag,$1)) yyerror(""); - free((void *)$1); + free($1); } ; @@ -75,7 +191,7 @@ hag_head: '(' tokenSTRING ')' { yyHag = asHagAdd($2); if(!yyHag) yyerror(""); - free((void *)$2); + free($2); } ; @@ -90,7 +206,7 @@ hag_host_list_name: tokenSTRING { if (asHagAddHost(yyHag,$1)) yyerror(""); - free((void *)$1); + free($1); } ; @@ -98,7 +214,7 @@ asg_head: '(' tokenSTRING ')' { yyAsg = asAsgAdd($2); if(!yyAsg) yyerror(""); - free((void *)$2); + free($2); } ; @@ -114,49 +230,49 @@ asg_body_item: inp_config | rule_config inp_config: tokenINP '(' tokenSTRING ')' { - if (asAsgAddInp(yyAsg,$3,$1)) + if (asAsgAddInp(yyAsg,$3,(int)$1)) yyerror(""); - free((void *)$3); + free($3); } ; rule_config: tokenRULE rule_head rule_body | tokenRULE rule_head -rule_head: rule_head_manditory rule_head_options +rule_head: '(' rule_head_mandatory ',' rule_log_option ')' + | '(' rule_head_mandatory ')' + ; -rule_head_manditory: '(' tokenINTEGER ',' tokenSTRING + +rule_head_mandatory: tokenINT64 ',' tokenSTRING { - asAccessRights rights; - - if((strcmp($4,"NONE")==0)) { - rights=asNOACCESS; - } else if((strcmp($4,"READ")==0)) { - rights=asREAD; - } else if((strcmp($4,"WRITE")==0)) { - rights=asWRITE; + if ($1 < 0) { + char message[40]; + sprintf(message, "RULE: LEVEL must be positive: %lld", $1); + yyerror(message); + } else if((strcmp($3,"NONE")==0)) { + yyAsgRule = asAsgAddRule(yyAsg,asNOACCESS,(int)$1); + } else if((strcmp($3,"READ")==0)) { + yyAsgRule = asAsgAddRule(yyAsg,asREAD,(int)$1); + } else if((strcmp($3,"WRITE")==0)) { + yyAsgRule = asAsgAddRule(yyAsg,asWRITE,(int)$1); } else { - yyerror("Access rights must be NONE, READ or WRITE"); - rights = asNOACCESS; + yywarn("Ignoring RULE that contains an unsupported keyword", $3); } - yyAsgRule = asAsgAddRule(yyAsg,rights,$2); - free((void *)$4); + free($3); } ; -rule_head_options: ')' - | rule_log_options - -rule_log_options: ',' tokenSTRING ')' +rule_log_option: tokenSTRING { - if((strcmp($2,"TRAPWRITE")==0)) { + if((strcmp($1,"TRAPWRITE")==0)) { long status; status = asAsgAddRuleOptions(yyAsgRule,AS_TRAP_WRITE); if(status) yyerror(""); - } else if((strcmp($2,"NOTRAPWRITE")!=0)) { + } else if((strcmp($1,"NOTRAPWRITE")!=0)) { yyerror("Log options must be TRAPWRITE or NOTRAPWRITE"); } - free((void *)$2); + free($1); } ; @@ -173,7 +289,14 @@ rule_list_item: tokenUAG '(' rule_uag_list ')' { if (asAsgRuleCalc(yyAsgRule,$3)) yyerror(""); - free((void *)$3); + free($3); + } + | rule_generic_block_elem + { + yywarn("Ignoring RULE that contains an unsupported keyword", $1); + free($1); + if (asAsgRuleDisable(yyAsgRule)) + yyerror(""); } ; @@ -185,7 +308,7 @@ rule_uag_list_name: tokenSTRING { if (asAsgRuleUagAdd(yyAsgRule,$1)) yyerror(""); - free((void *)$1); + free($1); } ; @@ -197,7 +320,7 @@ rule_hag_list_name: tokenSTRING { if (asAsgRuleHagAdd(yyAsgRule,$1)) yyerror(""); - free((void *)$1); + free($1); } ; %% @@ -207,12 +330,19 @@ rule_hag_list_name: tokenSTRING static int yyerror(char *str) { if (strlen(str)) - errlogPrintf("%s at line %d\n", str, line_num); + fprintf(stderr, ERL_ERROR " %s at line %d\n", str, line_num); else - errlogPrintf(ERL_ERROR " at line %d\n", line_num); + fprintf(stderr, ERL_ERROR " at line %d\n", line_num); yyFailed = TRUE; return 0; } +static int yywarn(char *str, char *token) +{ + if (!yyWarned && strlen(str) && strlen(token)) + fprintf(stderr, ERL_WARNING " %s at line %d: %s\n", str, line_num, token); + yyWarned = TRUE; + return 0; +} static int myParse(ASINPUTFUNCPTR inputfunction) { static int FirstFlag = 1; @@ -222,6 +352,7 @@ static int myParse(ASINPUTFUNCPTR inputfunction) if (!FirstFlag) { line_num=1; yyFailed = FALSE; + yyWarned = FALSE; yyreset(); yyrestart(NULL); } diff --git a/modules/libcom/src/as/asLibRoutines.c b/modules/libcom/src/as/asLibRoutines.c index 571471e2a..f86f985e5 100644 --- a/modules/libcom/src/as/asLibRoutines.c +++ b/modules/libcom/src/as/asLibRoutines.c @@ -66,6 +66,7 @@ static long asAsgAddRuleOptions(ASGRULE *pasgrule,int trapMask); static long asAsgRuleUagAdd(ASGRULE *pasgrule,const char *name); static long asAsgRuleHagAdd(ASGRULE *pasgrule,const char *name); static long asAsgRuleCalc(ASGRULE *pasgrule,const char *calc); +static long asAsgRuleDisable(ASGRULE *pasgrule); /* asInitialize can be called while access security is already active. @@ -585,8 +586,8 @@ int epicsStdCall asDumpFP( pasginp = (ASGINP *)ellNext(&pasginp->node); } while(pasgrule) { - int print_end_brace; - + int print_rule_end_brace = FALSE; + if (pasgrule->ignore) goto next_rule; fprintf(fp,"\tRULE(%d,%s,%s)", pasgrule->level,asAccessName[pasgrule->access], asTrapOption[pasgrule->trapMask]); @@ -594,10 +595,10 @@ int epicsStdCall asDumpFP( pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); if(pasguag || pasghag || pasgrule->calc) { fprintf(fp," {\n"); - print_end_brace = TRUE; + print_rule_end_brace = TRUE; } else { fprintf(fp,"\n"); - print_end_brace = FALSE; + print_rule_end_brace = FALSE; } if(pasguag) fprintf(fp,"\t\tUAG("); while(pasguag) { @@ -605,7 +606,6 @@ int epicsStdCall asDumpFP( pasguag = (ASGUAG *)ellNext(&pasguag->node); if(pasguag) fprintf(fp,","); else fprintf(fp,")\n"); } - pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); if(pasghag) fprintf(fp,"\t\tHAG("); while(pasghag) { fprintf(fp,"%s",pasghag->phag->name); @@ -618,7 +618,8 @@ int epicsStdCall asDumpFP( fprintf(fp," result=%s",(pasgrule->result==1 ? "TRUE" : "FALSE")); fprintf(fp,"\n"); } - if(print_end_brace) fprintf(fp,"\t}\n"); +next_rule: + if(print_rule_end_brace) fprintf(fp,"\t}\n"); pasgrule = (ASGRULE *)ellNext(&pasgrule->node); } pasgmember = (ASGMEMBER *)ellFirst(&pasg->memberList); @@ -735,7 +736,7 @@ int epicsStdCall asDumpRulesFP(FILE *fp,const char *asgname) pasg = (ASG *)ellFirst(&pasbase->asgList); if(!pasg) fprintf(fp,"No ASGs\n"); while(pasg) { - int print_end_brace; + int print_end_brace = FALSE; if(asgname && strcmp(asgname,pasg->name)!=0) { pasg = (ASG *)ellNext(&pasg->node); @@ -761,8 +762,8 @@ int epicsStdCall asDumpRulesFP(FILE *fp,const char *asgname) pasginp = (ASGINP *)ellNext(&pasginp->node); } while(pasgrule) { - int print_end_brace; - + int print_rule_end_brace = FALSE; + if ( pasgrule->ignore) goto next_rule; fprintf(fp,"\tRULE(%d,%s,%s)", pasgrule->level,asAccessName[pasgrule->access], asTrapOption[pasgrule->trapMask]); @@ -770,10 +771,10 @@ int epicsStdCall asDumpRulesFP(FILE *fp,const char *asgname) pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); if(pasguag || pasghag || pasgrule->calc) { fprintf(fp," {\n"); - print_end_brace = TRUE; + print_rule_end_brace = TRUE; } else { fprintf(fp,"\n"); - print_end_brace = FALSE; + print_rule_end_brace = FALSE; } if(pasguag) fprintf(fp,"\t\tUAG("); while(pasguag) { @@ -793,7 +794,8 @@ int epicsStdCall asDumpRulesFP(FILE *fp,const char *asgname) fprintf(fp," result=%s",(pasgrule->result==1 ? "TRUE" : "FALSE")); fprintf(fp,"\n"); } - if(print_end_brace) fprintf(fp,"\t}\n"); +next_rule: + if(print_rule_end_brace) fprintf(fp,"\t}\n"); pasgrule = (ASGRULE *)ellNext(&pasgrule->node); } if(print_end_brace) fprintf(fp,"}\n"); @@ -948,6 +950,7 @@ static long asComputeAsgPvt(ASG *pasg) if(!asActive) return(S_asLib_asNotActive); pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList); while(pasgrule) { + if ( pasgrule->ignore) goto next_rule; double result = pasgrule->result; /* set for VAL */ long status; @@ -960,6 +963,8 @@ static long asComputeAsgPvt(ASG *pasg) pasgrule->result = ((result>.99) && (result<1.01)) ? 1 : 0; } } + +next_rule: pasgrule = (ASGRULE *)ellNext(&pasgrule->node); } pasg->inpChanged = FALSE; @@ -995,6 +1000,7 @@ static long asComputePvt(ASCLIENTPVT asClientPvt) oldaccess=pasgclient->access; pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList); while(pasgrule) { + if (pasgrule->ignore) goto next_rule; if(access == asWRITE) break; if(access>=pasgrule->access) goto next_rule; if(pasgclient->level > pasgrule->level) goto next_rule; @@ -1408,3 +1414,14 @@ static long asAsgRuleCalc(ASGRULE *pasgrule,const char *calc) } return(status); } + +/** + * @brief Disable a rule if it contains unsupported elements + * @param pasgrule the rule to disable + * @return Non-zero if rule was not disabled + */ +static long asAsgRuleDisable(ASGRULE *pasgrule) { + if (!pasgrule) return 1; + pasgrule->ignore = 1; + return 0; +} diff --git a/modules/libcom/src/as/asLib_lex.l b/modules/libcom/src/as/asLib_lex.l index b7f01f5c5..7ba1c8da7 100644 --- a/modules/libcom/src/as/asLib_lex.l +++ b/modules/libcom/src/as/asLib_lex.l @@ -21,6 +21,8 @@ punctuation [(){},] link [A-U] %{ +#include "epicsStdlib.h" + static ASINPUTFUNCPTR *my_yyinput; #undef YY_INPUT #define YY_INPUT(b,r,ms) (r=(*my_yyinput)((char *)b,ms)) @@ -43,14 +45,31 @@ RULE { return(tokenRULE); } CALC { return(tokenCALC); } INP{link} { - yylval.Int = (unsigned char)yytext[3]; - yylval.Int -= 'A'; + yylval.Int64 = (unsigned char)yytext[3]; + yylval.Int64 -= 'A'; return(tokenINP); } -{digit}+ { /*integer*/ - yylval.Int = atoi((char *)yytext); - return(tokenINTEGER); +[-+]?{digit}*\.{digit}+([eE][-+]?{digit}+)? { + char *end; + if (epicsParseDouble((char *)yytext, &yylval.Float64, &end) ) { + char message[40]; + sprintf(message, "Error parsing Float64: %s", (char *)yytext); + yyerror(message); + } else { + return(tokenFLOAT64); + } +} + +[-+]?{digit}+ { /*integer 64*/ + char *end; + if (epicsParseInt64((char *)yytext, &yylval.Int64, 10, &end) ) { + char message[40]; + sprintf(message, "Error parsing Int64: %s", (char *)yytext); + yyerror(message); + } else { + return(tokenINT64); + } } {name}+ { /*unquoted string*/ diff --git a/modules/libcom/test/aslibtest.c b/modules/libcom/test/aslibtest.c index eefad5700..705823de5 100644 --- a/modules/libcom/test/aslibtest.c +++ b/modules/libcom/test/aslibtest.c @@ -22,18 +22,538 @@ static char *asUser, *asHost; static int asAsl; +/** + * @brief Test data with Host Access Groups (HAG) + * + * This includes a host access group (HAG) for localhost and a default Access Security Group (ASG) + * with rules for read and write access to the HAG. + */ +static const char hostname_config[] = "" + "HAG(foo) {localhost}\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro) {\n" + " RULE(0, NONE)\n" + " RULE(1, READ) {\n" + " HAG(foo)\n" + " }\n" + "}\n" + + "ASG(rw) {\n" + " RULE(1, WRITE) {\n" + " HAG(foo)\n" + " }\n" + "}\n"; + +/** + * Test data with unsupported elements. + * The unsupported element should be silently ignored, but the rest of the config is processed. + * + * top-unknown-keyword(WELL,FORMED,LIST) + * - valid top level keyword with well-formed arg list + */ +static const char supported_config_1[] = "" + "HAG(foo) {localhost}\n" + + "GENERIC(WELL, FORMED, ARG, LIST)\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro) {\n" + " RULE(0, NONE)\n" + " RULE(1, READ) {\n" + " HAG(foo)\n" + " }\n" + "}\n"; + +/** + * Test data with unsupported elements. + * The unsupported element should be silently ignored, but the rest of the config is processed. + * + * top-unknown-keyword(WELL,FORMED,LIST) { WELL,FORMED,LIST } + * - valid top level keyword with well-formed arg list and valid arg list body + */ +static const char supported_config_2[] = "" + "HAG(foo) {localhost}\n" + + "SIMPLE(WELL, FORMED, ARG, LIST) {\n" + " WELL, FORMED, LIST\n" + "}\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro) {\n" + " RULE(0, NONE)\n" + " RULE(1, READ) {\n" + " HAG(foo)\n" + " }\n" + "}\n"; + +/** + * Test data with unsupported elements. + * The unsupported element should be silently ignored, but the rest of the config is processed. + * + * top-unknown-keyword(WELL,FORMED,LIST) { recursive-body-keyword(WELL,FORMED,LIST) } + * - valid top level keyword with well-formed arg list and valid recursive body + * - includes quoted strings, integers, and floating point numbers + */ +static const char supported_config_3[] = "" + "HAG(foo) {localhost}\n" + + "COMPLEX_ARGUMENTS(1, WELL, \"FORMED\", ARG, LIST) {\n" + " ALSO_GENERIC(WELL, FORMED, ARG, LIST, 2.0) \n" + "}\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro) {\n" + " RULE(0, NONE)\n" + " RULE(1, READ) {\n" + " HAG(foo)\n" + " }\n" + "}\n"; + +/** + * Test data with unsupported elements. + * The unsupported element should be silently ignored, but the rest of the config is processed. + * + * top-unknown-keyword(WELL,FORMED,LIST) { recursive-body-keyword(WELL,FORMED,LIST) { AND_BODY } } + * - valid top level keyword with well-formed arg list and valid recursive body, with a nested body + * - includes floating point numbers, and an empty arg list + */ +static const char supported_config_4[] = "" +"HAG(foo) {localhost}\n" + +"SUB_BLOCKS(1.0, ARGS) {\n" +" ALSO_GENERIC() {\n" +" AND_LIST_BODY\n" +" }\n" +" ANOTHER_GENERIC() {\n" +" BIGGER, LIST, BODY\n" +" }\n" +"}\n" + +"ASG(DEFAULT) {\n" +" RULE(0, NONE)\n" +"}\n" + +"ASG(ro) {\n" +" RULE(0, NONE)\n" +" RULE(1, READ) {\n" +" HAG(foo)\n" +" }\n" +"}\n"; + +/** + * Test data with unsupported elements. + * The unsupported element should be silently ignored, but the rest of the config is processed. + * + * top-unknown-keyword(WELL,FORMED,LIST) { recursive-body-keyword(WELL,FORMED,LIST) { AND_RECURSIVE_BODY() {LIST, LIST } } + * - valid top level keyword with well-formed arg list and valid recursive body, with a nested recursion + * - includes floating point numbers, and an empty arg list + */ +static const char supported_config_5[] = "" + "HAG(foo) {localhost}\n" + + "RECURSIVE_SUB_BLOCKS(1.0, -2.3, +4.5, ARGS, +2.71828E-23, -2.71828e+23, +12, -13, +-14) {\n" + " ALSO_GENERIC() {\n" + " AND_RECURSIVE(FOO) {\n" + " LIST, BODY\n" + " }\n" + " }\n" + "}\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro) {\n" + " RULE(0, NONE)\n" + " RULE(+1, READ) {\n" + " HAG(foo)\n" + " }\n" + "}\n"; + +/** + * Test data with unsupported elements. + * The unsupported element should be silently ignored, but the rest of the config is processed. + * + * top-unknown-keyword(KEYWORD) { KEYWORD(KEYWORD) } + * - valid top level keyword with keyword for args, recursive body name, and arg list + * - top level generic items referenced in RULES, then RULES are ignored + */ +static const char supported_config_6[] = "" + "HAG(foo) {localhost}\n" + + "WITH_KEYWORDS(UAG) {\n" + " ASG(HAL, IMP, CALC, RULE)\n" + " HAL(USG, MAL) {\n" + " HAG(foo)\n" + " }\n" + "}\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ignored) {\n" + " RULE(0, NONE)\n" + " RULE(1, READ) {\n" + " WITH_KEYWORDS(UAG)\n" + " }\n" + "}\n" + + "ASG(ro) {\n" + " RULE(0, NONE)\n" + " RULE(1, READ) {\n" + " HAG(foo)\n" + " }\n" + " RULE(2, WRITE) {\n" + " WITH_KEYWORDS(UAG)\n" + " }\n" + "}\n"; + +/** + * Test data with unsupported elements. + * The unsupported elements should be silently ignored, and the rule will not match, + * but the rest of the config is processed. + * + * - RULE contains unsupported elements + */ +static const char supported_config_7[] = "" + "HAG(foo) {localhost}\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro) {\n" + " RULE(0, NONE)\n" + " RULE(1, READ) {\n" + " HAG(foo)\n" + " BAD_PREDICATE(\"x509\")\n" + " BAD_PREDICATE_AS_WELL(\"EPICS Certificate Authority\")\n" + " }\n" + "}\n"; + +/** + * Test data with unsupported elements. + * The unsupported elements should be silently ignored, and the rule will not match, + * but the rest of the config is processed. + * + * - unexpected permission name in arg list for RULE element ignored + */ +static const char supported_config_8[] = "" + "HAG(foo) {localhost}\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro) {\n" + " RULE(0, NONE)\n" + " RULE(1, ADDITIONAL_PERMISSION) {\n" + " HAG(foo)\n" + " }\n" + "}\n" + ; + +/** + * Test data with unsupported elements. + * The unsupported element should cause an error as the format is invalid. + * + * top-unknown-keyword( a b ) + * - invalid arg list missing commas + */ +static const char unsupported_config_1[] = "" + "HAG(foo) {localhost}\n" + + "GENERIC(not well-formed arg list)\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro) {\n" + " RULE(0, NONE)\n" + " RULE(1, READ) {\n" + " HAG(foo)\n" + " }\n" + "}\n"; + +/** + * Test data with unsupported elements. + * The unsupported element should cause an error as the format is invalid. + * + * top-unknown-keyword(WELL,FORMED,LIST) { a b } + * - invalid string list + */ +static const char unsupported_config_2[] = "" + "HAG(foo) {localhost}\n" + + "GENERIC(WELL, FORMED, ARG, LIST) {\n" + " NOT WELL-FORMED BODY\n" + "}\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro) {\n" + " RULE(0, NONE)\n" + " RULE(1, READ) {\n" + " HAG(foo)\n" + " }\n" + "}\n"; + +/** + * Test data with unsupported elements. + * The unsupported element should cause an error as the format is invalid. + * + * top-unknown-keyword { a, b } + * - missing parameters (must have at least an empty arg list) + */ +static const char unsupported_config_3[] = "" + "HAG(foo) {localhost}\n" + + "GENERIC {\n" + " WELL, FORMED, LIST, BODY\n" + "}\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro) {\n" + " RULE(0, NONE)\n" + " RULE(1, READ) {\n" + " HAG(foo)\n" + " }\n" + "}\n"; + +/** + * Test data with unsupported elements. + * The unsupported element should cause an error as the format is invalid. + * + * top-unknown-keyword(WELL,FORMED,LIST) { X, Y(a b c) } + * - bad arg list for recursive body + */ +static const char unsupported_config_4[] = "" + "HAG(foo) {localhost}\n" + + "GENERIC(WELL, FORMED, ARG, LIST) {\n" + " BODY(BAD ARG LIST)\n" + "}\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro) {\n" + " RULE(0, NONE)\n" + " RULE(1, READ) {\n" + " HAG(foo)\n" + " }\n" + "}\n"; + +/** + * Test data with unsupported elements. + * The unsupported element should cause an error as the format is invalid. + * + * top-unknown-keyword(WELL,FORMED,LIST) { X, Y(a b c) } + * - mix of list and recursive type bodies + */ +static const char unsupported_config_5[] = "" + "HAG(foo) {localhost}\n" + + "GENERIC(WELL, FORMED, ARG, LIST) {\n" + " LIST, BODY, MIXED, WITH,\n" + " RECURSIVE_BODY(ARG, LIST)\n" + "}\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro) {\n" + " RULE(0, NONE)\n" + " RULE(1, READ) {\n" + " HAG(foo)\n" + " }\n" + "}\n"; + +/** + * The modification to a well known element should cause an error. + * + * - bad arg list for ASG element + */ +static const char unsupported_mod_1[] = "" + "HAG(foo) {localhost}\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro BAD ARG LIST) {\n" + " RULE(0, NONE)\n" + " RULE(1, READ) {\n" + " HAG(foo)\n" + " }\n" + "}\n"; + +/** + * The modification to a well known element should cause an error. + * + * - bad arg list for HAG element + */ +static const char unsupported_mod_2[] = "" + "HAG(BAD ARG LIST) {localhost}\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro) {\n" + " RULE(0, NONE)\n" + " RULE(1, READ) {\n" + " HAG(foo)\n" + " }\n" + "}\n"; + +/** + * The modification to a well known element should cause an error. + * + * - bad arg list for RULE element + */ +static const char unsupported_mod_3[] = "" + "HAG(foo) {localhost}\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro) {\n" + " RULE(0 BAD ARG LIST)\n" + " RULE(1, READ) {\n" + " HAG(foo)\n" + " }\n" + "}\n"; + +/** + * The modification to a well known element should cause an error. + * + * - bad arg count for ASG element + */ +static const char unsupported_mod_4[] = "" + "HAG(foo) {localhost}\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro, UNKNOWN_PERMISSION) {\n" + " RULE(0, NONE)\n" + " RULE(1, READ) {\n" + " HAG(foo)\n" + " }\n" + "}\n"; + +/** + * The modification to a well known element should cause an error. + * + * - unexpected name in arg list for RULE element + */ +static const char unsupported_mod_5[] = "" + "HAG(foo) {localhost}\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro) {\n" + " RULE(0, NONE, UNKNOWN_FLAG)\n" + " RULE(1, READ) {\n" + " HAG(foo)\n" + " }\n" + "}\n"; + +/** + * The modification to a well known element should cause an error. + * + * - unexpected recursive body mixed in with HAG string list body + */ +static const char unsupported_mod_6[] = "" + "HAG(foo) {\n" + " localhost,\n" + " NETWORK(\"127.0.0.1\")\n" + "}\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro) {\n" + " RULE(0, NONE)\n" + " RULE(1, READ) {\n" + " HAG(foo)\n" + " }\n" + "}\n"; + +/** + * The modification to a well known element should cause an error. + * + * - unexpected recursive body mixed in with UAG string list body + */ +static const char unsupported_mod_7[] = "" + "UAG(foo) {\n" + " alice,\n" + " GROUP(admin)\n" + "}\n" + + "ASG(DEFAULT) {\n" + " RULE(0, NONE)\n" + "}\n" + + "ASG(ro) {\n" + " RULE(0, NONE)\n" + " RULE(1, READ) {\n" + " HAG(foo)\n" + " }\n" + "}\n"; + +/** + * Set the username for the authorization tests + */ static void setUser(const char *name) { free(asUser); asUser = epicsStrDup(name); } +/** + * Set the hostname for the authorization tests + */ static void setHost(const char *name) { free(asHost); asHost = epicsStrDup(name); } +/** + * Test the access control system with the given ASG, user, and hostname + * This will test that the expected access given by mask is granted + * when the Access Security Group (ASG) is interpreted in the + * context of the configured user, host, and Level (asl). + */ static void testAccess(const char *asg, unsigned mask) { ASMEMBERPVT asp = 0; /* aka dbCommon::asp */ @@ -67,18 +587,13 @@ static void testSyntaxErrors(void) long ret; testDiag("testSyntaxErrors()"); + asCheckClientIP = 0; eltc(0); ret = asInitMem(empty, NULL); testOk(ret==S_asLib_badConfig, "load \"empty\" config -> %s", errSymMsg(ret)); eltc(1); } -static const char hostname_config[] = "" - "HAG(foo) {localhost}\n" - "ASG(DEFAULT) {RULE(0, NONE)}\n" - "ASG(ro) {RULE(0, NONE)RULE(1, READ) {HAG(foo)}}\n" - "ASG(rw) {RULE(1, WRITE) {HAG(foo)}}\n" - ; static void testHostNames(void) { @@ -144,10 +659,132 @@ static void testUseIP(void) testAccess("rw", 0); } +static void testFutureProofParser(void) +{ + long ret; + + testDiag("testFutureProofParser()"); + asCheckClientIP = 0; + + eltc(0); /* Suppress error messages during test */ + + /* Test parsing should reject unsupported elements badly placed or formed */ + ret = asInitMem(unsupported_config_1, NULL); + testOk(ret==S_asLib_badConfig, "parsing rejects invalid arg list missing commas -> %s", errSymMsg(ret)); + + ret = asInitMem(unsupported_config_2, NULL); + testOk(ret==S_asLib_badConfig, "parsing rejects invalid string list -> %s", errSymMsg(ret)); + + ret = asInitMem(unsupported_config_3, NULL); + testOk(ret==S_asLib_badConfig, "parsing rejects missing parameters (must have at least an empty arg list) -> %s", errSymMsg(ret)); + + ret = asInitMem(unsupported_config_4, NULL); + testOk(ret==S_asLib_badConfig, "parsing rejects bad arg list for recursive body -> %s", errSymMsg(ret)); + + ret = asInitMem(unsupported_config_5, NULL); + testOk(ret==S_asLib_badConfig, "parsing rejects mix of list and recursive type bodies -> %s", errSymMsg(ret)); + + + /* Test supported elements badly modified should be rejected */ + ret = asInitMem(unsupported_mod_1, NULL); + testOk(ret==S_asLib_badConfig, "parsing rejects bad arg list for ASG element -> %s", errSymMsg(ret)); + + ret = asInitMem(unsupported_mod_2, NULL); + testOk(ret==S_asLib_badConfig, "parsing rejects bad arg list for HAG element-> %s", errSymMsg(ret)); + + ret = asInitMem(unsupported_mod_3, NULL); + testOk(ret==S_asLib_badConfig, "parsing rejects bad arg list for RULE element -> %s", errSymMsg(ret)); + + ret = asInitMem(unsupported_mod_4, NULL); + testOk(ret==S_asLib_badConfig, "parsing rejects bad arg count for ASG element -> %s", errSymMsg(ret)); + + ret = asInitMem(unsupported_mod_5, NULL); + testOk(ret==S_asLib_badConfig, "parsing rejects unexpected name in arg list for RULE element -> %s", errSymMsg(ret)); + + ret = asInitMem(unsupported_mod_6, NULL); + testOk(ret==S_asLib_badConfig, "parsing rejects unexpected recursive body in HAG element body -> %s", errSymMsg(ret)); + + ret = asInitMem(unsupported_mod_7, NULL); + testOk(ret==S_asLib_badConfig, "parsing rejects unexpected recursive body in UAG element body -> %s", errSymMsg(ret)); + + + eltc(1); + + /* Test supported for known elements containing unsupported elements, well-formed and ignored */ + setUser("testing"); + setHost("localhost"); + + ret = asInitMem(supported_config_1, NULL); + testOk(ret==0, "unknown elements ignored -> %s", errSymMsg(ret)); + if (!ret) { + asAsl = 0; + testAccess("DEFAULT", 0); + testAccess("ro", 1); + } + + ret = asInitMem(supported_config_2, NULL); + testOk(ret==0, "unknown elements with body ignored -> %s", errSymMsg(ret)); + if (!ret) { + asAsl = 0; + testAccess("DEFAULT", 0); + testAccess("ro", 1); + } + + ret = asInitMem(supported_config_3, NULL); + testOk(ret==0, "unknown elements with string and double args and a body, ignored -> %s", errSymMsg(ret)); + if (!ret) { + asAsl = 0; + testAccess("DEFAULT", 0); + testAccess("ro", 1); + } + + ret = asInitMem(supported_config_4, NULL); + testOk(ret==0, "unknown elements with recursive body ignored -> %s", errSymMsg(ret)); + if (!ret) { + asAsl = 0; + testAccess("DEFAULT", 0); + testAccess("ro", 1); + } + + ret = asInitMem(supported_config_5, NULL); + testOk(ret==0, "unknown elements with recursive body with recursion ignored -> %s", errSymMsg(ret)); + if (!ret) { + asAsl = 0; + testAccess("DEFAULT", 0); + testAccess("ro", 1); + } + + ret = asInitMem(supported_config_6, NULL); + testOk(ret==0, "unknown elements with keywords arguments and body names ignored -> %s", errSymMsg(ret)); + if (!ret) { + asAsl = 0; + testAccess("DEFAULT", 0); + testAccess("ignored", 0); + testAccess("ro", 1); + } + + ret = asInitMem(supported_config_7, NULL); + testOk(ret==0, "rules with unknown elements ignored -> %s", errSymMsg(ret)); + if (!ret) { + asAsl = 0; + testAccess("DEFAULT", 0); + testAccess("ro", 0); + } + + ret = asInitMem(supported_config_8, NULL); + testOk(ret==0, "rules with unknown permission names ignored -> %s", errSymMsg(ret)); + if (!ret) { + asAsl = 0; + testAccess("DEFAULT", 0); + testAccess("ro", 0); + } +} + MAIN(aslibtest) { - testPlan(27); + testPlan(64); testSyntaxErrors(); + testFutureProofParser(); testHostNames(); testUseIP(); errlogFlush(); From 1f4b39cd08020726ac73bf8388ede0b496595af0 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 10 Nov 2025 17:30:13 -0800 Subject: [PATCH 63/99] ascheck cleanup --- modules/database/src/ioc/as/ascheck.c | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/database/src/ioc/as/ascheck.c b/modules/database/src/ioc/as/ascheck.c index cbbeac48d..5b344efc8 100644 --- a/modules/database/src/ioc/as/ascheck.c +++ b/modules/database/src/ioc/as/ascheck.c @@ -53,5 +53,6 @@ int main(int argc,char **argv) status = -1; } errlogFlush(); + asFreeAll((ASBASE*)pasbase); return status; } From 45e3e58a1f242e88f455739ae2f04c9c20c92cbc Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 10 Nov 2025 18:23:53 -0800 Subject: [PATCH 64/99] asLib: fix generic lexer/parser --- modules/libcom/src/as/asLib.y | 46 +++++++++++++++------------ modules/libcom/src/as/asLibRoutines.c | 3 ++ modules/libcom/src/as/asLib_lex.l | 2 +- modules/libcom/test/aslibtest.c | 3 +- 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/modules/libcom/src/as/asLib.y b/modules/libcom/src/as/asLib.y index b947223b4..30e1db901 100644 --- a/modules/libcom/src/as/asLib.y +++ b/modules/libcom/src/as/asLib.y @@ -18,12 +18,22 @@ static UAG *yyUag=NULL; static HAG *yyHag=NULL; static ASG *yyAsg=NULL; static ASGRULE *yyAsgRule=NULL; + +static +char* yystrdup(const char *inp) { + char* ret = strdup(inp); + if(!ret) + yyerror("MALLOC"); + return ret; +} + %} %start asconfig -%token tokenUAG tokenHAG tokenASG tokenRULE tokenCALC tokenINP tokenSTRING -%token tokenINT64 +%token tokenUAG tokenHAG tokenASG tokenRULE tokenCALC +%token tokenSTRING +%token tokenINT64 tokenINP %token tokenFLOAT64 %union @@ -54,30 +64,40 @@ asconfig_item: tokenUAG uag_head uag_body | generic_item ; +/* uniformally yield a string for use in warning messages */ keyword: tokenUAG + { $$ = yystrdup("UAG"); } | tokenHAG + { $$ = yystrdup("HAG"); } | tokenCALC + { $$ = yystrdup("CALC"); } | non_rule_keyword ; non_rule_keyword: tokenASG + { $$ = yystrdup("ASG"); } | tokenRULE + { $$ = yystrdup("RULE"); } | tokenINP + { + if(!!($$ = yystrdup("INPA"))) + $$[3] += $1; /* 'A' + input number */ + } ; generic_item: tokenSTRING generic_head generic_list_block { - yywarn("Ignoring unsupported TOP LEVEL Access Control Definition", $1); + yywarn("Ignoring unsupported TOP LEVEL nested block", $1); free($1); } | tokenSTRING generic_head generic_block { - yywarn("Ignoring unsupported TOP LEVEL Access Control Definition", $1); + yywarn("Ignoring unsupported TOP LEVEL block", $1); free($1); } | tokenSTRING generic_head { - yywarn("Ignoring unsupported TOP LEVEL Access Control Definition", $1); + yywarn("Ignoring unsupported TOP LEVEL bare block", $1); free($1); } ; @@ -130,14 +150,7 @@ generic_block_elem: generic_block_elem_name generic_head generic_block ; generic_block_elem_name: keyword - { - $$ = strdup($1); - if (!$$) yyerror("Out of memory"); - } | tokenSTRING - { - $$ = $1; - } ; rule_generic_block_elem: rule_generic_block_elem_name generic_head generic_block @@ -151,14 +164,7 @@ rule_generic_block_elem: rule_generic_block_elem_name generic_head generic_block ; rule_generic_block_elem_name: non_rule_keyword - { - $$ = strdup($1); - if (!$$) yyerror("Out of memory"); - } | tokenSTRING - { - $$ = $1; - } ; uag_head: '(' tokenSTRING ')' @@ -247,7 +253,7 @@ rule_head: '(' rule_head_mandatory ',' rule_log_option ')' rule_head_mandatory: tokenINT64 ',' tokenSTRING { if ($1 < 0) { - char message[40]; + char message[60]; sprintf(message, "RULE: LEVEL must be positive: %lld", $1); yyerror(message); } else if((strcmp($3,"NONE")==0)) { diff --git a/modules/libcom/src/as/asLibRoutines.c b/modules/libcom/src/as/asLibRoutines.c index f86f985e5..07487af13 100644 --- a/modules/libcom/src/as/asLibRoutines.c +++ b/modules/libcom/src/as/asLibRoutines.c @@ -1065,6 +1065,9 @@ void asFreeAll(ASBASE *pasbase) ASGUAG *pasguag; void *pnext; + if(!pasbase) + return; + puag = (UAG *)ellFirst(&pasbase->uagList); while(puag) { puagname = (UAGNAME *)ellFirst(&puag->list); diff --git a/modules/libcom/src/as/asLib_lex.l b/modules/libcom/src/as/asLib_lex.l index 7ba1c8da7..69a88eb8b 100644 --- a/modules/libcom/src/as/asLib_lex.l +++ b/modules/libcom/src/as/asLib_lex.l @@ -79,7 +79,7 @@ INP{link} { {doublequote}({stringchar}|{escape})*{doublequote} { /* quoted string */ yylval.Str=asStrdup(yytext+1); - yylval.Str[strlen(yylval.Str)-1] = '\0'; + yylval.Str[strlen(yylval.Str)-1] = '\0'; /* overwrite trailing '"' */ return(tokenSTRING); } diff --git a/modules/libcom/test/aslibtest.c b/modules/libcom/test/aslibtest.c index 705823de5..d1ca03b99 100644 --- a/modules/libcom/test/aslibtest.c +++ b/modules/libcom/test/aslibtest.c @@ -109,6 +109,7 @@ static const char supported_config_3[] = "" "COMPLEX_ARGUMENTS(1, WELL, \"FORMED\", ARG, LIST) {\n" " ALSO_GENERIC(WELL, FORMED, ARG, LIST, 2.0) \n" + " RULE(WELL, FORMED, ARG, LIST, 2.0) \n" "}\n" "ASG(DEFAULT) {\n" @@ -137,7 +138,7 @@ static const char supported_config_4[] = "" " ALSO_GENERIC() {\n" " AND_LIST_BODY\n" " }\n" -" ANOTHER_GENERIC() {\n" +" RULE() {\n" " BIGGER, LIST, BODY\n" " }\n" "}\n" From e5618326c9c1d8de2461104f6f478757ed3eb1c0 Mon Sep 17 00:00:00 2001 From: george-mcintyre Date: Tue, 18 Nov 2025 18:11:55 +0100 Subject: [PATCH 65/99] Add detailed ACF forward compatibility documentation. --- documentation/new-notes/PR-624.md | 303 ++++++++++++++++++++++++++++++ 1 file changed, 303 insertions(+) create mode 100644 documentation/new-notes/PR-624.md diff --git a/documentation/new-notes/PR-624.md b/documentation/new-notes/PR-624.md new file mode 100644 index 000000000..c6ff6f0a0 --- /dev/null +++ b/documentation/new-notes/PR-624.md @@ -0,0 +1,303 @@ +# ACF Syntax Forward Compatibility + +This update modifies the Access Security Configuration File (ACF) parser to **standardize the ACF grammar +for forward compatibility**. All ACF definitions now adhere to a consistent syntax +format, which will allow future additions to the access security language without breaking +existing configurations. In practice, this means the structure of ACF files is now formally +defined and will remain stable going forward, so any new grammar features will fit +into the same pattern. (Existing ACF files continue to work as-is under the new parser, +so no changes are required for legacy configurations or tools.). + +**Generic ACF Syntax:** The ACF file consists of definitions for User Access Groups (UAG), +Host Access Groups (HAG), and Access Security Groups (ASG), +using the following general format (angle brackets denote placeholders): + +```text + +UAG() [{ [, ...] }] +... + +HAG() [{ [, ...] }] +... + +ASG() [{ + [INP() + ...] + + RULE(, NONE | READ | WRITE [, NOTRAPWRITE | TRAPWRITE]) { + [UAG( [, ...])] + [HAG( [, ...])] + [CALC()] + } + ... +}] +... + +``` + +Under this schema, each definition uses a keyword, a name in parentheses, +and (optionally) a braced block of contents. This uniform structure ensures that **future keywords or sections** can be introduced +in the same form, maintaining compatibility with the parser. For example, if a new type of condition +or group is added in a later release, it would follow the KEYWORD(name) { ... } pattern, +so 7.0.9-era parsers can handle or ignore it gracefully (instead of failing on unknown syntax). + +**Supported Syntax in EPICS 7.0.9:** The current release defines the following specific elements within the above generic format: + +- **UAG** -- *User Access Group*. Defines a group of user names. +- **HAG** -- *Host Access Group*. Defines a group of host names (or IP addresses) that clients can connect from. +- **ASG** -- *Access Security Group*. Defines a security group which records can be assigned to. An ASG entry may contain a block with input definitions and access rules. For example: + +```text +ASG(MyGroup) { + INPA(myPV1) + INPB(myPV2) + RULE(1, WRITE) { ... } + RULE(1, READ) { ... } +} +``` + +If no rules are defined for an ASG, access defaults to always allowed. + +- **INP()** -- *Input link*. Declares an input process variable whose value can be used in a CALC condition. +- **RULE(, [, ]) { ... }** -- Defines an access rule for the ASG. + +Inside the curly braces of a RULE, **optional conditions** can restrict when that rule applies. All conditions that are present must be satisfied (they function as a logical AND): + +- **UAG(, ...)** -- User-group condition. The rule only applies if the Channel Access client's user is a member of one of the listed UAGs. +- **HAG(, ...)** -- Host-group condition. The rule only applies if the client's host (as determined by its IP or hostname) is in one of the listed HAGs +- **CALC("")** -- Calculation condition. The rule only applies if the given expression evaluates to true (non-zero). + +**Special Semantics for RULEs:** Rules will continue to allow the prescribed access if and only if all the predicates the rule contains are satisfied. +- If the rule contains predicates that are unknown to the parser (future functionality), then the rule will NOT not match - but no syntax error will be reported as long as the syntax is correct. +- If the rule contains predicates that the parser does not recognise which are malformed (e.g. missing parentheses), then the rule will not match and the parser will report a syntax error. +- In this way rules can be extended with new predicates without breaking older clients or giving those older clients elevated privileges. + +**Special Semantics unrecognised ACF file elements:** Any new elements that are added to the ACF file will be ignored silently by a parser that does not understand them. +- If a new element is added to the ACF file that is not understood by the parser, the parser will simply ignore it silently, without reporting an error, as long as the syntax is correct. +- If new elements are added to the ACF file that are malformed (e.g. missing parentheses), then the parser will report a syntax error. +- In this way new elements can be added to ACF files without breaking older clients. + +In summary, **ACF forward compatibility** means that from EPICS 7.0.9 onward, +any new access security features will use this established syntax. The parser +will recognize new group types or rule options by the same (...) { ... } +convention, ensuring they can be added without breaking older files or requiring a new parser. +This change **does not require any modifications to existing ACF files or downstream tools** -- all +legacy syntax remains valid, and the new standardized grammar simply provides a robust foundation +for future extensions. + +--- + +# Full Language Specification: + +## Lexical tokens + +**Ignored stuff** + +- *Whitespace*: space, tab, `\r` (carriage return) -- may appear between tokens. + +- *Newlines*: `\n` -- same as whitespace for syntax, but tracked for error messages. + +- *Comments*: `#` ... end of line -- ignored. + +**Terminals** + +- `UAG` → literal string `"UAG"` +- `HAG` → `"HAG"` +- `ASG` → `"ASG"` +- `RULE` → `"RULE"` +- `CALC` → `"CALC"` +- `INP(link)` → literal `"INP"` followed immediately by one uppercase letter `A`-`U` +```text + link-letter ::= "A" | "B" | ... | "U" + INP(link) ::= "INP" link-letter +``` +- `INT` → integer literal + +```text + INT ::= [ "+" | "-" ]? DIGIT+ + DIGIT ::= "0" | "1" | ... | "9" +``` +- `FLOAT` → floating-point literal with decimal point +```text + FLOAT ::= [ "+" | "-" ]? DIGIT* "." DIGIT+ [ ( "e" | "E" ) [ "+" | "-" ] DIGIT+ ]? +``` +- `STRING` → either + - **unquoted**: one or more of +```text + NAMECHAR ::= letter | digit | "_" | "-" | "+" | ":" | "." | "[" | "]" | "<" | ">" | ";" + STRING(unquoted) ::= NAMECHAR+ +``` +- - **quoted**: +```text + STRING(quoted) ::= '"' { STRINGCHAR | ESCAPE } '"' + STRINGCHAR ::= any char except '"' "\" "\n" + ESCAPE ::= "\" any-char +``` + The surrounding quotes are stripped; escapes are kept literal at parse level. +- Punctuation tokens (single-character terminals): +```text + "(" ")" "{" "}" "," +``` +--- + +# Grammar + +## Top level + +```ebnf +acf-file ::= asconfig ; + +asconfig ::= asconfig-item { asconfig-item } ; + +asconfig-item ::= + uag-def + | hag-def + | asg-def + | generic-top-level-item ; +``` +### UAG / HAG groups + +```ebnf +uag-def ::= "UAG" uag-head [ uag-body ] ; + +uag-head ::= "(" STRING ")" ; + +uag-body ::= "{" uag-user-list "}" ; + +uag-user-list ::= STRING { "," STRING } ; +``` + +```ebnf +hag-def ::= "HAG" hag-head [ hag-body ] ; + +hag-head ::= "(" STRING ")" ; + +hag-body ::= "{" hag-host-list "}" ; + +hag-host-list ::= STRING { "," STRING } ; +``` + +### ASG (access security group) + +```ebnf +asg-def ::= "ASG" asg-head [ asg-body ] ; + +asg-head ::= "(" STRING ")" ; + +asg-body ::= "{" asg-body-item { asg-body-item } "}" ; + +asg-body-item ::= + inp-config + | rule-config ; +``` + +#### INP config + +```ebnf +inp-config ::= INP(link) "(" STRING ")" ;` +``` + +#### RULE config +```ebnf +rule-config ::= "RULE" rule-head [ rule-body ] ; + +rule-head ::= + "(" rule-head-mandatory "," rule-log-option ")" + | "(" rule-head-mandatory ")" ; + +rule-head-mandatory ::= INT "," STRING ; + +rule-log-option ::= STRING ; +``` +```ebnf +rule-body ::= "{" rule-list "}" ; + +rule-list ::= rule-list-item { rule-list-item } ; + +rule-list-item ::= + "UAG" "(" rule-uag-list ")" + | "HAG" "(" rule-hag-list ")" + | "CALC" "(" STRING ")" + | rule-generic-block-elem ; +``` + +```ebnf +rule-uag-list ::= STRING { "," STRING } ; + +rule-hag-list ::= STRING { "," STRING } ;` +``` + +### Generic / future-proof syntax + +These are the "catch-all" constructs that are **parsed** but currently **ignored** semantically. + +#### Keyword classes + +These are parser-level categories used inside generic constructs: +```ebnf +keyword ::= + "UAG" + | "HAG" + | "CALC" + | non-rule-keyword ; + +non-rule-keyword ::= + "ASG" + | "RULE" + | INP(link) ; (* INPA..INPU *) +``` +#### Generic head (argument list) + +```ebnf +generic-head ::= + "(" ")" + | "(" generic-element ")" + | "(" generic-list ")" ; + +generic-list ::= generic-element "," generic-element { "," generic-element } ; + +generic-element ::= + keyword + | STRING + | INT + | FLOAT ; +``` +#### Generic blocks + +```ebnf +generic-block ::= + "{" generic-element "}" + | "{" generic-list "}" + | "{" generic-block-list "}" ; + +generic-block-list ::= generic-block-elem { generic-block-elem } ; + +generic-block-elem ::= + generic-block-elem-name generic-head [ generic-block ] ; + +generic-block-elem-name ::= keyword | STRING ;` +``` +#### Generic top-level items + +These are "unknown" top-level constructs, all of which are parsed and then ignored with a warning. +```ebnf +generic-top-level-item ::= + STRING generic-head generic-list-block + | STRING generic-head generic-block + | STRING generic-head ; +``` +where +```ebnf +generic-list-block ::= + "{" generic-element "}" "{" generic-list "}" ; +``` +#### Generic blocks inside RULE bodies + +These are the "future predicates" in a RULE's body; they cause the RULE to be disabled with a warning, but they **must** still parse. + +```ebnf +rule-generic-block-elem ::= + rule-generic-block-elem-name generic-head [ generic-block ] ; + +rule-generic-block-elem-name ::= non-rule-keyword | STRING ; +``` From 4f547230c4d9e1376a1af721ca7b175a769a09f0 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 1 Dec 2025 12:30:01 -0600 Subject: [PATCH 66/99] CI - GHA Job fixes, standardize config - Fix MSC-22 c++14 job to actually set -std:c++14 - Move job names to the first line of the job settings - Group & reorder jobs --- .github/workflows/ci-scripts-build.yml | 176 +++++++++++++------------ 1 file changed, 89 insertions(+), 87 deletions(-) diff --git a/.github/workflows/ci-scripts-build.yml b/.github/workflows/ci-scripts-build.yml index 1639653b1..c1e2b90d3 100644 --- a/.github/workflows/ci-scripts-build.yml +++ b/.github/workflows/ci-scripts-build.yml @@ -50,9 +50,9 @@ jobs: matrix: # Job names also name artifacts, character limitations apply include: - - os: ubuntu-24.04 + - name: "Ub-24 gcc c++20 Werror" + os: ubuntu-24.04 cmp: gcc - name: "Ub-24 gcc-13 c++20 Werror" # Turn all warnings into errors, # except for those we could not fix (yet). # Remove respective -Wno-error=... flag once it is fixed. @@ -73,139 +73,141 @@ jobs: -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3' CMD_LDFLAGS=-Wl,-z,relro" - - os: ubuntu-22.04 - cmp: gcc - configuration: default - cross: "windows-x64-mingw" - name: "Ub-22 gcc + MinGW" - - - os: ubuntu-22.04 - cmp: gcc - configuration: static - cross: "windows-x64-mingw" - name: "Ub-22 gcc + MinGW, static" - - - os: ubuntu-22.04 + - name: "Ub-22 gcc C++11, static" + os: ubuntu-22.04 cmp: gcc configuration: static extra: "CMD_CXXFLAGS=-std=c++11" - name: "Ub-22 gcc C++11, static" - - os: ubuntu-22.04 + - name: "Ub-22 gcc u-char" + os: ubuntu-22.04 cmp: gcc configuration: static extra: "CMD_CFLAGS=-funsigned-char CMD_CXXFLAGS=-funsigned-char" - name: "Ub-22 gcc unsigned char" - - os: ubuntu-22.04 + - name: "Ub-22 clang" + os: ubuntu-22.04 cmp: clang configuration: default - name: "Ub-22 clang" - - os: ubuntu-22.04 + - name: "Ub-22 clang C++11" + os: ubuntu-22.04 cmp: clang configuration: default extra: "CMD_CXXFLAGS=-std=c++11" - name: "Ub-22 clang C++11" - - os: ubuntu-22.04 + - name: "MacOS clang" + os: macos-latest + cmp: clang + configuration: default + + # Cross builds + + - name: "Ub gcc + linux-aarch64" + os: ubuntu-latest + cmp: gcc + configuration: default + cross: "linux-aarch64" + + - name: "Ub gcc + linux-arm gnueabi" + os: ubuntu-latest + cmp: gcc + configuration: default + cross: "linux-arm@arm-linux-gnueabi" + + - name: "Ub gcc + linux-arm gnueabihf" + os: ubuntu-latest + cmp: gcc + configuration: default + cross: "linux-arm@arm-linux-gnueabihf" + + - name: "Ub-22 gcc + MinGW" + os: ubuntu-22.04 + cmp: gcc + configuration: default + cross: "windows-x64-mingw" + + - name: "Ub-22 gcc + MinGW, static" + os: ubuntu-22.04 + cmp: gcc + configuration: static + cross: "windows-x64-mingw" + + - name: "Ub-22 gcc + RT-4.9 pc386" + os: ubuntu-22.04 + cmp: gcc + configuration: default + cross: "RTEMS-pc386-qemu@4.9" + + - name: "Ub-22 gcc + RT-4.10 pc386" + os: ubuntu-22.04 + cmp: gcc + configuration: default + cross: "RTEMS-pc386-qemu@4.10" + test: NO + + - name: "Ub-22 gcc + RT-5.1 pc686" + os: ubuntu-22.04 cmp: gcc configuration: default cross: "RTEMS-pc686-qemu@5" - name: "Ub-22 gcc + RT-5.1 pc686" - - os: ubuntu-22.04 + - name: "Ub-22 gcc + RT-5.1 beatnik" + os: ubuntu-22.04 cmp: gcc configuration: default cross: "RTEMS-beatnik@5" test: NO - name: "Ub-22 gcc + RT-5.1 beatnik" - - os: ubuntu-22.04 + - name: "Ub-22 gcc + RT-5.1 xilinx_zynq_a9_qemu" + os: ubuntu-22.04 cmp: gcc configuration: default cross: "RTEMS-xilinx_zynq_a9_qemu@5" test: NO - name: "Ub-22 gcc + RT-5.1 xilinx_zynq_a9_qemu" - - os: ubuntu-22.04 + - name: "Ub-22 gcc + RT-5.1 uC5282" + os: ubuntu-22.04 cmp: gcc configuration: default cross: "RTEMS-uC5282@5" test: NO - name: "Ub-22 gcc + RT-5.1 uC5282" - - os: ubuntu-22.04 - cmp: gcc - configuration: default - name: "Ub-22 gcc + RT-4.10" - cross: "RTEMS-pc386-qemu@4.10" - test: NO + # Windows builds - - os: ubuntu-22.04 - cmp: gcc - configuration: default - name: "Ub-22 gcc + RT-4.9" - cross: "RTEMS-pc386-qemu@4.9" - - - os: macos-latest - cmp: clang - configuration: default - name: "MacOS clang" - - - os: windows-2022 + - name: "Win-22 MSC-22" + os: windows-2022 cmp: vs2022 configuration: debug - name: "Win-22 MSC-22" extra: "CMD_CXXFLAGS=-analyze" - - os: windows-2022 - cmp: vs2022 - configuration: debug - name: "Win-22 MSC-22 c++14" - extra: "CMD_CXXFLAGS='-analyze -Zc:__cplusplus -std:c++17'" - - - os: windows-2022 - cmp: vs2022 - configuration: debug - name: "Win-22 MSC-22 c++17" - extra: "CMD_CXXFLAGS='-analyze -Zc:__cplusplus -std:c++17'" - - - os: windows-2022 + - name: "Win-22 MSC-22, static" + os: windows-2022 cmp: vs2022 configuration: static-debug - name: "Win-22 MSC-22, static" extra: "CMD_CXXFLAGS=-analyze" - - os: windows-2022 + - name: "Win-22 MSC-22, debug" + os: windows-2022 cmp: vs2022 configuration: debug - name: "Win-22 MSC-22, debug" - - os: windows-2022 + - name: "Win-22 MSC-22 c++14, debug" + os: windows-2022 + cmp: vs2022 + configuration: debug + extra: "CMD_CXXFLAGS='-analyze -Zc:__cplusplus -std:c++14'" + + - name: "Win-22 MSC-22 c++17, debug" + os: windows-2022 + cmp: vs2022 + configuration: debug + extra: "CMD_CXXFLAGS='-analyze -Zc:__cplusplus -std:c++17'" + + - name: "Win-22 mingw" + os: windows-2022 cmp: gcc configuration: default - name: "Win-22 mingw" - - # Cross builds - - - os: ubuntu-latest - cmp: gcc - configuration: default - name: "Cross linux-aarch64" - cross: linux-aarch64 - - - os: ubuntu-latest - cmp: gcc - configuration: default - name: "Cross linux-arm gnueabi" - cross: linux-arm@arm-linux-gnueabi - - - os: ubuntu-latest - cmp: gcc - configuration: default - name: "Cross linux-arm gnueabihf" - cross: linux-arm@arm-linux-gnueabihf steps: - uses: actions/checkout@v4 From 90a727c901f34652fd0489db9c1b1726fb329a2b Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 25 Nov 2025 13:51:56 +0100 Subject: [PATCH 67/99] Fix spelling errors Most errors found with: codespell -L cach,thst,odly,aslo,parm,parms,inpu,ges,prset,pevent,ptd,pring,valu,noo,noe,ned,inout,ro,siz,froms,nd,fo,singl,sart,multy,tthe,allong,ment,inate,nodel,tring,alse,ture,thi,wille,numer Some more manually found (its -> it's) c++20: Do not use apostrophe (e.g. can't) in unquoted #error message --- configure/CONFIG.gnuCommon | 2 +- configure/CONFIG_ADDONS | 2 +- configure/CONFIG_COMMON | 2 +- configure/RULES.Db | 4 ++-- configure/RULES_ARCHS | 2 +- configure/RULES_DIRS | 2 +- configure/Sample.Makefile | 8 ++++---- configure/os/CONFIG.win32-x86.win32-x86 | 6 +++--- configure/os/CONFIG_SITE.linux-x86.linux-x86 | 2 +- configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 | 2 +- documentation/Doxyfile@ | 6 +++--- documentation/RELEASE-3.15.md | 8 ++++---- documentation/RELEASE-3.16.md | 2 +- documentation/RELEASE-7.0.1.1.md | 2 +- documentation/RELEASE-7.0.3.1.md | 2 +- documentation/RELEASE-7.0.5.md | 2 +- documentation/RELEASE-7.0.7.md | 2 +- documentation/RELEASE-7.0.8.md | 4 ++-- documentation/RELEASE-7.0.9.md | 6 +++--- documentation/ReleaseChecklist.html | 2 +- documentation/new-notes/PR-558.md | 2 +- documentation/new-notes/PR-644.md | 2 +- modules/ca/src/client/CAref.html | 10 +++++----- modules/ca/src/client/access.cpp | 8 ++++---- modules/ca/src/client/acctst.c | 12 ++++++------ modules/ca/src/client/bhe.cpp | 2 +- modules/ca/src/client/caProto.h | 2 +- modules/ca/src/client/ca_client_context.cpp | 4 ++-- modules/ca/src/client/ca_test.md | 2 +- modules/ca/src/client/cadef.h | 2 +- modules/ca/src/client/caerr.h | 6 +++--- modules/ca/src/client/comQueSend.cpp | 4 ++-- modules/ca/src/client/convert.cpp | 2 +- modules/ca/src/client/nciu.cpp | 4 ++-- modules/ca/src/client/netiiu.cpp | 2 +- modules/ca/src/client/oldAccess.h | 4 ++-- modules/ca/src/client/searchTimer.cpp | 2 +- modules/ca/src/client/syncgrp.cpp | 12 ++++++------ modules/ca/src/client/tcpRecvWatchdog.cpp | 6 +++--- modules/ca/src/client/tcpiiu.cpp | 4 ++-- modules/ca/src/client/udpiiu.cpp | 2 +- modules/ca/src/perl/Cap5.xs | 10 +++++----- modules/ca/src/perl/Makefile | 2 +- modules/ca/src/template/top/caPerlApp/caput.pl | 2 +- modules/ca/src/tools/cainfo.md | 2 +- modules/ca/src/tools/caput.c | 4 ++-- modules/database/src/ioc/as/asIocRegister.c | 2 +- modules/database/src/ioc/bpt/makeBpt.c | 2 +- modules/database/src/ioc/db/dbAccess.c | 4 ++-- modules/database/src/ioc/db/dbCa.c | 2 +- modules/database/src/ioc/db/dbChannel.h | 2 +- modules/database/src/ioc/db/dbCommon.dbd.pod | 2 +- modules/database/src/ioc/db/dbIocRegister.c | 4 ++-- modules/database/src/ioc/db/dbScan.h | 2 +- modules/database/src/ioc/db/dbTest.h | 2 +- modules/database/src/ioc/db/dbUnitTest.c | 2 +- modules/database/src/ioc/db/dbUnitTest.h | 2 +- modules/database/src/ioc/dbStatic/dbBase.h | 2 +- modules/database/src/ioc/dbStatic/dbLexRoutines.c | 2 +- .../database/src/ioc/dbStatic/dbStaticIocRegister.c | 2 +- .../database/src/ioc/dbtemplate/dbtoolsIocRegister.c | 2 +- modules/database/src/ioc/dbtemplate/msi.md | 4 ++-- modules/database/src/ioc/rsrv/camessage.c | 8 ++++---- modules/database/src/ioc/rsrv/caserverio.c | 8 ++++---- modules/database/src/std/link/links.dbd.pod | 2 +- modules/database/src/std/rec/aSubRecord.dbd.pod | 2 +- modules/database/src/std/rec/aiRecord.c | 2 +- modules/database/src/std/rec/aoRecord.c | 2 +- modules/database/src/std/rec/aoRecord.dbd.pod | 4 ++-- modules/database/src/std/rec/biRecord.dbd.pod | 6 +++--- modules/database/src/std/rec/boRecord.dbd.pod | 2 +- modules/database/src/std/rec/calcRecord.dbd.pod | 6 +++--- modules/database/src/std/rec/compressRecord.dbd.pod | 2 +- modules/database/src/std/rec/dfanoutRecord.dbd.pod | 8 ++++---- modules/database/src/std/rec/histogramRecord.dbd.pod | 2 +- modules/database/src/std/rec/longinRecord.dbd.pod | 4 ++-- modules/database/src/std/rec/longoutRecord.dbd.pod | 4 ++-- .../database/src/std/rec/mbboDirectRecord.dbd.pod | 4 ++-- modules/database/src/std/rec/printfRecord.dbd.pod | 2 +- modules/database/src/std/rec/selRecord.dbd.pod | 4 ++-- modules/database/src/std/rec/subRecord.dbd.pod | 6 +++--- modules/database/src/std/softIoc/base.dbd | 2 +- modules/database/src/std/softIoc/softMain.cpp | 2 +- modules/database/src/tools/dbdToHtml.pl | 2 +- modules/database/test/ioc/db/dbPutLinkTest.c | 2 +- .../test/ioc/db/dbStaticTestAliasAgainError1.db | 2 +- .../test/ioc/db/dbStaticTestAliasAgainError2.db | 2 +- modules/database/test/ioc/db/dbStaticTestRemove.db | 2 +- modules/database/test/ioc/db/devx.c | 2 +- modules/database/test/std/rec/asyncproctest.db | 2 +- modules/database/test/std/rec/boTest.c | 2 +- modules/libcom/RTEMS/posix/rtems_init.c | 2 +- modules/libcom/src/calc/calcPerform.c | 2 +- modules/libcom/src/calc/postfix.c | 6 +++--- modules/libcom/src/calc/postfix.h | 2 +- modules/libcom/src/cxxTemplates/resourceLib.h | 4 ++-- modules/libcom/src/dbmf/dbmf.c | 2 +- modules/libcom/src/env/envDefs.h | 2 +- modules/libcom/src/error/errSymTbl.h | 4 ++-- modules/libcom/src/fdmgr/fdmgr.h | 2 +- modules/libcom/src/flex/Changes | 6 +++--- modules/libcom/src/flex/flex.html | 2 +- modules/libcom/src/flex/flexdef.h | 4 ++-- modules/libcom/src/flex/tblcmp.c | 2 +- modules/libcom/src/iocsh/initHooks.h | 4 ++-- modules/libcom/src/iocsh/iocsh.cpp | 2 +- modules/libcom/src/iocsh/iocsh.h | 10 +++++----- modules/libcom/src/iocsh/libComRegister.c | 2 +- modules/libcom/src/log/iocLogServer.c | 2 +- modules/libcom/src/macLib/macLib.h | 2 +- modules/libcom/src/macLib/macLibREADME | 4 ++-- modules/libcom/src/misc/adjustment.c | 10 +++++----- modules/libcom/src/misc/epicsString.h | 2 +- .../libcom/src/misc/ipAddrToAsciiAsynchronous.cpp | 2 +- modules/libcom/src/osi/TODOfuture | 2 +- modules/libcom/src/osi/devLibVME.h | 2 +- modules/libcom/src/osi/epicsAtomic.h | 4 ++-- modules/libcom/src/osi/epicsEndian.h | 4 ++-- modules/libcom/src/osi/epicsMutex.cpp | 2 +- modules/libcom/src/osi/epicsTime.h | 2 +- modules/libcom/src/osi/os/RTEMS-score/devLibVMEOSD.c | 2 +- modules/libcom/src/osi/os/WIN32/epicsTempFile.c | 2 +- modules/libcom/src/osi/os/WIN32/osdThread.c | 6 +++--- modules/libcom/src/osi/os/WIN32/osdTime.cpp | 2 +- modules/libcom/src/osi/os/default/osdWireConfig.h | 12 ++++++------ modules/libcom/src/osi/os/default/osdWireFormat.h | 8 ++++---- modules/libcom/src/osi/os/posix/osdThread.c | 2 +- modules/libcom/src/osi/os/solaris/osdWireConfig.h | 4 ++-- modules/libcom/src/osi/os/vxWorks/devLibVMEOSD.c | 2 +- modules/libcom/src/osi/os/vxWorks/epicsAtomicOSD.h | 6 +++--- modules/libcom/src/osi/os/vxWorks/osdReadline.c | 2 +- modules/libcom/src/osi/os/vxWorks/osdWireConfig.h | 4 ++-- modules/libcom/src/osi/osdNetIfAddrs.c | 4 ++-- modules/libcom/src/osi/osdNetIfConf.c | 4 ++-- modules/libcom/src/osi/osiClockTime.c | 2 +- modules/libcom/src/osi/osiNTPTime.c | 2 +- modules/libcom/src/osi/osiSock.h | 4 ++-- modules/libcom/src/osi/osiWireFormat.h | 8 ++++---- modules/libcom/src/timer/timerQueue.cpp | 2 +- modules/libcom/src/yacc/EPICS_READ_THIS | 2 +- modules/libcom/src/yacc/NEW_FEATURES | 2 +- modules/libcom/src/yajl/yajl_lex.c | 2 +- modules/libcom/src/yajl/yajl_parse.h | 6 +++--- modules/libcom/test/epicsAtomicPerform.cpp | 2 +- modules/libcom/test/epicsAtomicTest.cpp | 2 +- modules/libcom/test/epicsStringTest.c | 2 +- modules/libcom/test/epicsThreadPerform.cpp | 2 +- modules/libcom/test/yajl_test.c | 2 +- modules/libcom/vxWorks/boost/config/auto_link.hpp | 2 +- modules/libcom/vxWorks/boost/config/suffix.hpp | 2 +- src/template/base/makeBaseApp.pl | 2 +- src/template/ext/makeBaseExt.pl | 6 +++--- src/template/ext/top/configure/RELEASE | 2 +- src/tools/EPICS/Getopts.pm | 2 +- src/tools/EPICS/Release.pm | 2 +- src/tools/Pod/Markdown.pm | 8 ++++---- src/tools/genVersionHeader.pl | 6 +++--- src/tools/makeAPIheader.pl | 2 +- src/tools/tap-to-junit-xml.pl | 2 +- 159 files changed, 275 insertions(+), 275 deletions(-) diff --git a/configure/CONFIG.gnuCommon b/configure/CONFIG.gnuCommon index a6305990b..63a32eca1 100644 --- a/configure/CONFIG.gnuCommon +++ b/configure/CONFIG.gnuCommon @@ -65,7 +65,7 @@ LOADABLE_SHRLIB_LDFLAGS = -shared -fPIC -Wl,-h$@ GNU_LDLIBS_YES = -lgcc -# Use compiler flags to generate header dependancies files +# Use compiler flags to generate header dependencies files HDEPENDS_METHOD = COMP HDEPENDS_COMPFLAGS = -MM -MF $@ diff --git a/configure/CONFIG_ADDONS b/configure/CONFIG_ADDONS index d32d91165..5b581f48c 100644 --- a/configure/CONFIG_ADDONS +++ b/configure/CONFIG_ADDONS @@ -24,7 +24,7 @@ # These rules apply to these Makefile-variables: # USR_CFLAGS C flags # USR_CXXFLAGS C++ flags -# USR_CPPFLAGS c preprocesser flags +# USR_CPPFLAGS c preprocessor flags # SRCS source files for building libraries and prods # USR_SRCS source files for building libraries and prods # PROD_SRCS source files for building prods diff --git a/configure/CONFIG_COMMON b/configure/CONFIG_COMMON index 9165ae294..c37b47238 100644 --- a/configure/CONFIG_COMMON +++ b/configure/CONFIG_COMMON @@ -147,7 +147,7 @@ CMPLR_SRC_DIRS += . $(foreach dir, .. $(SRC_DIRS), \ ALL_SRC_DIRS = $(CMPLR_SRC_DIRS) $(OS_SRC_DIRS) $(GENERIC_SRC_DIRS) #-------------------------------------------------- -# Directory for OS independant build created files +# Directory for OS independent build created files COMMON_DIR = ../O.Common # compile line include directories diff --git a/configure/RULES.Db b/configure/RULES.Db index 4a984f8c3..ae023cee1 100644 --- a/configure/RULES.Db +++ b/configure/RULES.Db @@ -55,7 +55,7 @@ DBD += $(foreach type, $(CROSS_TARGET_OS_TYPES), $(DBD_$(type))) # DBD_solaris += abcSolaris.dbd # # --------------------------------------------------- -# DBD concatination files +# DBD concatenation files COMMON_DBDCATS += $(addprefix $(COMMON_DIR)/,$(DBDCAT)) DBDCAT_SOURCES += $(foreach file, $($*_DBD), $(DBDCAT_SOURCE) ) @@ -194,7 +194,7 @@ ifneq (,$(strip $(DBDDEPENDS_FILES))) endif #--------------------------------------------------------------- -# build dependancies, clean rule +# build dependencies, clean rule inc: $(COMMON_INC) $(INSTALL_INC) $(COMMON_DBDS) $(COMMON_DBDCATS) \ $(INSTALL_DBDS) $(INSTALL_DBD_INSTALLS) $(COMMON_DOCS) diff --git a/configure/RULES_ARCHS b/configure/RULES_ARCHS index 4aaa75870..0d4d944d4 100644 --- a/configure/RULES_ARCHS +++ b/configure/RULES_ARCHS @@ -32,7 +32,7 @@ ifneq ($(RELEASE_CFG_DIR_RULES),) include $(RELEASE_CFG_DIR_RULES) endif -# Create EPICS_HOST_ARCH dependancies for GNU make -j option. +# Create EPICS_HOST_ARCH dependencies for GNU make -j option. # Needed in dirs where EPICS_HOST_ARCH build creates a tool used in # cross arch builds diff --git a/configure/RULES_DIRS b/configure/RULES_DIRS index f47df4a53..6314c530f 100644 --- a/configure/RULES_DIRS +++ b/configure/RULES_DIRS @@ -44,7 +44,7 @@ $(foreach dir, $(DIRS), $(dir)$(DIVIDER)install): \ rebuild: $(foreach dir, $(DIRS), $(dir)$(DIVIDER)install) endif -# Create directory dependancies lines for GNU make -j option +# Create directory dependencies lines for GNU make -j option # Only works with GNU make 3.81 or later (uses eval function) define DEP_template1 $(1): $$($(1)_DEPEND_DIRS) diff --git a/configure/Sample.Makefile b/configure/Sample.Makefile index e0e9675bf..0c0b313e2 100644 --- a/configure/Sample.Makefile +++ b/configure/Sample.Makefile @@ -82,7 +82,7 @@ INC = file.h # Platform specific files can also be put in # separate os/OS_CLASS directories! # -# For almost every file the seach order is: +# For almost every file the search order is: # ./os/OS_CLASS # ./os/generic # . @@ -136,11 +136,11 @@ PROD_SRCS = ppp.c qqq.c a_file_SRCS = aa.c bb.c # -# EPICS libs needed to link PROD, TESTPROD and sharable library +# EPICS libs needed to link PROD, TESTPROD and shareable library # # note that DLL_LIBS (the libraries needed to link a shareable # library) is created by default from the PROD/SYS libraries specified -# below minus the name of the sharable library (LIBRARY) +# below minus the name of the shareable library (LIBRARY) # # # ---------- libraries for a specific product pppp @@ -169,7 +169,7 @@ USR_LIBS_DEFAULT = foolib USR_LIBS_WIN32 = -nil- foolib_DIR = $(FOO_LIB) -# system libs needed to link PROD, TESTPROD and sharable library +# system libs needed to link PROD, TESTPROD and shareable library # # ---------- system libraries for all products # for all systems: diff --git a/configure/os/CONFIG.win32-x86.win32-x86 b/configure/os/CONFIG.win32-x86.win32-x86 index d45a48041..abf552ba9 100644 --- a/configure/os/CONFIG.win32-x86.win32-x86 +++ b/configure/os/CONFIG.win32-x86.win32-x86 @@ -222,7 +222,7 @@ INSTALL_SHRLIB = $(INSTALL_BIN) #-------------------------------------------------- -# Products dependancy definitions +# Products dependency definitions PROD_DEPLIBS = $(foreach lib, $(PROD_LIBS) $(USR_LIBS), \ $(firstword $(wildcard \ @@ -247,10 +247,10 @@ PROD_LDLIBS += $(STATIC_LDLIBS) \ $(LDLIBS_SHARED_$(SHARED_LIBRARIES)))) #-------------------------------------------------- -# Libraries dependancy definitions +# Libraries dependency definitions # libs that we need to link the DLL with -# (it isnt necessary to rebuild the dll if these change) +# (it isn't necessary to rebuild the dll if these change) SHRLIB_DEPLIBS = $(foreach lib, $(LIB_LIBS) $(USR_LIBS), \ $(firstword $(wildcard \ diff --git a/configure/os/CONFIG_SITE.linux-x86.linux-x86 b/configure/os/CONFIG_SITE.linux-x86.linux-x86 index 64810a835..ee7484e04 100644 --- a/configure/os/CONFIG_SITE.linux-x86.linux-x86 +++ b/configure/os/CONFIG_SITE.linux-x86.linux-x86 @@ -3,7 +3,7 @@ # Site specific definitions for linux-x86 host - linux-x86 target builds #------------------------------------------------------- -# Uncomment the followings lines to build with CLANG instead of GCC. +# Uncomment the following lines to build with CLANG instead of GCC. # #GNU = NO #CMPLR_CLASS = clang diff --git a/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 b/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 index 2f538151a..bdedf4f2c 100644 --- a/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 +++ b/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 @@ -3,7 +3,7 @@ # Site specific definitions for linux-x86_64 host - linux-x86_64 target builds #------------------------------------------------------- -# Uncomment the followings lines to build with CLANG instead of GCC. +# Uncomment the following lines to build with CLANG instead of GCC. # #GNU = NO #CMPLR_CLASS = clang diff --git a/documentation/Doxyfile@ b/documentation/Doxyfile@ index 2c0289602..ef2377119 100644 --- a/documentation/Doxyfile@ +++ b/documentation/Doxyfile@ @@ -1105,7 +1105,7 @@ HTML_STYLESHEET = # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. +# standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra stylesheet files is of importance (e.g. the last # stylesheet in the list overrules the setting of the previous ones in the @@ -1648,8 +1648,8 @@ EXTRA_PACKAGES = # Note: Only use a user-defined header if you know what you are doing! The # following commands have a special meaning inside the header: $title, # $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string, -# for the replacement values of the other commands the user is refered to +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty string, +# for the replacement values of the other commands the user is referred to # HTML_HEADER. # This tag requires that the tag GENERATE_LATEX is set to YES. diff --git a/documentation/RELEASE-3.15.md b/documentation/RELEASE-3.15.md index c304a461b..aa31805ee 100644 --- a/documentation/RELEASE-3.15.md +++ b/documentation/RELEASE-3.15.md @@ -191,7 +191,7 @@ parameter with one called `EPICS_TZ` and a routine for VxWorks that calculates the `TIMEZONE` environment variable from the current `TZ` value. This routine will be run once at start-up, when the EPICS clock has synchronized to its NTP server. The calculations it contains were worked out and donated to EPICS by -Larry Hoff in 2009; it is unforunate that it has taken 10 years for them to be +Larry Hoff in 2009; it is unfortunate that it has taken 10 years for them to be integrated into Base. The default value for the `EPICS_TZ` environment parameter is set in the Base @@ -521,7 +521,7 @@ a Windows system and an IOC or PCAS server is started, the IOC's attempt to bind a TCP socket to the CA server port number fails, but Windows returns a different error status value than the IOC is expecting in that circumstance (because the National Instruments code requests exclusive use of that port, -unlike the EPICS code) so the IOC fails to start properly. The relevent EPICS +unlike the EPICS code) so the IOC fails to start properly. The relevant EPICS bind() checks have now been updated so the IOC will request that a dynamic port number be allocated for this TCP socket instead when this happens. @@ -619,7 +619,7 @@ static versions of the compiler support libraries have now been moved into the The `iocInit` code now performs a sanity check of the current time returned by the generalTime subsystem and will print a warning if the wall-clock time returned has not been initialized yet. This is just a warning message; when -a time provider does synchonize the IOC will subsequently pick up and use the +a time provider does synchronize the IOC will subsequently pick up and use the correct time. This check code also primes the registered event system provider if there is one so the `epicsTimeGetEventInt()` routine will work on IOCs that ask for event time within an interrupt service routine. @@ -642,7 +642,7 @@ change will not cause the IOC to see time going backwards. ### Microsoft Visual Studio builds The build configuration files for builds using the Microsoft compilers have been -updated, although there should be no noticable difference at most sites. One +updated, although there should be no noticeable difference at most sites. One extra compiler warning is now being suppressed for C++ code, `C4344: behavior change: use of explicit template arguments results in ...` which is gratuitous and was appearing frequently in builds of the EPICS V4 modules. diff --git a/documentation/RELEASE-3.16.md b/documentation/RELEASE-3.16.md index 32656ba5d..5f9602ca3 100644 --- a/documentation/RELEASE-3.16.md +++ b/documentation/RELEASE-3.16.md @@ -661,7 +661,7 @@ the `DB_OPT` build configuration variable. The compressRecord has a new field `BALG` which can select between FIFO (append) and LIFO (prepend) ordering for insertion of new elements. FIFO -ordering is the default, matching the behviour of previous versions. +ordering is the default, matching the behaviour of previous versions. ### Valgrind Instrumentation diff --git a/documentation/RELEASE-7.0.1.1.md b/documentation/RELEASE-7.0.1.1.md index ee61fdcf9..3e0bbd467 100644 --- a/documentation/RELEASE-7.0.1.1.md +++ b/documentation/RELEASE-7.0.1.1.md @@ -59,7 +59,7 @@ been split into 4 separate Git repositories. External modules should build against this new structure with little or no changes needed, except that some allowance may be needed for the merging of the V4 modules. -There should be rather more description and documantation of these changes +There should be rather more description and documentation of these changes than is currently available, but as developers we generally much prefer to write code than documentation. Send questions to the tech-talk mailing list and we'll be happy to try and answer them! diff --git a/documentation/RELEASE-7.0.3.1.md b/documentation/RELEASE-7.0.3.1.md index 3f1180243..ed1998f72 100644 --- a/documentation/RELEASE-7.0.3.1.md +++ b/documentation/RELEASE-7.0.3.1.md @@ -56,7 +56,7 @@ A suggested form for IOC shell commands is: ### Relocatable Builds -Allows built trees to be copied or moved without invalidating RPATH entires. +Allows built trees to be copied or moved without invalidating RPATH entries. The `LINKER_USE_RPATH` Makefile variable (see `configure/CONFIG_SITE`) may be set to `YES`, `NO`, and a new third option `ORIGIN`. This is limited to diff --git a/documentation/RELEASE-7.0.5.md b/documentation/RELEASE-7.0.5.md index cab17a98d..afe1cab54 100644 --- a/documentation/RELEASE-7.0.5.md +++ b/documentation/RELEASE-7.0.5.md @@ -147,7 +147,7 @@ This also fixes [lauchpad bug #1714455](https://bugs.launchpad.net/bugs/1714455) declared in epicsString.h no longer accept octal escaped characters such as `\123` or `\41`. - The routine `epicsStrnEscapedFromRaw()` now generates hex - excaped characters for unprintable characters such as `\x1f`. + escaped characters for unprintable characters such as `\x1f`. - Hex escape character sequences `\xXX` must now contain exactly 2 hex digits. - An escape sequence `\0` now generates a zero byte in the raw string, but the other digits `1-9` should not appear after a back-slash. diff --git a/documentation/RELEASE-7.0.7.md b/documentation/RELEASE-7.0.7.md index 5ed0104f5..8c9043c58 100644 --- a/documentation/RELEASE-7.0.7.md +++ b/documentation/RELEASE-7.0.7.md @@ -117,7 +117,7 @@ with the TCP port number selected. DBD files generated by the `dbdExpand.pl` script are now sorted within each item type by the primary name of the item. The result should resolve any -issues with reproducable builds. No option is provided to prevent the sorting, +issues with reproducible builds. No option is provided to prevent the sorting, previously the order was essentially random and varied each time. ### `dbExpand.pl` sorts records by name diff --git a/documentation/RELEASE-7.0.8.md b/documentation/RELEASE-7.0.8.md index b9b071467..f8477a61b 100644 --- a/documentation/RELEASE-7.0.8.md +++ b/documentation/RELEASE-7.0.8.md @@ -35,7 +35,7 @@ record(bi, "bit1") { ### ANSI escapes in stderr -ANSI escape charactor sequences may now be printed to the stderr stream. +ANSI escape character sequences may now be printed to the stderr stream. These escapes will appear in logs captured from that stream. Tools which parse and/or render these logs may need to be adjusted to either strip out the escapes, or to translate them into markup. @@ -227,7 +227,7 @@ The pvDatabase module was updated to version 4.7.1: `_[distributor=group:;set:;trigger:;updates:;mode:]` - The plugin parameters are optional and are described bellow: + The plugin parameters are optional and are described below: - group: this parameter indicates a group that client application belongs to (default value: "default"); groups of clients are completely independent of each other diff --git a/documentation/RELEASE-7.0.9.md b/documentation/RELEASE-7.0.9.md index 015f50c06..71883905d 100644 --- a/documentation/RELEASE-7.0.9.md +++ b/documentation/RELEASE-7.0.9.md @@ -25,7 +25,7 @@ although it doesn't currently cover the new processes added to epics-base. Much of the documentation generated from .dbd.pod files at build time is now also being converted into MarkDown (.md) files and installed into the top-level `doc` directory. Some users might find it quicker to look up information about a -record type by opening these files in a text editor intead of opening a browser +record type by opening these files in a text editor instead of opening a browser and loading the HTML versions or finding and opening the files from the EPICS Documentation site. @@ -97,7 +97,7 @@ record("#", "unwanted") {} ### Only keep readline history for interactive sessions -Previously, all IOCsh commands were persisited in the libreadline history +Previously, all IOCsh commands were persisted in the libreadline history (when readline support is included). Going forward, only interactive commands are saved. @@ -111,7 +111,7 @@ already be conditionally casting to/from the appropriate type. ### Fix issues with `_FORTIFY_SOURCE=3` -This release fixes the false positives failures whhen building with `_FORTIFY_SOURCE` level 3. +This release fixes the false positives failures when building with `_FORTIFY_SOURCE` level 3. The override introduced in 7.0.8.1 has been removed. ### Other diff --git a/documentation/ReleaseChecklist.html b/documentation/ReleaseChecklist.html index 4ef9d98d9..19273a30f 100644 --- a/documentation/ReleaseChecklist.html +++ b/documentation/ReleaseChecklist.html @@ -419,7 +419,7 @@ everything that has to be done since it's so easy to miss steps.

Website Editor - Link to the release webpage from other relevent areas of the + Link to the release webpage from other relevant areas of the website - update front page and sidebars. diff --git a/documentation/new-notes/PR-558.md b/documentation/new-notes/PR-558.md index 17f8a90cf..d3a343bc9 100644 --- a/documentation/new-notes/PR-558.md +++ b/documentation/new-notes/PR-558.md @@ -9,7 +9,7 @@ This release incorporates [PR #558](https://github.com/epics-base/epics-base/pul - it replaces the community [`afterInit`](https://github.com/paulscherrerinstitute/iocsh_utilities/blob/master/afterInit.c) and [`doAfterIocInit`](https://github.com/epics-modules/std/blob/master/stdApp/src/delayCmd.cpp) IOC shell commands, - community usage examples: - [`pf4filters.iocsh`](https://github.com/epics-modules/optics/blob/master/opticsApp/iocsh/pf4filters.iocsh) - enable/disable with a single comment ([full description](https://github.com/epics-base/epics-base/pull/558#issuecomment-2430057167)) - - [ALS-U autosave managment](https://github.com/epics-base/epics-base/pull/558#issuecomment-2430447220) + - [ALS-U autosave management](https://github.com/epics-base/epics-base/pull/558#issuecomment-2430447220) #### Features diff --git a/documentation/new-notes/PR-644.md b/documentation/new-notes/PR-644.md index d30bb2018..a288ef1ed 100644 --- a/documentation/new-notes/PR-644.md +++ b/documentation/new-notes/PR-644.md @@ -12,7 +12,7 @@ prints. - The default IOC Shell prompt is now displayed in green; this color can be modified in the `configure/CONFIG_SITE_ENV` file for all targets, or set for a specific target by adding a `configure/os/CONFIG_SITE_ENV.` file. -The value of the `IOCSH_PS1` environment paremeter in those files can use the +The value of the `IOCSH_PS1` environment parameter in those files can use the `ANSI_ENV_*` and `ANSI_*()` color macros found in errlog.h to configure the appearance of the prompt. The C string literal concatenation syntax can be used to construct the prompt string: diff --git a/modules/ca/src/client/CAref.html b/modules/ca/src/client/CAref.html index 7b6ff143f..9b5c95bb3 100644 --- a/modules/ca/src/client/CAref.html +++ b/modules/ca/src/client/CAref.html @@ -103,7 +103,7 @@ in the file LICENSE that is included with this distribution.