EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  <20192020  2021  2022  2023  2024  Index 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  <20192020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: Re: Camonitor with client dictated update rate
From: Emanuele Laface via Tech-talk <[email protected]>
To: Matt Newville <[email protected]>
Cc: "[email protected]" <[email protected]>
Date: Sun, 2 Jun 2019 21:25:53 +0000
All of these approaches do not solve my issue.
I have this scenario: N PVs of waveforms (arrays) of 10^5 data in each PV.
The IOc runs at 14 Hz, it means that if I subscribe in monitor mode I will be updated 14 times per second with N x 10^5 numbers.

Now I have an interface that I want to update just once per second with the content of these waveforms.
If I am in monitor, I can easily get the value once per second but my python script will anyway use the bandwidth and the cpu to process at 14 Hz because the threads of the monitoring will be updated at that frequency.

If I use caget once per second I need to deal with all the timeouts that can occur if some pv is down, disconnected, unreachable etc.

I haven’t find a reliable way to get the data once per second in a full asynchronous mode but without rejecting the data that anyway occupy bandwidth and cpu.
Cheers,
e.

On 2 Jun 2019, at 22:50, Matt Newville <[email protected]> wrote:

Hi Hinko,

On Sun, Jun 2, 2019 at 12:53 PM Hinko Kocevar via Tech-talk <[email protected]> wrote:
Thank you all for the input.

I understand that adding new records to the IOC database is the "EPICS" way of doing it, and I've done it so far for IOCs under my control, with success. The thing is that there are not enough bodies, with EPICS knowledge and free time, for this to happen on all IOCs at ESS, leading such low priority requirements being forgotten.

I've never used the base filters before - this looks like a good opportunity to look into that!
The PVs that need throttling are mainly scalars, but in some cases, even more critical PVs as waveforms. I would imagine that "deadband" filter might not play well with those. In any case, deadband filter looks promising, but at the end of the day, I'm after simple decimation of PV update rate, not depending on the value change itself.
(How) can filters be used with python client (pyepics3)?

The periodic polling, sounds like to most straightforward approach, for sure.
In python, there might be unwanted delays introduced to the "caget" if the PV is not accessible/reachable at the time of the poll. And then the rest of the PV updates (in a single thread) suffer from not being updated in timely fashion. Someone will say, use threads; but I can have +50 PVs to look at on this slow monitoring client hence each PV would need to get its own thread? Or would the use of PV instead of caget in pyepics3 lead to better experience?

If I understand correctly, you want to report or act on PV changes at a fixed rate that is slower than you expect the real rate of change for the PVs.   I would probably start with something like this (for brevity it uses 8 PVs, but I think this should scale to 100s of PVs without change and to 1000s PVs without change except to worry about the i/o rates of the print() calls (and you probably wouldn't really be printing anyway):

####
import epics
import time
pvnames = ['S:SRcurrentAI',
          'S:SRlifeTimeHrsCC',
          '13IDA:DMM1Ch11_calc.VAL',
          '13IDA:DMM1Ch12_calc.VAL',
          '13IDE:userTran1.A',
          '13IDE:userTran1.B',
          '13IDE:userTran1.C',
          '13IDE:userTran1.D']

pvs = [epics.PV(name) for name in pvnames]

MESSAGE_DELAY = 2.0  # delay time in seconds

while True:
    try:
        time.sleep(MESSAGE_DELAY)
        print("# Time : %s" % (time.ctime()))
        for pv in pvs:
            print("  %s %s" % (pv.pvname, pv.char_value))
    except KeyboardInterrupt:
        break
####

Here, the PV objects are internally monitored, automatically updating their values on each real change event. We ignore the events and only use the fact that the values will be up-to-date after we've slept for a short, fixed time.   That will act on (here 'print()') every PV at a fixed interval.  FWIW,  pyepics 'caget()' and 'caput()' are built upon PV objects (so that connections are not re-established), so that the most naive script of 

###
import epics
import time
pvnames = ['S:SRcurrentAI',
          'S:SRlifeTimeHrsCC',
          '13IDA:DMM1Ch11_calc.VAL',
          '13IDA:DMM1Ch12_calc.VAL',
          '13IDE:userTran1.A',
          '13IDE:userTran1.B',
          '13IDE:userTran1.C',
          '13IDE:userTran1.D']

MESSAGE_DELAY = 2.0  # delay time in seconds
while True:
    try:
        time.sleep(MESSAGE_DELAY)
        print("# Time : %s" % (time.ctime()))
        for name in pvnames:
            print("  %s %s" % (name, epics.caget(name, as_string=True)))
    except KeyboardInterrupt:
        break
####

would give the same output, and not be very different in performance.


If you want to act only when the PV actually changes but not necessarily on *every* change, I would suggest testing the timestamp in the event handler, and ignore events that are too soon after the last change, perhaps something like this:


####
import epics
import time

pvnames = ['S:SRcurrentAI',
          'S:SRlifeTimeHrsCC',
          '13IDA:DMM1Ch11_calc.VAL',
          '13IDA:DMM1Ch12_calc.VAL',
          '13IDE:userTran1.A',
          '13IDE:userTran1.B',
          '13IDE:userTran1.C',
          '13IDE:userTran1.D']

MESSAGE_DELAY = 2.0
last_update = {}

def onchange(pvname, value=None, char_value=None, timestamp=0, **kws):
    if timestamp < (last_update.get(pvname, 0) + MESSAGE_DELAY):
        # un-comment this next line to see what you are missing!  
        # print("     <ignored event for %s>" % (pvname)) 
        return
    else:
        print("%s, value=%s at %s" % (pvname, char_value, time.ctime()))
        last_update[pvname] = timestamp

pvs = [epics.PV(name, callback=onchange) for name in pvnames]

# run, wait for changes (or run the rest of your app)
while True:
    try:
        time.sleep(0.05)
    except KeyboardInterrupt:
        break
####

In this version, we look at the timestamp each change event (by default, the TIME_* DBR type for a PV so that those timestamps are from the record) and ignore events that are too recent.

All of these rely on the default behavior for pyepics PVs that they are monitored internally and updated asynchronously by threads in the CA library, but you should not need to worry about this threading on the client end - it all happens in the background.   The  use of a rich PV (with internal monitor and connection callbacks and using the TIME_* DBR variant by default)  does use a few more resources than a bare channel access connection.  The expectation is that if you're using Python, programmer time is more valuable than counting bytes on the IOC.  But, if you're close to saturating an IOC or your network, you may want to be more careful. 

Hope that helps.  If I missed some important point, let me know,

--Matt

Replies:
Re: Camonitor with client dictated update rate Matt Newville via Tech-talk
Re: Camonitor with client dictated update rate Johnson, Andrew N. via Tech-talk
References:
Camonitor with client dictated update rate Hinko Kocevar via Tech-talk
Re: Camonitor with client dictated update rate William Layne via Tech-talk
Re: Camonitor with client dictated update rate Hinko Kocevar via Tech-talk
Re: Camonitor with client dictated update rate Matt Newville via Tech-talk

Navigate by Date:
Prev: Re: Camonitor with client dictated update rate Matt Newville via Tech-talk
Next: Re: Camonitor with client dictated update rate Matt Newville via Tech-talk
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  <20192020  2021  2022  2023  2024 
Navigate by Thread:
Prev: Re: Camonitor with client dictated update rate Matt Newville via Tech-talk
Next: Re: Camonitor with client dictated update rate Matt Newville via Tech-talk
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  <20192020  2021  2022  2023  2024 
ANJ, 03 Jun 2019 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·