Spock

Spock is the prefered CLI for sardana. It is based on IPython. Spock automatically loads other IPython extensions like the ones for PyTango and pylab. It as been extended in sardana to provide a customized interface for executing macros and automatic access to sardana elements.

Spock tries to mimic SPEC’s command line interface. Most SPEC commands are available from spock console.

../_images/spock_snapshot01.png

Spock CLI in action

Starting spock from the command line

To start spock just type in the command line:

marge@machine02:~$ spock

This will start spock with a “default profile” for the user your are logged with. There may be many sardana servers running on your system so the first time you start spock, it will ask you to which sardana system you want to connect to by asking to which of the existing doors you want to use:

marge@machine02:~$ spock
Profile 'spockdoor' does not exist. Do you want to create one now ([y]/n)?
Available Door devices from homer:10000 :
On Sardana LAB-01:
    LAB-01-D01 (running)
    LAB-01-D02 (running)
On Sardana LAB-02:
    LAB-02-D01
Please select a Door from the list? LAB-01-D01
Storing ipy_profile_spockdoor.py in /home/marge/.ipython... [DONE]

Note

If only one Door exists in the entire system, spock will automatically connect to that door thus avoiding the previous questions.

Afterward, spock CLI will start normally:

Spock 7.2.1 -- An interactive sardana client.

help      -> Spock's help system.
object?   -> Details about 'object'. ?object also works, ?? prints more.

Spock's sardana extension 1.0 loaded with profile: spockdoor (linked to door 'LAB-01-D01')

LAB-01-D01 [1]:

Note

If you want to connect to another gate you need to create a new spock profile.

Starting spock with a custom profile

spock allows each user to start a spock session with different configurations (known in spock as profiles). All you have to do is start spock with the profile name as an option.

If you use ipython version > 0.10 you can do it using –profile option:

spock --profile=<profile name>

Example:

marge@machine02:~$ spock --profile=D1

Otherwise (ipython version 0.10) you can do it using -p option:

spock -p <profile name>

Example:

marge@machine02:~$ spock -p D1

The first time a certain profile is used you will be asked to which door you want to connect to (see previous chapter).

Note

Spock profiles are stored by default in ~/.ipython/profile_<profile_name> directory. For more information please refer to the IPython documentation.

Spock IPython Primer

As mentioned before, spock console is based on IPython. Everything you can do in IPython is available in spock. The IPython documentation provides excelent tutorials, tips & tricks, cookbooks, videos, presentations and reference guide. For comodity we summarize some of the most interesting IPython chapters here:

Executing macros

Executing sardana macros in spock is the most useful feature of spock. It is very simple to execute a macro: just type the macro name followed by a space separated list of parameters (if the macro has any parameters). For example, one of the most used macros is the wa (stands for “where all”) that shows all current motor positions. To execute it just type:

LAB-01-D01 [1]: wa

Current Positions  (user, dial)

   Energy       Gap    Offset
 100.0000   43.0000  100.0000
 100.0000   43.0000  100.0000

(user for user position (number above); dial for dial position (number below).)

A similar macro exists that only shows the desired motor positions (wm):

LAB-01-D01 [1]: wm gap offset
                Gap     Offset
User
 High         500.0      100.0
 Current      100.0       43.0
 Low            5.0     -100.0
Dial
 High         500.0      100.0
 Current      100.0       43.0
 Low            5.0     -100.0

To get the list of all existing macros use lsdef:

LAB-01-D01 [1]: lsdef
               Name        Module                                            Brief Description
------------------- ------------- ------------------------------------------------------------
             a2scan         scans two-motor scan.     a2scan scans two motors, as specifi[...]
             a2scan         scans three-motor scan .     a3scan scans three motors, as sp[...]
              ascan         scans Do an absolute scan of the specified motor.     ascan s[...]
            defmeas        expert                               Create a new measurement group
              fscan         scans N-dimensional scan along user defined paths.     The mo[...]
                lsa         lists                                   Lists all existing objects
                lsm         lists                                             Lists all motors
              lsmac        expert                                            Lists all macros.
                 mv      standard                   Move motor(s) to the specified position(s)
                mvr      standard            Move motor(s) relative to the current position(s)
                 wa      standard                                     Show all motor position.
                 wm      standard                   Show the position of the specified motors.
