15. Malcolm

15.1. MalcolmEpicsV4Connection

MalcolmEpicsV4Connection is a Java class contained in the gda-core.git repository. The main purposes of the class is to provide an interface between GDA and Malcolm (which represents an EPICS V4 endpoint). It provides the ability to set and get a process variables (PV), call a method and subscribe or unsubscribe to a PV. It uses the EPICS V4 classes to provide the interface to EPICS. The EPICS V4 classes are contained in a JarFile with package name org.epics. More details about the classes contained within theses can be found externally here where Java docs are also available.

15.1.1. Class composition

The class diagram is shown below indicating the key classes (within GDA) used by MalcolmEpicsV4Connection.

scale70

  • The class implements the interface IMalcolmConnection which represents a connection to a Malcolm service and provides methods to communicate with Malcolm devices.

  • During instantiation of the class, a new MalcolmMessageGenerator is created and held as an object within the class. It can be accessed from outside via its get() method. The MalcolmMessageGenerator is simply a factory class for generating messages to send to and from Malcolm. An EpicsV4MessageMapper is also created. This is used by MalcolmEpicsV4Connection methods to convert a PV structure received from Malcolm to a MalcolmMessage (or vice versa by converting a MalcolmMessage to a PV structure). This is done using PVMarshaller class.

  • The PVMarshaller class to convert between Java Objects and pvData PVStructures. The EpicsV4MessageMapper registers a series of custom serialisers and deserialiser to the PVMarshaller. One of these includes the MalcolmMessageSerialiser which defines a mapping between a MalcolmMessage and a PVStructure.

  • A MalcolmMessage is GDA specific and is a wrapper for the messages sent to and from Malcolm. It encapsulates the information in the messages and makes them easier to understand and use in GDA (e.g. by the MalcolmDevice class). Within a MalcolmMessage, a ‘method’ field can be defined using MalcolmMethod which is an enumeration of the Malcolm methods that can be set in a ‘CALL’ type message (e.g. Abort, configure, run etc).

  • The MalcolmEpicsV4Connection also creates a maps of monitor listeners using EpicsV4MonitorListener to create an event listener and PVA (process variable access) client monitor. Finally it creates a PvaClient (an EPICS wrapper for the pvAccess API which provides communication between the client and server).

  • The class StateChangeRequester implements the EPICS V4 PvaClientChannelStateChangeRequester class which provides a callback for a change in the connection status. The StateChangeRequester contains an IMalcolmConnection.IMalcolmConnectionStateListener that listens to the state of the connection to the actual Malcolm device (e.g. connected or disconnected).

  • The EpicsV4ClientMonitorRequester class uses an IMalcolmConnectionEventListener (to listen for Malcolm events). It uses the EPICS V4 PvaClientMonitor to poll for events from Malcolm, which it then converts into a MalcolmMessage (using the EpicsV4MessageMapper).

15.1.2. Key Features

Below are some of the key methods in MalcolmEpicsV4Connection that would be called to interact with Malcolm. For example, in GDA they would be called by the MalcolmDevice class.

  • send(IMalcolmDevice, MalcolmMessage): This method is called commonly from the MalcolmDevice class when it wants to communicate with Malcolm either to get a PV, set one or call a method etc.

The method receives a MalcolmMessage from a MacolmDevice and begins by getting the message type. Valid message types are: CALL, GET, PUT, RETURN, ERROR, SUBSCRIBE, UNSUBSCRIBE, UPDATE, DELTA and READY. Note that the latter two in the list (DELTA and READY) are not currently in use. The message type defines what happens next. A CALL will invoke the sendCallMessage(). This is used if a method from Malcolm should be called (e.g. configure, validate). A GET type will invoke the sendGetMessage() method and is often used to get a variable back from Malcolm, for example to query the current state of the device. A PUT type will issue sendPutMessage(). This is used to set a variable on a device via Malcolm. The SUBSCRIBE and UNSUBSCRIBE types are fairly self explanatory and will invoke the methods subscribe and unsubscribe. A RETURN type is commonly used in the message returned from MalcolmEpicsV4Connection to the MalcolmDevice (see here for an example). An UPDATE type is the message returned from an event subscription (e.g. listening for a state change - example). The final important type is ERROR which be returned in the message from MalcolmEpicsV4Connection if an error occurred.

  • subscribe(MalcolmDevice, MalcolmMessage, IMalcolmConnectionEventListerner) - [sequence diagram]

