.. _arrays-label: ============================================ Working with waveform / array data ============================================ Though most EPICS Process Variables hold single values, PVs can hold array data from EPICS waveform records. These are always data of a homogenous data type, and have a fixed maximum element count (defined when the waveform is created from the host EPICS process). Epics waveforms are most naturally mapped to Arrays from the `numpy module `_, and this is strongly encouraged. Arrays without Numpy ~~~~~~~~~~~~~~~~~~~~~~~~ If you have numpy installed, and use the default *as_numpy=True* in :meth:`epics.ca.get`, :meth:`pv.get` or :meth:`epics.caget`, you will get a numpy array for the value of a waveform PV. If you do *not* have numpy installed, or explicitly use *as_numpy=False* in a get request, you will get the raw C-like array reference from the Python `ctypes module `_. These objects are not normally meant for casual use, but are not too difficult to work with either. They can be easily converted to a simple Python list with something like:: >>> import epics >>> epics.ca.HAS_NUMPY = False # turn numpy off for session >>> p = epics.PV('XX:scan1.P1PA') >>> p.get() >>> ldat = list(p.get()) Note that this conversion to a list can be very slow for large arrays. Variable Length Arrays: NORD and NELM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ While the maximum length of an array is fixed, the length of data you get back from a monitor, :meth:`epics.ca.get`, :meth:`pv.get`, or :meth:`epics.caget` may be shorter than the maximum length, reflecting the most recent data put to that PV. That is, if some process puts a smaller array to a PV than its maximum length, monitors on that PV may receive only the changed data. For example:: >>> import epics >>> p = epics.PV('Py:double2k') >>> print p >>> import numpy >>> p.put(numpy.arange(10)/5.0) >>> print p.get() array([ 0. , 0.2, 0.4, 0.6, 0.8, 1. , 1.2, 1.4, 1.6, 1.8]) To be clear, the :meth:`pv.put` above could be done in a separate process -- the :meth:`pv.get` is not using a value cached from the :meth:`pv.put`. This feature was introduced in Epics CA 3.14.12.1, and may not work for data from IOCs running extremely old versions of Epics base. Character Arrays ~~~~~~~~~~~~~~~~~~~~~~~~ As noted in other sections, character waveforms can be used to hold strings longer than 40 characters, which is otherwise a fundamental limit for native Epics strings. Character waveforms shorter than :data:`epics.ca.AUTOMONITOR_MAXLENGTH` can be turned into strings with an optional *as_string=True* to :meth:`epics.ca.get`, :meth:`pv.get` , or :meth:`epics.caget`. If you've defined a Epics waveform record as:: record(waveform,"$(P):filename") { field(DTYP,"Soft Channel") field(DESC,"file name") field(NELM,"128") field(FTVL,"CHAR") } Then you can use this record with: >>> import epics >>> pvname = 'PREFIX:filename.VAL' >>> pv = epics.PV(pvname) >>> print pv.info .... >>> plain_val = pv.get() >>> print plain_val array([ 84, 58, 92, 120, 97, 115, 95, 117, 115, 101, 114, 92, 77, 97, 114, 99, 104, 50, 48, 49, 48, 92, 70, 97, 115, 116, 77, 97, 112]) >>> char_val = pv.get(as_string=True) >>> print char_val 'T:\\xas_user\\March2010\\FastMap' This example uses :meth:`pv.get` but :meth:`epics.ca.get` is essentially equivalent, as its *as_string* parameter works exactly the same way. Note that Epics character waveforms as defined as above are really arrays of bytes. The conversion to a string assumes the ASCII character set. Unicode is not directly supported. If you are storing non-ASCII data, you would have to convert the raw array data yourself, perhaps like this (for Python3):: >>> arr_data = pv.get() >>> arr_bytes = bytes(list(array_data)) >>> arr_string = str(arr_bytes, 'LATIN-1') .. _arrays-large-label: Strategies for working with large arrays ============================================ EPICS Channels / Process Variables usually have values that can be stored with a small number of bytes. This means that their storage and transfer speeds over real networks is not a significant concern. However, some Process Variables can store much larger amounts of data (say, several megabytes) which means that some of the assumptions about dealing with Channels / PVs may need reconsideration. When using PVs with large array sizes (here, I'll assert that *large* means more than a few thousand elements), it is necessary to make sure that the environmental variable ``EPICS_CA_MAX_ARRAY_BYTES`` is suitably set. Unfortunately, this represents a pretty crude approach to memory management within Epics for handling array data as it is used not only sets how large an array the client can accept, but how much memory will be allocated on the server. In addition, this value must be set prior to using the CA library -- it cannot be altered during the running of a CA program. Normally, the default value for ``EPICS_CA_MAX_ARRAY_BYTES`` is 16384 (16k, and it turns out that you cannot set it smaller than this value!). As Python is used for clients, generally running on workstations or servers with sufficient memory, this default value is changed to 2**24, or 16Mb) when :mod:`epics.ca` is initialized. If the environmental variable ``EPICS_CA_MAX_ARRAY_BYTES`` has not already been set. The other main issue for PVs holding large arrays is whether they should be automatically monitored. For PVs holding scalar data or small arrays, any penalty for automatically monitoring these variables (that is, causing network traffic every time a PV changes) is a small price to pay for being assured that the latest value is always available. As arrays get larger (as for data streams from Area Detectors), it is less obvious that automatic monitoring is desirable. The Python :mod:`epics.ca` module defines a variable :data:`epics.ca.AUTOMONITOR_MAXLENGTH` which controls whether array PVs are automatically monitored. The default value for this variable is 65536, but can be changed at runtime. Arrays with fewer elements than :data:`epics.ca.AUTOMONITOR_MAXLENGTH` will be automatically monitored, unless explicitly set, and arrays larger than :data:`epics.ca.AUTOMONITOR_MAXLENGTH` will not be automatically monitored unless explicitly set. Auto-monitoring of PVs can be be explicitly set with >>> pv2 = epics.PV('ScalerPV', auto_monitor=True) >>> pv1 = epics.PV('LargeArrayPV', auto_monitor=False) Example handling Large Arrays ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Here is an example reading data from an `EPICS areaDetector `_, as if it were an image from a digital camera. This uses the common third-party library called `Python Imaging Library` or `pillow` for much of the image processing. This library can be installed with `pip install pillow` or `conda install pillow`: >>> import epics >>> import Image >>> pvname = '13IDCPS1:image1:ArrayData' >>> img_pv = epics.PV(pvname) >>> >>> raw_image = img_pv.get() >>> im_mode = 'RGB' >>> im_size = (1360, 1024) >>> img = Image.frombuffer(im_mode, im_size, raw_image, 'raw', im_mode, 0, 1) >>> img.show() The result looks like this (taken with a Prosilica GigE camera): .. image:: AreaDetector1.png A more complete application for reading and displaying image from Epics Area Detectors is included at `https://github.com/pyepics/epicsapps/ `_.