Merge PR #63, longout.OOPT
This commit is contained in:
@ -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.7
|
||||
|
||||
<!-- Insert new items immediately below here ... -->
|
||||
### 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.
|
||||
|
||||
### Tab completion for IOC shell
|
||||
|
||||
|
@ -81,10 +81,14 @@ rset longoutRSET={
|
||||
};
|
||||
epicsExportAddress(rset,longoutRSET);
|
||||
|
||||
#define DONT_EXEC_OUTPUT 0
|
||||
#define EXEC_OUTPUT 1
|
||||
|
||||
static void checkAlarms(longoutRecord *prec);
|
||||
static void monitor(longoutRecord *prec);
|
||||
static long writeValue(longoutRecord *prec);
|
||||
static void convert(longoutRecord *prec, epicsInt32 value);
|
||||
static long conditional_write(longoutRecord *prec);
|
||||
|
||||
static long init_record(struct dbCommon *pcommon, int pass)
|
||||
{
|
||||
@ -119,6 +123,9 @@ static long init_record(struct dbCommon *pcommon, int pass)
|
||||
prec->mlst = prec->val;
|
||||
prec->alst = prec->val;
|
||||
prec->lalm = prec->val;
|
||||
prec->pval = prec->val;
|
||||
prec->outpvt = EXEC_OUTPUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -210,6 +217,14 @@ static long special(DBADDR *paddr, int after)
|
||||
recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* Detect an output link re-direction (change) */
|
||||
if (dbGetFieldIndex(paddr) == longoutRecordOUT) {
|
||||
if ((after) && (prec->ooch == menuYesNoYES))
|
||||
prec->outpvt = EXEC_OUTPUT;
|
||||
return(0);
|
||||
}
|
||||
|
||||
default:
|
||||
recGblDbaddrError(S_db_badChoice, paddr, "longout: special");
|
||||
return(S_db_badChoice);
|
||||
@ -391,7 +406,7 @@ static long writeValue(longoutRecord *prec)
|
||||
|
||||
switch (prec->simm) {
|
||||
case menuYesNoNO:
|
||||
status = pdset->write_longout(prec);
|
||||
status = conditional_write(prec);
|
||||
break;
|
||||
|
||||
case menuYesNoYES: {
|
||||
@ -421,10 +436,61 @@ 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;
|
||||
}
|
||||
prec->val = value;
|
||||
}
|
||||
|
||||
/* Evaluate OOPT field to perform the write operation */
|
||||
static long conditional_write(longoutRecord *prec)
|
||||
{
|
||||
struct longoutdset *pdset = (struct longoutdset *) prec->dset;
|
||||
long status = 0;
|
||||
int doDevSupWrite = 0;
|
||||
|
||||
switch (prec->oopt)
|
||||
{
|
||||
case longoutOOPT_On_Change:
|
||||
/* Forces a write op if a change in the OUT field is detected OR is first process */
|
||||
if (prec->outpvt == EXEC_OUTPUT) {
|
||||
doDevSupWrite = 1;
|
||||
} else {
|
||||
/* Only write if value is different from the previous one */
|
||||
doDevSupWrite = (prec->val != prec->pval);
|
||||
}
|
||||
break;
|
||||
|
||||
case longoutOOPT_Every_Time:
|
||||
doDevSupWrite = 1;
|
||||
break;
|
||||
|
||||
case longoutOOPT_When_Zero:
|
||||
doDevSupWrite = (prec->val == 0);
|
||||
break;
|
||||
|
||||
case longoutOOPT_When_Non_zero:
|
||||
doDevSupWrite = (prec->val != 0);
|
||||
break;
|
||||
|
||||
case longoutOOPT_Transition_To_Zero:
|
||||
doDevSupWrite = ((prec->val == 0)&&(prec->pval != 0));
|
||||
break;
|
||||
|
||||
case longoutOOPT_Transition_To_Non_zero:
|
||||
doDevSupWrite = ((prec->val != 0)&&(prec->pval == 0));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (doDevSupWrite)
|
||||
status = pdset->write_longout(prec);
|
||||
|
||||
prec->pval = prec->val;
|
||||
prec->outpvt = DONT_EXEC_OUTPUT; /* reset status */
|
||||
return status;
|
||||
}
|
||||
|
@ -20,6 +20,15 @@ limits.
|
||||
|
||||
=cut
|
||||
|
||||
menu(longoutOOPT) {
|
||||
choice(longoutOOPT_Every_Time,"Every Time")
|
||||
choice(longoutOOPT_On_Change,"On Change")
|
||||
choice(longoutOOPT_When_Zero,"When Zero")
|
||||
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")
|
||||
}
|
||||
|
||||
recordtype(longout) {
|
||||
|
||||
=head2 Parameter Fields
|
||||
@ -72,7 +81,46 @@ See L<Address
|
||||
Specification|https://docs.epics-controls.org/en/latest/guides/EPICS_Process_Database_Concepts.html#address-specification>
|
||||
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<Every Time> -- write output every time record is processed. (DEFAULT)
|
||||
|
||||
=item *
|
||||
C<On Change> -- write output every time VAL 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 Changes in OUT field when OOPT = On Change
|
||||
|
||||
If OOCH is C<YES> (its default value) and the OOPT field is C<On Change>,
|
||||
the record will write to the device support the first time the record gets
|
||||
processed after its OUT link is modified, even when the output value has
|
||||
not actually changed.
|
||||
|
||||
=cut
|
||||
|
||||
@ -94,6 +142,7 @@ for information on the format of hardware addresses and database links.
|
||||
}
|
||||
field(OUT,DBF_OUTLINK) {
|
||||
prompt("Output Specification")
|
||||
special(SPC_MOD)
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
@ -365,7 +414,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")
|
||||
@ -378,6 +427,29 @@ for more information on simulation mode and its fields.
|
||||
promptgroup("50 - Output")
|
||||
interest(2)
|
||||
}
|
||||
field(PVAL,DBF_LONG) {
|
||||
prompt("Previous Value")
|
||||
}
|
||||
field(OUTPVT,DBF_NOACCESS) {
|
||||
prompt("Output Write Control Private")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("epicsEnum16 outpvt")
|
||||
}
|
||||
field(OOCH,DBF_MENU) {
|
||||
prompt("Output Exec. On Change (Opt)")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
menu(menuYesNo)
|
||||
initial("1")
|
||||
}
|
||||
field(OOPT,DBF_MENU) {
|
||||
prompt("Output Execute Opt")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
menu(longoutOOPT)
|
||||
initial("0")
|
||||
}
|
||||
|
||||
|
||||
=begin html
|
||||
|
@ -86,6 +86,13 @@ testHarness_SRCS += seqTest.c
|
||||
TESTFILES += ../seqTest.db
|
||||
TESTS += seqTest
|
||||
|
||||
TESTPROD_HOST += longoutTest
|
||||
longoutTest_SRCS += longoutTest.c
|
||||
longoutTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += longoutTest.c
|
||||
TESTFILES += ../longoutTest.db
|
||||
TESTS += longoutTest
|
||||
|
||||
TARGETS += $(COMMON_DIR)/asTestIoc.dbd
|
||||
DBDDEPENDS_FILES += asTestIoc.dbd$(DEP)
|
||||
asTestIoc_DBD += base.dbd
|
||||
|
270
modules/database/test/std/rec/longoutTest.c
Normal file
270
modules/database/test/std/rec/longoutTest.c
Normal file
@ -0,0 +1,270 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2020 Joao Paulo Martins
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
#include "dbLock.h"
|
||||
#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_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);
|
||||
|
||||
/* write two times with different values*/
|
||||
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 17);
|
||||
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 18);
|
||||
|
||||
/* Test if the counter_a was processed 4 times */
|
||||
testdbGetFieldEqual("counter_a", DBF_DOUBLE, 4.0);
|
||||
|
||||
// number of tests = 6
|
||||
}
|
||||
|
||||
static void test_oopt_onchange(void){
|
||||
/* change OOPT to On Change */
|
||||
testdbPutFieldOk("longout_rec.OOPT", DBF_ENUM, longoutOOPT_On_Change);
|
||||
|
||||
/* 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_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_a was processed 1 + 2 times */
|
||||
testdbGetFieldEqual("counter_a", DBF_DOUBLE, 3.0);
|
||||
|
||||
//number of tests 8
|
||||
}
|
||||
|
||||
static void test_oopt_whenzero(void){
|
||||
testdbPutFieldOk("longout_rec.OOPT", DBF_ENUM, longoutOOPT_When_Zero);
|
||||
|
||||
/* 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_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_a was still processed 2 times */
|
||||
testdbGetFieldEqual("counter_a", DBF_DOUBLE, 2.0);
|
||||
|
||||
//number of tests 8
|
||||
}
|
||||
|
||||
static void test_oopt_whennonzero(void){
|
||||
testdbPutFieldOk("longout_rec.OOPT", DBF_ENUM, longoutOOPT_When_Non_zero);
|
||||
|
||||
/* 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_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_a was still processed 2 times */
|
||||
testdbGetFieldEqual("counter_a", DBF_DOUBLE, 2.0);
|
||||
|
||||
//number of tests 8
|
||||
}
|
||||
|
||||
static void test_oopt_when_transition_zero(void){
|
||||
testdbPutFieldOk("longout_rec.OOPT", DBF_ENUM, longoutOOPT_Transition_To_Zero);
|
||||
|
||||
/* 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_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_a was processed once more */
|
||||
testdbGetFieldEqual("counter_a", DBF_DOUBLE, 2.0);
|
||||
|
||||
//number of tests 9
|
||||
}
|
||||
|
||||
static void test_oopt_when_transition_nonzero(void){
|
||||
testdbPutFieldOk("longout_rec.OOPT", DBF_ENUM, longoutOOPT_Transition_To_Non_zero);
|
||||
|
||||
/* write non-zero to start fresh */
|
||||
testdbPutFieldOk("longout_rec.VAL", DBF_LONG, 16);
|
||||
|
||||
/* 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_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_a was processed */
|
||||
testdbGetFieldEqual("counter_a", DBF_DOUBLE, 1.0);
|
||||
|
||||
//number of tests 8
|
||||
}
|
||||
|
||||
static void test_changing_out_field(void){
|
||||
/* change OOPT to On Change */
|
||||
testdbPutFieldOk("longout_rec.OOPT", DBF_ENUM, longoutOOPT_On_Change);
|
||||
|
||||
/* 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 was never processed */
|
||||
testdbGetFieldEqual("counter_a", DBF_DOUBLE, 0.0);
|
||||
|
||||
/* change the OUT link to another counter */
|
||||
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("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("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);
|
||||
|
||||
/* reset rec processing counters */
|
||||
testdbPutFieldOk("counter_a.VAL", DBF_DOUBLE, 0.0);
|
||||
|
||||
/* test if record with OOPT == On Change will
|
||||
write to output at its first process */
|
||||
testdbPutFieldOk("longout_rec2.VAL", DBF_LONG, 16);
|
||||
|
||||
/* Test if the counter was processed once */
|
||||
testdbGetFieldEqual("counter_a", DBF_DOUBLE, 1.0);
|
||||
|
||||
/* write the same value */
|
||||
testdbPutFieldOk("longout_rec2.VAL", DBF_LONG, 16);
|
||||
|
||||
/* Test if the counter was not processed again */
|
||||
testdbGetFieldEqual("counter_a", DBF_DOUBLE, 1.0);
|
||||
|
||||
//number of tests 29
|
||||
}
|
||||
|
||||
MAIN(longoutTest) {
|
||||
|
||||
testPlan(6+8+8+8+9+8+29);
|
||||
|
||||
testdbPrepare();
|
||||
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("longoutTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
test_oopt_everytime();
|
||||
test_oopt_onchange();
|
||||
test_oopt_whenzero();
|
||||
test_oopt_whennonzero();
|
||||
test_oopt_when_transition_zero();
|
||||
test_oopt_when_transition_nonzero();
|
||||
test_changing_out_field();
|
||||
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
25
modules/database/test/std/rec/longoutTest.db
Normal file
25
modules/database/test/std/rec/longoutTest.db
Normal file
@ -0,0 +1,25 @@
|
||||
record(calc, "counter_a") {
|
||||
field(INPA, "counter_a")
|
||||
field(CALC, "A+1")
|
||||
field(SCAN, "Passive")
|
||||
}
|
||||
|
||||
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_a.B PP")
|
||||
field(PINI, "YES")
|
||||
}
|
||||
|
||||
record(longout, "longout_rec2") {
|
||||
field(VAL, "16")
|
||||
field(OUT, "counter_a.B PP")
|
||||
field(PINI, "NO")
|
||||
field(OOPT, "On Change")
|
||||
}
|
||||
|
Reference in New Issue
Block a user