This method is commonly called from the MalcolmDevice class to subscribe to specified events from the Malcolm device. An EpicsV4ClientMonitorRequester object is created from the input listener and the MalcolmMessage indicating what endpoint to subscribe to. It creates a PvaClientChannel (EPICS V4) and waits for the connection to be made. From this channel it creates a PvaClientMonitor (EPICS V4) which calls start() on the monitor to begin monitoring. Finally an instance of an EpicsV4MonitorListener is created with the IMalcolmConnectionEventListerner and PvaClientMonitor and added to the map of listeners held by the MalcolmEpicsV4Connection to keep track of what listeners it has active.

Example use: A MalcolmDevice may decide to subscribe to the ‘STATE’ of the device to monitor any state change. It will use the MalcolmMessageGenerator to create a subscription message with a MalcolmMessage of Type = subscribe and an endpoint = state. The subscribe method described above will then be called with this message and relayed to the PvaClientMonitor to start monitoring the ‘state’.

  • subscribeToConnectionStateChange(IMalcolmDevice, IMalcolmConnectionStateListener): Used to subscribe to the connection state to the actual Malcolm device and monitor any changes.

Begins by creating a PvaClientChannel (EPICS V4) and waits for the connection to be made. Through the connection it sets the EPICS V4 stateChangeRequester (see PvaClientChannel) to its own StateChangeRequester instantiated with the input IMalcolmConnectionStateListener.

  • unsubscribe(IMalcolmDevice, MalcolmMessage, IMalcolmConnectionEventListener…): This is commonly used to unsubscribe from event monitoring on termination.

If a list of IMalcolmConnectionEventListeners is provided then each   EpicsV4MonitorListener will be stopped and removed from the map held by MalcolmEpicsV4Connection of listeners. If no list is provided then all listeners contained in the map will be stopped and removed from the map. A RETURN type message is returned by this method if successful, otherwise the type is set to ERROR and returned.

Begins by creating a PvaClientChannel (EPICS V4) and waits for the connection to be made. This is simply a check that the connection can be made and so it is subsequently destroyed. The input MalcolmMessage is converted into a PVStructure using the EpicsV4MessageMapper, which subsequently calls on the PVMarshaller class and its serialiser to do the conversion. The MalcolmMessageSerialiser takes the MalcolmMessage ‘method’ and ‘args’ fields and maps them to the PVStructure ‘method’ and ‘parameters’ fields, respectively (see example just below to see this). From the PVStructure, the fields ‘method’ and ‘parameters’ are extracted. The former is used to create a service client (RPCClientImpl). A request is then made on the client with the ‘parameters’, which waits for a response. A response is returned and converted back from a PVStructure to a MalcolmMessage (sequence diagram). Final tidy up destroys any connection and returns the response message.

Example: A CALL is made to ‘configure’ the Malcolm device with a scan model (EpicsMalcolmModel) as the arguments. The MalcolmMessage created and sent to the MalcolmEpicsV4Connection has the format:

MalcolmMessage [type=CALL, id=66, param=null, endpoint=null, method=configure, message=null, 
args=org.eclipse.scanning.malcolm.core.MalcolmDevice$EpicsMalcolmModel@5286e91d, value=null]

The MalcolmEpicsV4Connection converts this to the following PVStructure:

structure parameters
   scanpointgenerator:generator/CompoundGenerator:1.0 generator
       any[] mutators 
       double duration 0.01
       boolean continuous true
       any[] generators 
           any 
               scanpointgenerator:generator/LineGenerator:1.0 
                   string[] axes [stagey]
                   double[] start [-4.976190476190476]
                   boolean alternate true
                   string[] units [mm]
                   double[] stop [-4.023809523809524]
                   int size 21
           any 
               scanpointgenerator:generator/LineGenerator:1.0 
                   string[] axes [stagex]
                   double[] start [-8.995412844036696]
                   boolean alternate true
                   string[] units [mm]
                   double[] stop [-8.004587155963302]
                   int size 109
       any[] excluders 
           any 
               scanpointgenerator:excluder/ROIExcluder:1.0 
                   string[] axes [stagex,stagey]
                   any[] rois 
                       any 
                           scanpointgenerator:roi/RectangularROI:1.0 
                               double[] start [-9.0,-5.0]
                               double width 1.0
                               double angle 0.0
                               double height 1.0
   string[] axesToMove [stagey,stagex]
   string fileDir /dls/p45/data/2018/cm21013-4/p45-2738
   string fileTemplate p45-2738-%s.h5