<...>

You can also use lsmac if you want to know a macro’s location.

Miscellaneous

  • lsm shows the list of motors.

  • lsct shows the list of counters.

  • lsmeas shows the list of measurement groups

  • lsctrl shows the list of controllers

  • sar_info object displays detailed information about an element

Stopping macros

Some macros may take a long time to execute. To stop a macro in the middle of its execution type Control+c. If the stopping process last too long, you may trigger the aborting process with a second Control+c. Here be patient, further issuing of Control+c may leave your macro in an uncontrolled way. Use them only if you are sure that the aborting process will not bring your system to a safe state.

Macros that move motors or acquire data from sensors will automatically stop all motion and/or all acquisition.

While stopping and aborting macros Spock reports you what happens behind the scene with informative messages:

LAB-01-D01 [1]: ascan mot01 0 10 100 0.1
Operation will be saved in /tmp/test.h5 (HDF5::NXscan from NXscanH5_FileRecorder)
Scan #342 started at Wed Sep  9 23:01:14 2020. It will take at least 0:00:10.174246
                                       tg_test
 #Pt No    mot01      ct01     gct01    double_scalar     dt
   0         0        0.1     2.98023e-08      243.47     0.0967791
   1        0.1       0.1     5.91929e-08      243.47     0.239136
   2        0.2       0.1     1.1595e-07      243.47     0.384191
^C
Ctrl-C received: Stopping...
Stopping Motion(['mot01']) reserved by ascan
Motion(['mot01']) stopped
Stopping mntgrp_expconf reserved by ascan
mntgrp_expconf stopped
Operation saved in /tmp/test.h5 (HDF5::NXscan)
Scan #342 ended at Wed Sep  9 23:01:15 2020, taking 0:00:01.055814. Dead time 33.7% (motion dead time 12.8%)
Executing ascan.on_stop method...
Stopping done!

Executing macro sequences

Macro sequences can be composed and executed directly in Spock using multiline input feature of IPython. One can enter the multiline input edit mode with the Control+o keyboard sequence. In the below example it was pressed after typing %ct:

LAB-01-D01 [1]: %ct
           ...:

and then simply add macros to be executed in the sequence:

LAB-01-D01 [1]: %ct
           ...: %ascan mot01 0 10 100 0.1
           ...: %lsm

Sequences can be stored in a file and loaded into IPython using %load magic command:

LAB-01-D01 [1]: %load /tmp/sequence_file

which after hitting Enter will load the file content into a multiline input:

LAB-01-D01 [1]: # %load /tmp/sequence_file
           ...: lsmac
           ...: mv mot01 10
           ...: dscan mot01 -5 5 100 0.1
           ...:

You can even mix the macros execution with Python code, for example to compose loops, conditions, etc:

LAB-01-D01 [1]: for i in range(10):
           ...:     print("Iteration {}".format(i))
           ...:     %ascan mot01 0 10 100 0.1
           ...:

Macro sequences can be stopped as an arbitrary macro execution (see Stopping macros), so using Control+c. Sardana will take care of propagating the KeyboardInterrupt exception to IPython. If you do not explicitelly handle this exception it will be raised in Spock and you will see an exception report.

Exiting spock

To exit spock type Control+d or exit() inside a spock console.

Getting help

spock not only knows all the macros the sardana server can run but it also information about each macro parameters, result and documentation. Therefore it can give you precise help on each macro. To get help about a certain macro just type the macro name directly followed by a question mark(‘?’):

LAB-01-D01 [1]: ascan?

Syntax:
        ascan <motor> <start_pos> <final_pos> <nr_interv> <integ_time>

