12. Integrating EPICS in GDA

../_images/epics_logo.png

EPICS

Integration of EPICS within GDA covers:

  • configuration of Java Channel Access (JCA);

  • the creation of Scannables and Detectors that talk to EPICS as a Channel Access client allowing devices to be incorporated into a full Data Acquisition system;

12.1. Configure JCA

GDA uses Channel Access for Java (CAJ) - 100% pure Java implementation of the EPICS Channel Access JCA library - to communicate with EPICS IOCs.

To enable GDA access to EPICS Processing Variables (PVs), GDA servers (or clients, if clients need to talk to PVs directly) must be started with a property gov.aps.jca.JCALibrary.properties setting, for example:

-Dgov.aps.jca.JCALibrary.properties=${gda.config}/properties/<mode>/JCALibrary.properties

The property file JCALibrary.properties should contain the following properties (corresponding to EPICS Channel Access environment variables:

com.cosylab.epics.caj.CAJContext.addr_list           = 172.23.111.255
com.cosylab.epics.caj.CAJContext.auto_addr_list      = true
com.cosylab.epics.caj.CAJContext.connection_timeout  = 30.0
com.cosylab.epics.caj.CAJContext.beacon_period       = 15.0
com.cosylab.epics.caj.CAJContext.repeater_port       = 5065
com.cosylab.epics.caj.CAJContext.server_port         = 5064
com.cosylab.epics.caj.CAJContext.max_array_bytes     = 4000000

#com.cosylab.epics.caj.CAJContext.event_dispatcher= gov.aps.jca.event.QueuedEventDispatcher
#com.cosylab.epics.caj.CAJContext.event_dispatcher= gov.aps.jca.event.LatestMonitorOnlyQueuedEventDispatcher
com.cosylab.epics.caj.CAJContext.event_dispatcher= gov.aps.jca.event.SplitQueuedEventDispatcher

com.cosylab.epics.caj.CAJContext.logger              = com.cosylab.epics.caj.CAJContext
com.cosylab.epics.caj.impl.CachedByteBufferAllocator.buffer_size=32000
com.cosylab.epics.caj.impl.reactor.lf.LeaderFollowersThreadPool.thread_pool_size = 5

Among these, the properties that are most often changed are:

com.cosylab.epics.caj.CAJContext.addr_list           = 172.0.0.1
com.cosylab.epics.caj.CAJContext.auto_addr_list      = false
com.cosylab.epics.caj.CAJContext.repeater_port       = 6065
com.cosylab.epics.caj.CAJContext.server_port         = 6064

for communicating with EPICS IOC running on your localhost and using CA server port of 6064.

GDA will also set com.cosylab.epics.caj.CAJContext.event_dispatcher to gov.aps.jca.event.SplitQueuedEventDispatcher

The last two properties are CAJ specific. In most cases, you do not need to set them as the defaults are sufficient. However, if required, you can use these properties to customise CAJ’s internal byte buffer size and thread pool size.

At Diamond, we run an EPICS simulation server on dasc-epics.diamond.ac.uk for off beamline development of GDA software. Its IP address is 172.23.7.113, server port is 6064, repeater port is 6065.

12.2. Fastest method to get/put in a script

  • To get the value of pv test:sensor1:

    from gda.epics import CAClient
    ca = CAClient()
    val = ca.caget("test:sensor1")
    

    returns String values,

  • To put to pv test:sensor1 a value of 1:

    from gda.epics import CAClient
    ca = CAClient()
    ca.caput("test:sensor", 1.0)
    ca.caput("test:sensor", "1.0")
    

    accepts native types and String

12.3. More performant method to get/put in a script

  • Create an object once and reuse:

    from gda.epics import CAClient
    ca = CAClient("test:sensor1")
    val = ca.caget()
    ... some time later
    ca.caput(1.0)
    ... when done close the channel
    ca.clearup()
    
  • variations of caget/caput exist for different types

  • caput can accept a listener or timeout

12.4. Using CAClient to make a Scannable

  • Defined class in epics_scannables.py:

    from gda.device.scannable import ScannableMotionBase	
    from gda.epics import CAClient
    class SimpleEPICSMonitor(ScannableMotionBase):
    	def __init__(self, name, pvstring, unitstring, formatstring):
    		self.setName(name);
    		self.setInputNames([])
    		self.setExtraNames([name])
    		self.Units=[unitstring]
    		self.setOutputFormat([formatstring])
    		self.ca=CAClient(pvstring)
    		self.ca.configure()
    
    	def getPosition(self):
    		return self.ca.caget()
    
    	def asynchronousMoveTo(self,position):
    		pass
    
    	def isBusy(self):
    		return False
    
  • Import the class definition and create the scannable:

    import epics_scannables
    sensor1 = epics_scannables.SimpleEPICSMonitor('sensor1', 
                                       'test:sensor1', 'mm', '%.4f')
    ....
    pos sensor1
    scan motor1 1. 10. 1. sensor1
    

12.5. Access EPICS in Java

  • A cut down Java Scannable class to get/put a double field:

    public class SimpleDoubleScannable extends ScannableBase{
    ... 
    configure(){
      controller = EpicsController.getInstance();
      channel = controller.createChannel(pvName);
    ...
    asynchronousMoveTo(Object value){
      busy = true;
      controller.caput(getChannel(), (Double) value, putListener);
    ...
    (In putListener putCompleted method set busy to false)
    ...
    boolean isBusy(){
      return busy
    ...
    Object getPosition(){
       return controller.cagetDouble(channel);
    ...
    }
    

12.6. A General Purpose Class EpicsScannable

  • Use in script to get/set test:sensor2:

    from gda.device.scannable import EpicsScannable
    sensor2=EpicsScannable()
    sensor2.setName("sensor2")
    sensor2.setPvName("test:sensor2")
    sensor2.setUseNameAsInputName(True)
    sensor2.setUserUnits("mm")
    sensor2.configure()
    ...
    pos sensor2
    pos sensor2 "1.2 mm"
    pos sensor2 "1.2 m"
    
  • Add to system using Spring:

    <bean id="sensor3" class="gda.device.scannable.EpicsScannable">
        <property name="pvName" value="test:sensor3"/>
        <property name="useNameAsInputName" value="true"/>
        <property name="userUnits" value="mm"/>
    </bean>
    

12.7. Particular device classes in GDA

  • Add to system using Spring:

    <bean id="m1_motor" class="gda.device.motor.EpicsMotor">
        <property name="pvName" value="test:m1"/>
    </bean>
    ...
    <bean id="m1" class="gda.device.scannable.ScannableMotor">
        <property name="motor" ref="m1_motor" />
    </bean>