The response from Malcolm is a PVStructure (received by MalcolmEpicsV4Connection) in the form:

structure 
  scanpointgenerator:generator/CompoundGenerator:1.0 generator
      any[] excluders 
          any 
              scanpointgenerator:excluder/ROIExcluder:1.0 
                  string[] axes [stagex,stagey]
                  any[] rois 
                      any 
                          scanpointgenerator:roi/RectangularROI:1.0 
                              double height 1.0
                              double angle 0.0
                              double width 1.0
                              double[] start [-9.0,-5.0]
      boolean continuous true
      any[] generators 
          any 
              scanpointgenerator:generator/LineGenerator:1.0 
                  boolean alternate true
                  string[] axes [stagey]
                  double[] stop [-4.023809523809524]
                  double[] start [-4.976190476190476]
                  string[] units [mm]
                  long size 21
          any 
              scanpointgenerator:generator/LineGenerator:1.0 
                  boolean alternate true
                  string[] axes [stagex]
                  double[] stop [-8.004587155963302]
                  double[] start [-8.995412844036696]
                  string[] units [mm]
                  long size 109
      double duration 0.01
      any[] mutators 
  string[] axesToMove [stagey,stagex]
  string fileDir /dls/p45/data/2018/cm21013-4/p45-2738
  string fileTemplate p45-2738-%s.h5

Finally the MalcolmEpicsV4Connection converts back into a MalcolmMessage which it returns to the MalcolmDevice as:

MalcolmMessage [type=RETURN, id=57, param=null, endpoint=null, method=null, message=null, args=null, 
  value={generator={excluders=[{axes=[stagex, stagey], rois=[{height=1.0, angle=0.0, width=1.0, start=[-9.0, -5.0]}]}], 
  continuous=true, generators=[{alternate=true, axes=[stagey], stop=[-4.023809523809524], start=[-4.976190476190476], units=[mm], size=21}, 
  {alternate=true, axes=[stagex], stop=[-8.004587155963302], start=[-8.995412844036696], units=[mm], size=109}], duration=0.01, mutators=[]}, 
  axesToMove=[stagey, stagex], fileDir=/dls/p45/data/2018/cm21013-4/p45-2738, fileTemplate=p45-2738-%s.h5}]

Begins by creating a PvaClientChannel and waits for the connection to be made. From this channel it creates a PvaClientGet (EPICS V4) object and creates a ‘get’ with the endpoint that is specified in the input MalcolmMessage (e.g. state). It connects and waits for the connection to complete. It gets the pvaData (getData()) from the PvaClientGet and from this gets the PVStructure (getPVStructure()). Finally this is converted back to a MalcolmMessage (sequence diagram) and returned (destroying the connection as well). If an error occurs, then the MalcolmMesssage type will be set to ERROR and returned with the error message.

Example: A message to get the ‘state’ of the device is sent from the MalcolmDevice in the form:

MalcolmDevice - Sending message to malcolm device:
MalcolmMessage [type=GET, id=60, param=null, endpoint=state, method=null, message=null, args=null, value=null]

The MalcolmEpicsV4Connection send a simple get with the desired endpoint and receives a response from Malcolm as a PVStructure:

structure 
    epics:nt/NTScalar:1.0 state
        string value Ready
        alarm_t alarm
            int severity 0
            int status 0
            string message 
        time_t timeStamp
            long secondsPastEpoch 1539786208
            int nanoseconds 308497905
            int userTag 0
        malcolm:core/ChoiceMeta:1.0 meta
            string description StateMachine State of Block
            string[] choices [Ready,Resetting,Saving,Loading,Configuring,
                              Armed,Running,Seeking,PostRun,Paused,Aborting,Aborted,Fault,Disabling,Disabled]
            string[] tags [widget:textupdate]
            boolean writeable false
            string label State