Do an absolute scan of the specified motor.
    ascan scans one motor, as specified by motor. The motor starts at the
    position given by start_pos and ends at the position given by final_pos.
    The step size is (start_pos-final_pos)/nr_interv. The number of data points collected
    will be nr_interv+1. Count time is given by time which if positive,
    specifies seconds and if negative, specifies monitor counts.

Parameters:
        motor : (Motor) Motor to move
        start_pos : (Float) Scan start position
        final_pos : (Float) Scan final position
        nr_interv : (Integer) Number of scan intervals
        integ_time : (Float) Integration time

Moving motors

A single motor may be moved using the mv motor position macro. Example:

LAB-01-D01 [1]: mv gap 50

will move the gap motor to 50. The prompt only comes back after the motion as finished.

Alternatively, you can have the motor position displayed on the screen as it is moving by using the umv macro instead. To stop the motor(s) before they have finished moving, type Control+c.

You can use the mvr motor relative_position macro to move a motor relative to its current position:

LAB-01-D01 [1]: mvr gap 2

will move gap by two user units.

Counting

You can count using the ct value macro. Without arguments, this macro counts for one second using the active measurement group set by the environment variable ActiveMntGrp.

Door_lab-01_1 [1]: ct 1.6

Wed Jul 11 11:47:55 2012

  ct01  =         1.6
  ct02  =         3.2
  ct03  =         4.8
  ct04  =         6.4

To see the list of available measurement groups type lsmeas. The active measuremnt group is marked with an asterisk (*):

Door_lab-01_1 [1]: lsmeas

  Active        Name   Timer Experim. channels
 -------- ---------- ------- -----------------------------------------------------------
    *       mntgrp01    ct01 ct01, ct02, ct03, ct04
            mntgrp21    ct04 ct04, pcII0, pcII02
            mntgrp24    ct04 ct04, pcII0

to switch active measurement groups type senv ActiveMntGrp mg_name.

You can also create, modify and select measurement groups using the expconf command

Scanning

Sardana provides a catalog of different standard scan macros. Absolute-position motor scans such as ascan, a2scan and a3scan move one, two or three motors at a time. Relative-position motor scans are dscan, d2scan and d3scan. The relative-position scans all return the motors to their starting positions after the last point. Two motors can be scanned over a grid of points using the mesh scan.

Continuous versions exist of many of the standard scan macros (e.g. ascanc, d3scanc, meshc,…). The continuous scans differ from their standard counterparts (also known as step scans) in that the data acquisition is done without stopping the motors. Continuous scans are generally faster but less precise than step scans, and some details must be considered (see Scans).

As it happens with ct, the scan macros will also use the active measurement group to decide which experiment channels will be involved in the operation.

Here is the output of performing an ascan of the gap in a slit:

LAB-01-D01 [1]: ascan gap 0.9 1.1 20 1
ScanDir is not defined. This operation will not be stored persistently. Use "senv ScanDir <abs directory>" to enable it
Scan #4 started at Wed Jul 11 12:56:47 2012. It will take at least 0:00:21
 #Pt No    gap       ct01      ct02      ct03
  0        0.9          1       4604      8939
  1       0.91          1       5822      8820
  2       0.92          1       7254      9544
  3       0.93          1       9254      8789
  4       0.94          1      11265      8804
  5       0.95          1      13583      8909
  6       0.96          1      15938      8821
  7       0.97          1      18076      9110
  8       0.98          1      19638      8839
  9       0.99          1      20825      8950
 10          1          1      21135      8917
 11       1.01          1      20765      9013
 12       1.02          1      19687      9135
 13       1.03          1      18034      8836
 14       1.04          1      15876      8901
 15       1.05          1      13576      8933
 16       1.06          1      11328      9022
 17       1.07          1       9244      9205
 18       1.08          1       7348      8957
 19       1.09          1       5738      8801
 20        1.1          1       4575      8975
Scan #4 ended at Wed Jul 11 12:57:18 2012, taking 0:00:31.656980 (dead time was 33.7%)

