From 26cb0dcbdcf7253f08aa7c6eae67a8cb552abc77 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Wed, 8 Mar 2023 15:14:13 +0000 Subject: [PATCH 01/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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/42] 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