Hi,
One advantage of using FLNK to point to the associated read record for a parameter is that you can use channel access put callback like:
caput -c {set record} {some value}
caget {read record)
So you can do the set then immediately read the value back from the read record to check it worked ok. If you only update the read records periodically using a SCAN thread, then that doesn’t work. This can be useful if the device is controlled
via a script rather than manually via an OPI.
Another method that may be useful is the use of sequence records to periodically process the read records. This gives you the option of adding in delays between commands at the database level, and chaining together seq records so that you
can guarantee processing order (helpful for slow devices, or lots of records that would take too long to process periodically in a SCAN thread).
Cheers,
Matt
From: Tech-talk <tech-talk-bounces at aps.anl.gov>
On Behalf Of Wang, Andrew via Tech-talk
Sent: Wednesday, May 5, 2021 12:38 PM
To: EPICS tech-talk <tech-talk at aps.anl.gov>
Subject: [EXTERNAL] Recommendations for StreamDevice "architecture" flow
All,
I would like to get an opinion from you all about whether these “architectures” are appropriate for StreamDevice. I’ve been looking at some posts on Tech-Talk about the general use for StreamDevice, specifically
when there are multiple commands. Suppose I need to implement EPICS drivers for an instrument that has two commands: cmd1, cmd2, and cmd3. Here are some “architectures” I’ve tried and used:
1.
“Processing chain” method
record(ao, “cmd_1_set”)
{
field(SCAN, “Passive”)
field(DTYP, “stream”)
field(OUT, “@instrument.proto setCmd1 $(PORT)”)
field(FLNK, “cmd_1_get”)
}
record(ao, “cmd_2_set”)
{
field(SCAN, “Passive”)
field(DTYP, “stream”)
field(OUT, “@instrument.proto setCmd2 $(PORT)”)
field(FLNK, “cmd_2_get”)
}
record(ao, “cmd_3_set”)
{
field(SCAN, “Passive”)
field(DTYP, “stream”)
field(OUT, “@instrument.proto setCmd3 $(PORT)”)
field(FLNK, “cmd_3_get”)
}
record(ai, “cmd_1_get”)
{
field(SCAN, “1 second”)
field(INP, “@instrument.proto getCmd1 $(PORT)”)
field(FLNK, “cmd_2_get”)
}
record(ai, “cmd_2_get”)
{
field(SCAN, “Passive”)
field(INP, “@instrument.proto getCmd2 $(PORT)”)
field(FLNK, “cmd_3_get”)
}
record(ai, “cmd_3_get”)
{
field(SCAN, “Passive”)
field(INP, “@instrument.proto getCmd3 $(PORT)”)
}
2.
Fanout method
record(fanout, “slow_scan”)
{
field(SCAN, “1 second”)
field(LNK0, “cmd_1_get”)
field(LNK1, “cmd_2_get”)
field(LNK2, “cmd_3_get”)
}
record(ao, “cmd_1_set”)
{
field(SCAN, “Passive”)
field(DTYP, “stream”)
field(OUT, “@instrument.proto setCmd1 $(PORT)”)
field(FLNK, “cmd_1_get”)
}
record(ao, “cmd_2_set”)
{
field(SCAN, “Passive”)
field(DTYP, “stream”)
field(OUT, “@instrument.proto setCmd2 $(PORT)”)
field(FLNK, “cmd_2_get”)
}
record(ao, “cmd_3_set”)
{
field(SCAN, “Passive”)
field(DTYP, “stream”)
field(OUT, “@instrument.proto setCmd3 $(PORT)”)
field(FLNK, “cmd_3_get”)
}
record(ai, “cmd_1_get”)
{
field(SCAN, “Passive”)
field(INP, “@instrument.proto getCmd1 $(PORT)”)
}
record(ai, “cmd_2_get”)
{
field(SCAN, “Passive”)
field(INP, “@instrument.proto getCmd2 $(PORT)”)
}
record(ai, “cmd_3_get”)
{
field(SCAN, “Passive”)
field(INP, “@instrument.proto getCmd3 $(PORT)”)
}
3.
Record scan method
record(ao, “cmd_1_set”)
{
field(SCAN, “Passive”)
field(DTYP, “stream”)
field(OUT, “@instrument.proto setCmd1 $(PORT)”)
}
record(ao, “cmd_2_set”)
{
field(SCAN, “Passive”)
field(DTYP, “stream”)
field(OUT, “@instrument.proto setCmd2 $(PORT)”)
}
record(ao, “cmd_3_set”)
{
field(SCAN, “Passive”)
field(DTYP, “stream”)
field(OUT, “@instrument.proto setCmd3 $(PORT)”)
}
record(ai, “cmd_1_get”)
{
field(SCAN, “1 second”)
field(INP, “@instrument.proto getCmd1 $(PORT)”)
}
record(ai, “cmd_2_get”)
{
field(SCAN, “1 second”)
field(INP, “@instrument.proto getCmd2 $(PORT)”)
}
record(ai, “cmd_3_get”)
{
field(SCAN, “1 second”)
field(INP, “@instrument.proto getCmd3 $(PORT)”)
}
Are all valid or is there one that is better than the other. Or is there an even better one that I haven’t thought of…
Thanks,
Andy