diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md
index 587b52448..213370363 100644
--- a/documentation/RELEASE_NOTES.md
+++ b/documentation/RELEASE_NOTES.md
@@ -16,6 +16,13 @@ should also be read to understand what has changed since earlier releases.
## Changes made on the 7.0 branch since 7.0.5
+### Extend longout conditions to write the OUT link (OOPT field)
+
+The longout record has now the capacity to condition its output write operation to
+different options, using the OOPT field (similar to calcout record). This is
+the first output record to have such feature as a result of the
+[Make output records only write on change](https://bugs.launchpad.net/epics-base/+bug/1398215)
+issue on Launchpad.
-----
diff --git a/modules/database/src/std/rec/longoutRecord.c b/modules/database/src/std/rec/longoutRecord.c
index f5bb4a292..e60e01c79 100644
--- a/modules/database/src/std/rec/longoutRecord.c
+++ b/modules/database/src/std/rec/longoutRecord.c
@@ -81,6 +81,9 @@ rset longoutRSET={
};
epicsExportAddress(rset,longoutRSET);
+#define OUT_LINK_UNCHANGED 0
+#define OUT_LINK_CHANGED 1
+
static void checkAlarms(longoutRecord *prec);
static void monitor(longoutRecord *prec);
static long writeValue(longoutRecord *prec);
@@ -120,7 +123,8 @@ static long init_record(struct dbCommon *pcommon, int pass)
prec->mlst = prec->val;
prec->alst = prec->val;
prec->lalm = prec->val;
- prec->oval = prec->val;
+ prec->pval = prec->val;
+ prec->outpvt = OUT_LINK_UNCHANGED;
return 0;
}
@@ -213,14 +217,13 @@ static long special(DBADDR *paddr, int after)
return(0);
}
- /* If OOPT is "on change" we force a write operation */
+ /* Detect an output link re-direction (change)*/
if (dbGetFieldIndex(paddr) == longoutRecordOUT) {
- if ((!after) && (prec->oopt == longoutOOPT_On_Change))
- prec->oopt = longoutOOPT_Write_Once_Then_On_Change;
- return 0;
+ if (!after)
+ prec->outpvt = OUT_LINK_CHANGED;
+ return(0);
}
-
default:
recGblDbaddrError(S_db_badChoice, paddr, "longout: special");
return(S_db_badChoice);
@@ -392,10 +395,7 @@ static void monitor(longoutRecord *prec)
static long writeValue(longoutRecord *prec)
{
-<<<<<<< HEAD
longoutdset *pdset = (longoutdset *) prec->dset;
-=======
->>>>>>> 2b7ca9598 (Added OOPT to longout record)
long status = 0;
if (!prec->pact) {
@@ -435,7 +435,7 @@ static long writeValue(longoutRecord *prec)
static void convert(longoutRecord *prec, epicsInt32 value)
{
- /* check drive limits */
+ /* check drive limits */
if(prec->drvh > prec->drvl) {
if (value > prec->drvh) value = prec->drvh;
else if (value < prec->drvl) value = prec->drvl;
@@ -453,11 +453,15 @@ static long conditional_write(longoutRecord *prec)
switch (prec->oopt)
{
case longoutOOPT_On_Change:
- doDevSupWrite = (prec->val != prec->oval);
+ /* Forces a write op if a change in the OUT field is detected */
+ if ((prec->ooch == menuYesNoYES) && (prec->outpvt == OUT_LINK_CHANGED)) {
+ doDevSupWrite = 1;
+ } else {
+ /* Only write if value is different from the previous one */
+ doDevSupWrite = (prec->val != prec->pval);
+ }
break;
- case longoutOOPT_Write_Once_Then_On_Change:
- prec->oopt = longoutOOPT_On_Change;
case longoutOOPT_Every_Time:
doDevSupWrite = 1;
break;
@@ -471,11 +475,11 @@ static long conditional_write(longoutRecord *prec)
break;
case longoutOOPT_Transition_To_Zero:
- doDevSupWrite = ((prec->val == 0)&&(prec->oval != 0));
+ doDevSupWrite = ((prec->val == 0)&&(prec->pval != 0));
break;
case longoutOOPT_Transition_To_Non_zero:
- doDevSupWrite = ((prec->val != 0)&&(prec->oval == 0));
+ doDevSupWrite = ((prec->val != 0)&&(prec->pval == 0));
break;
default:
@@ -485,6 +489,7 @@ static long conditional_write(longoutRecord *prec)
if (doDevSupWrite)
status = pdset->write_longout(prec);
- prec->oval = prec->val;
+ prec->pval = prec->val;
+ prec->outpvt = OUT_LINK_UNCHANGED; /* reset status of OUT link */
return status;
}
\ No newline at end of file
diff --git a/modules/database/src/std/rec/longoutRecord.dbd.pod b/modules/database/src/std/rec/longoutRecord.dbd.pod
index 3f11769ba..fe596c118 100644
--- a/modules/database/src/std/rec/longoutRecord.dbd.pod
+++ b/modules/database/src/std/rec/longoutRecord.dbd.pod
@@ -27,10 +27,8 @@ menu(longoutOOPT) {
choice(longoutOOPT_When_Non_zero,"When Non-zero")
choice(longoutOOPT_Transition_To_Zero,"Transition To Zero")
choice(longoutOOPT_Transition_To_Non_zero,"Transition To Non-zero")
- choice(longoutOOPT_Write_Once_Then_On_Change, "Write Once Then On Change")
}
-
recordtype(longout) {
=head2 Parameter Fields
@@ -82,7 +80,45 @@ DTYP field must then specify the C<<< Soft Channel >>> device support routine.
See L
for information on the format of hardware addresses
and database links.
-=fields OUT, DTYP
+=fields OUT, DTYP, OOPT, OOCH
+
+=head4 Menu longoutOOPT
+
+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 longoutOOPT
+
+=over
+
+=item *
+C -- write output every time record is processed. (DEFAULT)
+
+=item *
+C -- write output every time VAL changes, i.e., every time the
+result of the expression changes.
+
+=item *
+C -- when record is processed, write output if VAL is zero.
+
+=item *
+C -- when record is processed, write output if VAL is
+non-zero.
+
+=item *
+C -- when record is processed, write output only if VAL
+is zero and the last value was non-zero.
+
+=item *
+C -- when record is processed, write output only if
+VAL is non-zero and last value was zero.
+
+=back
+
+=head4 Changes in OUT field when OOPT = On Change
+
+The OOCH field determines if a change in OUT field should cause a write operation
+even when the value is the same and OOPT = On Change. By default, OOCH is set to YES.
=cut
@@ -372,7 +408,7 @@ for more information on simulation mode and its fields.
prompt("Sim. Mode Private")
special(SPC_NOMOD)
interest(4)
- extra("epicsCallback *simpvt")
+ extra("epicsCallback *simpvt")
}
field(IVOA,DBF_MENU) {
prompt("INVALID output action")
@@ -385,11 +421,21 @@ for more information on simulation mode and its fields.
promptgroup("50 - Output")
interest(2)
}
- field(OVAL,DBF_LONG) {
- prompt("Last Value Written")
- promptgroup("50 - Output")
- asl(ASL1)
+ field(PVAL,DBF_LONG) {
+ prompt("Previous Value")
+ }
+ field(OUTPVT,DBF_NOACCESS) {
+ prompt("Output Link Changed Private")
special(SPC_NOMOD)
+ interest(4)
+ extra("epicsEnum16 outpvt")
+ }
+ field(OOCH,DBF_MENU) {
+ prompt("Output Execute On Change")
+ promptgroup("50 - Output")
+ interest(1)
+ menu(menuYesNo)
+ initial("1")
}
field(OOPT,DBF_MENU) {
prompt("Output Execute Opt")
diff --git a/modules/database/test/std/rec/longoutTest.c b/modules/database/test/std/rec/longoutTest.c
index 60da091b2..c69a6b386 100644
--- a/modules/database/test/std/rec/longoutTest.c
+++ b/modules/database/test/std/rec/longoutTest.c
@@ -10,14 +10,15 @@
#include "errlog.h"
#include "dbAccess.h"
#include "epicsMath.h"
+#include "menuYesNo.h"
#include "longoutRecord.h"
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
static void test_oopt_everytime(void){
- /* reset rec processing counter */
- testdbPutFieldOk("counter.VAL", DBF_DOUBLE, 0.0);
+ /* reset rec processing counter_a */
+ testdbPutFieldOk("counter_a.VAL", DBF_DOUBLE, 0.0);
/* write the same value two times */
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 16);
@@ -27,8 +28,8 @@ static void test_oopt_everytime(void){
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 17);
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 18);
- /* Test if the counter was processed 4 times */
- testdbGetFieldEqual("counter", DBF_DOUBLE, 4.0);
+ /* Test if the counter_a was processed 4 times */
+ testdbGetFieldEqual("counter_a", DBF_DOUBLE, 4.0);
// number of tests = 6
}
@@ -37,22 +38,22 @@ static void test_oopt_onchange(void){
/* change OOPT to On Change */
testdbPutFieldOk("longout_rec.OOPT", DBF_ENUM, longoutOOPT_On_Change);
- /* reset rec processing counter */
- testdbPutFieldOk("counter.VAL", DBF_DOUBLE, 0.0);
+ /* reset rec processing counter_a */
+ testdbPutFieldOk("counter_a.VAL", DBF_DOUBLE, 0.0);
/* write the same value two times */
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 16);
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 16);
- /* Test if the counter was processed only once */
- testdbGetFieldEqual("counter", DBF_DOUBLE, 1.0);
+ /* Test if the counter_a was processed only once */
+ testdbGetFieldEqual("counter_a", DBF_DOUBLE, 1.0);
/* write two times with different values*/
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 17);
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 18);
- /* Test if the counter was processed 1 + 2 times */
- testdbGetFieldEqual("counter", DBF_DOUBLE, 3.0);
+ /* Test if the counter_a was processed 1 + 2 times */
+ testdbGetFieldEqual("counter_a", DBF_DOUBLE, 3.0);
//number of tests 8
}
@@ -60,22 +61,22 @@ static void test_oopt_onchange(void){
static void test_oopt_whenzero(void){
testdbPutFieldOk("longout_rec.OOPT", DBF_ENUM, longoutOOPT_When_Zero);
- /* reset rec processing counter */
- testdbPutFieldOk("counter.VAL", DBF_DOUBLE, 0.0);
+ /* reset rec processing counter_a */
+ testdbPutFieldOk("counter_a.VAL", DBF_DOUBLE, 0.0);
/* write zero two times */
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 0);
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 0);
- /* Test if the counter was processed twice */
- testdbGetFieldEqual("counter", DBF_DOUBLE, 2.0);
+ /* Test if the counter_a was processed twice */
+ testdbGetFieldEqual("counter_a", DBF_DOUBLE, 2.0);
/* write two times with non-zero values*/
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 17);
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 18);
- /* Test if the counter was still processed 2 times */
- testdbGetFieldEqual("counter", DBF_DOUBLE, 2.0);
+ /* Test if the counter_a was still processed 2 times */
+ testdbGetFieldEqual("counter_a", DBF_DOUBLE, 2.0);
//number of tests 8
}
@@ -83,22 +84,22 @@ static void test_oopt_whenzero(void){
static void test_oopt_whennonzero(void){
testdbPutFieldOk("longout_rec.OOPT", DBF_ENUM, longoutOOPT_When_Non_zero);
- /* reset rec processing counter */
- testdbPutFieldOk("counter.VAL", DBF_DOUBLE, 0.0);
+ /* reset rec processing counter_a */
+ testdbPutFieldOk("counter_a.VAL", DBF_DOUBLE, 0.0);
/* write zero two times */
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 0);
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 0);
- /* Test if the counter was never processed */
- testdbGetFieldEqual("counter", DBF_DOUBLE, 0.0);
+ /* Test if the counter_a was never processed */
+ testdbGetFieldEqual("counter_a", DBF_DOUBLE, 0.0);
/* write two times with non-zero values*/
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 17);
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 18);
- /* Test if the counter was still processed 2 times */
- testdbGetFieldEqual("counter", DBF_DOUBLE, 2.0);
+ /* Test if the counter_a was still processed 2 times */
+ testdbGetFieldEqual("counter_a", DBF_DOUBLE, 2.0);
//number of tests 8
}
@@ -106,23 +107,23 @@ static void test_oopt_whennonzero(void){
static void test_oopt_when_transition_zero(void){
testdbPutFieldOk("longout_rec.OOPT", DBF_ENUM, longoutOOPT_Transition_To_Zero);
- /* reset rec processing counter */
- testdbPutFieldOk("counter.VAL", DBF_DOUBLE, 0.0);
+ /* reset rec processing counter_a */
+ testdbPutFieldOk("counter_a.VAL", DBF_DOUBLE, 0.0);
/* write non-zero then zero */
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 16);
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 0);
- /* Test if the counter was processed */
- testdbGetFieldEqual("counter", DBF_DOUBLE, 1.0);
+ /* Test if the counter_a was processed */
+ testdbGetFieldEqual("counter_a", DBF_DOUBLE, 1.0);
/* write another transition to zero */
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 17);
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 0);
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 17);
- /* Test if the counter was processed once more */
- testdbGetFieldEqual("counter", DBF_DOUBLE, 2.0);
+ /* Test if the counter_a was processed once more */
+ testdbGetFieldEqual("counter_a", DBF_DOUBLE, 2.0);
//number of tests 9
}
@@ -133,21 +134,21 @@ static void test_oopt_when_transition_nonzero(void){
/* write non-zero to start fresh */
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 16);
- /* reset rec processing counter */
- testdbPutFieldOk("counter.VAL", DBF_DOUBLE, 0.0);
+ /* reset rec processing counter_a */
+ testdbPutFieldOk("counter_a.VAL", DBF_DOUBLE, 0.0);
/* write non-zero then zero */
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 17);
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 0);
- /* Test if the counter was never processed */
- testdbGetFieldEqual("counter", DBF_DOUBLE, 0.0);
+ /* Test if the counter_a was never processed */
+ testdbGetFieldEqual("counter_a", DBF_DOUBLE, 0.0);
/* write a transition to non-zero */
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 18);
- /* Test if the counter was processed */
- testdbGetFieldEqual("counter", DBF_DOUBLE, 1.0);
+ /* Test if the counter_a was processed */
+ testdbGetFieldEqual("counter_a", DBF_DOUBLE, 1.0);
//number of tests 8
}
@@ -159,35 +160,74 @@ static void test_changing_out_field(void){
/* write an initial value */
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 16);
- /* reset rec processing counter */
- testdbPutFieldOk("counter.VAL", DBF_DOUBLE, 0.0);
- testdbPutFieldOk("counter2.VAL", DBF_DOUBLE, 0.0);
+ /* reset rec processing counters */
+ testdbPutFieldOk("counter_a.VAL", DBF_DOUBLE, 0.0);
+ testdbPutFieldOk("counter_b.VAL", DBF_DOUBLE, 0.0);
/* write the same value */
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 16);
/* Test if the counter was never processed */
- testdbGetFieldEqual("counter", DBF_DOUBLE, 0.0);
+ testdbGetFieldEqual("counter_a", DBF_DOUBLE, 0.0);
/* change the OUT link to another counter */
- testdbPutFieldOk("longout_rec.OUT", DBF_STRING, "counter2.B PP");
+ testdbPutFieldOk("longout_rec.OUT", DBF_STRING, "counter_b.B PP");
/* write the same value */
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 16);
/* Test if the counter was processed once */
- testdbGetFieldEqual("counter2", DBF_DOUBLE, 1.0);
+ testdbGetFieldEqual("counter_b", DBF_DOUBLE, 1.0);
/* write the same value */
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 16);
/* Test if the counter was not processed again */
- testdbGetFieldEqual("counter2", DBF_DOUBLE, 1.0);
+ testdbGetFieldEqual("counter_b", DBF_DOUBLE, 1.0);
+
+ /* Set option to write ON CHANGE even when the OUT link was changed */
+ testdbPutFieldOk("longout_rec.OOCH", DBF_ENUM, menuYesNoNO);
+
+ /* write an initial value */
+ testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 16);
+
+ /* reset rec processing counters */
+ testdbPutFieldOk("counter_a.VAL", DBF_DOUBLE, 0.0);
+ testdbPutFieldOk("counter_b.VAL", DBF_DOUBLE, 0.0);
+
+ /* write the same value */
+ testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 16);
+
+ /* Test if the counter_b was never processed */
+ testdbGetFieldEqual("counter_b", DBF_DOUBLE, 0.0);
+
+ /* change back the OUT link to counter_a */
+ testdbPutFieldOk("longout_rec.OUT", DBF_STRING, "counter_a.B PP");
+
+ /* write the same value */
+ testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 16);
+
+ /* Test if the counter was never processed */
+ testdbGetFieldEqual("counter_a", DBF_DOUBLE, 0.0);
+
+ /* write the same value */
+ testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 16);
+
+ /* Test if the counter was not processed again */
+ testdbGetFieldEqual("counter_a", DBF_DOUBLE, 0.0);
+
+ /* write new value */
+ testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 17);
+
+ /* Test if the counter was processed once */
+ testdbGetFieldEqual("counter_a", DBF_DOUBLE, 1.0);
+
+ //number of tests 24
}
MAIN(longoutTest) {
- testPlan(6+8+8+8+9+8+11);
+ testPlan(6+8+8+8+9+8+24);
testdbPrepare();
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
diff --git a/modules/database/test/std/rec/longoutTest.db b/modules/database/test/std/rec/longoutTest.db
index 93428a070..b988792cd 100644
--- a/modules/database/test/std/rec/longoutTest.db
+++ b/modules/database/test/std/rec/longoutTest.db
@@ -1,17 +1,17 @@
-record(calc, "counter") {
- field(INPA, "counter")
+record(calc, "counter_a") {
+ field(INPA, "counter_a")
field(CALC, "A+1")
field(SCAN, "Passive")
}
-record(calc, "counter2") {
- field(INPA, "counter2")
+record(calc, "counter_b") {
+ field(INPA, "counter_b")
field(CALC, "A+1")
field(SCAN, "Passive")
}
record(longout, "longout_rec") {
field(VAL, "0")
- field(OUT, "counter.B PP")
+ field(OUT, "counter_a.B PP")
field(PINI, "YES")
}