Scan storage

As you can see, by default, the scan is not recorded into any file. To store your scans in a file, you must set the environment variables ScanDir and ScanFile:

LAB-01-D01 [1]: senv ScanDir /tmp
ScanDir = /tmp

LAB-01-D01 [2]: senv ScanFile scans.h5
ScanFile = scans.h5

Sardana will activate a proper recorder to store the scans persistently (currently, .h5 will store in NeXus format. All other extensions are interpreted as SPEC format).

You can also store in multiples files by assigning the ScanFile with a list of files:

LAB-01-D01 [2]: senv ScanFile "['scans.h5', 'scans.dat']"
ScanFile = ['scans.h5', 'scans.dat']

Viewing scan data

You can show plots for the current scan by launching the showscan command.

Sardana provides also a scan data viewer for scans which were stored in a NeXus file: Showscan offline. It can be launched using showscan offline spock command. It accepts scan number as an argument, and will show the last scan when invoked without arguments.

The history of scans is available through the scanhist macro:

LAB-01-D01 [1]: scanhist
   #                           Title            Start time              End time        Stored
 --- ------------------------------- --------------------- --------------------- -------------
   1    dscan mot01 20.0 30.0 10 0.1   2012-07-03 10:35:30   2012-07-03 10:35:30   Not stored!
   3    dscan mot01 20.0 30.0 10 0.1   2012-07-03 10:36:38   2012-07-03 10:36:43   Not stored!
   4   ascan gap01 10.0 100.0 20 1.0              12:56:47              12:57:18   Not stored!
   5     ascan gap01 1.0 10.0 20 0.1              13:19:05              13:19:13      scans.h5

Accessing macro data

The command macrodata allows to retrieve the data of the last macro run in spock. If this macro does not provide any data an error message is thrown. Example accesing scan data:

Door_1 [9]: ascan mot17 1 10 2 1
ScanDir is not defined. This operation will not be stored persistently. Use "expconf" (or "senv ScanDir <abs directory>") to enable it
Scan #2 started at Tue Feb 13 11:16:18 2018. It will take at least 0:00:05.048528
0         1         1         3         4      0.865325
1        5.5        1         3         4      2.51148
2         10        1         3         4      4.16662
Scan #2 ended at Tue Feb 13 11:16:24 2018, taking 0:00:05.201949. Dead time 42.3% (motion dead time 40.5%)
#Pt No    mot17      ct17      ct19      ct20       dt
Door_1 [10]: r = %macrodata
Door_1 [11]: r[0].data.keys()
Result [11]:
['point_nb',
'timestamp',
'mot17',
'haso111n:10000/expchan/ctctrl05/4',
'haso111n:10000/expchan/ctctrl05/1',
'haso111n:10000/expchan/ctctrl05/3']
Door_1 [12]: r[0].data['point_nb']
Result [12]: 0
Door_1 [13]: r[0].data['mot17']
Result [13]: 1.0
Door_1 [16]: r[0].data['haso111n:10000/expchan/ctctrl05/1']
Result [16]: 1.0

Changing appearance with View Options

The View Options allow the users to customize the output displayed by certain macros. They are set by the macro setvo. The macro usetvo returns the View Options to the default value. And the macro lsvo lists the current values.

Available View Options:

  • ShowDial: Select if the dial information of the motor should be displayed.
    Default value False (no dial but only user information).

  • ShowCtrlAxis: Select if the name of the controller the motor belongs to should be displayed. Default value False (no controller name).

  • PosFormat: Set the number of decimal digits displayed in the motor position/limits.
    Default value -1 (all digits).

  • OutputBlock: Set if the line information during scans is appended to the output or updated.
    Default value False (lines are appended to the displayed output during the scan).

  • DescriptionLength: Length (number of characters) of the macro description printed by lsdef macro.
    Default value 60.

Editing macros

The command edmac allows to edit the macros directly from spock. See Writing macros section.

Debugging problems