It converts this back to a MalcolmMessage (in the sendGetMessage method) signalling that the device is ready (value=Ready):

MalcolmMessage [type=RETURN, id=60, param=null, endpoint=state, method=null, message=null, args=null, value=ChoiceAttribute [value=Ready, getName()=state]]

Note: This method is not currently made use of in GDA as Malcolm is not used to ‘set’ values. However it is described here for completeness as it potentially could be implemented by other EPICS V4 endpoints. Begins by creating a PvaClientChannel (EPICS V4) and waits for the connection to be made. From this channel it creates a PvaClientPut (EPICS V4) object and creates a ‘put’ with the endpoint that is specified in the input MalcolmMessage. It connects and waits for the connection to be complete. It gets the pvaData (getData()) from the PvaClientPut object and gets the PVStructure (getPVStructure()). Using the EpicsV4MessageMapper it populates the PVStructure with the put parameter name and value. A put() is called on the PvaClientPut. If successful the MalcolmMessage type is set to RETURN and the channel connection is terminated. If it fails then a MalcolmMessage of type ERROR is returned from this method .

15.2. MalcolmDevice

The MalcolmDevice class is an object that makes a connection to Malcolm via the MalcolmEpicsV4Connection and monitors its status. It extends AbstractMalcolmDevice (a base class for Malcolm devices) which itself extends AbstractRunnableDevice. The MalcolmDevice also extends MalcolmModel which defines a model for a Malcolm device.

scale90

15.2.1. Initializing a Malcolm Device.

On server start-up (see here for full details) the SpringObjectServer will initialise all of the ‘beans’ defined in server xml file. If the ‘server.xml’ file contains a MalcolmDevice then it will be instantiated and initialised during start-up. The device is registered by a call to register() on AbstractMalcolmDevice. This registers the detector with the runnable device services (AbstractRunnableDevice.register()) and then calls initialize().

The MalcolmDevice contains a IMalcolmConnection (which may be the MalcolmEpicsV4Connection), an IMalcolmMessageGenerator (which it gets from the connection class) and two IMalcolmConnectionEventListener. One is configured to monitor and listen for any state change. If it receives a MalcolmMessage it extracts the devices state and uses it to set the state in of the MalcolmDevice object. It also creates a MalcolmEvent to signal a state change from the old value to the new and sends this out to any subscribed listeners. The second IMalcolmConnectionEventListener is configured to monitor Malcolm for messages containing ‘completedSteps’. This is particularly relevant during a scan. From this it computes the step that has been completed and creates a MalcolmEvent signalling the number of ‘steps completed’, which is sent and received by any listeners.

For example, when a message is received by the state change IMalcolmConnectionEventListener with type=UPDATE and endPoint=state, it will get the value (value=Running) and send an event to signal that the device is running a scan.

MalcolmDevice - Received malcolm state change with message: 
  MalcolmMessage [type=UPDATE, id=1, param=null, endpoint=state, method=null, message=null, args=null, value=ChoiceAttribute [value=Running, getName()=state]]
MalcolmDevice - Sending malcolm event: Sending malcolm event: 
  MalcolmStateChangedEvent [deviceState=Running, previousState=Armed, getMalcolmDeviceName()=BL45P-ML-SCAN-02, getMessage()=null]

ASIDE on MalcolmEvent: there are two types of MalcolmEvents: STATE_CHANGE and STEPS_COMPLETED. An event created for a state change returns a MalcolmStateChangedEvent containing the device, new state, previous state and message. An event created for a ‘steps completed’ event instead returns a MalcolmStepsCompletedEvent object containing the device, steps completed and message.

The IMalcolmConnection and IRunnableDeviceService objects that are used to construct the MalcolmDevice are provided by the Services class. The IMalcolmConnection for EPICS V4 is MalcolmEpicsV4Connection (but any connection class could be used).

