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
.
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. TheMalcolmMessageGenerator
is simply a factory class for generating messages to send to and from Malcolm. An EpicsV4MessageMapper is also created. This is used byMalcolmEpicsV4Connection
methods to convert a PV structure received from Malcolm to a MalcolmMessage (or vice versa by converting aMalcolmMessage
to a PV structure). This is done usingPVMarshaller
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 aMalcolmMessage
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 theMalcolmDevice
class). Within aMalcolmMessage
, a ‘method’ field can be defined usingMalcolmMethod
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 aMalcolmMessage
(using theEpicsV4MessageMapper
).
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 aMacolmDevice
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 fromMalcolmEpicsV4Connection
to theMalcolmDevice
(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 fromMalcolmEpicsV4Connection
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. AnEpicsV4ClientMonitorRequester
object is created from the input listener and theMalcolmMessage
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 callsstart()
on the monitor to begin monitoring. Finally an instance of anEpicsV4MonitorListener
is created with theIMalcolmConnectionEventListerner
andPvaClientMonitor
and added to the map of listeners held by theMalcolmEpicsV4Connection
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 theMalcolmMessageGenerator
to create a subscription message with aMalcolmMessage
ofType = subscribe
and anendpoint = state
. The subscribe method described above will then be called with this message and relayed to thePvaClientMonitor
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 ownStateChangeRequester
instantiated with the inputIMalcolmConnectionStateListener
.
unsubscribe(IMalcolmDevice, MalcolmMessage, IMalcolmConnectionEventListener…): This is commonly used to unsubscribe from event monitoring on termination.
If a list of
IMalcolmConnectionEventListeners
is provided then eachEpicsV4MonitorListener
will be stopped and removed from the map held byMalcolmEpicsV4Connection
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.
sendCallMessage(IMalcolmDevice, MalcolmMessage) - [sequence diagram]
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 theEpicsV4MessageMapper
, which subsequently calls on the PVMarshaller class and its serialiser to do the conversion. TheMalcolmMessageSerialiser
takes theMalcolmMessage
‘method’ and ‘args’ fields and maps them to the PVStructure ‘method’ and ‘parameters’ fields, respectively (see example just below to see this). From thePVStructure
, 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 aPVStructure
to aMalcolmMessage
(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. TheMalcolmMessage
created and sent to theMalcolmEpicsV4Connection
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
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}]
sendGetMessage(IMalcolmDevice, MalcolmMessage) - [sequence diagram]
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 thePvaClientGet
and from this gets thePVStructure
(getPVStructure()). Finally this is converted back to aMalcolmMessage
(sequence diagram) and returned (destroying the connection as well). If an error occurs, then theMalcolmMesssage
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 simpleget
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 thesendGetMessage
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]]
sendPutMessage(IMalcolmDevice, MalcolmMessage) - [sequence diagram]
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 thePvaClientPut
object and gets thePVStructure
(getPVStructure()). Using theEpicsV4MessageMapper
it populates the PVStructure with the put parameter name and value. Aput()
is called on thePvaClientPut
. If successful theMalcolmMessage
type is set to RETURN and the channel connection is terminated. If it fails then aMalcolmMessage
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.
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 ofMalcolmEvent
s: 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
withtype=GET
and withendPoint=state
using aIMalcolmMessageGenerator
method.Calls on the
MalcolmEpicsV4Connection.send()
method with theMalcolmMessage
created above. It will wait for aMalcolmMessage
to be returned with a given timeout.On receipt of the
MalcolmMessage
, thevalue
of theendPoint
is extracted from the message, i.e. it will extract the value forendPoint=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
withtype=SUBSCRIBE
andendPoint=state
using aIMalcolmMessageGenerator
meth od.It calls on the
MalcolmEpicsV4Connection.subscribe()
method with theMalcolmMessage
created above and theIMalcolmConnectionEventListener
created for the ‘state change’ listener.Creates a
MalcolmMessage
withtype=SUBSCRIBE
andendPoint=completedSteps
(as above).It calls on the
MalcolmEpicsV4Connection.subscribe()
method with theMalcolmMessage
created above and theIMalcolmConnectionEventListener
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 aGET
typeMalcolmMessage
to find out what axes can be moved. For example,type=GET
andendPoint=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 aIMalcolmMessageGenerator
method) to theMalcolmEpicsV4Connection
withtype=CALL
,method=CONFIGURE
andargs
set as theEpicsMalcolmModel
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 theMalcolmEpicsV4Connection.send()
method withtype=CALL
andmethod=RUN
. The device will enter the state ‘running’ and the ‘state change’ listener for theMalcolmDevice
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
MalcolmMessage
s 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 inMalcolmDevice
which sends aMalcolmMessage
withtype=CALL
andmethod=RESET
through theMalcolmEpicsV4Connection
. Following this the Malcolm device will ‘reset’ and respond with a state change tostate=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 Scannable
s 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:
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 theEpicsV4Connection
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:
|
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.