#*************************************************************************
# Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne
#     National Laboratory.
# Copyright (c) 2002 The Regents of the University of California, as
#     Operator of Los Alamos National Laboratory.
# EPICS BASE is distributed subject to a Software License Agreement found
# in file LICENSE that is included with this distribution.
#*************************************************************************

=title Calculation Output Record (calcout)

The Calculation Output or "Calcout" record is similar to the Calc record
with the added feature of having outputs (an "output link" and an "output
event") which are conditionally executed based on the result of the
calculation. This feature allows conditional branching to be implemented
within an EPICS database (e.g. process Record_A only if Record_B has a
value of 0). The Calcout record is also similar to the Wait record (with
additional features) but uses EPICS standard INLINK and OUTLINK fields
rather than the DBF_STRING fields used in the Wait record. For new
databases, it is recommended that the Calcout record be used instead of the
Wait record.

=head2 Parameter Fields

The record-specific fields are described below, grouped by functionality.

=recordtype calcout

=cut

menu(calcoutOOPT) {
	choice(calcoutOOPT_Every_Time,"Every Time")
	choice(calcoutOOPT_On_Change,"On Change")
	choice(calcoutOOPT_When_Zero,"When Zero")
	choice(calcoutOOPT_When_Non_zero,"When Non-zero")
	choice(calcoutOOPT_Transition_To_Zero,"Transition To Zero")
	choice(calcoutOOPT_Transition_To_Non_zero,"Transition To Non-zero")
}
menu(calcoutDOPT) {
	choice(calcoutDOPT_Use_VAL,"Use CALC")
	choice(calcoutDOPT_Use_OVAL,"Use OCAL")
}
menu(calcoutINAV) {
	choice(calcoutINAV_EXT_NC,"Ext PV NC")
	choice(calcoutINAV_EXT,"Ext PV OK")
	choice(calcoutINAV_LOC,"Local PV")
	choice(calcoutINAV_CON,"Constant")
}

recordtype(calcout) {

=head3 Scan Parameters

The Calcout record has the standard fields for specifying under what
circumstances the record will be processed.
These fields are listed in L<Scan Fields|dbCommonRecord/Scan Fields>.

=head3 Read Parameters

The read parameters for the Calcout record consists of 12 input links INPA,
INPB, ... INPL. The fields can be database links, channel access links, or
constants. If they are links, they must specify another record's field. If
they are constants, they will be initialized with the value they are
configured with and can be changed via C<dbPuts>. These fields cannot be
hardware addresses. In addition, the Calcout record contains the INAV,
INBV, ... INLV fields which indicate the status of the link fields, for
example, whether or not the specified PV was found and a link to it
established. See L<Operator Display Parameters> for an explanation of these
fields.

=fields INPA, INPB, INPC, INPD, INPE, INPF, INPG, INPH, INPI, INPJ, INPK, INPL

=head3 Expression

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<Output Parameters>) 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.

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.

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.

=fields CALC, VAL, RPCL

=head3 Literals

=over 1

=item *
Standard double precision floating point numbers

=item *
Inf: Infinity

=item *
Nan: Not a Number

=back

=head3 Constants

=over

=item *
PI: returns the mathematical constant E<pi>

=item *
D2R: evaluates to E<pi>/180 which, when used as a multiplier, converts an
angle from degrees to radians

=item *
R2D: evaluates to 180/E<pi> which, when used as a multiplier, converts an
angle from radians to degrees

=back

=head3 Operands

The expression can use the values retrieved from the INPx links as
operands, though constants can be used as operands too. These values
retrieved from the input links are stored in the A-L fields. The values to
be used in the expression are simple references by the field letter. For
instance, the value obtained from the INPA link is stored in field A, and
the values obtained from the INPB link is stored in the field B. The names
can be included in the expression will operate on their respective values,
as in A+B.

=fields A, B, C, D, E, F, G, H, I, J, K, L

The keyword VAL returns the current contents of the expression's result
field, i.e. the VAL field for the CALC expression and the OVAL field for
the OCAL expression. (These fields can be written to by CA put, so it might
I<not> be the result from the last time the expression was evaluated).

=head3 Algebraic Operations

=over 1

=item *
ABS: Absolute value (unary)

=item *
SQR: Square root (unary)

=item *
MIN: Minimum (any number of args)

=item *
MAX: Maximum (any number of args)

=item *
FINITE: returns non-zero if none of the arguments are NaN or Inf (any
number of args)

=item *
ISNAN: returns non-zero if any of the arguments is NaN or Inf (any number
of args)

=item *
CEIL: Ceiling (unary)

=item *
FLOOR: Floor (unary)

=item *
FMOD: Floating point modulo (binary)  Added in 7.0.8

=item *
LOG: Log base 10 (unary)

=item *
LOGE: Natural log (unary)

=item *
LN: Natural log (unary)

=item *
EXP: Exponential function (unary)

=item *
^ : Exponential (binary)

=item *
** : Exponential (binary)

=item *
+ : Addition (binary)

=item *
- : Subtraction (binary)

=item *
* : Multiplication (binary)

=item *
/ : Division (binary)

=item *
% : Modulo (binary)

=item *
NOT: Negate (unary)

=back

=head3 Trigonometric Operators

=over 1

=item *
SIN: Sine

=item *
SINH: Hyperbolic sine

=item *
ASIN: Arc sine

=item *
COS: Cosine

=item *
COSH: Hyperbolic cosine

=item *
ACOS: Arc cosine

=item *
TAN: Tangent

=item *
TANH: Hyperbolic tangent

=item *
ATAN: Arc tangent

=back

=head3 Relational Operators

=over 1

=item *
C<<< >= >>> : Greater than or equal to

=item *
C<<< > >>> : Greater than

=item *
C<<< <= >>> : Less than or equal to

=item *
C<<< < >>> : Less than

=item *
C<<< # >>> : Not equal to

=item *
C<<< = >>> : Equal to

=back

=head3 Logical Operators

=over 1

=item *
&& : And

=item *
|| : Or

=item *
! : Not

=back

=head3 Bitwise Operators

=over 1

=item *
C<|> : Bitwise Or

=item *
C<&> : Bitwise And

=item *
OR : Bitwise Or

=item *
AND : Bitwise And

=item *
XOR : Bitwise Exclusive Or

=item *
C<~> : One's Complement

=item *
C<<< << >>> : Arithmetic Left Shift

=item *
C<<< >> >>> : Arithmetic Right Shift

=item *
C<<<< >>> >>>> : Logical Right Shift

=back

=head3 Assignment Operator

=over 1

=item *
C<:=> : assigns a value (right hand side) to a variable (i.e. field)

=back

=head3 Parentheses, Comma, and Semicolon

The open and close parentheses are supported. Nested parentheses are
supported.

The comma is supported when used to separate the arguments of a binary
function.

The semicolon is used to separate expressions. Although only one
traditional calculation expression is allowed, multiple assignment
expressions are allowed.

=head3 Conditional Expression

The C language's question mark operator is supported. The format is:
C<condition ? True result : False result>

=head3 Expression Examples

=head3 Algebraic

C<A + B + 10>

=over 1

=item *
Result is C<A + B + 10>

=back

=head3 Relational

C<<< (A + B) < (C + D) >>>

=over 1

=item *
Result is 1 if C<<< (A + B) < (C + D) >>>

=item *
Result is 0 if C<<< (A + B) >= (C + D) >>>

=back

=head3 Question Mark

C<<< (A + B) < (C + D) ? E : F + L + 10 >>>

=over 1

=item *
Result is C<E> if C<<< (A + B) < (C + D) >>>

=item *
Result is C<F + L + 10> 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:

C<(A + B)<(C + D) ? E>

=over 1

=item
Result is E if (A + B)<(C + D)

=item
Result is unchanged if (A + B)>=(C + D)

From 3.14.9 onwards, this expression must be written as
C<(A + B) < (C + D) ? E : VAL>

=back

=head3 Logical

C<A & B>

=over 1

=item *
Causes the following to occur:

=over 1

=item *
Convert A to integer

=item *
Convert B to integer

=item *
Bitwise And A and B

=item *
Convert result to floating point

=back

=back

=head3 Assignment

C<sin(a); a:=a+D2R>

=over 1

=item *
Causes the Calc record to output the successive values of a sine curve in
1 degree intervals.

=back

=head3 Output Parameters

These parameters specify and control the output capabilities of the Calcout
record. They determine when to write the output, where to write it, and what
the output will be. The OUT link specifies the Process Variable to which
the result will be written.

=head4 Menu calcoutOOPT

The OOPT field determines the condition that causes the output link to be
written to. It's a menu field that has six choices:

=menu calcoutOOPT

=over

=item *
C<Every Time> -- write output every time record is processed.

=item *
C<On Change> -- write output every time VAL changes, i.e., every time the
result of the expression changes.

=item *
C<When Zero> -- when record is processed, write output if VAL is zero.

=item *
C<When Non-zero> -- when record is processed, write output if VAL is
non-zero.

=item *
C<Transition To Zero> -- when record is processed, write output only if VAL
is zero and the last value was non-zero.

=item *
C<Transition To Non-zero> -- when record is processed, write output only if
VAL is non-zero and last value was zero.

=back

=head4 Menu calcoutDOPT

The DOPT field determines what data is written to the output link when the
output is executed. The field is a menu field with two options: 

=menu calcoutDOPT

If C<Use CALC> is specified, when the record writes its
output it will write the result of the expression in the CALC field, that
is, it will write the value of the VAL field. If C<Use OCAL> is specified,
the record will instead write the result of the expression in the OCAL
field, which is contained in the OVAL field. The OCAL field is exactly like
the CALC field and has the same functionality it can contain the string
representation of an expression which is evaluated at run-time. Thus, if
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.

The IVOA field specifies what action to take with the OUT link if the
Calcout record enters an INVALID alarm status. The options are
C<Continue normally>, C<Don't drive outputs>, and C<Set output to IVOV>.
If the IVOA field is C<Set output to IVOV>, the data entered into the
IVOV field is written to the OUT link if the record alarm severity is
INVALID.

=fields OUT, OOPT, DOPT, OCAL, OVAL, OEVT, ODLY, IVOA, IVOV

=head3 Operator Display Parameter

These parameters are used to present meaningful data to the operator. Some
are also meant to represent the status of the record at run-time.

The EGU field contains a string of up to 16 characters which is supplied by
the user and which describes the values being operated upon. The string is
retrieved whenever the routine C<get_units()> is called. The EGU string is
solely for an operator's sake and does not have to be used.

The HOPR and LOPR fields only refer to the limits of the VAL, HIHI, HIGH,
LOW, and LOLO fields. PREC controls the precision of the VAL field.

=head4 Menu calcoutINAV

The INAV-INLV fields indicate the status of the link to the PVs specified
in the INPA-INPL fields respectively. These fields can have four possible
values:

=menu calcoutINAV

=over 1

=item *
C<Ext PV NC> -- the PV wasn't found on this IOC and a Channel Access link
hasn't been established.

=item *
C<Ext PV OK> -- the PV wasn't found on this IOC and a Channel Access link
has been established.

=item *
C<Local PV> -- the PV was found on this IOC.

=item *
C<Constant> -- the corresponding link field is a constant.

=back

The OUTV field indicates the status of the OUT link. If has the same
possible values as the INAV-INLV fields.

The CLCV and OLCV fields indicate the validity of the expression in the
CALC and OCAL fields respectively. If the expression in invalid, the field
is set to one.

The DLYA field is set to one during the delay specified in ODLY.

See L<Fields Common to All Record Types|dbCommonRecord/Operator Display
Parameters> for more on the record name (NAME) and description (DESC) fields.

=fields EGU, PREC, HOPR, LOPR, INAV, INBV, INCV, INDV, INEV, INFV, INGV, INHV, INIV, INJV, INKV, INLV, OUTV, CLCV, OCLV, DLYA, NAME, DESC

=head3 Alarm Parameters

The possible alarm conditions for the Calcout record are the SCAN, READ,
Calculation, and limit alarms. The SCAN and READ alarms are called by the
record support routines. The Calculation alarm is called by the record
processing routine when the CALC expression is an invalid one, upon which
an error message is generated.

The following alarm parameters, which are configured by the user, define the
limit alarms for the VAL field and the severity corresponding to those
conditions.

The HYST field defines an alarm deadband for each limit.

See L<Alarm Specification|https://docs.epics-controls.org/en/latest/guides/EPICS_Process_Database_Concepts.html#alarm-specification>
for a complete explanation of record alarms and of the standard fields.
L<Alarm Fields|dbCommonRecord/Alarm Fields> lists other fields related
to alarms that are common to all record types.

=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST

=head3 Monitor Parameters

These parameters are used to determine when to send monitors for the value
fields. These monitors are sent when the value field exceeds the last
monitored field by the appropriate deadband, the ADEL for archiver monitors
and the MDEL field for all other types of monitors. If these fields have a
value of zero, every time the value changes, monitors are triggered; if
they have a value of -1, every time the record is scanned, monitors are
triggered. See L<Monitor Specification> for a complete explanation of
monitors.

=fields ADEL, MDEL

=head3 Run-time Parameters

These fields are not configurable using a configuration tool and none are
modifiable at run-time. They are used to process the record.

The LALM field is used to implement the hysteresis factor for the alarm
limits.

The LA-LL fields are used to decide when to trigger monitors for the
corresponding fields. For instance, if LA does not equal the value for A,
monitors for A are triggered. The MLST and ALST fields are used in the same
manner for the VAL field.

=fields LALM, ALST, MLST, LA, LB, LC, LD, LE, LF, LG, LH, LI, LJ, LK, LL

=cut

	include "dbCommon.dbd" 
    %/* Declare Device Support Entry Table */
    %struct calcoutRecord;
    %typedef struct calcoutdset {
    %    dset common;
    %    long (*write)(struct calcoutRecord *prec);
    %} calcoutdset;
    %#define HAS_calcoutdset
    %
	field(RPVT,DBF_NOACCESS) {
		prompt("Record Private")
		special(SPC_NOMOD)
		interest(4)
		extra("struct rpvtStruct *rpvt")
	}
	field(VAL,DBF_DOUBLE) {
		prompt("Result")
		promptgroup("50 - Output")
		asl(ASL0)
	}
	field(PVAL,DBF_DOUBLE) {
		prompt("Previous Value")
	}
	field(CALC,DBF_STRING) {
		prompt("Calculation")
		promptgroup("30 - Action")
		special(SPC_CALC)
		pp(TRUE)
		size(80)
		initial("0")
	}
	field(CLCV,DBF_LONG) {
		prompt("CALC Valid")
		interest(1)
	}
	field(INPA,DBF_INLINK) {
		prompt("Input A")
		special(SPC_MOD)
		promptgroup("41 - Input A-F")
		interest(1)
	}
	field(INPB,DBF_INLINK) {
		prompt("Input B")
		special(SPC_MOD)
		promptgroup("41 - Input A-F")
		interest(1)
	}
	field(INPC,DBF_INLINK) {
		prompt("Input C")
		special(SPC_MOD)
		promptgroup("41 - Input A-F")
		interest(1)
	}
	field(INPD,DBF_INLINK) {
		prompt("Input D")
		special(SPC_MOD)
		promptgroup("41 - Input A-F")
		interest(1)
	}
	field(INPE,DBF_INLINK) {
		prompt("Input E")
		special(SPC_MOD)
		promptgroup("41 - Input A-F")
		interest(1)
	}
	field(INPF,DBF_INLINK) {
		prompt("Input F")
		special(SPC_MOD)
		promptgroup("41 - Input A-F")
		interest(1)
	}
	field(INPG,DBF_INLINK) {
		prompt("Input G")
		special(SPC_MOD)
		promptgroup("42 - Input G-L")
		interest(1)
	}
	field(INPH,DBF_INLINK) {
		prompt("Input H")
		special(SPC_MOD)
		promptgroup("42 - Input G-L")
		interest(1)
	}
	field(INPI,DBF_INLINK) {
		prompt("Input I")
		special(SPC_MOD)
		promptgroup("42 - Input G-L")
		interest(1)
	}
	field(INPJ,DBF_INLINK) {
		prompt("Input J")
		special(SPC_MOD)
		promptgroup("42 - Input G-L")
		interest(1)
	}
	field(INPK,DBF_INLINK) {
		prompt("Input K")
		special(SPC_MOD)
		promptgroup("42 - Input G-L")
		interest(1)
	}
	field(INPL,DBF_INLINK) {
		prompt("Input L")
		special(SPC_MOD)
		promptgroup("42 - Input G-L")
		interest(1)
	}
	field(OUT,DBF_OUTLINK) {
		prompt("Output Specification")
		special(SPC_MOD)
		promptgroup("50 - Output")
		interest(1)
	}
	field(INAV,DBF_MENU) {
		prompt("INPA PV Status")
		special(SPC_NOMOD)
		interest(1)
		menu(calcoutINAV)
		initial("1")
	}
	field(INBV,DBF_MENU) {
		prompt("INPB PV Status")
		special(SPC_NOMOD)
		interest(1)
		menu(calcoutINAV)
		initial("1")
	}
	field(INCV,DBF_MENU) {
		prompt("INPC PV Status")
		special(SPC_NOMOD)
		interest(1)
		menu(calcoutINAV)
		initial("1")
	}
	field(INDV,DBF_MENU) {
		prompt("INPD PV Status")
		special(SPC_NOMOD)
		interest(1)
		menu(calcoutINAV)
		initial("1")
	}
	field(INEV,DBF_MENU) {
		prompt("INPE PV Status")
		special(SPC_NOMOD)
		interest(1)
		menu(calcoutINAV)
		initial("1")
	}
	field(INFV,DBF_MENU) {
		prompt("INPF PV Status")
		special(SPC_NOMOD)
		interest(1)
		menu(calcoutINAV)
		initial("1")
	}
	field(INGV,DBF_MENU) {
		prompt("INPG PV Status")
		special(SPC_NOMOD)
		interest(1)
		menu(calcoutINAV)
		initial("1")
	}
	field(INHV,DBF_MENU) {
		prompt("INPH PV Status")
		special(SPC_NOMOD)
		interest(1)
		menu(calcoutINAV)
		initial("1")
	}
	field(INIV,DBF_MENU) {
		prompt("INPI PV Status")
		special(SPC_NOMOD)
		interest(1)
		menu(calcoutINAV)
		initial("1")
	}
	field(INJV,DBF_MENU) {
		prompt("INPJ PV Status")
		special(SPC_NOMOD)
		interest(1)
		menu(calcoutINAV)
		initial("1")
	}
	field(INKV,DBF_MENU) {
		prompt("INPK PV Status")
		special(SPC_NOMOD)
		interest(1)
		menu(calcoutINAV)
		initial("1")
	}
	field(INLV,DBF_MENU) {
		prompt("INPL PV Status")
		special(SPC_NOMOD)
		interest(1)
		menu(calcoutINAV)
		initial("1")
	}
	field(OUTV,DBF_MENU) {
		prompt("OUT PV Status")
		special(SPC_NOMOD)
		interest(1)
		menu(calcoutINAV)
	}
	field(OOPT,DBF_MENU) {
		prompt("Output Execute Opt")
		promptgroup("50 - Output")
		interest(1)
		menu(calcoutOOPT)
	}
	field(ODLY,DBF_DOUBLE) {
		prompt("Output Execute Delay")
		promptgroup("50 - Output")
		asl(ASL0)
		interest(1)
	}
	field(DLYA,DBF_USHORT) {
		prompt("Output Delay Active")
		special(SPC_NOMOD)
		asl(ASL0)
	}
	field(DOPT,DBF_MENU) {
		prompt("Output Data Opt")
		promptgroup("30 - Action")
		interest(1)
		menu(calcoutDOPT)
	}
	field(OCAL,DBF_STRING) {
		prompt("Output Calculation")
		promptgroup("30 - Action")
		special(SPC_CALC)
		pp(TRUE)
		size(80)
		initial("0")
	}
	field(OCLV,DBF_LONG) {
		prompt("OCAL Valid")
		interest(1)
	}
	field(OEVT,DBF_STRING) {
		prompt("Event To Issue")
		promptgroup("30 - Action")
		special(SPC_MOD)
		asl(ASL0)
		size(40)
	}
	%#include "dbScan.h"
	field(EPVT, DBF_NOACCESS) {
		prompt("Event private")
		special(SPC_NOMOD)
		interest(4)
		extra("EVENTPVT epvt")
	}
	field(IVOA,DBF_MENU) {
		prompt("INVALID output action")
		promptgroup("50 - Output")
		interest(2)
		menu(menuIvoa)
	}
	field(IVOV,DBF_DOUBLE) {
		prompt("INVALID output value")
		promptgroup("50 - Output")
		interest(2)
	}
	field(EGU,DBF_STRING) {
		prompt("Engineering Units")
		promptgroup("80 - Display")
		interest(1)
		size(16)
		prop(YES)
	}
	field(PREC,DBF_SHORT) {
		prompt("Display Precision")
		promptgroup("80 - Display")
		interest(1)
		prop(YES)
	}
	field(HOPR,DBF_DOUBLE) {
		prompt("High Operating Rng")
		promptgroup("80 - Display")
		interest(1)
		prop(YES)
	}
	field(LOPR,DBF_DOUBLE) {
		prompt("Low Operating Range")
		promptgroup("80 - Display")
		interest(1)
		prop(YES)
	}
	field(HIHI,DBF_DOUBLE) {
		prompt("Hihi Alarm Limit")
		promptgroup("70 - Alarm")
		pp(TRUE)
		interest(1)
		prop(YES)
	}
	field(LOLO,DBF_DOUBLE) {
		prompt("Lolo Alarm Limit")
		promptgroup("70 - Alarm")
		pp(TRUE)
		interest(1)
		prop(YES)
	}
	field(HIGH,DBF_DOUBLE) {
		prompt("High Alarm Limit")
		promptgroup("70 - Alarm")
		pp(TRUE)
		interest(1)
		prop(YES)
	}
	field(LOW,DBF_DOUBLE) {
		prompt("Low Alarm Limit")
		promptgroup("70 - Alarm")
		pp(TRUE)
		interest(1)
		prop(YES)
	}
	field(HHSV,DBF_MENU) {
		prompt("Hihi Severity")
		promptgroup("70 - Alarm")
		pp(TRUE)
		interest(1)
		prop(YES)
		menu(menuAlarmSevr)
	}
	field(LLSV,DBF_MENU) {
		prompt("Lolo Severity")
		promptgroup("70 - Alarm")
		pp(TRUE)
		interest(1)
		prop(YES)
		menu(menuAlarmSevr)
	}
	field(HSV,DBF_MENU) {
		prompt("High Severity")
		promptgroup("70 - Alarm")
		pp(TRUE)
		interest(1)
		prop(YES)
		menu(menuAlarmSevr)
	}
	field(LSV,DBF_MENU) {
		prompt("Low Severity")
		promptgroup("70 - Alarm")
		pp(TRUE)
		interest(1)
		prop(YES)
		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(A,DBF_DOUBLE) {
		prompt("Value of Input A")
		pp(TRUE)
	}
	field(B,DBF_DOUBLE) {
		prompt("Value of Input B")
		pp(TRUE)
	}
	field(C,DBF_DOUBLE) {
		prompt("Value of Input C")
		pp(TRUE)
	}
	field(D,DBF_DOUBLE) {
		prompt("Value of Input D")
		pp(TRUE)
	}
	field(E,DBF_DOUBLE) {
		prompt("Value of Input E")
		pp(TRUE)
	}
	field(F,DBF_DOUBLE) {
		prompt("Value of Input F")
		pp(TRUE)
	}
	field(G,DBF_DOUBLE) {
		prompt("Value of Input G")
		pp(TRUE)
	}
	field(H,DBF_DOUBLE) {
		prompt("Value of Input H")
		pp(TRUE)
	}
	field(I,DBF_DOUBLE) {
		prompt("Value of Input I")
		pp(TRUE)
	}
	field(J,DBF_DOUBLE) {
		prompt("Value of Input J")
		pp(TRUE)
	}
	field(K,DBF_DOUBLE) {
		prompt("Value of Input K")
		pp(TRUE)
	}
	field(L,DBF_DOUBLE) {
		prompt("Value of Input L")
		pp(TRUE)
	}
	field(OVAL,DBF_DOUBLE) {
		prompt("Output Value")
		asl(ASL0)
	}
	field(LA,DBF_DOUBLE) {
		prompt("Prev Value of A")
		special(SPC_NOMOD)
		interest(3)
	}
	field(LB,DBF_DOUBLE) {
		prompt("Prev Value of B")
		special(SPC_NOMOD)
		interest(3)
	}
	field(LC,DBF_DOUBLE) {
		prompt("Prev Value of C")
		special(SPC_NOMOD)
		interest(3)
	}
	field(LD,DBF_DOUBLE) {
		prompt("Prev Value of D")
		special(SPC_NOMOD)
		interest(3)
	}
	field(LE,DBF_DOUBLE) {
		prompt("Prev Value of E")
		special(SPC_NOMOD)
		interest(3)
	}
	field(LF,DBF_DOUBLE) {
		prompt("Prev Value of F")
		special(SPC_NOMOD)
		interest(3)
	}
	field(LG,DBF_DOUBLE) {
		prompt("Prev Value of G")
		special(SPC_NOMOD)
		interest(3)
	}
	field(LH,DBF_DOUBLE) {
		prompt("Prev Value of H")
		special(SPC_NOMOD)
		interest(3)
	}
	field(LI,DBF_DOUBLE) {
		prompt("Prev Value of I")
		special(SPC_NOMOD)
		interest(3)
	}
	field(LJ,DBF_DOUBLE) {
		prompt("Prev Value of J")
		special(SPC_NOMOD)
		interest(3)
	}
	field(LK,DBF_DOUBLE) {
		prompt("Prev Value of K")
		special(SPC_NOMOD)
		interest(3)
	}
	field(LL,DBF_DOUBLE) {
		prompt("Prev Value of L")
		special(SPC_NOMOD)
		interest(3)
	}
	field(POVL,DBF_DOUBLE) {
		prompt("Prev Value of OVAL")
		asl(ASL0)
	}
	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 "postfix.h"
	field(RPCL,DBF_NOACCESS) {
		prompt("Reverse Polish Calc")
		special(SPC_NOMOD)
		interest(4)
		extra("char	rpcl[INFIX_TO_POSTFIX_SIZE(80)]")
	}
	field(ORPC,DBF_NOACCESS) {
		prompt("Reverse Polish OCalc")
		special(SPC_NOMOD)
		interest(4)
		extra("char	orpc[INFIX_TO_POSTFIX_SIZE(80)]")
	}

=head2 Record Support

=head3 Record Support Routines

=head2 C<init_record>

For each constant input link, the corresponding value field is initialized
with the constant value if the input link is CONSTANT or a channel access
link is created if the input link is PV_LINK.

A routine postfix is called to convert the infix expression in CALC and
OCAL to Reverse Polish Notation. The result is stored in RPCL and ORPC,
respectively.

=head2 C<process>

See next section.

=head2 C<special>

This is called if CALC or OCAL is changed. C<special> calls postfix.

=head2 C<get_units>

Retrieves EGU.

=head2 C<get_precision>

Retrieves PREC.

=head2 C<get_graphic_double>

Sets the upper display and lower display limits for a field. If the field
is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else
if the field has upper and lower limits defined they will be used, else the
upper and lower maximum values for the field type will be used.

=head2 C<get_control_double>

Sets the upper control and lower control limits for a field. If the VAL,
HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the
field has upper and lower limits defined they will be used, else the upper
and lower maximum values for the field will be used.

=head2 C<get_alarm_double>

Sets the following values:

=over

upper_alarm_limit = HIHI

upper_warning_limit = HIGH

lower warning_limit = LOW

lower_alarm_limit = LOLO

=back

=head3 Record Processing

=head2 C<process()>

The C<process()> routine implements the following algorithm:

=over

=item 1.
Fetch all arguments.

=item 2.
Call routine C<calcPerform()>, which calculates VAL from the prefix version
of the expression given in CALC. If C<calcPerform()> returns success, UDF
is set to FALSE.

=item 3.
Check alarms. This routine checks to see if the new VAL causes the alarm
status and severity to change. If so, NSEV, NSTA and LALM are set. If also
honors the alarm hysteresis factor (HYST). Thus the value must change by at
least HYST before the alarm status and severity changes.

=item 4.
Determine if the Output Execution Option (OOPT) is met. If met, either
execute the output link (and output event) immediately (if ODLY = 0), or
schedule a callback after the specified interval. See the explanation for
the C<execOutput()> routine below.

=item 5.
Check to see if monitors should be invoked.

=over

=item *
Alarm monitors are invoked if the alarm status or severity has changed.

=item *
Archive and value change monitors are invoked if ADEL and MDEL conditions
are met.

=item *
Monitors for A-L are checked whenever other monitors are invoked.

=item *
NSEV and NSTA are reset to 0

=back

=item 6.
If no output delay was specified, scan forward link if necessary, set PACT
FALSE, and return.

=back

=head2 C<execOutput()>

=over

=item 1.
If DOPT field specifies the use of OCAL, call the routine C<calcPerform()>
for the postfix version of the expression in OCAL. Otherwise, use VAL.

=item 2.
If the Alarm Severity is INVALID, follow the option as designated by the
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).

=item 4.
If an output delay was implemented, process the forward link.

=back

=cut
}

variable(calcoutODLYprecision, int)
variable(calcoutODLYlimit, double)