When the MalcolmDevice is initialized() it carries out the following:

  • Gets the initial state of the MalcolmDevice:

    • Creates a MalcolmMessage with type=GET and with endPoint=state using a IMalcolmMessageGenerator method.

    • Calls on the MalcolmEpicsV4Connection.send() method with the MalcolmMessage created above. It will wait for a MalcolmMessage to be returned with a given timeout.

    • On receipt of the MalcolmMessage, the value of the endPoint is extracted from the message, i.e. it will extract the value for endPoint=state.

      MalcolmMessage [type=RETURN, id=60, param=null, endpoint=state, method=null, message=null, args=null, value=ChoiceAttribute [value=Ready, getName()=state]]
      
  • Subscribes to two events for ‘state change’ and ‘steps completed’.

    • Creates a MalcolmMessage with type=SUBSCRIBE and endPoint=state using a IMalcolmMessageGenerator meth od.

    • It calls on the MalcolmEpicsV4Connection.subscribe() method with the MalcolmMessage created above and the IMalcolmConnectionEventListener created for the ‘state change’ listener.

    • Creates a MalcolmMessage with type=SUBSCRIBE and endPoint=completedSteps (as above).

    • It calls on the MalcolmEpicsV4Connection.subscribe() method with the MalcolmMessage created above and the IMalcolmConnectionEventListener created for the ‘steps completed’ listener.

15.2.2. Configuring the MalcolmDevice

During a scan all RunnableDevices involved will be configured (more details on scanning with Malcolm can be found here). If the device is a MalcolmDevice, then the MalcolmDevice.configure() method is called. This carries out the following:

  • Creates an EpicsMalcolmModel which describes the scan, i.e. defines the IPointGenerator and the directory to write to.

    • This calls the IPointGenerator and CompoundModel (objects that define a scan)

    • It also queries Malcolm via the MalcolmEpicsV4Connection.send() method using a GET type MalcolmMessage to find out what axes can be moved. For example, type=GET and endPoint=simultaneousAxes.

      MalcolmDevice - Sending message to malcolm device: 
        MalcolmMessage [type=GET, id=54, param=null, endpoint=simultaneousAxes, method=null, message=null, args=null, value=null]
      

    This returns the values ‘stagex’ and ‘stagey’ as the axes that can be moved:

    MalcolmDevice - Received reply from malcolm device: 
    MalcolmMessage [type=RETURN, id=54, param=null, endpoint=simultaneousAxes, method=null, message=null, args=null, 
    value=StringArrayAttribute [value=[stagex, stagey], getName()=simultaneousAxes]]
    
  • Configures the device by sending a MalcolmMessage (created using a IMalcolmMessageGenerator method) to the MalcolmEpicsV4Connection with type=CALL, method=CONFIGURE and args set as the EpicsMalcolmModel previously created.
    E.g.

    MalcolmDevice - Sending message to malcolm device: 
      MalcolmMessage [type=CALL, id=66, param=null, endpoint=null, method=configure, message=null, 
      args=org.eclipse.scanning.malcolm.core.MalcolmDevice$EpicsMalcolmModel@5286e91d, value=null]
    

    At this point, the ‘state change’ listener receives a message from Malcolm with a state change to show that the Malcolm device is configuring.

    MalcolmDevice - Received malcolm state change with message: 
      MalcolmMessage [type=UPDATE, id=1, param=null, endpoint=state, method=null, message=null, args=null, value=ChoiceAttribute [value=Configuring, getName()=state]]
    

If successful, then a MalcolmMessage of type=RETURN will be returned from the MalcolmEpicsV4Connection to the MalcolmDevice and this will signify the end of the configuring process.

15.2.3. Running the MalcolmDevice

