Merge remote-tracking branch 'remotes/github/extend_calc' into PSI-7.0

This commit is contained in:
2025-07-14 10:55:06 +02:00
5 changed files with 229 additions and 32 deletions

View File

@@ -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 have been doubled to 160 chars.

View File

@@ -121,7 +121,7 @@ the expression which will operate on their respective values, as in A+B.
Also, the RNDM nullary function can be included as an operand in the
expression in order to generate a random number between 0 and 1.
=fields A - L
=fields A - U
The keyword VAL returns the current contents of the VAL field (which can be
written to by a CA put, so it might I<not> be the result from the last time

View File

@@ -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;
}

View File

@@ -30,17 +30,17 @@ These fields are described in L<Scan Fields|dbCommonRecord/Scan Fields>.
=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)

View File

@@ -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-2);
testPlan(687-2);
/* 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