From 9921a8ae19ccda2c2b32bdf99a960a13a4810f39 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 22 May 2025 11:14:16 +0200 Subject: [PATCH 1/7] extend calc records and expressions to inputs A-U --- modules/database/src/std/rec/calcRecord.c | 4 +- .../database/src/std/rec/calcRecord.dbd.pod | 149 +++++++++++- modules/database/src/std/rec/calcoutRecord.c | 13 +- .../src/std/rec/calcoutRecord.dbd.pod | 226 ++++++++++++++++-- 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 | 14 +- modules/libcom/src/calc/postfixPvt.h | 6 +- modules/libcom/test/epicsCalcTest.cpp | 102 ++++++-- 10 files changed, 513 insertions(+), 54 deletions(-) diff --git a/modules/database/src/std/rec/calcRecord.c b/modules/database/src/std/rec/calcRecord.c index 4467c7080..0bd7beb49 100644 --- a/modules/database/src/std/rec/calcRecord.c +++ b/modules/database/src/std/rec/calcRecord.c @@ -159,9 +159,9 @@ static long special(DBADDR *paddr, int after) #define indexof(field) calcRecord##field static long get_linkNumber(int fieldIndex) { - if (fieldIndex >= indexof(A) && fieldIndex <= indexof(L)) + if (fieldIndex >= indexof(A) && fieldIndex < indexof(A) + CALCPERFORM_NARGS) return fieldIndex - indexof(A); - if (fieldIndex >= indexof(LA) && fieldIndex <= indexof(LL)) + if (fieldIndex >= indexof(LA) && fieldIndex < indexof(LA) + CALCPERFORM_NARGS) return fieldIndex - indexof(LA); return -1; } diff --git a/modules/database/src/std/rec/calcRecord.dbd.pod b/modules/database/src/std/rec/calcRecord.dbd.pod index 4203555da..b20cafec4 100644 --- a/modules/database/src/std/rec/calcRecord.dbd.pod +++ b/modules/database/src/std/rec/calcRecord.dbd.pod @@ -34,8 +34,8 @@ These fields are described in L. =head3 Read Parameters -The read parameters for the Calc record consist of 12 input links INPA, -INPB, ... INPL. The fields can be database links, channel access links, or +The read parameters for the Calc record consist of 21 input links INPA - +INPU. The fields can be database links, channel access links, or constants. If they are links, they must specify another record's field or a channel access link. If they are constants, they will be initialized with the value they are configured with and can be changed via C. They @@ -45,7 +45,7 @@ See L
for information on how to specify database links. -=fields INPA - INPL +=fields INPA - INPU =head3 Expression @@ -113,7 +113,7 @@ radians to degrees The expression uses the values retrieved from the INPx links as operands, though constants can be used as operands too. These values retrieved from -the input links are stored in the A-L fields. The values to be used in the +the input links are stored in the A-U fields. The values to be used in the expression are simply referenced by the field letter. For instance, the value obtained from INPA link is stored in the field A, and the value obtained from INPB is stored in field B. The field names can be included in @@ -492,12 +492,12 @@ modifiable at run-time. They are used to process the record. The LALM field is used to implement the hysteresis factor for the alarm limits. -The LA-LL fields are used to decide when to trigger monitors for the +The LA-LU fields are used to decide when to trigger monitors for the corresponding fields. For instance, if LA does not equal the value A, monitors for A are triggered. The MLST and ALST fields are used in the same manner for the VAL field. -=fields LALM, ALST, MLST, LA - LL +=fields LALM, ALST, MLST, LA - LU =cut @@ -575,6 +575,60 @@ manner for the VAL field. promptgroup("42 - Input G-L") interest(1) } + field(INPM,DBF_INLINK) { + prompt("Input M") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPN,DBF_INLINK) { + prompt("Input N") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPO,DBF_INLINK) { + prompt("Input O") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPP,DBF_INLINK) { + prompt("Input P") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPQ,DBF_INLINK) { + prompt("Input Q") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPR,DBF_INLINK) { + prompt("Input R") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPS,DBF_INLINK) { + prompt("Input S") + special(SPC_MOD) + promptgroup("44 - Input S-U") + interest(1) + } + field(INPT,DBF_INLINK) { + prompt("Input T") + special(SPC_MOD) + promptgroup("44 - Input S-U") + interest(1) + } + field(INPU,DBF_INLINK) { + prompt("Input U") + special(SPC_MOD) + promptgroup("44 - Input S-U") + interest(1) + } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") @@ -733,6 +787,42 @@ manner for the VAL field. prompt("Value of Input L") pp(TRUE) } + field(M,DBF_DOUBLE) { + prompt("Value of Input M") + pp(TRUE) + } + field(N,DBF_DOUBLE) { + prompt("Value of Input N") + pp(TRUE) + } + field(O,DBF_DOUBLE) { + prompt("Value of Input O") + pp(TRUE) + } + field(P,DBF_DOUBLE) { + prompt("Value of Input P") + pp(TRUE) + } + field(Q,DBF_DOUBLE) { + prompt("Value of Input Q") + pp(TRUE) + } + field(R,DBF_DOUBLE) { + prompt("Value of Input R") + pp(TRUE) + } + field(S,DBF_DOUBLE) { + prompt("Value of Input S") + pp(TRUE) + } + field(T,DBF_DOUBLE) { + prompt("Value of Input T") + pp(TRUE) + } + field(U,DBF_DOUBLE) { + prompt("Value of Input U") + pp(TRUE) + } field(LA,DBF_DOUBLE) { prompt("Prev Value of A") special(SPC_NOMOD) @@ -793,6 +883,51 @@ manner for the VAL field. special(SPC_NOMOD) interest(3) } + field(LM,DBF_DOUBLE) { + prompt("Prev Value of M") + special(SPC_NOMOD) + interest(3) + } + field(LN,DBF_DOUBLE) { + prompt("Prev Value of N") + special(SPC_NOMOD) + interest(3) + } + field(LO,DBF_DOUBLE) { + prompt("Prev Value of O") + special(SPC_NOMOD) + interest(3) + } + field(LP,DBF_DOUBLE) { + prompt("Prev Value of P") + special(SPC_NOMOD) + interest(3) + } + field(LQ,DBF_DOUBLE) { + prompt("Prev Value of Q") + special(SPC_NOMOD) + interest(3) + } + field(LR,DBF_DOUBLE) { + prompt("Prev Value of R") + special(SPC_NOMOD) + interest(3) + } + field(LS,DBF_DOUBLE) { + prompt("Prev Value of S") + special(SPC_NOMOD) + interest(3) + } + field(LT,DBF_DOUBLE) { + prompt("Prev Value of T") + special(SPC_NOMOD) + interest(3) + } + field(LU,DBF_DOUBLE) { + prompt("Prev Value of U") + special(SPC_NOMOD) + interest(3) + } field(LALM,DBF_DOUBLE) { prompt("Last Value Alarmed") special(SPC_NOMOD) @@ -913,7 +1048,7 @@ Archive and values change monitors are invoked if ADEL and MDEL conditions are met. =item * -Monitors for A-L are checked whenever other monitors are invoked. +Monitors for A-U are checked whenever other monitors are invoked. =item * NSEV and NSTA are reset to 0. diff --git a/modules/database/src/std/rec/calcoutRecord.c b/modules/database/src/std/rec/calcoutRecord.c index 319994596..d6c2a145c 100644 --- a/modules/database/src/std/rec/calcoutRecord.c +++ b/modules/database/src/std/rec/calcoutRecord.c @@ -355,6 +355,15 @@ static long special(DBADDR *paddr, int after) case(calcoutRecordINPJ): case(calcoutRecordINPK): case(calcoutRecordINPL): + case(calcoutRecordINPM): + case(calcoutRecordINPN): + case(calcoutRecordINPO): + case(calcoutRecordINPP): + case(calcoutRecordINPQ): + case(calcoutRecordINPR): + case(calcoutRecordINPS): + case(calcoutRecordINPT): + case(calcoutRecordINPU): case(calcoutRecordOUT): lnkIndex = fieldIndex - calcoutRecordINPA; plink = &prec->inpa + lnkIndex; @@ -406,9 +415,9 @@ static long special(DBADDR *paddr, int after) #define indexof(field) calcoutRecord##field static long get_linkNumber(int fieldIndex) { - if (fieldIndex >= indexof(A) && fieldIndex <= indexof(L)) + if (fieldIndex >= indexof(A) && fieldIndex < indexof(A) + CALCPERFORM_NARGS) return fieldIndex - indexof(A); - if (fieldIndex >= indexof(LA) && fieldIndex <= indexof(LL)) + if (fieldIndex >= indexof(LA) && fieldIndex < indexof(LA) + CALCPERFORM_NARGS) return fieldIndex - indexof(LA); return -1; } diff --git a/modules/database/src/std/rec/calcoutRecord.dbd.pod b/modules/database/src/std/rec/calcoutRecord.dbd.pod index 3e1dbd4a9..cab9fd902 100644 --- a/modules/database/src/std/rec/calcoutRecord.dbd.pod +++ b/modules/database/src/std/rec/calcoutRecord.dbd.pod @@ -57,18 +57,18 @@ These fields are listed in L. =head3 Read Parameters -The read parameters for the Calcout record consists of 12 input links INPA, -INPB, ... INPL. The fields can be database links, channel access links, or +The read parameters for the Calcout record consists of 21 input links INPA - +INPU. The fields can be database links, channel access links, or constants. If they are links, they must specify another record's field. If they are constants, they will be initialized with the value they are configured with and can be changed via C. These fields cannot be -hardware addresses. In addition, the Calcout record contains the INAV, -INBV, ... INLV fields which indicate the status of the link fields, for +hardware addresses. In addition, the Calcout record contains the INAV - +INUV fields which indicate the status of the link fields, for example, whether or not the specified PV was found and a link to it established. See L for an explanation of these fields. -=fields INPA - INPL +=fields INPA - INPU =head3 Expression @@ -139,14 +139,14 @@ angle from radians to degrees The expression can use the values retrieved from the INPx links as operands, though constants can be used as operands too. These values -retrieved from the input links are stored in the A-L fields. The values to +retrieved from the input links are stored in the A-U fields. The values to be used in the expression are simple references by the field letter. For instance, the value obtained from the INPA link is stored in field A, and the values obtained from the INPB link is stored in the field B. The names can be included in the expression will operate on their respective values, as in A+B. -=fields A - L +=fields A - U The keyword VAL returns the current contents of the expression's result field, i.e. the VAL field for the CALC expression and the OVAL field for @@ -543,8 +543,8 @@ LOW, and LOLO fields. PREC controls the precision of the VAL field. =head4 Menu calcoutINAV -The INAV-INLV fields indicate the status of the link to the PVs specified -in the INPA-INPL fields respectively. These fields can have four possible +The INAV-INUV fields indicate the status of the link to the PVs specified +in the INPA-INPU fields respectively. These fields can have four possible values: =menu calcoutINAV @@ -568,7 +568,7 @@ C -- the corresponding link field is a constant. =back The OUTV field indicates the status of the OUT link. If has the same -possible values as the INAV-INLV fields. +possible values as the INAV-INUV fields. The CLCV and OLCV fields indicate the validity of the expression in the CALC and OCAL fields respectively. If the expression in invalid, the field @@ -579,7 +579,7 @@ The DLYA field is set to one during the delay specified in ODLY. See L for more on the record name (NAME) and description (DESC) fields. -=fields EGU, PREC, HOPR, LOPR, INAV - INLV, OUTV, CLCV, OCLV, DLYA, NAME, DESC +=fields EGU, PREC, HOPR, LOPR, INAV - INUV, OUTV, CLCV, OCLV, DLYA, NAME, DESC =head3 Alarm Parameters @@ -623,12 +623,12 @@ modifiable at run-time. They are used to process the record. The LALM field is used to implement the hysteresis factor for the alarm limits. -The LA-LL fields are used to decide when to trigger monitors for the +The LA-LU fields are used to decide when to trigger monitors for the corresponding fields. For instance, if LA does not equal the value for A, monitors for A are triggered. The MLST and ALST fields are used in the same manner for the VAL field. -=fields LALM, ALST, MLST, LA - LL +=fields LALM, ALST, MLST, LA - LU =cut @@ -739,6 +739,60 @@ manner for the VAL field. promptgroup("42 - Input G-L") interest(1) } + field(INPM,DBF_INLINK) { + prompt("Input M") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPN,DBF_INLINK) { + prompt("Input N") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPO,DBF_INLINK) { + prompt("Input O") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPP,DBF_INLINK) { + prompt("Input P") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPQ,DBF_INLINK) { + prompt("Input Q") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPR,DBF_INLINK) { + prompt("Input R") + special(SPC_MOD) + promptgroup("43 - Input M-R") + interest(1) + } + field(INPS,DBF_INLINK) { + prompt("Input S") + special(SPC_MOD) + promptgroup("44 - Input S-U") + interest(1) + } + field(INPT,DBF_INLINK) { + prompt("Input T") + special(SPC_MOD) + promptgroup("44 - Input S-U") + interest(1) + } + field(INPU,DBF_INLINK) { + prompt("Input U") + special(SPC_MOD) + promptgroup("44 - Input S-U") + interest(1) + } field(OUT,DBF_OUTLINK) { prompt("Output Specification") special(SPC_MOD) @@ -829,6 +883,69 @@ manner for the VAL field. menu(calcoutINAV) initial("1") } + field(INMV,DBF_MENU) { + prompt("INPM PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INNV,DBF_MENU) { + prompt("INPN PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INOV,DBF_MENU) { + prompt("INPO PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INPV,DBF_MENU) { + prompt("INPP PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INQV,DBF_MENU) { + prompt("INPQ PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INRV,DBF_MENU) { + prompt("INPR PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INSV,DBF_MENU) { + prompt("INPS PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INTV,DBF_MENU) { + prompt("INPT PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INUV,DBF_MENU) { + prompt("INPU PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } field(OUTV,DBF_MENU) { prompt("OUT PV Status") special(SPC_NOMOD) @@ -1043,6 +1160,42 @@ manner for the VAL field. prompt("Value of Input L") pp(TRUE) } + field(M,DBF_DOUBLE) { + prompt("Value of Input M") + pp(TRUE) + } + field(N,DBF_DOUBLE) { + prompt("Value of Input N") + pp(TRUE) + } + field(O,DBF_DOUBLE) { + prompt("Value of Input O") + pp(TRUE) + } + field(P,DBF_DOUBLE) { + prompt("Value of Input P") + pp(TRUE) + } + field(Q,DBF_DOUBLE) { + prompt("Value of Input Q") + pp(TRUE) + } + field(R,DBF_DOUBLE) { + prompt("Value of Input R") + pp(TRUE) + } + field(S,DBF_DOUBLE) { + prompt("Value of Input S") + pp(TRUE) + } + field(T,DBF_DOUBLE) { + prompt("Value of Input T") + pp(TRUE) + } + field(U,DBF_DOUBLE) { + prompt("Value of Input U") + pp(TRUE) + } field(OVAL,DBF_DOUBLE) { prompt("Output Value") asl(ASL0) @@ -1107,6 +1260,51 @@ manner for the VAL field. special(SPC_NOMOD) interest(3) } + field(LM,DBF_DOUBLE) { + prompt("Prev Value of M") + special(SPC_NOMOD) + interest(3) + } + field(LN,DBF_DOUBLE) { + prompt("Prev Value of N") + special(SPC_NOMOD) + interest(3) + } + field(LO,DBF_DOUBLE) { + prompt("Prev Value of O") + special(SPC_NOMOD) + interest(3) + } + field(LP,DBF_DOUBLE) { + prompt("Prev Value of P") + special(SPC_NOMOD) + interest(3) + } + field(LQ,DBF_DOUBLE) { + prompt("Prev Value of Q") + special(SPC_NOMOD) + interest(3) + } + field(LR,DBF_DOUBLE) { + prompt("Prev Value of R") + special(SPC_NOMOD) + interest(3) + } + field(LS,DBF_DOUBLE) { + prompt("Prev Value of S") + special(SPC_NOMOD) + interest(3) + } + field(LT,DBF_DOUBLE) { + prompt("Prev Value of T") + special(SPC_NOMOD) + interest(3) + } + field(LU,DBF_DOUBLE) { + prompt("Prev Value of U") + special(SPC_NOMOD) + interest(3) + } field(POVL,DBF_DOUBLE) { prompt("Prev Value of OVAL") asl(ASL0) @@ -1246,7 +1444,7 @@ Archive and value change monitors are invoked if ADEL and MDEL conditions are met. =item * -Monitors for A-L are checked whenever other monitors are invoked. +Monitors for A-U are checked whenever other monitors are invoked. =item * NSEV and NSTA are reset to 0 diff --git a/modules/libcom/src/as/asLib_lex.l b/modules/libcom/src/as/asLib_lex.l index df936ba34..b7f01f5c5 100644 --- a/modules/libcom/src/as/asLib_lex.l +++ b/modules/libcom/src/as/asLib_lex.l @@ -18,7 +18,7 @@ stringchar [^"\n\\] name [a-zA-Z0-9_\-+:.\[\]<>;] digit [0-9] punctuation [(){},] -link [A-L] +link [A-U] %{ static ASINPUTFUNCPTR *my_yyinput; diff --git a/modules/libcom/src/calc/calcPerform.c b/modules/libcom/src/calc/calcPerform.c index 81fbdc10c..8678a573d 100644 --- a/modules/libcom/src/calc/calcPerform.c +++ b/modules/libcom/src/calc/calcPerform.c @@ -86,6 +86,15 @@ LIBCOM_API long case FETCH_J: case FETCH_K: case FETCH_L: + case FETCH_M: + case FETCH_N: + case FETCH_O: + case FETCH_P: + case FETCH_Q: + case FETCH_R: + case FETCH_S: + case FETCH_T: + case FETCH_U: *++ptop = parg[op - FETCH_A]; break; @@ -101,6 +110,15 @@ LIBCOM_API long case STORE_J: case STORE_K: case STORE_L: + case STORE_M: + case STORE_N: + case STORE_O: + case STORE_P: + case STORE_Q: + case STORE_R: + case STORE_S: + case STORE_T: + case STORE_U: parg[op - STORE_A] = *ptop--; break; @@ -441,6 +459,15 @@ calcArgUsage(const char *pinst, unsigned long *pinputs, unsigned long *pstores) case FETCH_J: case FETCH_K: case FETCH_L: + case FETCH_M: + case FETCH_N: + case FETCH_O: + case FETCH_P: + case FETCH_Q: + case FETCH_R: + case FETCH_S: + case FETCH_T: + case FETCH_U: /* Don't claim to use an arg we already stored to */ inputs |= (1 << (op - FETCH_A)) & ~stores; break; @@ -457,6 +484,15 @@ calcArgUsage(const char *pinst, unsigned long *pinputs, unsigned long *pstores) case STORE_J: case STORE_K: case STORE_L: + case STORE_M: + case STORE_N: + case STORE_O: + case STORE_P: + case STORE_Q: + case STORE_R: + case STORE_S: + case STORE_T: + case STORE_U: stores |= (1 << (op - STORE_A)); break; diff --git a/modules/libcom/src/calc/postfix.c b/modules/libcom/src/calc/postfix.c index d9dce515d..2bc3e121e 100644 --- a/modules/libcom/src/calc/postfix.c +++ b/modules/libcom/src/calc/postfix.c @@ -117,20 +117,29 @@ static const ELEMENT operands[] = { {"LN", 7, 8, 0, UNARY_OPERATOR, LOG_E}, {"LOG", 7, 8, 0, UNARY_OPERATOR, LOG_10}, {"LOGE", 7, 8, 0, UNARY_OPERATOR, LOG_E}, +{"M", 0, 0, 1, OPERAND, FETCH_M}, {"MAX", 7, 8, 0, VARARG_OPERATOR,MAX}, {"MIN", 7, 8, 0, VARARG_OPERATOR,MIN}, +{"N", 0, 0, 1, OPERAND, FETCH_N}, {"NINT", 7, 8, 0, UNARY_OPERATOR, NINT}, {"NAN", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, {"NOT", 7, 8, 0, UNARY_OPERATOR, BIT_NOT}, +{"O", 0, 0, 1, OPERAND, FETCH_O}, +{"P", 0, 0, 1, OPERAND, FETCH_P}, {"PI", 0, 0, 1, OPERAND, CONST_PI}, +{"Q", 0, 0, 1, OPERAND, FETCH_Q}, +{"R", 0, 0, 1, OPERAND, FETCH_R}, {"R2D", 0, 0, 1, OPERAND, CONST_R2D}, {"RNDM", 0, 0, 1, OPERAND, RANDOM}, +{"S", 0, 0, 1, OPERAND, FETCH_S}, {"SIN", 7, 8, 0, UNARY_OPERATOR, SIN}, {"SINH", 7, 8, 0, UNARY_OPERATOR, SINH}, {"SQR", 7, 8, 0, UNARY_OPERATOR, SQU_RT}, {"SQRT", 7, 8, 0, UNARY_OPERATOR, SQU_RT}, +{"T", 0, 0, 1, OPERAND, FETCH_T}, {"TAN", 7, 8, 0, UNARY_OPERATOR, TAN}, {"TANH", 7, 8, 0, UNARY_OPERATOR, TANH}, +{"U", 0, 0, 1, OPERAND, FETCH_U}, {"VAL", 0, 0, 1, OPERAND, FETCH_VAL}, {"~", 7, 8, 0, UNARY_OPERATOR, BIT_NOT}, }; @@ -286,7 +295,7 @@ LIBCOM_API long case STORE_OPERATOR: if (pout == pdest || pstacktop > stack || - *--pout < FETCH_A || *pout > FETCH_L) { + *--pout < FETCH_A || *pout >= FETCH_A + CALCPERFORM_NARGS) { *perror = CALC_ERR_BAD_ASSIGNMENT; goto bad; } @@ -542,9 +551,13 @@ LIBCOM_API void "LITERAL_DOUBLE", "LITERAL_INT", "VAL", "FETCH_A", "FETCH_B", "FETCH_C", "FETCH_D", "FETCH_E", "FETCH_F", "FETCH_G", "FETCH_H", "FETCH_I", "FETCH_J", "FETCH_K", "FETCH_L", + "FETCH_M", "FETCH_N", "FETCH_O", "FETCH_P", "FETCH_Q", "FETCH_R", + "FETCH_S", "FETCH_T", "FETCH_U", /* Assignment */ "STORE_A", "STORE_B", "STORE_C", "STORE_D", "STORE_E", "STORE_F", "STORE_G", "STORE_H", "STORE_I", "STORE_J", "STORE_K", "STORE_L", + "STORE_M", "STORE_N", "STORE_O", "STORE_P", "STORE_Q", "STORE_R", + "STORE_S", "STORE_T", "STORE_U", /* Trigonometry Constants */ "CONST_PI", "CONST_D2R", diff --git a/modules/libcom/src/calc/postfix.h b/modules/libcom/src/calc/postfix.h index 5d75a7ddf..248a46a41 100644 --- a/modules/libcom/src/calc/postfix.h +++ b/modules/libcom/src/calc/postfix.h @@ -22,8 +22,8 @@ #include "libComAPI.h" -/** \brief Number of input arguments to a calc expression (A-L) */ -#define CALCPERFORM_NARGS 12 +/** \brief Number of input arguments to a calc expression (A-U) */ +#define CALCPERFORM_NARGS 21 /** \brief Size of the internal partial result stack */ #define CALCPERFORM_STACK 80 @@ -163,11 +163,11 @@ extern "C" { * * -# ***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 + * using the single letters A through U 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, + * links INPA through INPU 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). * @@ -310,7 +310,7 @@ LIBCOM_API long * * Evaluates the postfix expression against a set ot input values. * - * \param parg Pointer to an array of double values for the arguments A-L + * \param parg Pointer to an array of double values for the arguments A-U * that can appear in the expression. Note that the argument values may be * modified if the expression uses the assignment operator. * \param presult Where to put the calculated result, which may be a NaN or Infinity. @@ -331,8 +331,8 @@ LIBCOM_API long * for either of these pointers is legal if only the other is needed. * * The least significant bit (bit 0) of the bitmap at \c *pinputs will be set - * if the expression depends on the argument A, and so on through bit 11 for - * the argument L. An argument that is not used until after a value has been + * if the expression depends on the argument A, and so on through bit 20 for + * the argument U. An argument that is not used until after a value has been * assigned to it will not be set in the pinputs bitmap, thus the bits can * be used to determine whether a value needs to be supplied for their * associated argument or not for the purposes of evaluating the expression. diff --git a/modules/libcom/src/calc/postfixPvt.h b/modules/libcom/src/calc/postfixPvt.h index 3e84e1fb4..23eb35b46 100644 --- a/modules/libcom/src/calc/postfixPvt.h +++ b/modules/libcom/src/calc/postfixPvt.h @@ -13,7 +13,7 @@ */ /* Notes: - * 1. The FETCH_A through FETCH_L and STORE_A through STORE_L opcodes must + * 1. The FETCH_A through FETCH_U and STORE_A through STORE_U opcodes must * be contiguous. * 2. The LITERAL opcodes are followed by a binary representation of their * values, but these are not aligned properly. @@ -34,9 +34,13 @@ typedef enum { LITERAL_DOUBLE, LITERAL_INT, FETCH_VAL, FETCH_A, FETCH_B, FETCH_C, FETCH_D, FETCH_E, FETCH_F, FETCH_G, FETCH_H, FETCH_I, FETCH_J, FETCH_K, FETCH_L, + FETCH_M, FETCH_N, FETCH_O, FETCH_P, FETCH_Q, FETCH_R, + FETCH_S, FETCH_T, FETCH_U, /* Assignment */ STORE_A, STORE_B, STORE_C, STORE_D, STORE_E, STORE_F, STORE_G, STORE_H, STORE_I, STORE_J, STORE_K, STORE_L, + STORE_M, STORE_N, STORE_O, STORE_P, STORE_Q, STORE_R, + STORE_S, STORE_T, STORE_U, /* Trigonometry Constants */ CONST_PI, CONST_D2R, diff --git a/modules/libcom/test/epicsCalcTest.cpp b/modules/libcom/test/epicsCalcTest.cpp index 4f0bfbecb..feae60570 100644 --- a/modules/libcom/test/epicsCalcTest.cpp +++ b/modules/libcom/test/epicsCalcTest.cpp @@ -22,7 +22,8 @@ double doCalc(const char *expr) { /* Evaluate expression, return result */ double args[CALCPERFORM_NARGS] = { - 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, + 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0 }; char *rpn = (char*)malloc(INFIX_TO_POSTFIX_SIZE(strlen(expr)+1)); short err; @@ -48,7 +49,8 @@ void testCalc(const char *expr, double expected) { /* Evaluate expression, test against expected result */ bool pass = false; double args[CALCPERFORM_NARGS] = { - 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, + 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0 }; char *rpn = (char*)malloc(INFIX_TO_POSTFIX_SIZE(strlen(expr)+1)); short err; @@ -85,7 +87,8 @@ void testUInt32Calc(const char *expr, epicsUInt32 expected) { /* Evaluate expression, test against expected result */ bool pass = false; double args[CALCPERFORM_NARGS] = { - 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, + 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0 }; char *rpn = (char*)malloc(INFIX_TO_POSTFIX_SIZE(strlen(expr)+1)); short err; @@ -162,18 +165,27 @@ void testBadExpr(const char *expr, short expected_err) { #define testExpr(expr) testCalc(#expr, expr); /* These are the argument bits for testArgs */ -#define A_A 0x001 -#define A_B 0x002 -#define A_C 0x004 -#define A_D 0x008 -#define A_E 0x010 -#define A_F 0x020 -#define A_G 0x040 -#define A_H 0x080 -#define A_I 0x100 -#define A_J 0x200 -#define A_K 0x400 -#define A_L 0x800 +#define A_A 0x000001 +#define A_B 0x000002 +#define A_C 0x000004 +#define A_D 0x000008 +#define A_E 0x000010 +#define A_F 0x000020 +#define A_G 0x000040 +#define A_H 0x000080 +#define A_I 0x000100 +#define A_J 0x000200 +#define A_K 0x000400 +#define A_L 0x000800 +#define A_M 0x001000 +#define A_N 0x002000 +#define A_O 0x004000 +#define A_P 0x008000 +#define A_Q 0x010000 +#define A_R 0x020000 +#define A_S 0x040000 +#define A_T 0x080000 +#define A_U 0x100000 /* Macros and functions to make some expressions into valid C code */ @@ -296,9 +308,11 @@ MAIN(epicsCalcTest) { int repeat; const double a=1.0, b=2.0, c=3.0, d=4.0, e=5.0, f=6.0, - g=7.0, h=8.0, i=9.0, j=10.0, k=11.0, l=12.0; + g=7.0, h=8.0, i=9.0, j=10.0, k=11.0, l=12.0, + m=13.0, n=14.0, o=15.0, p=16.0, q=17.0, r=18.0, + s=19.0, t=20.0, u=21.0; - testPlan(637); + testPlan(687); /* LITERAL_OPERAND elements */ testExpr(0); @@ -335,6 +349,15 @@ MAIN(epicsCalcTest) testExpr(j); testExpr(k); testExpr(l); + testExpr(m); + testExpr(n); + testExpr(o); + testExpr(p); + testExpr(q); + testExpr(r); + testExpr(s); + testExpr(t); + testExpr(u); testExpr(PI); testExpr(D2R); testExpr(R2D); @@ -832,6 +855,15 @@ MAIN(epicsCalcTest) testCalc("j := 0; j", 0); testCalc("k := 0; k", 0); testCalc("l := 0; l", 0); + testCalc("m := 0; m", 0); + testCalc("n := 0; n", 0); + testCalc("o := 0; o", 0); + testCalc("p := 0; p", 0); + testCalc("q := 0; q", 0); + testCalc("r := 0; r", 0); + testCalc("s := 0; s", 0); + testCalc("t := 0; t", 0); + testCalc("u := 0; u", 0); testCalc("a; a := 0", a); testCalc("b; b := 0", b); @@ -845,6 +877,15 @@ MAIN(epicsCalcTest) testCalc("j; j := 0", j); testCalc("k; k := 0", k); testCalc("l; l := 0", l); + testCalc("m; m := 0", m); + testCalc("n; n := 0", n); + testCalc("o; o := 0", o); + testCalc("p; p := 0", p); + testCalc("q; q := 0", q); + testCalc("r; r := 0", r); + testCalc("s; s := 0", s); + testCalc("t; t := 0", t); + testCalc("u; u := 0", u); // Check relative precedences. testExpr(0 ? 1 : 2 | 4); // 0 1 @@ -921,8 +962,17 @@ MAIN(epicsCalcTest) testArgs("J", A_J, 0); testArgs("K", A_K, 0); testArgs("L", A_L, 0); - testArgs("A+B+C+D+E+F+G+H+I+J+K+L", - A_A|A_B|A_C|A_D|A_E|A_F|A_G|A_H|A_I|A_J|A_K|A_L, 0); + testArgs("M", A_M, 0); + testArgs("N", A_N, 0); + testArgs("O", A_O, 0); + testArgs("P", A_P, 0); + testArgs("Q", A_Q, 0); + testArgs("R", A_R, 0); + testArgs("S", A_S, 0); + testArgs("T", A_T, 0); + testArgs("U", A_U, 0); + testArgs("A+B+C+D+E+F+G+H+I+J+K+L+M+N+O+P+Q+R+S+T+U", + A_A|A_B|A_C|A_D|A_E|A_F|A_G|A_H|A_I|A_J|A_K|A_L|A_M|A_N|A_O|A_P|A_Q|A_R|A_S|A_T|A_U, 0); testArgs("0.1;A:=0", 0, A_A); testArgs("1.1;B:=0", 0, A_B); testArgs("2.1;C:=0", 0, A_C); @@ -935,6 +985,15 @@ MAIN(epicsCalcTest) testArgs("9.1;J:=0", 0, A_J); testArgs("10.1;K:=0", 0, A_K); testArgs("11.1;L:=0", 0, A_L); + testArgs("12.1;M:=0", 0, A_M); + testArgs("13.1;N:=0", 0, A_N); + testArgs("14.1;O:=0", 0, A_O); + testArgs("15.1;P:=0", 0, A_P); + testArgs("16.1;Q:=0", 0, A_Q); + testArgs("17.1;R:=0", 0, A_R); + testArgs("18.1;S:=0", 0, A_S); + testArgs("19.1;T:=0", 0, A_T); + testArgs("20.1;U:=0", 0, A_U); testArgs("12.1;A:=0;B:=A;C:=B;D:=C", 0, A_A|A_B|A_C|A_D); testArgs("13.1;B:=A;A:=B;C:=D;D:=C", A_A|A_D, A_A|A_B|A_C|A_D); @@ -955,6 +1014,11 @@ MAIN(epicsCalcTest) testBadExpr(":1", CALC_ERR_SYNTAX); testBadExpr("0,", CALC_ERR_BAD_SEPERATOR); testBadExpr("0)", CALC_ERR_PAREN_NOT_OPEN); + testBadExpr("V", CALC_ERR_SYNTAX); + testBadExpr("W", CALC_ERR_SYNTAX); + testBadExpr("X", CALC_ERR_SYNTAX); + testBadExpr("Y", CALC_ERR_SYNTAX); + testBadExpr("Z", CALC_ERR_SYNTAX); // Bit manipulations wrt bit 31 (bug lp:1514520) // using integer literals From 04a59727ec8f054efec41841e0dd5057ce4b9a8b Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 22 May 2025 17:24:21 +0200 Subject: [PATCH 2/7] make CALC/OCAL fields twice as long --- modules/database/src/std/rec/calcRecord.dbd.pod | 4 ++-- modules/database/src/std/rec/calcoutRecord.dbd.pod | 8 ++++---- modules/libcom/src/calc/postfix.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/database/src/std/rec/calcRecord.dbd.pod b/modules/database/src/std/rec/calcRecord.dbd.pod index b20cafec4..c14e535df 100644 --- a/modules/database/src/std/rec/calcRecord.dbd.pod +++ b/modules/database/src/std/rec/calcRecord.dbd.pod @@ -512,7 +512,7 @@ manner for the VAL field. promptgroup("30 - Action") special(SPC_CALC) pp(TRUE) - size(80) + size(160) initial("0") } field(INPA,DBF_INLINK) { @@ -948,7 +948,7 @@ manner for the VAL field. prompt("Reverse Polish Calc") special(SPC_NOMOD) interest(4) - extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]") + extra("char rpcl[INFIX_TO_POSTFIX_SIZE(160)]") } =head2 Record Support diff --git a/modules/database/src/std/rec/calcoutRecord.dbd.pod b/modules/database/src/std/rec/calcoutRecord.dbd.pod index cab9fd902..1c41929e4 100644 --- a/modules/database/src/std/rec/calcoutRecord.dbd.pod +++ b/modules/database/src/std/rec/calcoutRecord.dbd.pod @@ -660,7 +660,7 @@ manner for the VAL field. promptgroup("30 - Action") special(SPC_CALC) pp(TRUE) - size(80) + size(160) initial("0") } field(CLCV,DBF_LONG) { @@ -980,7 +980,7 @@ manner for the VAL field. promptgroup("30 - Action") special(SPC_CALC) pp(TRUE) - size(80) + size(160) initial("0") } field(OCLV,DBF_LONG) { @@ -1329,13 +1329,13 @@ manner for the VAL field. prompt("Reverse Polish Calc") special(SPC_NOMOD) interest(4) - extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]") + extra("char rpcl[INFIX_TO_POSTFIX_SIZE(160)]") } field(ORPC,DBF_NOACCESS) { prompt("Reverse Polish OCalc") special(SPC_NOMOD) interest(4) - extra("char orpc[INFIX_TO_POSTFIX_SIZE(80)]") + extra("char orpc[INFIX_TO_POSTFIX_SIZE(160)]") } =head2 Record Support diff --git a/modules/libcom/src/calc/postfix.h b/modules/libcom/src/calc/postfix.h index 248a46a41..90d34e1cf 100644 --- a/modules/libcom/src/calc/postfix.h +++ b/modules/libcom/src/calc/postfix.h @@ -63,7 +63,7 @@ * * This is not a hard limit, just the default size for the database */ -#define MAX_INFIX_SIZE 100 +#define MAX_INFIX_SIZE 160 /** * \brief Size of a "standard" postfix buffer. * From 6196554b17f83c45a0168a62424ca3a21391e3fa Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 22 May 2025 18:04:22 +0200 Subject: [PATCH 3/7] extend sub record to inputs A-U --- modules/database/src/std/rec/subRecord.c | 6 +- .../database/src/std/rec/subRecord.dbd.pod | 144 ++++++++++++++++-- 2 files changed, 138 insertions(+), 12 deletions(-) diff --git a/modules/database/src/std/rec/subRecord.c b/modules/database/src/std/rec/subRecord.c index be274d18f..401c6a8eb 100644 --- a/modules/database/src/std/rec/subRecord.c +++ b/modules/database/src/std/rec/subRecord.c @@ -86,7 +86,7 @@ static long do_sub(subRecord *); static long fetch_values(subRecord *); static void monitor(subRecord *); -#define INP_ARG_MAX 12 +#define INP_ARG_MAX 21 static long init_record(struct dbCommon *pcommon, int pass) { @@ -196,9 +196,9 @@ static long special(DBADDR *paddr, int after) #define indexof(field) subRecord##field static long get_linkNumber(int fieldIndex) { - if (fieldIndex >= indexof(A) && fieldIndex <= indexof(L)) + if (fieldIndex >= indexof(A) && fieldIndex < indexof(A) + INP_ARG_MAX) return fieldIndex - indexof(A); - if (fieldIndex >= indexof(LA) && fieldIndex <= indexof(LL)) + if (fieldIndex >= indexof(LA) && fieldIndex < indexof(LA) + INP_ARG_MAX) return fieldIndex - indexof(LA); return -1; } diff --git a/modules/database/src/std/rec/subRecord.dbd.pod b/modules/database/src/std/rec/subRecord.dbd.pod index 6e6edd4bb..6ba8a7060 100644 --- a/modules/database/src/std/rec/subRecord.dbd.pod +++ b/modules/database/src/std/rec/subRecord.dbd.pod @@ -30,17 +30,17 @@ These fields are described in L. =head3 Read Parameters -The subroutine record has twelve input links (INPA-INPL), each of which has a -corresponding value field (A-L). These fields are used to retrieve and store +The subroutine record has 21 input links (INPA-INPU), each of which has a +corresponding value field (A-U). These fields are used to retrieve and store values that can be passed to the subroutine that the record calls. The input links can be either channel access or database links, or constants. When constants, the corresponding value field for the link is initialized with the constant value and the field's value can be changed at run-time via dbPuts. -Otherwise, the values for (A-F) are fetched from the input links when the record +Otherwise, the values for (A-U) are fetched from the input links when the record is processed. -=fields INPA - INPL, A - L +=fields INPA - INPU, A - U =head3 Subroutine Connection @@ -115,11 +115,11 @@ processing routines or the monitors. VAL should be set by the subroutine. SADR holds the subroutine address and is set by the record processing routine. -The rest of these fields--LALM, ALST, MLST, and the LA-LL fields--are used to +The rest of these fields--LALM, ALST, MLST, and the LA-LU fields--are used to implement the monitors. For example, when LA is not equal to A, the value-change monitors are called for that field. -=fields VAL, SADR, LALM, ALST, MLST, LA - LL +=fields VAL, SADR, LALM, ALST, MLST, LA - LU =head2 Record Support @@ -161,7 +161,7 @@ recGblGetPrec() >>>. long (*get_graphic_double)(struct dbAddr *paddr, struct dbr_grDouble *p) Sets the upper display and lower display limits for a field. If the field is -VAL, A-L, LA-LL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, +VAL, A-U, LA-LU, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower maximum values for the field type will be used. @@ -170,7 +170,7 @@ upper and lower maximum values for the field type will be used. long (*get_control_double)(struct dbAddr *paddr, struct dbr_ctrlDouble *p) Sets the upper control and the lower control limits for a field. If the field is -VAL, A-L, LA-LL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, +VAL, A-U, LA-LU, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower maximum values for the field type will be used. @@ -241,7 +241,7 @@ met. =item * -Monitors for A-L are invoked if value has changed. +Monitors for A-U are invoked if value has changed. =item * @@ -500,6 +500,51 @@ processing. promptgroup("42 - Input G-L") interest(1) } + field(INPM,DBF_INLINK) { + prompt("Input M") + promptgroup("43 - Input M-R") + interest(1) + } + field(INPN,DBF_INLINK) { + prompt("Input N") + promptgroup("43 - Input M-R") + interest(1) + } + field(INPO,DBF_INLINK) { + prompt("Input O") + promptgroup("43 - Input M-R") + interest(1) + } + field(INPP,DBF_INLINK) { + prompt("Input P") + promptgroup("43 - Input M-R") + interest(1) + } + field(INPQ,DBF_INLINK) { + prompt("Input Q") + promptgroup("43 - Input M-R") + interest(1) + } + field(INPR,DBF_INLINK) { + prompt("Input R") + promptgroup("43 - Input M-R") + interest(1) + } + field(INPS,DBF_INLINK) { + prompt("Input S") + promptgroup("44 - Input S-U") + interest(1) + } + field(INPT,DBF_INLINK) { + prompt("Input T") + promptgroup("44 - Input S-U") + interest(1) + } + field(INPU,DBF_INLINK) { + prompt("Input U") + promptgroup("44 - Input S-U") + interest(1) + } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") @@ -655,6 +700,42 @@ processing. prompt("Value of Input L") pp(TRUE) } + field(M,DBF_DOUBLE) { + prompt("Value of Input M") + pp(TRUE) + } + field(N,DBF_DOUBLE) { + prompt("Value of Input N") + pp(TRUE) + } + field(O,DBF_DOUBLE) { + prompt("Value of Input O") + pp(TRUE) + } + field(P,DBF_DOUBLE) { + prompt("Value of Input P") + pp(TRUE) + } + field(Q,DBF_DOUBLE) { + prompt("Value of Input Q") + pp(TRUE) + } + field(R,DBF_DOUBLE) { + prompt("Value of Input R") + pp(TRUE) + } + field(S,DBF_DOUBLE) { + prompt("Value of Input S") + pp(TRUE) + } + field(T,DBF_DOUBLE) { + prompt("Value of Input T") + pp(TRUE) + } + field(U,DBF_DOUBLE) { + prompt("Value of Input U") + pp(TRUE) + } field(LA,DBF_DOUBLE) { prompt("Prev Value of A") special(SPC_NOMOD) @@ -715,6 +796,51 @@ processing. special(SPC_NOMOD) interest(3) } + field(LM,DBF_DOUBLE) { + prompt("Prev Value of M") + special(SPC_NOMOD) + interest(3) + } + field(LN,DBF_DOUBLE) { + prompt("Prev Value of N") + special(SPC_NOMOD) + interest(3) + } + field(LO,DBF_DOUBLE) { + prompt("Prev Value of O") + special(SPC_NOMOD) + interest(3) + } + field(LP,DBF_DOUBLE) { + prompt("Prev Value of P") + special(SPC_NOMOD) + interest(3) + } + field(LQ,DBF_DOUBLE) { + prompt("Prev Value of Q") + special(SPC_NOMOD) + interest(3) + } + field(LR,DBF_DOUBLE) { + prompt("Prev Value of R") + special(SPC_NOMOD) + interest(3) + } + field(LS,DBF_DOUBLE) { + prompt("Prev Value of S") + special(SPC_NOMOD) + interest(3) + } + field(LT,DBF_DOUBLE) { + prompt("Prev Value of T") + special(SPC_NOMOD) + interest(3) + } + field(LU,DBF_DOUBLE) { + prompt("Prev Value of U") + special(SPC_NOMOD) + interest(3) + } field(LALM,DBF_DOUBLE) { prompt("Last Value Alarmed") special(SPC_NOMOD) From 65cc904262f5a0930cae3af121cd3d75260ed9db Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Fri, 23 May 2025 12:12:55 +0200 Subject: [PATCH 4/7] fix calc link range check --- modules/database/src/std/link/lnkCalc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database/src/std/link/lnkCalc.c b/modules/database/src/std/link/lnkCalc.c index ed5796f8a..5f2b4888e 100644 --- a/modules/database/src/std/link/lnkCalc.c +++ b/modules/database/src/std/link/lnkCalc.c @@ -177,7 +177,7 @@ static jlif_result lnkCalc_string(jlink *pjlink, const char *val, size_t len) if (clink->pstate == ps_time) { char tinp; - if (len != 1 || (tinp = toupper((int) val[0])) < 'A' || tinp > 'L') { + if (len != 1 || (tinp = toupper((int) val[0])) < 'A' || tinp >= 'A' + CALCPERFORM_NARGS) { errlogPrintf("lnkCalc: Bad 'time' parameter \"%.*s\"\n", (int) len, val); return jlif_stop; } From 525cddc43f57534cc9b20d16d58564db038d0aaf Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Fri, 23 May 2025 13:46:49 +0200 Subject: [PATCH 5/7] fix calc link documentation --- modules/database/src/std/link/links.dbd.pod | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/database/src/std/link/links.dbd.pod b/modules/database/src/std/link/links.dbd.pod index 5247f6950..9a3f7b9c7 100644 --- a/modules/database/src/std/link/links.dbd.pod +++ b/modules/database/src/std/link/links.dbd.pod @@ -81,7 +81,7 @@ 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. @@ -95,7 +95,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. @@ -127,7 +127,7 @@ 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 +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 +149,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