After configuring the scan devices, the devices are then run in order of priority using a LevelRunner/DeviceRunner. If the device in the scan is a MalcolmDevice then the MalcolmDevice.run() method will be called. This carries out the following:

  • A MalcolmMessage is sent via the MalcolmEpicsV4Connection.send() method with type=CALL and method=RUN. The device will enter the state ‘running’ and the ‘state change’ listener for the MalcolmDevice will receive this event, e.g.

    Received Malcolm state change with message: 
      MalcolmMessage [type=UPDATE, id=1, param=null, endpoint=state, method=null, message=null, args=null, value=ChoiceAttribute [value=Running, getName()=state]]
    

    The scan is then running and the ‘completed steps’ listener receives updates from the device in the form of MalcolmMessages each time a step is completed, e.g.

    Received state change event with message: 
      MalcolmMessage [type=UPDATE, id=2, param=null, endpoint=completedSteps, method=null, message=null, args=null, 
      value=NumberAttribute [value=0, getName()=completedSteps]]
    Received state change event with message: 
      MalcolmMessage [type=UPDATE, id=2, param=null, endpoint=completedSteps, method=null, message=null, args=null, 
      value=NumberAttribute [value=1, getName()=completedSteps]]
    

    (Note the value)

  • After completion of the scan (i.e. when all steps defined in the EpicsMalcolmModel have been completed), the ‘state change’ listeners receives the state change from the device:

    Received malcolm state change with message: 
      MalcolmMessage [type=UPDATE, id=1, param=null, endpoint=state, method=null, message=null, args=null, value=ChoiceAttribute [value=PostRun, getName()=state]]
    

    After ‘PostRun’, Malcolm will enter the state ‘Finished’:

    Received malcolm state change with message: 
     MalcolmMessage [type=UPDATE, id=1, param=null, endpoint=state, method=null, message=null, args=null, value=ChoiceAttribute [value=Finished, getName()=state]]
    

    In order to return Malcolm to ‘Ready’ state (indicating that it is ready to run another scan), reset() is called in MalcolmDevice which sends a MalcolmMessage with type=CALL and method=RESET through the MalcolmEpicsV4Connection. Following this the Malcolm device will ‘reset’ and respond with a state change to state=Ready:

    Received malcolm state change event MalcolmStateChangedEvent [deviceState=Ready, previousState=Finished, getMalcolmDeviceName()=BL45P-ML-SCAN-02, getMessage()=null]
    

The Malcolm device is now ready to be configured/run again which may be the case if the Malcolm device is used as an inner scan within an outer scan. In this case the outer scan ‘Scannable’ will move and then run() will once again be called on the MalcolmDevice for the new outer position. On the other hand, if this is not the case and Malcolm is the only device involved in the scan then the can will be complete but the device is still ready to run a completely new scan. See below for more details.

15.3. Use of Malcolm in ‘new’ style scanning

A MalcolmDevice can be used in the new style of scanning (i.e Mapping view). Within the new scanning framework it is essentially treated as just a ‘detector’. In fact, it would be the only ‘detector’ involved as it will manage all other detectors underneath it (note it is not actually a detector but treated like one in scanning). A Malcolm device can be used on its own (i.e. the only device scanned), or as part of a layered scan. In the latter case the Malcolm device will be the inner scan. For example, the outer scan may be a scan of beam energy and the inner scan will be of the Malcolm device which may move the x and y stages.

Please reference the use case section for full details of new-style scanning including details of how the calls are made to configure() and run() devices during the scan.

The ScanProcess.execute() method calls runScan which begins by creating RunnableDevice. Firstly the detectors are made into RunnableDevices, which are subsequently configured. This calls the configure() method on the MalcolmDevice, which configures the Malcolm device with the scan model. Following configuration of the detectors (which in this instance will just be the Malcolm device), all other devices are configured. This includes the AcquisitionDevice which is responsible for running the actual scan. It sets up the Scannables and configures the NexusScanFileManager. During this process a call will be made to the AbstractMalcolmDevice to getNexusProvider. This will eventually call a method in MalcolmDevice to getDatasets. The MalcolmDevice sends a type=GET MalcolmMessage with endpoint=datasets through the MalcolmEpicsV4Connection, e.g.