Spock provides some commands that help to debug or recognize the errors in case a macro fails when being executed.

  • www prints the error message from the last macro execution

  • debug used with on as parameter activates the print out of the debug messages during macro execution. Set it to off to deactivate it.

  • post_mortem prints the current logger messages. If no argument is specified it reads the debug stream. Valid values are output, critical, error, warning, info, debug and result.

Spock syntax

Spock syntax is used to execute macros. It is based on space separated list of parameter values. If the string parameter values contain spaces itself these must be enclosed in quotes, either single quotes '' or double quotes "".

The spock syntax was extended with the use of square brackets [] for macros which define repeat parameters as arguments. Repeat parameter values must be enclosed in square brackets. If the repeat parameter is composed from more than one internal parameter its every repetition must be enclosed in another square brackets as well.

For example, the move_with_timeout macro:

class move_with_timeout(Macro):
    """Execute move with a timeout"""

    param_def = [
        ['m_p_pair',
         [['motor', Type.Motor, None, 'Motor to move'],
          ['pos',  Type.Float, None, 'Position to move to']],
         None, 'List of motor/position pairs'],
        ['timeout', Type.Float, None, 'Timeout value']
    ]

    def run(self, *args, **kwargs):
        pass

Must use the square brackets for the m_p_pair parameter and its repeats:

Door_1 [1]: move_with_timeout [[th 8.4] [tth 16.8]] 50

However for the commodity reasons the square brackets may be skipped. The following examples explain in which cases.

Repeat parameter is the last one

When the repeat parameter is the last one in the parameters definition both square brackets (for the repeat parameter and for the repetition) may be skipped.

For example, the move macro:

class move(Macro):
    """Execute move"""

    param_def = [
        ['m_p_pair',
         [['motor', Type.Motor, None, 'Motor to move'],
          ['pos',  Type.Float, None, 'Position to move to']],
         None, 'List of motor/position pairs']
    ]

    def run(self, *args, **kwargs):
        pass

May skip the square brackets for the m_p_pair parameter and its repeats:

Door_1 [1]: move th 8.4 tth 16.8

This is equivalent to:

Door_1 [1]: move [[th 8.4] [tth 16.8]]

Repeat parameter has only one internal parameter

When the repeat parameter contains only one internal parameter the square brackets for the repetition must be skipped.

For example, the power_motor macro:

class power_motor(Macro):
    """Power on/off motor(s)"""

    param_def = [
        ['motor_list', [['motor', Type.Motor, None, 'motor name']],
            None, 'List of motors'],
        ['power_on', Type.Boolean, None, 'motor power state']
    ]

    def run(self, *args, **kwargs):
        pass

Must use the square brackets for the motor_list parameter but not for its repeats:

Door_1 [1]: power_motor [th tth] True

Repeat parameter has only one internal parameter and only one repetition value

When the repeat parameter contains only one internal parameter and you would like to pass only one repetition value then the square brackets for the repeat parameter may be skipped as well resulting in no square brackets being used.

This assumes the power_motor macro from the previous example. The following two macro executions are equivalent:

Door_1 [1]: power_motor th True
Door_1 [2]: power_motor [th] True

A set of macro examples defining complex repeat parameters can be found in Macro parameter examples. You can see the invocation example for each of these macros in its docstring.

Using spock as a Python console

You can write any Python code inside a spock console since spock uses IPython as a command line interpreter. For example, the following will work inside a spock console:

LAB-01-D01 [1]: def f():
           ...:     print("Hello, World!")
           ...:
           ...:

LAB-01-D01 [2]: f()
Hello, World!

Using spock as a Tango console

As mentioned in the beginning of this chapter, the sardana spock automatically activates the PyTango ‘s ipython console extension [1]. Therefore all Tango features are automatically available on the sardana spock console. For example, creating a tango.DeviceProxy will work inside the sardana spock console:

LAB-01-D01 [1]: tgtest = Device("sys/tg_test/1")

LAB-01-D01 [2]: print(tgtest.state())
RUNNING

Footnotes