MalcolmDevice - Sending message to malcolm device: 
  MalcolmMessage [type=GET, id=69, param=null, endpoint=datasets, method=null, message=null, args=null, value=null

The Malcolm device will return the datasets to the MalcolmEpicsV4Conenction which returns it to the MalcolmDevice:

MalcolmDevice - Received reply from malcolm device: 
  MalcolmMessage [type=RETURN, id=69, param=null, endpoint=datasets, method=null, message=null, args=null, 
  value=TableAttribute [name=datasets, tableValue=org.eclipse.scanning.api.malcolm.MalcolmTable@72a8e6bb, headings=[name, filename, type, rank, path, uniqueid]]]

After configuration, the scan is run (normally with blocking scan). The AcquisitionDevice.run() method is called. Firstly the Malcolm listener are added so that the inner Malcolm scan can be monitored. Next run() is called on the ‘runners’ (LevelRunner/DeviceRunner). As the Malcolm device is treated as a detector in scanning, the DeviceRunner is used (implements LevelRunner). According to the level set on the device, the run() method will be called on the MalcolmDevice. The Malcolm device will run to completion for this scan (see above for more details). The MalcolmDevice has monitors that will monitor the progress and send out events. When the DeviceRunner ‘hears’ that the scan has finished it will return. Assuming that the Malcolm device is being scanned as part of a ‘layered’ scan, this will mean the inner scan has completed. The outer scan will then be signalled to move to its next scan position (e.g. change the energy). Once complete, the MalcolmDevice.run() will be called again by the DeviceRunner to perform another scan at this energy. This will continue until the outer scan has also completed. This is the end of the scan. The AcquisitionDevice will call close() to tidy up and finally use the AnnotationManager to invoke @ScanFinally (see ‘New Style Scanning’ for more details). This is what invokes the MalcolmDevice.reset() method described above to reset Malcolm and return it to a ‘ready’ state.

15.4. Future: Detector synchronisation with Malcolm

The future plan is to have Malcolm as the only ‘source of truth’ for the detectors. In this respect, GDA will not communicate with the devices/detectors themselves but will ask Malcolm for all the information that it requires. For example GDA might want to know the detector name or minimum exposure time for all detectors involved in a scan. Malcolm will also persist the values saved from previous scans.

Currently the Malcolm configuration is stored in both GDA and Malcolm (from the Spring configuration). In fact, only the Malcolm device address will be configured in GDA and all other Malcolm properties will be returned from Malcolm itself when queried by GDA.

The architect should look as follows:

scale70

15.4.1. UI

The ‘Detector Configuration’ dialog is available from the cog wheel button in the Mapping perspective. This dialog will request all of the detectors associated with a scan from the IRunnableDeviceService. It will be pre-populated with the defaults or last used setting persisted by Malcolm. The dialog will be validated within GDA in real time as the user begins editing. Finally, on saving the current settings, the settings are validated by Malcolm and a message will be displayed if the validation fails in which case the dialog will remain open for amendment.

15.4.2. The Malcolm Interface

This will be used to abstract the communication with Malcolm. This will talk to the MalcolmEpicsV4Connection which sits just on top of the EpicsV4Connection class and contains Malcolm specific code. The   EpicsV4Connection class is a generic interface to EPICS V4 endpoints from GDA (i.e. not just providing communication with Malcolm). The MalcolmEpicsV4Connection will simply use this class to do the ‘raw’ communicating will Malcolm and so for this case the EpicsV4Connection represents the last point in GDA before information is sent to Malcolm via the EPICS V4 classes.

Note: the current version (May 2019) of the MalcolmEpicsV4Connection class was originally intended to be a generic interface but was changed since it only ever talked to Malcolm. It contains only a small amount of ‘Malcolm’ specific code and so in the future this class will be changed back to being completely generic. In this way it will become the EpicsV4Connection class mentioned above and a new class with the Malcolm specific code will sit on top of it.

The MalcolmInterface layer also allows for a MalcolmTestStub to be created which can be used in GDA tests and development environments.

The MalcolmInterface layer will contain the following definitions:

Method

Direction

Description

getScanDetectors

GDA->Malcolm

For a given Malcolm and Malcolm scan, a map of detectors and detector characteristics are returned.

Detector values:

  • PV

  • Display Name

  • Minimum Sample Rate

  • Estimated Minimum Exposure

  • Last frames per point (if first time of use then the default value is returned)

  • Last exposure used (if first time of use then the default value is returned)

validateDetectors

GDA->Malcolm

For a given Malcolm and Malcolm Scan, a map of detector settings area passed to Malcolm for validation. A boolean ‘TRUE’ is returned id valid or an exception is thrown containing an error message if the validation fails (which will be passed from Malcolm).

scan

GDA->Malcolm

Regular scan with request containing generator, file path and detector settings.

15.4.3. Server side

The server-side data model is almost unchanged from the existing data model with the exception of the additional MalcolmDetectorModel class. The IRunnableDeviceService must be updated to allow for the extra request for detector information and validation methods required by the UI and Malcolm Interface layers.

A partial class diagram is shown below containing only the information required for detector synchronisation.

scale70

invisible invisible invisible invisible invisible invisible invisible invisible invisible