Sardana Home Page¶
Sardana is a software suite for Supervision, Control and Data Acquisition in scientific installations. It aims to reduce cost and time of design, development and support of the control and data acquisition systems. Sardana development was started at the ALBA synchrotron and today is supported by a larger community which includes several other laboratories and individuals (ALBA, DESY, MaxIV, Solaris, ESRF).
You can download Sardana from PyPi, check its Documentation or get support from its community and the latest code from the project page.
Projects related to Sardana¶
- Sardana uses Taurus for control system access and user interfaces
- Sardana is based on Tango
- The command line interface for Sardana (Spock) is based on IPython
Sardana 1.6 Documentation¶
Sardana is a software suite for Supervision, Control and Data Acquisition in scientific installations.
User’s Guide¶
Overview¶
Sardana is the control program initially developed at ALBA. Our mission statement:
Produce a modular, high performance, robust, and generic user environment for control applications in large and small installations. Make Sardana the generic user environment distributed in the Tango project and the standard basis of collaborations in control.
Up to now, control applications in large installations have been notoriously difficult to share. Inspired by the success of the Tango collaboration, ALBA decided to start the creation of a generic tool to enlarge the scope of the Tango project to include a standard client program - or better a standard generic user environment. From the beginning our aim has been to involve others in this process. At this moment in time the user environment consists of a highly configurable standard graphical user interface, a standard command line interface understanding SPEC commands, and a standard way to compose new applications either by programming or with a graphical tool. It further consists of a standard macro executer, standard set of macros, a standard range of common hardware types (like motors, counters, cameras and so on) and a configuration editor to set all this up. The origin of the Sardana name comes from a Catalan dance to honor the region where the ALBA synchrotron is build. The toolkit to build Sardana has been C++, Python, Qt and Tango. If you like the tools you will love Sardana.
What do we “sell” to our users¶
Let’s start our excursion into the Sardana world by a word of caution. We will talk a lot about technical possibilities and implementation details. Our users will judge us on the ease of use of the final GUI, its robustness and the features it offers. There are millions of ways to arrive at this end result. Our claim is however that by doing it the Sardana way and developing the application out of lego components in a collaborative environment we will arrive at higher quality software with much higher efficiency.
The following screen shot of an early prototype of a specific beamline application should serve as a reminder of this final goal.

Inside this application we have many features common to other beamline control applications or w some accelerator applications. The following screen shot shows such a standard application which has been done without programming - just by configuring the application. This illustrates one of the design guidelines in Sardana: Always provide a generic interface which can be specialized for an application.

Starting a procedure¶
At the heart of the Sardana system are standard reusable procedures. From past experiences, the importance of standard procedures has been realized and has influenced most of the major design decisions. To illustrate this point, please let me walk you through different ways how to start such a procedure without going into too many details. You might want to think of a scan as an example. One way of starting a procedure is with a command line interface. Users familiar with SPEC will immediately recognize this way. In effect, inside Sardana most of the standard SPEC commands (including many diffractometer geometries thanks to Frédéric Picca from the SOLEIL synchrotron) are provided as standard procedures and can be invoked in the same way.

Every procedure can also be started from a GUI. This does not need any programming or configuration from the user of the system. When a new procedure is created, it is automatically visible inside the GUI and command line tools.

This GUI interface will mainly be used for procedures which are rarely used and where a specialized interface has not yet been developed. An example of how to use the same procedure in order to carry out energy spread and emittance measurements is presented in the following picture.

The standard Qt designer can be used to create new graphical elements (widgets) and connect them to the system for even greater flexibility. The following screen shot shows the standard qt designer with some fancy widgets developed in house.

Taurus as a toolkit for applications¶
The GUI toolkit for Sardana is called Taurus. The graphical user interfaces in this paper have been created with this toolkit. It can be used in conjunction or independent from the rest of the system. It can be used to create custom panels inside the generic GUI or to create stand alone applications. Again, this approach of take what you need has been implemented to foster the widest range of collaborations. Almost all applications in the ALBA machine control system have been created with this toolkit. Creating the applications out of standard components has been proven to be extremely powerful. In the Graphical user interface screen shots chapter you can see some of the graphical user interfaces used.
Configure – don’t program¶
The Sardana system comes with a configuration editor to allow non-experts to add and configure components. The editor adapts dynamically to the hardware controllers present. New hardware controller can be easily written and integrated into the system without restarting it.

This configuration editor is currently being rewritten to be more wizard based and provide better guidance for the end user.
How to write your own procedure¶
Another example I would like to look into is how to write your own procedure. The simplest possible way is to use an editor to assemble commands and execute them. This batch files type of procedures are useful to automatically run procedures over night and for similar simple applications. The following screen shots show the procedure executer with this feature enabled.

To go further I would like to explain some internal details. All procedures are executed in a central place (called the macro server). There can be more than one macro server per system but for the following I assume the common case of a unique macro server. This macro server holds all the general procedures centrally. It provides a controlled environment for these procedures. They can be edited, run, debugged under its supervision. This allows for example to automatically roll back changes made in case of problems, log access and grant permissions. The procedures executed in the macro server provided by the current Sardana system are Python classes. A class is a way to group the different methods which concerns this procedure. As an example, in some procedures it could be possible to do very specific things in case the user orders an emergency abort of the procedure. The following example shows the procedure to move a motor.
As you can see in the example, the procedure must be documented and the input parameters described. From this information, the graphical user interface is constructed. It is also possible now to start the procedure from the command line interface and use the tab key to automatically complete the input. The actual action is actually carried out in the run method. The motor movement is started and the procedure waits until it arrives at its destiny. The Python classes should stay small and very simple. All complicated code can be put into modules and tested separately from the system.
How to adapt it to your own hardware¶
As the system has been thought from the beginning to be used at different institutes, no assumptions of the hardware used could be made. There exists therefore a mechanism to adapt the Sardana system to your own hardware. This adaptor also has another very important role to play. This is best explained with the motor as example. We consider more or less everything which can be changed in the system a motor. The term which should have better been used to describe this thing should have been therefore movable. A motor can be a temperature of a temperature controller which can be changed, a motor from an insertion device which needs a highly complicated protocol to be moved, or just about anything. Sometimes we also consider calculated value like H,K,L, the height of a table, and the gap of a slit to be a motor. All these different motors can be scanned with the same generic procedures without having to worry about on which elements it is working on. You can add one of these pseudo motors with the configuration editor. It is easily possible to add new types of pseudo motors. This has only to be done once and the Sardana system already provides a large variety of these types.

Please find in the following an example for adding a completely new type in the case of a slit.
The actual information how to create a motor of type slit is kept in the two methods calc_physical and calc_pseudo which can be used to do the transformation between the different coordinate systems. Or to say it in the language of Sardana between the pseudo motors gap and offset and the real motors left blade and right blade.

Once again the information in the beginning allows the graphical user interface to be created automatically once it is loaded into the system.
Symbolic Sketch¶
I would like to end this summary with a symbolic sketch of the different subsystems in Sardana.

The user will normally not be concerned with these implementation details. It is presented here to allow appreciating the modularity of the system.
Getting started¶
The next chapters describe the necessary steps to get started with sardana, from installation to having a running system on your machine.
Installing¶
- Install sardana:
From easy_install [1]
easy_install -U sardana
From source code:
- Download the sardana source code:
- from latest stable version of sardana (1.6)
- from SVN snapshot
Extract the downloaded tar.gz into a temporary directory
type [2]
python setup.py build python setup.py install
test the installation:
python -c "import sardana; print sardana.Release.version"
You can also work from SVN trunk checkout (please look here for instructions).
This chapter provides a quick shortcut to all windows packages which are necessary to run sardana on your windows machine
Install all dependencies:
- from Python(x,y) (by far the easiest choise)
- Download and install a python 2.6/2.7 compatible version of python(x,y) from here
Download and install latest PyTango from PyTango downdoad page
Download and install latest taurus from Taurus downdoad page
Finally download and install latest sardana from Sardana downdoad page
Sometimes it is convenient to work directly from the git source without installing. To do so, you can clone sardana from our main git repository:
git clone git://git.code.sf.net/p/sardana/sardana.git sardana
And then you can directly execute sardana binaries (Pool, MacroServer, Sardana or spock from the command line):
homer@pc001:~/workspace$ cd sardana
homer@pc001:~/workspace/sardana$ scripts/Sardana
Tip
If you plan to work normally from git without installing, you may want to add the sardana/scripts directory to your PATH variable and sardana/src to your PYTHONPATH variable.
Footnotes
[1] | This command requires super user previledges on linux systems. If your
user has them you can usually prefix the command with sudo:
sudo easy_install -U sardana . Alternatively, if you don’t have
administrator previledges, you can install locally in your user
directory with: easy_install --user sardana
In this case the executables are located at <HOME_DIR>/.local/bin. Make
sure the PATH is pointing there or you execute from there. |
[2] | setup.py install requires user previledges on linux systems. If your
user has them you can usually prefix the command with sudo:
sudo python setup.py install . Alternatively, if you don’t have
administrator previledges, you can install locally in your user directory
with: python setup.py install --user
In this case the executables are located at <HOME_DIR>/.local/bin. Make
sure the PATH is pointing there or you execute from there. |
Running Sardana as a tango server¶
Note
if you have Tango <= 7.2.6 without all patches applied, Sardana server will not work due to a known bug. Please follow the instructions from Running Pool and MacroServer tango servers separately instead.
Sardana is based on a client-server architecture. On the server part, sardana can be setup with many different configurations. Advanced details on sardana server configuration can be found here <LINK>.
This chapter describes how to run sardana server with it’s simplest configuration. The only decision you have to make is which name you will give to your system. From here on lab-01 will be used as the system name. Please replace this name with your own system name whenever apropriate.
The sardana server is called (guess what) Sardana. To start the server just type in the command line:
homer@pc001:~$ Sardana lab-01
The first time the server is executed, it will inform you that server lab-01 is not registered and it will offer to register it. Just answer ‘y’. This will register a new instance of Sardana called lab-01 and the server will be started. You should get an output like this:
homer@pc001:~$ Sardana lab-01
lab-01 does not exist. Do you wish create a new one (Y/n) ? y
DServer/Sardana/Lab-01 has no event channel defined in the database - creating it
That’t it! You now have a running sardana server. Not very impressive, is it? The Running the client chapter describes how to start up a CLI application called spock which connects to the sardana server you have just started through an object of type Door called Door_lab-01_1.
You can therefore skip the next chapter and go directly to Running the client.
Note
You should only read this chapter if you have Tango <= 7.2.6 without all patches applied. If you do, please follow in instructions from Running Sardana as a tango server instead.
It is possible to separate sardana server into two different servers (in the first sardana versions, this was actually the only way start the sardana system). These servers are called Pool and MacroServer. The Pool server takes care of hardware communication and MacroServer executes procedures (macros) using a connection to Pool(s) server(s).
To start the Pool server just type in the command line:
homer@pc001:~$ Pool lab-01
The first time the server is executed, it will inform you that server lab-01 is not registered and it will offer to register it. Just answer ‘y’. This will register a new instance of Pool called lab-01 and the server will be started. You should get an output like this:
homer@pc001:~$ Pool lab-01
lab-01 does not exist. Do you wish create a new one (Y/n) ? y
DServer/Pool/Lab-01 has no event channel defined in the database - creating it
Next, start the MacroServer server in the command line:
homer@pc001:~$ MacroServer lab-01
The first time the server is executed, it will inform you that server lab-01
is not registered and it will offer to register it. Just answer ‘y’. Next, it
will ask you to which Pool(s) you want your MacroServer to communicate with.
Select the previously created Pool from the list, press Return
once and
Return
again to finish with Pool selection. This will register a new
instance of MacroServer called lab-01 and the server will be started.
You should get an output like this:
homer@pc001:~$ MacroServer lab-01
lab-01 does not exist. Do you wish create a new one (Y/n) ?
Pool_lab-01_1 (a.k.a. Pool/lab-01/1) (running)
Please select pool to connect to (return to finish): Pool_lab-01_1
Please select pool to connect to (return to finish):
DServer/MacroServer/lab-01 has no event channel defined in the database - creating it
Running the client¶
After the server has been started, you can start one or more client applications (CLIs and/or GUIs) that connect to the server. Each client connects to a specific door on the server. A single sardana can be configured with many doors allowing multiple clients to be connected at the same time.
When the sardana server was first executed, part of the registration process created one door for you so now you just have to start the client application from the command line:
homer@pc001:~$ spock
Spock is an IPython based CLI. When you start spock without arguments it will assume a default profile called spockdoor. The first time spock is executed, it will inform you that profile spockdoor doesn’t exist and it will offer to create one. Just answer ‘y’. After, it will ask you to which door should the default spockdoor profile connect to. Select the door name corresponding to your sardana server (Door_lab-01_1) and press return. By now you should get an output like this:
homer@pc001:~$ spock
Profile 'spockdoor' does not exist. Do you want to create one now ([y]/n)? y
Available Door devices from sardanamachine:10000 :
Door_lab-01_1 (a.k.a. Door/lab-01/1)
Door name from the list? Door_lab-01_1
Storing ipython_config.py in /home/homer/.config/ipython/profile_spockdoor... [DONE]
Spock 1.0.0 -- An interactive laboratory application.
help -> Spock's help system.
object? -> Details about 'object'. ?object also works, ?? prints more.
IPython profile: spockdoor
Connected to Door_lab-01_1
Door_lab-01_1 [1]:
That’s it! You now have a running sardana client. Still not impressed, I see! The next chapter describes how to start adding new elements to your sardana environment.
One of sardana’s goals is to allow you to execute procedures (what we call in sardana macros, hence from here on we will use the term macro). A macro is basically a piece of code. You can write macros using the Python language to do all sorts of things. The sky is the limit here!
Sardana comes with a catalog of macros that help
users in a laboratory to run their experiments. Most of these macros
involve interaction with sardana elements like motors and experimental channels.
Therefore, the first step in a new sardana demo is to populate your system with
some elements. Fortunately, sardana comes with a macro called sar_demo that
does just that. To execute this macro just type on the command line
sar_demo
.
You should get an output like this:
Door_lab-01_1 [1]: sar_demo
Creating controllers motctrl01, ctctrl01... [DONE]
Creating motors mot01, mot02, mot03, mot04... [DONE]
Creating measurement group mntgrp01... [DONE]
You should now have in your sardana system a set of simulated motors and counters with which you can play.
Hint
for clearing sardana from the elements created by the demo, execute
clear_sar_demo
The next chapter (spock) will give you a complete overview of spock’s interface.
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.

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]:
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).
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
lsmac
:
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.
<...>
Stopping macros¶
Some macros may take a long time to execute. To stop a macro in the middle of
its execution type Control+c
.
Macros that move motors or acquire data from sensors will automatically stop all motion and/or all acquisition.
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%)
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']
Sardana provides a scan data viewer for scans which were stored in a NeXus
file. Without arguments, showscan
will show you the result of the last scan in a GUI:
showscan
scan_number will display
data for the given scan number.
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
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 metioned in the beggining of this chapter, the sardana spock automatically
activates the PyTango ‘s ipython console extension. Therefore all Tango
features are automatically available on the sardana spock console. For example,
creating a DeviceProxy
will work inside the sardana spock
console:
LAB-01-D01 [1]: tgtest = PyTango.DeviceProxy("sys/tg_test/1")
LAB-01-D01 [2]: print( tgtest.state() )
RUNNING
Footnotes
[1] | The PyTango ipython documentation can be found here |
Scans¶
Perhaps the most used type of macro is the scan macros. In general terms, we call scan to a macro that moves one or more motors and acquires data along the path of the motor(s).
Note
Sardana provides a Scan Framework for developing scan macros so that the scan macros behave in a consistent way. Unless otherwise specified, the following discussion applies to scan macros based on such framework.
The various scan macros mostly differ in how many motors are moved and the definition of their paths.
Typically, the selection of which data is going to be acquired depends on the active measurement group and is not fixed by the macro itself (although there is no limitation in this sense).
Depending on whether the motors are stopped before acquiring the data or not, we can classify the scan macros in step scans or continuous scans, respectively.

Trend plot showing a step scan (ascan
m_cp1_1 0 1000 8 .5)
followed by a continuous scan (ascanc
m_cp1_1 0 1000 .5).
The line corresponds to the motor position and the blue shaded areas
correspond to the intervals in which the data acquisition took place.
Step scans¶
In a step scan, the motors are moved to given points, and once they reach each point they stop. Then, one or more channels are acquired for a certain amount of time, and only when the data acquisition is finished, the motors proceed to the next point.
In this way, the position associated to a data readout is well known and does not change during the acquisition time.
Some examples of step scan macros are:
ascan
,
a2scan
, ...
dscan
,
d2scan
, ...
mesh
.
Continuous scans¶
In a continuous scan, the motors are not stopped for acquisition, which therefore takes place while the motors are moving. The most common reason for using this type of scan is optimizing the acquisition time by not having to wait for motors to accelerate and decelerate between acquisitions.
Note
The synchronization of movement and acquisition can be done via hardware or via software. Currently Sardana only provides an interface for software-synchronized continuous scans. An API abstracting the specificities of hardware-synchronized systems is being implemented too but it is not yet available for production.
The (software-synchronized) continuous scans introduce some constraints and issues that should be considered.
- If a continuous scan involves moving more than one motor simultaneously
(as it is done, e.g. in
a2scan
), then the movements of the motors should be synchronized so that they all start their path at the same time and finish it at the same time. - If motors do not maintain a constant velocity along the path of their movement, the trajectories followed when using more than one motor may not be linear.
- While in step scans it is possible to scan two pseudo-motors that access the same physical motors (e.g. the gap and offset of a slit, being both pseudo-motors accessing the same physical motors attached to each blade of the slit), in a continuous scan the motions cannot be decoupled in a synchronized way.
- In order to optimize the acquisition time, Sardana attempts to perform as many acquisitions as allowed during the scan time. Due to the uncertainty in the delay times involved, it is not possible to know beforehand how many acquisitions will be completed. In other words, the number of acquired points along a continuous scan is not fixed (but it is guaranteed to be as large as possible).
- Backslash correction is incompatible with continuous scans, so you should keep in mind that continuous scans should only be done in the backslash-free direction of the motor (typically, by convention the positive one for a physical motor).
In order to address the first two issues, the scan framework attempts the following:
- If the motors support changing their velocity, Sardana will adjust the velocities of the motors so that they all start and finish the required path simultaneously. For motors that specify a range of allowed velocities, this range will be used (for motors that do not specify a maximum allowed velocity, the current “top velocity” will be assumed to be the maximum)
- For motors that can maintain a constant velocity after an acceleration phase (this is the case for most physical motors), Sardana will transparently extend the user-given path both at the beginning and the end in order to allow for the motors to move at constant velocity along all the user defined path (i.e., the motors are allowed time and room to accelerate before reaching the start of the path and to decelerate after the end of the nominal path selected by the user)
These two actions can be seen in the following plot of the positions of the two
motors involved in a a2scanc
.

Trend plot showing a two-motor continuous scan
(a2scanc
m_cp1_1 100 200 m_cp1_2 0 500 .1).
The lines correspond to the motor positions and the blue shaded areas correspond to the intervals in
which the data acquisition took place.
Both motors are capable of same velocity and acceleration, but since the required scan path for m_cp1_1 is shorter than that for m_cp1_2, its top velocity has been adjusted (gentler slope for m_cp1_1) so that both motors go through the user-requested start and stop positions simultaneously.
The same figure also shows how the paths for both motors have been automatically (and transparently, for the user) extended to guarantee that the user defined path is followed at constant velocity and that the data acquisition takes place also while the motors are running at constant velocity.
Some examples of continuous scan macros are:
ascanc
,
a2scanc
, ...
dscanc
,
d2scanc
, ...
meshc
.
See also
For more information about the implementation details of the scan macros in Sardana, see scan framework
Screenshots¶
Here you will find a host of example figures.
Sardana oriented graphical user interfaces¶
Graphical user interface screen shots¶

ALBA‘s Storage ring GUI

ALBA‘s LINAC to booster beam charge monitor GUI

ALBA‘s beam position monitor GUI

ALBA‘s Radio frequency plant GUI

ALBA‘s tune excitation panel

ALBA‘s fluorescent screen main panel

ALBA‘s front end GUI

ALBA‘s digital low level radio frequency GUI

ALBA‘s vaccum GUI
Todo
The FAQ is work-in-progress. Many answers need polishing and mostly links need to be added
FAQ¶
What is the Sardana SCADA and how do I get an overview over the different components?¶
An overview over the different Sardana components is shown in the following figure:

How do I install Sardana?¶
The Sardana SCADA system consists of different components which have to be installed:
The complete sardana installation instructions can be found here.
How to work with Taurus GUI?¶
A user documentation for the Taurus GUI application can be found here.
How to produce your own Taurus GUI panel?¶
The basic philosophy of Taurus GUI is to provide automatic GUI s which are automatically replaced by more and more specific GUI s if these are found.
Refer to the user documentation on TaurusGUI for more details on how to work with panels
How to call procedures?¶
The central idea of the Sardana SCADA system is to execute procedures centrally. The execution can be started from either:
- spock offers a command line interface with commands very similar to SPEC. It is documented here.
- Procedures can also be executed with from a GUI. Taurus provides generic widgets for macro execution.
- Procedures can also be executed in specific GUI s and specific Taurus widgets. The API to execute macros from python code is documented here <LINK>.
How to write procedures?¶
User written procedures are central to the Sardana SCADA system. Documentation how to write macros can be found here. Macro writers might also find the following documentation interesting:
- Documentation on how to debug macros can be found here <LINK>
- In addition of the strength of the python language macro writers can interface with common elements (motors, counters) , call other macros and use many utilities provided. The macro API can be found here.
- Documentation how to document your macros can be found here
How to write scan procedures?¶
A very common type of procedure is the scan where some quantity is varied while recording some other quantities. See the documentation on the Sardana Scan API
How to adapt SARDANA to your own hardware?¶
Sardana is meant to be interfaced to all types of different hardware with all types of control systems. For every new hardware item the specific behavior has to be programmed by writing a controller code. The documentation how to write Sardana controllers and pseudo controllers can be found here. This documentation also includes the API which can be used to interface to the specific hardware item.
How to add your own file format?¶
Documentation how to add your own file format can be found here <LINK>.
How to use the standard macros?¶
The list of all standard macros and their usage can be found here <LINK>.
How to add conditions in macros?¶
Executing macros and moving elements can be subject to external conditions (for example an interlock). New types of software interlocks can be easily added to the system and are documented here <LINK>.
How to write your own Taurus application?¶
You have basically two possibilities to write your own Taurus application Start from get General TaurusGUI and create a configuration file. This approach is documented here <LINK>. Start to write your own Qt application in python starting from the Taurus main window. This approach is documented here <LINK>.
Which are the standard Taurus graphical GUI components?¶
A list of all standard Taurus GUI components together with screen shots and example code can be found here <LINK>
How to write your own Taurus widget?¶
A tutorial of how to write your own Taurus widget can be found here.
How to work with the graphical GUI editor?¶
Taurus uses the QtDesigner/QtCreator as a graphical editor. Documentation about QtDesigner/QtCreator. The Taurus specific parts here.
What are the minimum software requirements for sardana?¶
Sardana is developed under GNU/Linux, but should run also on Windows and OS-X. The dependencies for installing Sardana can be found here <LINK>.
How to configure the system?¶
Adding and configuring hardware items on an installation is described here <LINK>.
How to write your own Taurus schema?¶
Taurus is not dependent on Tango. Other control systems or just python modules can be interfaced to it by writing a schema. This approach is documented here <LINK> and a tutorial can be found here <LINK>
What are the interfaces to the macro server and the pool?¶
The low level interfaces to the Sardana Device Pool and the Macro server can be found here <LINK>.
What are the data file formats used in the system and how can I read them?¶
It is easily possible to add your own file format but the standard file formats are documented here:
- The SPEC file format is documented here <LINK> and here is a list of tools to read it <LINK>
- The EDF file format is documented here <LINK> and here is a list of tools to read it <LINK>
- The NEXUS file format is documented here <LINK> and here is a list of tools to read it <LINK>
Developer’s Guide¶
Overview¶
Global overview¶
This chapter gives an overview of the sardana architecture and describes each of the different components in some detail. If you find this document to be to technical please consider reading the Overview guide first.
The following chapters assume a that you have a minimum knowledge of the Tango system and basic computer science.
Sardana consists of a software library which contains sardana kernel engine, a server and a client library which allow sardana to run as a client-server based distributed control system. The communication protocols between servers and clients are plug-ins in sardana. At this time, the only implemented protocol is Tango. In earlier versions, sardana was tightly connected to Tango. This documentation, is therefore centered in the Tango server implementation. When other comunication protocols become available, the documentation will be revised.
Client applications (both GUI and CLI) can connect to the sardana server through the high level sardana client API or through the low level pure Tango channels. Client applications can be build with the purpose of operating an existing sardana server or of configuring it.
The sardana server consists of a sardana tango device server (SDS) running a sardana kernel engine. This server runs as an OS daemon. Once configured, this server acts as a container of device objects which can be accessed by the outside world as tango device objects. Typically, a sardana server will consist of:
- a low level Pool object which manages all the server objects related to motion control and data acquisition (controllers, motors, counters, experiment channels, etc).
- a Macro Server object which manages the execution of macros (procedures) and client connection points (called doors).
- a set of low level objects (controllers, motors, counters, experiment channels, etc) controlled by the Pool object
- a set of Door objects managed by the macro server. A Door is the preferred access point from a client application to the to the sardana server
A sardana server may contain only a Pool object or a Macro Server object or both. It may NOT contain more than one Pool object or more than one Macro Server object.
If necessary, your sardana system may be splitted into two (or more) sardana servers. A common configuration is to have a sardana server with a Pool (in this case we call the server a Device Pool server) and a second server with a Macro Server (this server is called MacroServer server).
The following figures show some of the possible alternative configurations
The following chapters describe each of the Sardana objects in more detail.
Macro Server overview¶
The Macro Server object is the sardana server object which manages all high level sardana objects related to macro execution, namely doors, macro libraries and macros themselves.
The main purpose of the Macro Server is to run macros. Macros are just pieces of Python code (functions or classes) which reside in a macro library (Python file). Macros can be written by anyone with knowledge of Python.
The Macro Server is exposed on the sardana server as a Tango device. Through configuration, the Macro Server can be told to connect to a Pool device. This is the most common configuration. You can, however, tell the Macro Server to connect to more than one Pool device or to no Pool devices at all.
When connected to a Pool device(s), the Macro Server uses the Pool device introspection API to discover which elements are available. The existing macros will be able to access these elements (through parameters passed to the macro or using the macro API) and act on them.
In order to be able to run macros, you must first connect to the Macro Server entry point object called Door. A single Macro Server can have many active Doors at the same time but a Door can only run one macro at a time. Each Door is exposed on the sardana server as a Tango device.
You are not in any way restricted to the standard macros provided by the sardana system. You can write as many macros as you need. Writing your own macros is easy. The macro equivalent of Python‘s Hello, World! example:
1 2 3 4 5 | from sardana.macroserver.macro import macro
@macro()
def hello_world(self):
self.output("Hello, World!")
|
Here is a simple example of a macro to move any moveable element to a certain value:
1 2 3 4 5 6 7 8 9 | from sardana.macroserver.macro import macro, Type
@macro([ ["moveable", Type.Moveable, None, "moveable to move"],
["position", Type.Float, None, "absolute position"] ])
def my_move(self, moveable, position):
"""This macro moves a moveable to the specified position"""
moveable.move(position)
self.output("%s is now at %s", moveable, moveable.getPosition())
|
Information on how to write your own sardana macros can be found here.
Pool overview¶
The Pool object is the sardana server object which manages all other hardware level sardana objects related with motion control and data acquisition. This object is exposed to the world as a Tango device. It’s API consists of a series of methods (Tango commands) and members (Tango attributes) which allow external applications to create/remove/rename and monitor the different hardware level sardana objects.
The Pool could be seen as a kind of intelligent device container to control the experiment hardware. It has two basic features which are:
- Hardware access using dynamically created/deleted devices according to the experiment needs
- Management of some very common and well defined actions regularly done on a laboratory/factory (motion control, data acquisition, etc.)
Most of the times, it is possible to define a list of very common objects found in most of the experiments. Objects commonly used to drive an experiment usually fit in one of the following categories:
- Moveables
- Motor
- Pseudo motor
- Group of moveables
- IORegister (a.k.a. discrete motor)
Communication channels
Each different controlled hardware object will also be exposed as an independent Tango class. The sardana device server will embed all these Tango classes together. The pool Tango device is the “container interface” and allows the user to create/delete classical Tango devices which are instances of these embedded classes.
Controller overview¶
Each different hardware object is directly controlled by a software object called controller. This object is responsible for mapping the communication between a set of hardware objects (example motors) and the underlying hardware (example: a motor controller crate). The controller object is also exposed as a Tango device.
Usually a controller is capable of handling several hardware objects. For example, a motor controller crate is capable of controlling several motors (generally called axis [1]).
The controller objects can be created/deleted/renamed dynamically in a running pool.
A specific type of controller needs to be created to handle each specific type of hardware. Therefore, to each type of hardware controller there must be associated a specific controller software component. You can write a specific controller software component (plug-in) that is able to communicate with the specific hardware. You can this way extend the initial pool capabilities to talk to all kinds of different hardware.

A diagram representing a sardana server with a controller class NSC200Controller, an instance of that controller np200ctrl_1 “connected” to a real hardware and a single motor npm_1.
A sardana controller is responsible for it’s sardana element(s). Example: an Icepap hardware motor controller can control up to 128 individual motor axis. In the same way, the coresponding software motor controller IcepapController will own the individual motor axises.

A diagram representing a sardana server with a controller class IcepapController, an instance of that controller icectrl_1 “connected” to a real hardware and motors icem_[1..5].
These are the different types of controllers recognized by sardana:
MotorController
You should use/write a
MotorController
sardana plug-in if the the device you want to control has a moveable interface. TheMotorController
actually fullfils a changeable interface. This means that, for example, a power supply that has a current which you want to ramp could also be implemented as aMotorController
.Example: the Newport NSC200 motor controller
CounterTimerController
This controller type is designed to control a device capable of counting scalar values (and, optionaly have a timer).
Example: The National Instruments 6602 8-Channel Counter/Timer
ZeroDController
This controller type is designed to control a device capable of supplying scalar values. The API provides a way to obtain a value over a certain acquisition time through different algorithms (average, maximum, integration).
Example: an electrometer
OneDController
This controller type is designed to control a device capable of supplying 1D values. It has a very similar API to
CounterTimerController
Example: an MCA
TwoDController
This controller type is designed to control a device capable of supplying 2D values. It has a very similar API to
CounterTimerController
Example: a CCD
PseudoMotorController
A controller designed to export virtual motors that represent a new view over the actual physical motors.
Example: A slit pseudo motor controller provides gap and offset virtual motors over the physical blades
PseudoCounterController
- A controller designed to export virtual counters that represent a new view over the actual physical counters/0Ds.
IORegisterController
- A controller designed to control hardware registers.
Controller plug-ins can be written in Python (and in the future in C++). Each controller code is basically a Python class that needs to obey a specific API.
Here is an a extract of the pertinent part of a Python motor controller code that is able to talk to a Newport motor controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | from sardana.pool.controller import MotorController, \
Type, Description, DefaultValue
class NSC200Controller(MotorController):
"""This class is the Tango Sardana motor controller for the Newport NewStep
handheld motion controller NSC200.
This controller communicates through a Device Pool serial communication
channel."""
ctrl_properties = \
{ 'SerialCh' : { Type : str,
Description : 'Communication channel name for the serial line' },
'SwitchBox': { Type : bool,
Description : 'Using SwitchBox',
DefaultValue : False},
'ControllerNumber' : { Type : int,
Description : 'Controller number',
DefaultValue : 1 } }
def __init__(self, inst, props, *args, **kwargs):
MotorController.__init__(self, inst, props, *args, **kwargs)
self.serial = None
self.serial_state_event_id = -1
if self.SwitchBox:
self.MaxDevice = 8
def AddDevice(self, axis):
if axis > 1 and not self.SwitchBox:
raise Exception("Without using a Switchbox only axis 1 is allowed")
if self.SwitchBox:
self._setCommand("MX", axis)
def DeleteDevice(self, axis):
pass
_STATE_MAP = { NSC200.MOTOR_OFF : State.Off, NSC200.MOTOR_ON : State.On,
NSC200.MOTOR_MOVING : State.Moving }
def StateOne(self, axis):
if self.SwitchBox:
self._setCommand("MX", axis)
status = int(self._queryCommand("TS"))
status = self._STATE_MAP.get(status, State.Unknown)
register = int(self._queryCommand("PH"))
lower = int(NSC200.getLimitNegative(register))
upper = int(NSC200.getLimitPositive(register))
switchstate = 0
if lower == 1 and upper == 1: switchstate = 6
elif lower == 1: switchstate = 4
elif upper == 1: switchstate = 2
return status, "OK", switchstate
def ReadOne(self, axis):
try:
if self.SwitchBox:
self._setCommand("MX", axis)
return float(self._queryCommand("TP"))
except:
raise Exception("Error reading position, axis not available")
def PreStartOne(self, axis, pos):
return True
def StartOne(self, axis, pos):
if self.SwitchBox:
self._setCommand("MX", axis)
status = int(self._queryCommand("TS"))
if status == NSC200.MOTOR_OFF:
self._setCommand("MO","")
self._setCommand("PA", pos)
self._log.debug("[DONE] sending position")
def StartAll(self):
pass
def AbortOne(self, axis):
if self.SwitchBox:
self._setCommand("MX", axis)
self._setCommand("ST", "")
|
See also
- Writing controllers
- How to write controller plug-ins in sardana
- Controller API reference
- the controller API
Controller
- the controller tango device API
Footnotes
[1] | The term axis will be used from here on to refer to the ID of a specific hardware object (like a motor) with respect to its controller. |
Motor overview¶
The motor is one of the most used elements in sardana. A motor represents anything that can be changed (and can potentially take some time to do it), so, not only physical motors (like a stepper motors) fit into this category but also, for example, a power supply for which the electrical current can be modified. As it happens with the motor controller hardware and its physical motor(s), a sardana motor is always associated with it’s sardana motor controller.

A diagram representing a sardana server with a several motor controllers and their respective motors.
The motor object is also exposed as a Tango device.
See also
- Motor API reference
- the motor API
Motor
- the motor tango device API
Pseudo motor overview¶
Todo
document pseudo motor overview
See also
- Pseudo motor API reference
- the pseudo motor API
PseudoMotor
- the pseudo motor tango device API
Pseudomotors which have siblings and are based on physical motors with an inaccurate or a finite precision positioning system could be affected by the drift effect.
Why does it happen?
Each move of a pseudomotor requires calculation of the physical motors positions in accordance with the current positions of its siblings. The consecutive movements of a pseudomotor can accumulate errors of the positioning system and cause drift of its siblings.
Who is affected?
- Inaccurate positioning systems which lead to a discrepancy between the write and the read position of the physical motors. In this case the physical motors must have a position sensor e.g. encoder but must not be configured in closed loop (in some special cases, where the closed loop is not precise enough, the drift effect can be observed as well). This setup can lead to the situation where write and read values of the position attribute of the physical motors are different e.g. due to the loosing steps problems or the inaccurate step_per_unit calibration.
- Finite precision physical motors e.g. stepper is affected by the rounding error when moving to a position which does not translate into a discrete number of steps that must be commanded to the hardware.
How is it solved in Sardana?
Sardana implements the drift correction which use is optional but enabled by default for all pseudomotors. It is based on the use of the write value, instead of the read value, of the siblings’ positions, together with the new desired position of the pseudomotor being moved, during the calculation of the physical positions. The write value of the pseudomotor’s position gets updated at each move of the pseudomotor or any of the underneath motors.
Note
Movements being stopped unexpectedly: abort by the user, over-travel limit or any other exceptional condition may cause considerable discrepancy in the motor’s write and read positions. In the subsequent pseudomotor’s move, Sardana will also correct this difference by using the write instead of read values.
The drift correction is configurable with the DriftCorrection property either globally (on the Pool device level) or locally (on each PseudoMotor device level).
Example
Let’s use the slit pseudomotor controller to visualize the drift effect. This controller comprises two pseudomotors: gap and offset, each of them based on the same two physical motors: right and left. In this example we will simulate the inaccurate positioning of the left motor (loosing of 0.002 unit every 1 unit move).
Drift correction disabled
Initial state: gap and offset are at positions 0 (gap totally closed and offset at the nominal position)
Door_lab_1 [1]: wm right left gap offset right left gap offset User High Not specified Not specified Not specified Not specified Current 0.000 0.000 0.000 0.000 Low Not specified Not specified Not specified Not specified
Move gap to 1
Door_lab_1 [2]: mv gap 1
The calculation of the physical motors’ positions gives us 0.5 for both right and left (in accordance with the current offset of 0)
Door_lab_1 [3]: wm right left gap offset right left gap offset User High Not specified Not specified Not specified Not specified Current 0.500 0.498 0.998 0.001 Low Not specified Not specified Not specified Not specified
We observe that the gap pseudomotor did not reach the desired position of 1 due to the left’s positioning problem. Left’s position write and read discrepancy of 0.002 causes that the gap reached only 0.998 and that the offset drifted to 0.001.
Move gap to 2
Door_lab_1 [4]: mv gap 2
The calculation of the physical motors’ positions gives us 1.001 for right and 0.999 for left (in accordance with the current offset of 0.001).
Door_lab_1 [5]: wm right left gap offset right left gap offset User High Not specified Not specified Not specified Not specified Current 1.001 0.997 1.998 0.002 Low Not specified Not specified Not specified Not specified
We observe that the gap pseudomotor did not reach the desired position of 2 due to the left’s positioning problem. Left’s position write and read discrepancy of 0.002 causes that the gap reached only 1.998 and that the offset drifted again by 0.001 and the total accumulated drift is 0.002.
Move gap to 3
The calculation of the physical motors’ positions gives us 1.502 for right and 1.498 for left (in accordance with the current offset of 0.002).
Door_lab_1 [6]: mv gap 3 Door_lab_1 [7]: wm right left gap offset right left gap offset User High Not specified Not specified Not specified Not specified Current 1.502 1.496 2.998 0.003 Low Not specified Not specified Not specified Not specified
We observe that the gap pseudomotor did not reach the desired position of 3 due to the left’s positioning problem. Left’s position write and read discrepancy of 0.002 causes that the gap reached only 2.998 and that the offset drifted by 0.001 and the total accumulated drift is 0.003.
Drift correction enabled
Initial state: gap and offset are at positions 0 (gap totally closed and offset at the nominal position)
Door_lab_1 [1]: wm right left gap offset right left gap offset User High Not specified Not specified Not specified Not specified Current 0.000 0.000 0.000 0.000 Low Not specified Not specified Not specified Not specified
Move gap to 1
Door_lab_1 [2]: mv gap 1
The calculation of the physical motors’ positions gives us 0.5 for both right and left (in accordance with the last set offset of 0).
Door_lab_1 [3]: wm right left gap offset right left gap offset User High Not specified Not specified Not specified Not specified Current 0.500 0.498 0.998 0.001 Low Not specified Not specified Not specified Not specified
We observe that the gap pseudomotor did not reach the desired position of 1 due to the left’s positioning problem. Left’s position write and read discrepancy of 0.002 causes that the gap reached only 0.998 and that the offset drifted to 0.001.
Move gap to 2
Door_lab_1 [4]: mv gap 2
The calculation of the physical motors’ positions gives us 1 for right and 1 for left (in accordance to the last set offset 0).
Door_lab_1 [5]: wm right left gap offset right left gap offset User High Not specified Not specified Not specified Not specified Current 1.000 0.998 1.998 0.001 Low Not specified Not specified Not specified Not specified
We observe that the gap pseudomotor did not reach the desired position of 2 due to the left’s positioning problem. Left’s position write and read discrepancy of 0.002 causes that the gap reached only 1.998 and that the offset drifted again by 0.001 but thanks to the drift correction is maintained at this value.
Move gap to 3
Door_lab_1 [6]: mv gap 3
The calculation of the physical motors’ positions gives us 1.5 for right and 1.5 for left (in accordance to the last set offset of 0).
Door_lab_1 [7]: wm right left gap offset right left gap offset User High Not specified Not specified Not specified Not specified Current 1.500 1.498 2.998 0.001 Low Not specified Not specified Not specified Not specified
We observe that the gap pseudomotor did not reach the desired position of 3 due to the left’s positioning problem. Left’s position write and read discrepancy of 0.002 causes that the gap reached only 2.998 and that the offset drifted again by 0.001 but thanks to the drift correction is maintained at this value.
I/O register overview¶
Todo
document I/O register overview
See also
- I/O register API reference
- the I/O register API
IORegister
- the I/O register tango device API
Counter/timer overview¶
Todo
document counter/timer overview
See also
- Counter/Timer API reference
- the counter/timer API
CTExpChannel
- the counter/timer tango device API
0D channel overview¶
Todo
document 0D experiment channel overview
See also
- 0D channel API reference
- the 0D experiment channel API
ZeroDExpChannel
- the 0D experiment channel tango device API
1D channel overview¶
Todo
document 1D experiment channel overview
See also
- 1D channel API reference
- the 1D experiment channel API
OneDExpChannel
- the 1D experiment channel tango device API
2D channel overview¶
Todo
document 2D experiment channel overview
See also
- 2D channel API reference
- the 2D experiment channel API
TwoDExpChannel
- the 2D experiment channel tango device API
Pseudo counter overview¶
Todo
document pseudo counter overview
See also
- Pseudo counter API reference
- the pseudo counter API
PseudoCounter
- the pseudo counter tango device API
Writing macros¶
Writing macros¶
This chapter provides the necessary information to write macros in sardana. The complete macro API can be found here.
A macro in sardana describes a specific procedure that can be executed at any time. Macros run inside the sardana sandbox. This simply means that each time you run a macro, the system makes sure the necessary environment for it to run safely is ready.
Macros can only be written in Python. A macro can be a function or a
class. In order for a function to be recognized as a macro, it
must be properly labeled as a macro (this is done with a special
macro
decorator. Details are explaind below). In the same way, for a
class to be recognized as a macro, it must inherit from a
Macro
super-class. Macros are case sensitive. This means that
helloworld is a different macro than HelloWorld.
The choice between writing a macro function or a macro class depends not only on the type of procedure you want to write, but also (and probably, most importantly) on the type of programming you are most confortable with.
If you are a scientist, and you have a programming background on a functional language (like fortran, matlab, SPEC), then you might prefer to write macro functions. Computer scientists (young ones, specially), on the other hand, often have a background on object oriented languages (Java, C++, C#) and feel more confortable writing macro classes.
Classes tend to scale better with the size of a program or library. By writing a macro class you can benefit from all advantages of object-oriented programming. This means that, in theory:
- it would reduce the amount of code you need to write
- reduce the complexity of your code y by dividing it into small, reasonably independent and re-usable components, that talk to each other using only well-defined interfaces
- Improvement of productivity by using easily adaptable pre-defined software components
In practice, however, and specially if you don’t come from a programming background, writing classes requires a different way of thinking. It will also require you to extend your knowledge in terms of syntax of a programming language.
Furthermore, most tasks you will probably need to execute as macros, often don’t fit the class paradigm that object-oriented languages offer. If you are writing a sequencial procedure to run an experiment then you are probably better of writing a python function which does the job plain and simple.
One reason to write a macro as a class is if, for example, you want to extend
the behaviour of the mv
macro. In
this case, probably you would want to extend the existing macro by writing
your own macro class which inherits from the original macro and this way
benefit from most of the functionallity already existing in the original macro.
The idea of a macro is simply a piece of Python code that can be executed from control system interface (GUI/CLI). Therefore, anything that you don’t need to be executed by the interface should NOT be a macro.
When you have a big library of functions and classes, the approach to expose them to sardana should be to first carefully decide which procedures should be invoked by a GUI/CLI (namely the name of the procedure, which parameters it should receive and if it returns any value). Then write the macro(s) which invoke the code of the original library (see Using external python libraries). Avoid the temptation to convert the functions/classes of the original library into macros because:
- This will most certainly break your code (any code that calls a function or class that has been converted to a macro will fail)
- It will excessively polute the macro list (imagine a GUI with a combo box to select which macro to execute. If you have hundreds of macros it will take forever to find the one to execute even if they are in alphabetical order)
Since macros are essencially Python code, they reside inside a Python file. In sardana, we call a Python file which contains macros a macro library.
At the time of writing, the easiest way to create a new macro is from spock (we are currently working on a macro editor GUI).
Before launching spock it is important to decide which text editor you will use
to write your macros. Unless configured otherwise, spock will use the editor
specified by the system environment variable EDITOR
. If this variable
is not set, it will default to vi under Linux/Unix and to notepad under
Windows. The following line explains how to set the EDITOR
environment variable to gedit under linux using bash shell:
$ export EDITOR=gedit
If you choose gedit it is important to properly configure it to write Python code:
Go to Edit ‣ Preferences ‣ Editor and select:
- Tab width : 4
- Insert spaces instead of tabs
![]()
If you choose kwrite it is important to properly configure it to write Python code:
Go to Settings ‣ Configure editor... and choose Editing:
- In General tab:
- Tab width : 4
- Insert spaces instead of tabulators
- In Indentation tab:
- Default indentation mode : Python
- Indentation width : 4
![]()
Now you are ready to start writing your macro! Type spock on the command
line. Once you are in spock, you can use the
edmac
to create/edit macros. Let’s
say you want to create a new macro called hello_world in a new macro library
called salute. Just type in:
LAB-01-D01 [1]: edmac hello_world salute
Opening salute.hello_world...
Editing...
This will bring your favorite editor to life with a macro function template code for the macro hello_world.
The next chapter will explain how to fill this template with useful code. After you finish editing the macro, save the file, exit the editor and go back to spock. You’ll be asked if you want the new code to be load on the server. Just answer ‘y’.
LAB-01-D01 [1]: edmac hello_world salute
Openning salute.hello_world...
Editing...
Do you want to apply the new code on the server? [y] y
As mentioned before, macros are just simple Python functions which have been labeled as macros. In Python, these labels are called decorators. Here is the macro function version of Hello, World!:
1 2 3 4 5 6 | from sardana.macroserver.macro import macro
@macro()
def hello_world(self):
"""This is a hello world macro"""
self.output("Hello, World!")
|
- line 1
- imports the macro symbol from the sardana macro package.
sardana.macroserver.macro
is the package which contains most symbols you will require from sardana to write your macros. - line 3
- this line decorates de following function as a macro. It is crucial to use this decorator in order for your function to be recognized by sardana as a valid macro.
- line 4
- this line contains the hello_world function definition. Every
macro needs at least one parameter. The first parameter is the macro
execution context. It is usually called
self
but you can name it anything. This parameter gives you access to the entire context where the macro is being run. Through it, you’ll be able to do all sorts of things, from sending text to the output to ask for motors or even execute other macros. - line 5
- Documentation for this macro. You should always document your macro!
- line 6
- this line will print Hello, World! on your screen.
Note
If you already know a little about Python your are probably wondering why
not use print "Hello, World!"
?
Remember that your macro will be executed by a Sardana server which may be
running in a different computer than the computer you are working on.
Executing a normal print would just print the text in the server.
Therefore you need to explicitly say you want the text on the computer you
are working and not the server. The way to do it is using
output()
instead of print.
If you prefer, you can use the context version of Python print()
function (it is a bit more powerful than
output()
, and has a slightly
different syntax)
1 2 3 4 5 6 7 8 9 | # mandatory first line in your code if you use Python < 3.0
from __future__ import print_function
from sardana.macroserver.macro import macro
@macro()
def hello_world(self):
"""This is an hello world macro"""
self.print("Hello, World!")
|
The following footnote describes how to discover your Python version [2].
Remeber that a macro is, for all purposes, a normal Python function.
This means you CAN inside a macro write ANY valid Python code. This
includes for
and while
loops, if
...
elif
... else
conditional execution, etc...
1 2 3 4 5 6 7 | import numpy.fft
@macro()
def fft_my_wave(self):
wave_device = self.getDevice("sys/tg_test/1")
wave = wave_device.wave
wave_fft = numpy.fft.fft(wave)
|
Standard Python allows you to specify parameters to a function by placing comma
separated parameter names between the ()
in the function definition. The
macro API, in adition, enforces you to specify some extra parameter
information. At first, this may look like a useless complication, but you will
apreciate clear benefits soon enough. Here are some of them:
- error prevention: a macro will not be allowed to run if the given parameter if of a wrong type
- CLIs like Spock will be able to offer autocomplete facilities (press <tab> and list of allowed parameters show up)
- GUIs can display list of allowed parameter values in combo boxes which gives increased usability and prevents errors
- Documentation can be generated automatically
So, here is an example on how to define a macro that needs one parameter:
@macro([["moveable", Type.Moveable, None, "moveable to get position"]])
def where_moveable(self, moveable):
"""This macro prints the current moveable position"""
self.output("%s is now at %s", moveable.getName(), moveable.getPosition())
Here is another example on how to define a macro that needs two parameters:
- Moveable (motor, pseudo motor)
- Float (motor absolute position to go to)
1 2 3 4 5 6 7 8 | from sardana.macroserver.macro import macro, Type
@macro([ ["moveable", Type.Moveable, None, "moveable to move"],
["position", Type.Float, None, "absolute position"] ])
def move(self, moveable, position):
"""This macro moves a moveable to the specified position"""
moveable.move(position)
self.output("%s is now at %s", moveable.getName(), moveable.getPosition())
|
The parameter information is a list
of list
s. Each list
being a composed of four elements:
- parameter name
- parameter type
- parameter default value (None means no default value)
- parameter description
Here is a list of the most common allowed parameter types:
Integer
: an integer numberFloat
: a real numberBoolean
: a boolean True or FalseString
: a stringMoveable
: a moveable element (motor, pseudo-motor)Motor
: a pure motorExpChannel
: an experimental channel (counter/timer, 0D, pseudo-counter, ...)Controller
: a controllerControllerClass
: an existing controller class pluginMacroCode
: a macroMeasurementGroup
: a measurement groupAny
: anything, really
The complete list of types distributed with sardana is made up by these five
simple types: Integer
, Float
, Boolean
, String
, Any
, plus
all available sardana interfaces (Interface
)
One of the most powerfull features of macros is that the entire context of
sardana is at your disposal. Simply put, it means you have access to all
sardana elements by means of the first parameter on your macro (you can give
this parameter any name but usually, by convention it is called self
).
self
provides access to an extensive catalog of functions you can use in
your macro to do all kinds of things. The complete catalog of functions can be
found here.
Let’s say you want to write a macro that explicitly moves a known theta motor to a certain position. You could write a macro which receives the motor as parameter but that would be a little silly since you already know beforehand which motor you will move. Instead, a better solution would be to ask sardana for a motor named “theta” and use it directly. Here is how you can acomplish that:
1 2 3 4 5 6 | @macro([["position", Type.Float, None, "absolute position"]])
def move_theta(self, position):
"""This macro moves theta to the specified position"""
th = self.getMotor("th")
th.move(position)
self.output("Motor ended at %s", moveable.getPosition())
|
One of the functions of the macro decorator is to pass the knowledge of all existing macros to your macro. This way, without any special imports, your macro will know about all other macros on the system even if they have been written in other files.
Lets recreate the two previous macros (where_moveable and move) to execute
two of the macros that exist in the standard macro catalog
(wm
and
mv
)
Here is the new version of where_moveable
@macro([["moveable", Type.Moveable, None, "moveable to get position"]])
def where_moveable(self, moveable):
"""This macro prints the current moveable position"""
self.wm(moveable)
... and the new version of move
1 2 3 4 5 6 | @macro([ ["moveable", Type.Moveable, None, "moveable to move"],
["position", Type.Float, None, "absolute position"] ])
def move(self, moveable, position):
"""This macro moves a moveable to the specified position"""
self.mv(moveable, position)
self.output("%s is now at %s", moveable.getName(), moveable.getPosition())
|
The sardana server provides a global space to store variables, called environment. The environment is a dictionary storing a value for each variable. This environment is stored persistently so if the sardana server is restarted the environment is properly restored.
Variables are case sensitive.
The value of an existing environment variable can be accessed using
getEnv()
. Setting the value of an environment variable is done
with setEnv()
.
For example, we know the ascan macro increments a ScanID
environment
variable each time it is executed. The following example executes a scan and
outputs the new ScanID
value:
@macro([["moveable", Type.Moveable, None, "moveable to get position"]])
def fixed_ascan(self, moveable):
"""This does an ascan starting at 0 ending at 100, in 10 intervals
with integration time of 0.1s"""
self.ascan(moveable, 0, 100, 10, 0.1)
scan_id = self.getEnv('ScanID')
self.output("ScanID is now %d", scan_id)
The Macro API includes a set of methods that allow you to write log messages with different levels:
As you’ve seen, the special output()
function has the same effect
as a print statement (with slightly different arguments).
Log messages may have several destinations depending on how your sardana server
is configured. At least, one destination of each log message is the client(s)
(spock, GUI, other) which are connected to the server. Spock, for example,
handles the log messages by printing to the console with different colours. By
default, spock prints all log messages with level bigger than
debug()
(You can change this behaviour by typing debug on
in
spock). Another typical destination for log messages is a log file.
Here is an example on how to write a logging information message:
1 2 3 4 5 | @macro()
def lets_log(self):
self.info("Starting to execute %s", self.getName())
self.output("Hello, World!")
self.info("Finished to executing %s", self.getName())
|
Once the report facility has been properly configured, report messages can be sent to the previously configured report file.
There are several differences between reporting and logging. The first difference is that log messages may or may not be recorded, depending on the configured filters on the target (example: log file). A report will always be recorded.
Another difference is that report messages are not sent to the clients. The idea of a report is to silently record in a file that something as happened.
A third difference is that unlike logs, reports have no message level associated to them (actually since internally the log library is used to report messages, every report record as the predefined level INFO but this is just an implementation detail).
A report message can be emited at any time in the macro using the
report()
method:
@macro()
def lets_report(self):
self.report("this is an official report of macro '%s'", self.getName())
This would generate the following report message in the report file:
INFO 2012-07-18 09:39:34,943: this is an official report of macro ‘lets_report’
As previously explained (see calling macros), you can use the Macro API to call other macros from inside your own macro:
@macro([["moveable", Type.Moveable, None, "moveable to get position"]])
def fixed_ascan(self, moveable):
"""This does an ascan starting at 0 ending at 100, in 10 intervals
with integration time of 0.1s"""
self.ascan(moveable, 0, 100, 10, 0.1)
An explicit call to execMacro()
would have the same effect:
@macro([["moveable", Type.Moveable, None, "moveable to get position"]])
def fixed_ascan(self, moveable):
"""This does an ascan starting at 0 ending at 100, in 10 intervals
with integration time of 0.1s"""
self.execMacro('ascan', moveable, '0', '100', '10', '0.2')
The advantage of using execMacro()
is that it supports passing
parameters with different flavors:
parameters as strings:
self.execMacro('ascan', motor.getName(), '0', '100', '10', '0.2')parameters as space separated string:
self.execMacro('ascan %s 0 100 10 0.2' % motor.getName())parameters as concrete types:
self.execMacro(['ascan', motor, 0, 100, 10, 0.2])
Sometimes it is desirable to access data generated by the macro we just called.
For these cases, the Macro API provides a pair of low level methods
createMacro()
and runMacro()
together with
data()
.
Let’s say that you need access to the data generated by an ascan. First you call
createMacro()
with the same parameter you would give to
execMacro()
. This will return a tuple composed from a macro object
and the result of the prepare()
method. Afterward you call runMacro()
giving
as parameter the macro object returned by createMacro()
.
In the end, you can access the data generated by the macro
using data()
:
@macro([["moveable", Type.Moveable, None, "moveable to get position"]])
def fixed_ascan(self, moveable):
"""This does an ascan starting at 0 ending at 100, in 10 intervals
with integration time of 0.1s"""
ret = self.createMacro('ascan', moveable, '0', '100', '10', '0.2')
# createMacro returns a tuple composed from a macro object
# and the result of the Macro.prepare method
my_scan, _ = ret
self.runMacro(my_scan)
print len(my_scan.data)
A set of macro call examples can be found here.
This chapter describes an advanced alternative to writing macros as Python classes. If words like inheritance, polimorphism sound like a lawyer’s horror movie then you probably should only read this if someone expert in sardana already told you that the task you intend to do cannot be accomplished by writing macro functions.
The simplest macro class that you can write MUST obey the following rules:
The run()
method is the place where you write the code of your
macro. So, without further delay, here is the Hello, World! example:
1 2 3 4 5 6 7 | from sardana.macroserver.macro import Macro
class HelloWorld(Macro):
"""Hello, World! macro"""
def run(self):
print "Hello, World!"
|
Let’s say you want to pass an integer parameter to your macro. All you have to
do is declare the parameter by using the param_def
Macro member:
1 2 3 4 5 6 7 8 9 | from sardana.macroserver.macro import Macro, Type
class twice(Macro):
"""Macro twice. Prints the double of the given value"""
param_def = [ [ "value", Type.Float, None, "value to be doubled" ] ]
def run(self, value):
self.output(2*value)
|
Note
As soon as you add a param_def
you also need to
modify the run()
method to support the new paramter(s).
A set of macro parameter examples can be found here.
Additionaly to the run()
method, you may write a
prepare()
method where you may put code to prepare the macro for
execution (for example, checking pre-conditions for running the macro). By
default, the prepare method is an empty method. Here is an example on how to
prepare HelloWorld to run only after year 1989:
import datetime
from sardana.macroserver.macro import Macro
class HelloWorld(Macro):
"""Hello, World! macro"""
def prepare(self):
if datetime.datetime.now() < datetime.datetime(1990,01,01):
raise Exception("HelloWorld can only run after year 1989")
def run(self):
print "Hello, World!"
Macro libraries can use code e.g. call functions and instantiate classes defined by external python libraries. In order to import the external libraries inside the macro library, they must be available for the python interpreter running the Sardana/MacroServer server (see Running server).
This could be achieved in two ways:
- Adding the directory containing the external library to the PythonPath property of the MacroServer tango device (path separators can be
\n
or:
).- Adding the directory containing the external library to the PYTHONPATH OS environment variable of the Sardana/MacroServer process.
The external libraries can be reloaded at Sardana/MacroServer server
runtime using the rellib
macro.
Remember that your macro will be executed by a Sardana server which may be
running in a different computer than the computer you are working on. Executing
a normal plot (from matplotlib
or guiqwt
) would just try to show
a plot in the server machine. The macro API provides a way to plot
graphics from inside your macro whenver the client that runs the macro
understands the plot request (don’t worry, spock does understand!)
The plotting API is the same used by pyplot
. The
API is accessible through the macro context (self
). Here is an
example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import math
from numpy import linspace
from scipy.integrate import quad
from scipy.special import j0
from sardana.macroserver.macro import macro
def j0i(x):
"""Integral form of J_0(x)"""
def integrand(phi):
return math.cos(x * math.sin(phi))
return (1.0/math.pi) * quad(integrand, 0, math.pi)[0]
@macro()
def J0_plot(self):
"""Sample J0 at linspace(0, 20, 200)"""
x = linspace(0, 20, 200)
y = j0(x)
x1 = x[::10]
y1 = map(j0i, x1)
self.pyplot.plot(x, y, label=r'$J_0(x)$') #
self.pyplot.plot(x1, y1, 'ro', label=r'$J_0^{integ}(x)$')
self.pyplot.title(r'Verify $J_0(x)=\frac{1}{\pi}\int_0^{\pi}\cos(x \sin\phi)\,d\phi$')
self.pyplot.xlabel('$x$')
self.pyplot.legend()
|
Running this macro from spock will result in something like:
Just for fun, the following macro computes a fractal and plots it as an image:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import numpy
@macro([["interactions", Type.Integer, None, ""],
["density", Type.Integer, None, ""]])
def mandelbrot(self, interactions, density):
x_min, x_max = -2, 1
y_min, y_max = -1.5, 1.5
x, y = numpy.meshgrid(numpy.linspace(x_min, x_max, density),
numpy.linspace(y_min, y_max, density))
c = x + 1j * y
z = c.copy()
fractal = numpy.zeros(z.shape, dtype=numpy.uint8) + 255
finteractions = float(interactions)
for n in range(interactions):
z *= z
z += c
mask = (fractal == 255) & (abs(z) > 10)
fractal[mask] = 254 * n / finteractions
self.pyplot.imshow(fractal)
|
And the resulting image (interactions=20, density=512):

A set of macro plotting examples can be found here.
When you plot from inside a macro with self.pyplot.plot
, the sardana server
will “ask” spock to execute the desired function with the given parameters.
This means that the result of plotting (a sequence of
Line2D
) is not available in the sardana server (since
the actual line is in spock). The result of any function call in
self.pyplot
will always be None!
This means that the following code which works in a normal IPython console will NOT work inside a macro:
LAB-01-D01 [1]: line = plot(range(10))[0]
LAB-01-D01 [2]: line.set_linewidth(5)
Also consider that each time you plot the complete data to be plotted is sent from the server to the client... so please avoid plotting arrays of 10,000,000 points!
It is possible to ask for user input inside a macro.
Hint
Asking for input in the middle of long macros will cause the macro to stop and wait for user input. If you write a long macro that might be executed in the middle of the night please take the appropriate steps to make sure you don’t arrive in the morning and you are faced with a message box waiting for you to answer a question that could be avoided with a proper default value. To make sure your macro can run in unattended mode make sure that:
- it implements the interactive interface
- every
input()
gives a default_value keyword argument
(read on to see how to meet these requirements)
In pure Python, to ask for user input you can use the raw_input()
(Python
2) / input()
(Python 3)
>>> answer = raw_input('--> ')
--> Monty Python's Flying Circus
>>> answer
"Monty Python's Flying Circus"
The Macro API provides a much more powerful version of
input()
since it can accept a wide variaty of options.
Similar to what happens with Plotting, when input is requested from inside a macro, the question will be sent to the client (example: spock) which ordered the macro to be executed. At this time the macro is stopped waiting for the client to answer. The client must “ask” the user for a proper value and the answer is sent back to the server which then resumes the macro execution.
Asking for user input is straightforward:
@macro()
def ask_name(self):
"""Macro function version to ask for user name"""
answer = self.input("What's your name?")
self.output("So, your name is '%s'", answer)
Executing this macro will make spock popup an Input Dialog Box like this one:
When you type your name and press OK the macro finishes printing the output:
LAB-01-D01 [1]: ask_name
Non interactive macro 'ask_name' is asking for input (please set this macro interactive to True)
So, your name is 'Homer Simpson'
The macro prints a warning message saying that the macro was not declared as
interactive. All macros that request user input should be declared as
interactive. This is because the sardana server can run a macro in unattended
mode. When an interactive macro is run in unattended mode, all
input()
instructions that have a default value will return
automatically the default value without asking the user for input.
To declare a macro as interactive set the interactive
keyword argument in the macro decorator to True
(default value for interactive
is False
), like this:
@macro(interactive=True)
def ask_name(self):
"""Macro function version to ask for user name"""
answer = self.input("What's your name?")
self.output("So, your name is '%s'", answer)
To declare a macro class as interactive set the interactive
member to
True
(default value for interactive
is False
), like this:
class ask_name(Macro):
"""Macro class version to ask for user name"""
interactive = True
def run(self):
answer = self.input("What's your name?")
self.output("So, your name is '%s'", answer)
a helper imacro
decorator and a iMacro
class exist which can
be used instead of the macro
decorator and Macro
class to
transparently declare your macro as interactive:
from sardana.macroserver.macro import imacro, iMacro
# interactive macro function version
@imacro()
def ask_name(self):
"""Macro function version to ask for user name"""
answer = self.input("What's your name?")
self.output("So, your name is '%s'", answer)
# interactive macro class version
class ask_name(iMacro):
"""Macro class version to ask for user name"""
def run(self):
answer = self.input("What's your name?")
self.output("So, your name is '%s'", answer)
The following sub-chapters explain the different options available for macro user input.
The default return type of input
is str
which mimics the
pure Python input function. However, often you want to restrict the user input
to a specific data type like Integer
, Float
or even complex object like
Moveable
or to a list of possible options.
The macro input
API provides an easy way to do this by
specifying the concrete data type in the
keyword argument data_type. The following examples
shows how to ask for an Integer
, a Moveable
, and single/multiple
selection from a list of options:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | from sardana.macroserver.macro import imacro, Type
@imacro()
def ask_number_of_points(self):
"""asks user for the number of points"""
nb_points = self.input("How many points?", data_type=Type.Integer)
self.output("You selected %d points", nb_points)
@imacro()
def ask_for_moveable(self):
"""asks user for a motor"""
moveable = self.input("Which moveable?", data_type=Type.Moveable)
self.output("You selected %s which is at %f", moveable, moveable.getPosition())
@imacro()
def ask_for_car_brand(self):
"""asks user for a car brand"""
car_brands = "Mazda", "Citroen", "Renault"
car_brand = self.input("Which car brand?", data_type=car_brands)
self.output("You selected %s", car_brand)
@imacro()
def ask_for_multiple_car_brands(self):
"""asks user for several car brands"""
car_brands = "Mazda", "Citroen", "Renault", "Ferrari", "Porche", "Skoda"
car_brands = self.input("Which car brand(s)?", data_type=car_brands,
allow_multiple=True)
self.output("You selected %s", ", ".join(car_brands))
|
... and these are the corresponding dialogs that will popup in spock:
Providing a default value is very important since it will allow your macro to run in unattended mode. When given, the default_value keyword argument value type must be compatible with the data_type keyword argument. Providing a default value is easy. The following examples repeat the previous data type examples giving compatible default values:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | from sardana.macroserver.macro import imacro, Type
@imacro()
def ask_number_of_points(self):
"""asks user for the number of points"""
nb_points = self.input("How many points?", data_type=Type.Integer,
default_value=100)
self.output("You selected %d points", nb_points)
@imacro()
def ask_for_moveable(self):
"""asks user for a motor"""
moveable = self.input("Which moveable?", data_type=Type.Moveable,
default_value="gap01")
self.output("You selected %s which is at %f", moveable, moveable.getPosition())
@imacro()
def ask_for_car_brand(self):
"""asks user for a car brand"""
car_brands = "Mazda", "Citroen", "Renault"
car_brand = self.input("Which car brand?", data_type=car_brands,
default_value=car_brands[1])
self.output("You selected %s", car_brand)
@imacro()
def ask_for_multiple_car_brands(self):
"""asks user for several car brands. Default is every other car brand
in the list"""
car_brands = "Mazda", "Citroen", "Renault", "Ferrari", "Porche", "Skoda"
car_brands = self.input("Which car brand(s)?", data_type=car_brands,
allow_multiple=True,
default_value=car_brands[::2])
self.output("You selected %s", ", ".join(car_brands))
|
By default, the Dialog window title will contain the name of the macro which triggered user input. You can override the default behaviour with the keyword argument title:
1 2 3 4 5 6 7 | @imacro()
def ask_peak(self):
"""asks use for peak current of points with a custom title"""
peak = self.input("What is the peak current?", data_type=Type.Float,
title="Peak selection")
self.output("You selected a peak of %f A", peak)
|
... and this is the corresponding dialog:
The key and unit keyword arguments can be used to provide additional label and unit information respectively and prevent user mistakes:
1 2 3 4 5 6 7 8 9 10 | @imacro()
def ask_peak_v2(self):
"""asks use for peak current of points with a custom title,
default value, label and units"""
label, unit = "peak", "mA"
peak = self.input("What is the peak current?", data_type=Type.Float,
title="Peak selection", key=label, unit=unit,
default_value=123.4)
self.output("You selected a %s of %f %s", label, peak, unit)
|
... and this is the corresponding dialog:
When numeric input is requested, it might be useful to prevent user input outside a certain range. This can be achieved with the minimum and maximum keyword arguments:
1 2 3 4 5 6 7 8 9 10 | @imacro()
def ask_peak_v3(self):
"""asks use for peak current of points with a custom title,
default value, label, units and ranges"""
label, unit = "peak", "mA"
peak = self.input("What is the peak current?", data_type=Type.Float,
title="Peak selection", key=label, unit=unit,
default_value=123.4, minimum=0.0, maximum=200.0)
self.output("You selected a %s of %f %s", label, peak, unit)
|
An additional step keyword argument may help increase usability by setting the step size in a input spin box:
1 2 3 4 5 6 7 8 9 10 11 | @imacro()
def ask_peak_v4(self):
"""asks use for peak current of points with a custom title,
default value, label, units, ranges and step size"""
label, unit = "peak", "mA"
peak = self.input("What is the peak current?", data_type=Type.Float,
title="Peak selection", key=label, unit=unit,
default_value=123.4, minimum=0.0, maximum=200.0,
step=5)
self.output("You selected a %s of %f %s", label, peak, unit)
|
When asking for a decimal number, it might be useful to use the decimals keyword argument to indicate how many decimal places to show in a input spin box:
1 2 3 4 5 6 7 8 9 10 11 | @imacro()
def ask_peak_v5(self):
"""asks use for peak current of points with a custom title,
default value, label, units, ranges, step size and decimal places"""
label, unit = "peak", "mA"
peak = self.input("What is the peak current?", data_type=Type.Float,
title="Peak selection", key=label, unit=unit,
default_value=123.4, minimum=0.0, maximum=200.0,
step=5, decimals=2)
self.output("You selected a %s of %f %s", label, peak, unit)
|
A set of macro input examples can be found here.
Some of the macros you write may take a long time to execute. It could be useful
to provide frequent feedback on the current progress of your macro to prevent
users from thinking the system is blocked. The way to do this is by
yield
ing a new progress number in the ode everytime you want to
send a progress.
The following code shows an example:
import time
@macro([["duration", Type.Integer, 1, "time to sleep (s)"]])
def nap(self, duration):
fduration = float(duration)
for i in range(duration):
time.sleep(1)
yield (i+1) / fduration * 100
The important code here is line 9. Everytime the macro execution reaches this line of code, basically it tells sardana to send a progress with the desired value. By default, the value is interpreted has a percentage and should have the range between 0.0 and 100.0.
Actually, even if your macro doesn’t explicitly send macro progress reports, sardana always generates a 0.0 progress at the beginning of the macro and a last 100.0 progress at the end so for example, in a GUI, the progress bar showing the macro progress will always reach the end (unless an error occurs) no matter how you program the progress.
It is possible to generate a progress that doesn’t fit the 0 - 100.0 range. The above macro has been modified to send a progress with a customized range:
import time
@macro([["duration", Type.Integer, 1, "time to sleep (s)"]])
def nap(self, duration):
status = { 'range' : [0, duration] }
fduration = float(duration)
for i in range(duration):
time.sleep(1)
status['step'] = i+1
yield status
You may notice that this way, the range can be changed dynamically. A progress bar in a GUI is programmed to adjust not only the current progress value but also the ranges so it is safe to change them if necessary.
Footnotes
[1] | To find the absolute path for sardana’s source code type on the
command line python -c "import sys, sardana; sys.stdout.write(str(sardana.__path__))" |
[2] | To check which version of Python you are using type on the command
line python -c "import sys; sys.stdout.write(sys.version)" |
Scan Framework¶
In general terms, we call scan to a macro that moves one or more motors and acquires data along the path of the motor(s). See the introduction to the concept of scan in Sardana.
While a scan macro could be written from scratch, Sardana provides a higher- level API (the scan framework) that greatly simplifies the development of scan macros by taking care of the details about synchronization of motors and of acquisitions.
The scan framework is implemented in the scan
module, which provides the GScan
base class
and its specialized derived classes SScan
and CScan
for step and continuous scans,
respectively.
Creating a scan macro consists in writing a generic macro (see
the generic macro writing instructions) in
which an instance of GScan
is created
(typically in the prepare()
method) which is then invoked in the
run()
method.
Central to the scan framework is the
generator()
function, which
must be passed to the GScan constructor. This generator is a function that
allows to construct the path of the scan (see
GScan
for detailed information on the
generator).
Step scans are built using an instance of the
SScan
class, which requires a step generator
that defines the path for the motion. Since in a step scan the data is acquired
at each step, the generator controls both the motion and the acquisition.
Note that in general, the generator does not need to generate a determinate (or even finite) number of steps. Also note that it is possible to write generators that vary their current step based on the acquired values (e.g., changing step sizes as a function of some counter reading).
The ascan_demo
macro illustrates
the most basic features of a step scan:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | class ascan_demo(Macro):
"""
This is a basic reimplementation of the ascan` macro for demonstration
purposes of the Generic Scan framework. The "real" implementation of
:class:`sardana.macroserver.macros.ascan` derives from
:class:`sardana.macroserver.macros.aNscan` and provides some extra features.
"""
hints = { 'scan' : 'ascan_demo'} #this is used to indicate other codes that the macro is a scan
env = ('ActiveMntGrp',) #this hints that the macro requires the ActiveMntGrp environment variable to be set
param_def = [
['motor', Type.Moveable, None, 'Motor to move'],
['start_pos', Type.Float, None, 'Scan start position'],
['final_pos', Type.Float, None, 'Scan final position'],
['nr_interv', Type.Integer, None, 'Number of scan intervals'],
['integ_time', Type.Float, None, 'Integration time']
]
def prepare(self, motor, start_pos, final_pos, nr_interv, integ_time, **opts):
#parse the user parameters
self.start = numpy.array([start_pos], dtype='d')
self.final = numpy.array([final_pos], dtype='d')
self.integ_time = integ_time
self.nr_points = nr_interv+1
self.interv_size = ( self.final - self.start) / nr_interv
self.name='ascan_demo'
env = opts.get('env',{}) #the "env" dictionary may be passed as an option
#create an instance of GScan (in this case, of its child, SScan
self._gScan=SScan(self, generator=self._generator, moveables=[motor], env=env)
def _generator(self):
step = {}
step["integ_time"] = self.integ_time #integ_time is the same for all steps
for point_no in xrange(self.nr_points):
step["positions"] = self.start + point_no * self.interv_size #note that this is a numpy array
step["point_id"] = point_no
yield step
def run(self,*args):
for step in self._gScan.step_scan(): #just go through the steps
yield step
@property
def data(self):
return self._gScan.data #the GScan provides scan data
|
The ascan_demo
shows only basic
features of the scan framework, but it already shows that writing a step scan
macro is mostly just a matter of writing a generator function.
It also shows that the scan.gscan.GScan.data()
method can be used to
provide the needed return value of data()
Continuous scans are built using an instance of the
CScan
class. Since in the continuous scans
the acquisition and motion are decoupled, CScan requires two independent
generators:
- a waypoint generator: which defines the path for the motion in a very similar way as the step generator does for a continuous scan. The steps generated by this generator are also called “waypoints”.
- a period generator which controls the data acquisition steps.
Essentially, CScan
implements the continuous
scan as an acquisition loop (controlled by the period generator) nested within
a motion loop (controlled by the waypoint generator). Note that each loop is
run on an independent thread, and only limited communication occurs between the
two (basically the acquisition starts at the beginning of each movement and
ends when a waypoint is reached).
The ascanc_demo
macro illustrates
the most basic features of a continuous scan::
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | class ascanc_demo(Macro):
"""
This is a basic reimplementation of the ascanc` macro for demonstration
purposes of the Generic Scan framework. The "real" implementation of
:class:`sardana.macroserver.macros.ascanc` derives from
:class:`sardana.macroserver.macros.aNscan` and provides some extra features.
"""
hints = { 'scan' : 'ascanc_demo'} #this is used to indicate other codes that the macro is a scan
env = ('ActiveMntGrp',) #this hints that the macro requires the ActiveMntGrp environment variable to be set
param_def = [
['motor', Type.Moveable, None, 'Motor to move'],
['start_pos', Type.Float, None, 'Scan start position'],
['final_pos', Type.Float, None, 'Scan final position'],
['integ_time', Type.Float, None, 'Integration time']
]
def prepare(self, motor, start_pos, final_pos, integ_time, **opts):
self.name='ascanc_demo'
#parse the user parameters
self.start = numpy.array([start_pos], dtype='d')
self.final = numpy.array([final_pos], dtype='d')
self.integ_time = integ_time
env = opts.get('env',{}) #the "env" dictionary may be passed as an option
#create an instance of GScan (in this case, of its child, CScan
self._gScan = CScan(self,
waypointGenerator=self._waypoint_generator,
periodGenerator=self._period_generator,
moveables=[motor],
env=env)
def _waypoint_generator(self):
#a very simple waypoint generator! only start and stop points!
yield {"positions":self.start, "waypoint_id": 0}
yield {"positions":self.final, "waypoint_id": 1}
def _period_generator(self):
step = {}
step["integ_time"] = self.integ_time
point_no = 0
while(True): #infinite generator. The acquisition loop is started/stopped at begin and end of each waypoint
point_no += 1
step["point_id"] = point_no
yield step
def run(self,*args):
for step in self._gScan.step_scan():
yield step
|
See also
for another example of a continuous scan implementation
(with more elaborated waypoint generator), see the code of
meshc
In general, the Hooks API provided by the
Hookable
base class allows a macro to run
other code (the hook callable) at certain points of its execution. The hooks
use a “hints” mechanism to pass the receiving macro some extra information on
how/when they should be executed. The hints are strings, and its content is not
fixed by the API, being up to each macro to identify, use and/or ignore them.
You can find some examples of the use of hooks in the
hooks
module.
In the case of the scan macros, the hooks can be either registered directly via
the Hooks API or passed as key:values of the “step” dictionary returned by the
scan generator()
(see
GScan
for more details).
The hints for a given hook are used by the scan framework to select the moment of the scan execution that the given hook is run. The following is a list of hint strings that scan macros support (other hints are ignored):
- ‘pre-scan-hooks’ : before starting the scan.
- ‘pre-move-hooks’ : for steps: before starting to move.
- ‘post-move-hooks’: for steps: after finishing the move.
- ‘pre-acq-hooks’ : for steps: before starting to acquire.
- ‘post-acq-hooks’ : for steps: after finishing acquisition but before recording the step.
- ‘post-step-hooks’ : for steps: after finishing recording the step.
- ‘post-scan-hooks’ : after finishing the scan
See the code of hooked_scan
for a macro that demonstrates the use of the hook points of a scan.
Other examples of the hooks
module
can be illustrative.
Also, note that the Taurus MacroExecutor widget allows the user to dynamically add hooks to existing macros before execution.
Other macros in the examples
module
illustrate more features of the scan framework.
See also the code of the standard scan macros in the
scan
module.
Finally, the documentation and code of GScan
,
SScan
and
CScan
may be helpful.
Writing controllers¶
This chapter provides the necessary information to write controllers in sardana.
An overview of the pool controller concept can be found here.
The complete controller API can be found here.
First, the common interface to all controller types is explained. After, a detailed chapter will focus on each specific controller type:
What is a controller¶
A controller in sardana is a piece of software capable of translating between the sardana API and a specific hardware API. Sardana expects a controller to obey a specific API in order to be able to properly configure and operate with it. The hardware API used by the controller could be anything, from a pure serial line to shared memory or a remote server written in Tango, Taco or even EPICS.
Controllers can only be written in Python (in future also C++ will be possible). A controller must be a class inheriting from one of the existing controller types:
A controller is designed to incorporate a set of generic individual elements. Each element has a corresponding axis. For example, in a motor controller the elements will be motors, but in a counter/timer controller the elements will be experimental channels.
Some controller classes are designed to target a specific type of hardware. Other classes of controllers, the pseudo classes, are designed to provide a high level view over a set of underlying lower level controller elements.
We will focus first on writing low level hardware controllers since they share some of the API and after on the pseudo controllers.
The first thing to do is to import the necessary symbols from sardana library.
As you will see, most symbols can be imported through the
sardana.pool.controller
module:
import springfieldlib
from sardana.pool.controller import MotorController
class SpringfieldMotorController(MotorController):
"""A motor controller intended from demonstration purposes only"""
pass
The common API to all low level controllers includes the set of methods to:
In the following chapters the examples will be based on a motor controller scenario.
The examples use a springfieldlib
module which emulates a motor hardware
access library.
The springfieldlib
can be downloaded from
here
.
The Springfield motor controller can be downloaded from
here
.
The constructor consists of the
__init__()
method. This method is
called when you create a new controller of that type and every time the sardana
server is started. It will also be called if the controller code has changed
on the file and the new code is reloaded into sardana.
It is NOT mandatory to override the __init__()
from MotorController
. Do it only
if you need to add some initialization code. If you do it, it is very important
to follow the two rules:
- use the method signature:
__init__(self, inst, props, *args, **kwargs)
- always call the super class constructor
The example shows how to implement a constructor for a motor controller:
class SpringfieldMotorController(MotorController):
def __init__(self, inst, props, *args, **kwargs):
super(SpringfieldMotorController, self).__init__(inst, props, *args, **kwargs)
# initialize hardware communication
self.springfield = springfieldlib.SpringfieldMotorHW()
# do some initialization
self._motors = {}
Each individual element in a controller is called axis. An axis is represented by a number. A controller can support one or more axes. Axis numbers don’t need to be sequencial. For example, at one time you may have created for your motor controller instance only axis 2 and 5.
Two methods are called when creating or removing an element from a controller.
These methods are AddDevice()
and
DeleteDevice()
. The
AddDevice()
method is called when a
new axis belonging to the controller is created in sardana. The
DeleteDevice()
method is
called when an axis belonging to the controller is removed from sardana.
The example shows an example how to implement these methods on a motor
controller:
class SpringfieldMotorController(MotorController):
def AddDevice(self, axis):
self._motors[axis] = True
def DeleteDevice(self, axis):
del self._motor[axis]
To get the state of an axis, sardana calls the
StateOne()
method. This method
receives an axis as parameter and should return either:
(For motor controller see get motor state ):
The state should be a member of State
(For backward
compatibility reasons, it is also supported to return one of
PyTango.DevState
). The status could be any string.
If you return a State
object, sardana will compose a
status string with:
<axis name> is in <state name>
Here is an example of the possible implementation of
StateOne()
:
from sardana import State
class SpringfieldMotorController(MotorController):
StateMap = {
1 : State.On,
2 : State.Moving,
3 : State.Fault,
}
def StateOne(self, axis):
springfield = self.springfield
state = self.StateMap[ springfield.getState(axis) ]
status = springfield.getStatus(axis)
return state, status
Each axis is associated a set of standard attributes. These attributes depend on the type of controller (example, a motor will have velocity, acceleration but a counter won’t).
Additionally, you can specify an additional set of extra attributes on each axis.
Lets suppose that a Springfield motor controller can do close loop on hardware. We could define an extra motor attribute on each axis that (de)actives close loop on demand.
The first thing to do is to specify which are the extra attributes.
This is done through the axis_attributes
.
This is basically a dictionary were the keys are attribute names and the value
is a dictionary describing the folowing properties for each attribute:
config. parameter | Mandatory | Key | Default value | Example |
---|---|---|---|---|
data type & format | Yes | Type |
— | int |
data access | No | Access |
ReadWrite |
ReadOnly |
description | No | Description |
“” (empty string) | “the motor encoder source” |
default value | No | DefaultValue |
— | 12345 |
getter method name | No | FGet |
“get” + <name> | “getEncoderSource” |
setter method name | No | FSet |
“set” + <name> | “setEncoderSource” |
memorize value | No | Memorize |
Memorized |
NotMemorized |
max dimension size | No | MaxDimSize |
Scalar: () ; 1D: (2048,) ; 2D: (2048, 2048) |
(2048,) |
Here is an example of how to specify the scalar, boolean, read-write CloseLoop extra attribute in a Springfield motor controller:
from sardana import DataAccess
from sardana.pool.controller import Type, Description, DefaultValue, Access, FGet, FSet
class SpringfieldMotorController(MotorController):
axis_attributes = {
"CloseLoop" : {
Type : bool,
Description : "(de)activates the motor close loop algorithm",
DefaultValue : False,
},
}
def getCloseLoop(self, axis):
return self.springfield.isCloseLoopActive(axis)
def setCloseLoop(self, axis, value):
self.springfield.setCloseLoop(axis, value)
When sardana needs to read the close loop value, it will first check if the
controller has the method specified by the FGet
keyword (we didn’t specify it in
axis_attributes
so it defaults to
getCloseLoop). It will then call this controller method which
should return a value compatible with the attribute data type.
As an alternative, to avoid filling the controller code with pairs of get/set
methods, you can choose not to write the getCloseLoop and setCloseLoop methods.
This will trigger sardana to call the
GetAxisExtraPar()
/SetAxisExtraPar()
pair of methods.
The disadvantage is you will end up with a forest of if
...
elif
... else
statements. Here is the alternative
implementation:
from sardana import DataAccess
from sardana.pool.controller import Type, Description, DefaultValue, Access, FGet, FSet
class SpringfieldMotorController(MotorController):
axis_attributes = {
"CloseLoop" : {
Type : bool,
Description : "(de)activates the motor close loop algorithm",
DefaultValue : False,
},
}
def GetAxisExtraPar(self, axis, parameter):
if parameter == 'CloseLoop':
return self.springfield.isCloseLoopActive(axis)
def SetAxisExtraPar(self, axis, parameter, value):
if parameter == 'CloseLoop':
self.springfield.setCloseLoop(axis, value)
Sardana gives you the choice: we leave it up to you to decide which is the better option for your specific case.
Besides extra attributes per axis, you can also define extra attributes at the
controller level.
In order to do that you have to specify the extra controller attribute(s) within
the ctrl_attributes
member. The
syntax for this dictionary is the same as the one used for
axis_attributes
.
Here is an example on how to specify a read-only float matrix attribute called ReflectionMatrix at the controller level:
class SpringfieldMotorController(MotorController):
ctrl_attributes = {
"ReflectionMatrix" : {
Type : ( (float,), ),
Description : "The reflection matrix",
Access : DataAccess.ReadOnly,
},
}
def getReflectionMatrix(self):
return ( (1.0, 0.0), (0.0, 1.0) )
Or, similar to what you can do with axis attributes:
class SpringfieldMotorController(MotorController):
ctrl_attributes = \
{
"ReflectionMatrix" : {
Type : ( (float,), ),
Description : "The reflection matrix",
Access : DataAccess.ReadOnly,
},
}
def GetCtrlPar(self, name):
if name == "ReflectionMatrix":
return ( (1.0, 0.0), (0.0, 1.0) )
A more static form of attributes can be defined at the controller level.
These properties are loaded into the controller at the time of object
construction. They are accessible to your controller at any time but it is
not possible for a user from outside to modify them.
The way to define ctrl_properties
is
very similar to the way you define extra axis attributes or extra controller
attributes.
Here is an example on how to specify a host and port properties:
class SpringfieldMotorController(MotorController):
ctrl_properties = \
{
"host" : {
Type : str,
Description : "host name"
},
"port" : {
Type : int,
Description : "port number",
DefaultValue: springfieldlib.SpringfieldMotorHW.DefaultPort
},
}
def __init__(self, inst, props, *args, **kwargs):
super(SpringfieldMotorController, self).__init__(inst, props, *args, **kwargs)
host = self.host
port = self.port
# initialize hardware communication
self.springfield = springfieldlib.SpringfieldMotorHW(host=host, port=port)
# do some initialization
self._motors = {}
As you can see from lines 15 and 16, to access your controller properties
simply use self.<property name>
. Sardana assures that every property has a
value. In our case, when a SpringfieldMotorController is created, if port
property is not specified by the user (example: using the defctrl
macro in
spock), sardana assignes the default value
springfieldlib.SpringfieldMotorHW.DefaultPort
. On the other hand, since host
has no default value, if it is not specified by the user, sardana will complain
and fail to create and instance of SpringfieldMotorController.
When you write a controller it is important to properly handle errors (example: motor power overload, hit a limit switch, lost of communication with the hardware).
These are the two basic sardana rules you should have in mind:
- The exceptions which are not handled by the controller are handled by sardana,
usually by re-raising the exception (when sardana runs as a Tango DS a
translation is done from the Python exception to a Tango exception).
The
StateOne()
method is handled a little differently: the state is set toFault
and the status will contain the exception information. - When the methods which are supposed to return a value (like
GetAxisPar()
) don’t return a value compatible with the expected data type (includingNone
) aTypeError
exception is thrown.
In every method you should carefully choose how to do handle the possible exceptions/errors.
Usually, catch and handle is the best technique since it is the code of your controller which knows exactly the workings of the hardware. You can discriminate errors and decide a proper handle for each. Essencially, this technique consists of:
- catching the error (if an exception: with
try
...except
clause, if an expected return of a function: with aif
...elif
...else
statement, etc) - raise a proper exception (could be the same exception that has been catched)
or, if in
StateOne()
, return the apropriate error state (Fault
,Alarm
) and a descriptive status.
Here is an example: if the documentation of the underlying library says that:
reading the motor closeloop raises CommunicationFailed if it is not possible to communicate with the Springfield hardware
reading the motor state raises MotorPowerOverload if the motors has a power overload or a MotorTempTooHigh when the motor temperature is too high
then you should handle the exception in the controller and return a proper state information:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | def getCloseLoop(self, axis):
# Here the "proper exception" to raise in case of error is actually the
# one that is raised from the springfield library so handling the
# exception is transparent. Nice!
return self.springfield.isCloseLoopActive(axis)
def StateOne(self, axis):
springfield = self.springfield
try:
state = self.StateMap[ springfield.getState(axis) ]
status = springfield.getStatus(axis)
except springfieldlib.MotorPowerOverload:
state = State.Fault
status = "Motor has a power overload"
except springfieldlib.MotorTempTooHigh:
temp = springfield.getTemperature(axis)
state = State.Alarm
status = "Motor temperature is too high (%f degrees)" % temp
limit_switches = MotorController.NoLimitSwitch
hw_limit_switches = springfield.getLimits(axis)
if hw_limit_switches[0]:
limit_switches |= MotorController.HomeLimitSwitch
if hw_limit_switches[1]:
limit_switches |= MotorController.UpperLimitSwitch
if hw_limit_switches[2]:
limit_switches |= MotorController.LowerLimitSwitch
return state, status, limit_switches
|
Hiding the exception is usually a BAD technique since it prevents the user from finding what was the cause of the problem. You should only use it in extreme cases (example: if there is a bug in sardana which crashes the server if you try to properly raise an exception, then you can temporarely use this technique until the bug is solved).
Example:
1 2 3 4 5 6 | def getCloseLoop(self, axis):
# BAD error handling technique
try:
return self.springfield.isCloseLoopActive(axis)
except:
pass
|
Footnotes
[1] | Pseudo controllers don’t need to manage their individual axis. Therefore, for pseudos you will not implement these methods |
[2] | For pseudo controllers, sardana will calculate the state of each pseudo axis based on the state of the elements that serve as input to the pseudo controller. Therefore, for pseudos you will not implement these methods |
How to write a motor controller¶
An example of a hypothetical Springfield motor controller will be build incrementally from scratch to aid in the explanation.
By now you should have read the general controller basics chapter. You should now have a MotorController with a proper constructor, add and delete axis methods:
import springfieldlib
from sardana.pool.controller import MotorController
class SpringfieldMotorController(MotorController):
def __init__(self, inst, props, *args, **kwargs):
super(SpringfieldMotorController, self).__init__(inst, props, *args, **kwargs)
# initialize hardware communication
self.springfield = springfieldlib.SpringfieldMotorHW()
# do some initialization
self._motors = {}
def AddDevice(self, axis):
self._motors[axis] = True
def DeleteDevice(self, axis):
del self._motor[axis]
The get axis state method has some details that will be explained below.
The examples use a springfieldlib
module which emulates a motor hardware
access library.
The springfieldlib
can be downloaded from
here
.
The Springfield motor controller can be downloaded from
here
.
The following code describes a minimal Springfield base motor controller which is able to return both the state and position of a motor as well as move a motor to the desired position:
class SpringfieldBaseMotorController(MotorController):
"""The most basic controller intended from demonstration purposes only.
This is the absolute minimum you have to implement to set a proper motor
controller able to get a motor position, get a motor state and move a
motor.
This example is so basic that it is not even directly described in the
documentation"""
MaxDevice = 128
def __init__(self, inst, props, *args, **kwargs):
"""Constructor"""
super(SpringfieldBaseMotorController, self).__init__(inst, props, *args, **kwargs)
self.springfield = springfieldlib.SpringfieldMotorHW()
def ReadOne(self, axis):
"""Get the specified motor position"""
return self.springfield.getPosition(axis)
def StateOne(self, axis):
"""Get the specified motor state"""
springfield = self.springfield
state = springfield.getState(axis)
if state == 1:
return State.On, "Motor is stopped"
elif state == 2:
return State.Moving, "Motor is moving"
elif state == 3:
return State.Fault, "Motor has an error"
def StartOne(self, axis, position):
"""Move the specified motor to the specified position"""
self.springfield.move(axis, position)
def StopOne(self, axis):
"""Stop the specified motor"""
self.springfield.stop(axis)
This code is shown only to demonstrate the minimal controller API. The advanced motor controller chapters describe how to account for more complex behaviour like reducing the number of hardware accesses or synchronize motion of multiple motors.
To get the state of a motor, sardana calls the
StateOne()
method. This method
receives an axis as parameter and should return a sequence of three values:
To get the state of a motor, sardana calls the
StateOne()
method. This method
receives an axis as parameter and should return either:
The state should be a member of State
(For backward
compatibility reasons, it is also supported to return one of
PyTango.DevState
). The status could be any string. The limit switches
is a integer with bits representing the three possible limits: home, upper
and lower. Sardana provides three constants which can be ored together to
provide the desired limit switch:
To say both home and lower limit switches are active (rare!) you can do:
limit_switches = MotorController.HomeLimitSwitch | MotorController.LowerLimitSwitch
If you don’t return a status, sardana will compose a status string with:
<axis name> is in <state name>
If you don’t return limit switches, sardana will assume all limit switches are off.
Here is an example of the possible implementation of
StateOne()
:
from sardana import State
class SpringfieldMotorController(MotorController):
StateMap = {
1 : State.On,
2 : State.Moving,
3 : State.Fault,
}
def StateOne(self, axis):
springfield = self.springfield
state = self.StateMap[ springfield.getState(axis) ]
status = springfield.getStatus(axis)
limit_switches = MotorController.NoLimitSwitch
hw_limit_switches = springfield.getLimits(axis)
if hw_limit_switches[0]:
limit_switches |= MotorController.HomeLimitSwitch
if hw_limit_switches[1]:
limit_switches |= MotorController.UpperLimitSwitch
if hw_limit_switches[2]:
limit_switches |= MotorController.LowerLimitSwitch
return state, status, limit_switches
To get the motor position, sardana calls the
ReadOne()
method. This method
receives an axis as parameter and should return a valid position. Sardana
interprets the returned position as a dial position.
Here is an example of the possible implementation of
ReadOne()
:
class SpringfieldMotorController(MotorController):
def ReadOne(self, axis):
position = self.springfield.getPosition(axis)
return position
When an order comes for sardana to move a motor, sardana will call the
StartOne()
method. This method receives
an axis and a position. The controller code should trigger the hardware motion.
The given position is always the dial position.
Here is an example of the possible implementation of
StartOne()
:
class SpringfieldMotorController(MotorController):
def StartOne(self, axis, position):
self.springfield.move(axis, position)
As soon as StartOne()
is invoked,
sardana expects the motor to be moving. It enters a high frequency motion
loop which asks for the motor state through calls to
StateOne()
. It will keep the loop
running as long as the controller responds with State.Moving
.
If StateOne()
raises an exception
or returns something other than State.Moving
, sardana will assume the motor
is stopped and exit the motion loop.
For a motion to work properly, it is therefore, very important that
StateOne()
responds correctly.
It is possible to stop a motor when it is moving. When sardana is ordered to
stop a motor motion, it invokes the StopOne()
method. This method receives an axis parameter. The controller should make
sure the desired motor is gracefully stopped, if possible, respecting the
configured motion parameters (like deceleration and base_rate).
Here is an example of the possible implementation of
StopOne()
:
class SpringfieldMotorController(MotorController):
def StopOne(self, axis):
self.springfield.stop(axis)
In a danger situation (motor moving a table about to hit a wall), it is
desirable to abort a motion as fast as possible. When sardana is ordered to
abort a motor motion, it invokes the AbortOne()
method. This method receives an axis parameter. The controller should make
sure the desired motor is stopped as fast as it can be done, possibly losing
track of position.
Here is an example of the possible implementation of
AbortOne()
:
class SpringfieldMotorController(MotorController):
def AbortOne(self, axis):
self.springfield.abort(axis)
Note
The default implementation of StopOne()
calls AbortOne()
so, if your
controller cannot distinguish stopping from aborting, it is sufficient
to implement AbortOne()
.
By default, sardana expects every axis to have a set of attributes:
- acceleration
- deceleration
- velocity
- base rate
- steps per unit
To set and retrieve the value of these attributes, sardana invokes pair of
methods: GetAxisPar()
/SetAxisPar()
Here is an example of the possible implementation:
class SpringfieldMotorController(MotorController):
def GetAxisPar(self, axis, name):
springfield = self.springfield
name = name.lower()
if name == "acceleration":
v = springfield.getAccelerationTime(axis)
elif name == "deceleration":
v = springfield.getDecelerationTime(axis)
elif name == "base_rate":
v = springfield.getMinVelocity(axis)
elif name == "velocity":
v = springfield.getMaxVelocity(axis)
elif name == "step_per_unit":
v = springfield.getStepPerUnit(axis)
return v
def SetAxisPar(self, axis, name, value):
springfield = self.springfield
name = name.lower()
if name == "acceleration":
springfield.setAccelerationTime(axis, value)
elif name == "deceleration":
springfield.setDecelerationTime(axis, value)
elif name == "base_rate":
springfield.setMinVelocity(axis, value)
elif name == "velocity":
springfield.setMaxVelocity(axis, value)
elif name == "step_per_unit":
springfield.setStepPerUnit(axis, value)
See also
- What to do when...
- What to do when your hardware motor controller doesn’t support steps per unit
Sometimes it is useful to reset the current position to a certain value.
Imagine you are writing a controller for a hardware controller which handles
stepper motors. When the hardware is asked for a motor position it will
probably answer some value from an internal register which is
incremented/decremented each time the motor goes up/down a step. Probably this
value as physical meaning so the usual procedure is to move the motor to a known
position (home switch, for example) and once there, set a meaningful position to
the current position. Some motor controllers support reseting the internal
register to the desired value. If your motor controller can do this the
implementation is as easy as writing the
DefinePosition()
and call the
proper code of your hardware library to do it:
class SpringfieldMotorController(MotorController):
def DefinePosition(self, axis, position):
self.springfield.setCurrentPosition(axis, position)
See also
What to do when your hardware motor controller doesn’t support defining the position
This chapter describes common difficult situations you may face when writing a motor controller in sardana, and possible solutions to solve them.
- my controller doesn’t support steps per unit
Many (probably, most) hardware motor controllers don’t support steps per unit at the hardware level. This means that your sardana controller should be able to emulate steps per unit at the software level. This can be easily done, but it requires you to make some changes in your code.
We will assume now that the Springfield motor controller doesn’t support steps per unit feature. The first that needs to be done is to modify the
AddDevice()
method so it is able to to store the resulting conversion factor between the hardware read position and the position the should be returned (the step_per_unit). TheReadOne()
also needs to be rewritten to make the proper calculation. FinallyGetAxisPar()
/SetAxisPar()
methods need to be rewritten to properly get/set the step per unit value:class SpringfieldMotorController(MotorController): def AddDevice(self, axis): self._motor[axis] = dict(step_per_unit=1.0) def ReadOne(self, axis): step_per_unit = self._motor[axis]["step_per_unit"] position = self.springfield.getPosition(axis) return position / step_per_unit def GetAxisPar(self, axis, name): springfield = self.springfield name = name.lower() if name == "acceleration": v = springfield.getAccelerationTime(axis) elif name == "deceleration": v = springfield.getDecelerationTime(axis) elif name == "base_rate": v = springfield.getMinVelocity(axis) elif name == "velocity": v = springfield.getMaxVelocity(axis) elif name == "step_per_unit": v = self._motor[axis]["step_per_unit"] return v def SetAxisPar(self, axis, name, value): springfield = self.springfield name = name.lower() if name == "acceleration": springfield.setAccelerationTime(axis, value) elif name == "deceleration": springfield.setDecelerationTime(axis, value) elif name == "base_rate": springfield.setMinVelocity(axis, value) elif name == "velocity": springfield.setMaxVelocity(axis, value) elif name == "step_per_unit": self._motor[axis]["step_per_unit"] = value
- my controller doesn’t support defining the position
Some controllers may not be able to reset the position to a different value. In these cases, your controller code should be able to emulate such a feature. This can be easily done, but it requires you to make some changes in your code.
We will now assume that the Springfield motor controller doesn’t support steps per unit feature. The first thing that needs to be done is to modify the
AddDevice()
method so it is able to store the resulting offset between the hardware read position and the position the should be returned (the define_position_offset). TheReadOne()
also needs to be rewritten to take the define_position_offset into account. FinallyDefinePosition()
needs to be written to update the define_position_offset to the desired value:class SpringfieldMotorController(MotorController): def AddDevice(self, axis): self._motor[axis] = dict(define_position_offset=0.0) def ReadOne(self, axis): dp_offset = self._motor[axis]["define_position_offset"] position = self.springfield.getPosition(axis) return position + dp_offset def DefinePosition(self, axis, position): current_position = self.springfield.getPosition(axis) self._motor[axis]["define_position_offset"] = position - current_position
When you read the position of a motor from the hardware sometimes it is necessary to associate a timestamp with that position so you can track the position of a motor in time.
If sardana is executed as a Tango device server, reading the position
attribute from the motor device triggers the execution of your controller’s
ReadOne()
method. Tango responds with
the value your controller returns from the call to
ReadOne()
and automatically assigns
a timestamp. However this timestamp has a certain delay since the time the
value was actually read from hardware and the time Tango generates the timestamp.
To avoid this, sardana supports returning in
ReadOne()
an object that contains both
the value and the timestamp instead of the usual numbers.Number
.
The object must be an instance of SardanaValue
.
Here is an example of associating a timestamp in
ReadOne()
:
import time
from sardana.pool.controller import SardanaValue
class SpringfieldMotorController(MotorController):
def ReadOne(self, axis):
return SardanaValue(value=self.springfield.getPosition(axis),
timestamp=time.time())
If your controller communicates with a Tango device, Sardana also supports
returning a DeviceAttribute
object. Sardana will use this
object’s value and timestamp. Example:
class TangoMotorController(MotorController):
def ReadOne(self, axis):
return self.device.read_attribute("position")
This chapter describes an extended API that allows you to better synchronize motions involing more than one motor, as well as optimize hardware communication (in case the hardware interface also supports this).
Often it is the case that the experiment/procedure the user runs requires to
move more than one motor at the same time.
Imagine that the user requires motor at axis 1 to be moved to 100mm and motor
axis 2 to be moved to -20mm.
Your controller will receive two consecutive calls to
StartOne()
:
StartOne(1, 100)
StartOne(2, -20)
and each StartOne will probably connect to the hardware (through serial line, socket, Tango or EPICS) and ask the motor to be moved. This will do the job but, there will be a slight desynchronization between the two motors because hardware call of motor 1 will be done before hardware call to motor 2.
Sardana provides an extended start motion which gives you the possibility to improve the syncronization (and probably reduce communications) but your hardware controller must somehow support this feature as well.
The complete start motion API consists of four methods:
Except for StartOne()
, the
implemenation of all other start methods is optional and their default
implementation does nothing (PreStartOne()
actually returns True
).
So, actually, the complete algorithm for motor motion in sardana is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /FOR/ Each controller(s) implied in the motion
- Call PreStartAll()
/END FOR/
/FOR/ Each motor(s) implied in the motion
- ret = PreStartOne(motor to move, new position)
- /IF/ ret is not true
/RAISE/ Cannot start. Motor PreStartOne returns False
- /END IF/
- Call StartOne(motor to move, new position)
/END FOR/
/FOR/ Each controller(s) implied in the motion
- Call StartAll()
/END FOR/
|
So, for the example above where we move two motors, the complete sequence of calls to the controller is:
PreStartAll()
if not PreStartOne(1, 100):
raise Exception("Cannot start. Motor(1) PreStartOne returns False")
if not PreStartOne(2, -20):
raise Exception("Cannot start. Motor(2) PreStartOne returns False")
StartOne(1, 100)
StartOne(2, -20)
StartAll()
Sardana assures that the above sequence is never interrupted by other calls, like a call from a different user to get motor state.
Suppose the springfield library tells us in the documentation that:
... to move multiple motors at the same time use:
moveMultiple(seq<pair<axis, position>>)Example:
moveMultiple([[1, 100], [2, -20]])
We can modify our motor controller to take profit of this hardware feature:
class SpringfieldMotorController(MotorController):
def PreStartAll(self):
# clear the local motion information dictionary
self._moveable_info = []
def StartOne(self, axis, position):
# store information about this axis motion
motion_info = axis, position
self._moveable_info.append(motion_info)
def StartAll(self):
self.springfield.moveMultiple(self._moveable_info)
A similar principle applies when sardana asks for the state and position of multiple axis. The two sets of methods are, in these cases:
The main differences between these sets of methods and the ones from start motion
is that StateOne()
/
ReadOne()
methods are called AFTER
the corresponding StateAll()
/
ReadAll()
counterparts and they are
expeced to return the state/position of the requested axis.
The internal sardana algorithm to read position is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /FOR/ Each controller(s) implied in the reading
- Call PreReadAll()
/END FOR/
/FOR/ Each motor(s) implied in the reading
- PreReadOne(motor to read)
/END FOR/
/FOR/ Each controller(s) implied in the reading
- Call ReadAll()
/END FOR/
/FOR/ Each motor(s) implied in the reading
- Call ReadOne(motor to read)
/END FOR/
|
Here is an example assuming the springfield library tells us in the documentation that:
... to read the position of multiple motors at the same time use:
getMultiplePosition(seq<axis>) -> dict<axis, position>Example:
positions = getMultiplePosition([1, 2])
The new improved code could look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class SpringfieldMotorController(MotorController):
def PreRealAll(self):
# clear the local position information dictionary
self._position_info = []
def PreReadOne(self, axis):
self._position_info.append(axis)
def ReadAll(self):
self._positions = self.springfield.getMultiplePosition(self._position_info)
def ReadOne(self, axis):
return self._positions[axis]
|
How to write a counter/timer controller¶
An example of a hypothetical Springfield counter/timer controller will be build incrementally from scratch to aid in the explanation.
By now you should have read the general controller basics chapter. You should be able to create a CounterTimerController with:
- a proper constructor,
- add and delete axis methods
- get axis state
import springfieldlib
from sardana.pool.controller import CounterTimerController
class SpringfieldCounterTimerController(CounterTimerController):
def __init__(self, inst, props, *args, **kwargs):
super(SpringfieldCounterTimerController, self).__init__(inst, props, *args, **kwargs)
# initialize hardware communication
self.springfield = springfieldlib.SpringfieldCounterHW()
# do some initialization
self._counters = {}
def AddDevice(self, axis):
self._counters[axis] = True
def DeleteDevice(self, axis):
del self._counters[axis]
StateMap = {
1 : State.On,
2 : State.Moving,
3 : State.Fault,
}
def StateOne(self, axis):
springfield = self.springfield
state = self.StateMap[ springfield.getState(axis) ]
status = springfield.getStatus(axis)
return state, status
The examples use a springfieldlib
module which emulates a counter/timer
hardware access library.
The springfieldlib
can be downloaded from
here
.
The Springfield counter/timer controller can be downloaded from
here
.
The following code describes a minimal Springfield base counter/timer controller which is able to return both the state and value of an individual counter as well as to start an acquisition:
class SpringfieldBaseCounterTimerController(CounterTimerController):
"""The most basic controller intended from demonstration purposes only.
This is the absolute minimum you have to implement to set a proper counter
controller able to get a counter value, get a counter state and do an
acquisition.
This example is so basic that it is not even directly described in the
documentation"""
def __init__(self, inst, props, *args, **kwargs):
"""Constructor"""
super(SpringfieldBaseCounterTimerController, self).__init__(inst, props, *args, **kwargs)
self.springfield = springfieldlib.SpringfieldCounterHW()
def ReadOne(self, axis):
"""Get the specified counter value"""
return self.springfield.getValue(axis)
def StateOne(self, axis):
"""Get the specified counter state"""
springfield = self.springfield
state = springfield.getState(axis)
if state == 1:
return State.On, "Counter is stopped"
elif state == 2:
return State.Moving, "Counter is acquiring"
elif state == 3:
return State.Fault, "Counter has an error"
def StartAll(self):
self.springfield.start_count()
def StartOne(self, axis, value=None):
"""acquire the specified counter"""
self.springfield.activate_channel(axis)
def LoadOne(self, axis, value):
self.springfield.set_master(axis, value)
def StopOne(self, axis):
"""Stop the specified counter"""
self.springfield.stop(axis)
This code is shown only to demonstrate the minimal controller API. The advanced counter/timer controller chapters describe how to account for more complex behaviour like reducing the number of hardware accesses.
Todo
finish counter/timer controller howto
Sardana Testing¶
Sardana Testing¶
A testing framework allowing to test the Sardana features is included with the Sardana distribution. It is useful for test-driven development and it allows to find bugs in the code.
The first implementation of the Framework is an outcome of the Sardana Enhancement Proposal 5 (SEP5).
Ideally, whenever possible, bug reports should be accompanied by a test revealing the bug.
The first tests implemented are focused on Unit Tests, but the same framework should be used for integration and system tests as well.
The sardana.test module includes testsuite.py. This file provides an auto-discovering suite for all tests implemented in Sardana.
The following are some key points to keep in mind when using this framework:
The Sardana Test Framework is based on
unittest
which should be imported fromtaurus.external
in order to be compatible with all versions of python supported by Taurus.all test-related code is contained in submodules named test which appear in any module of Sardana.
- test-related code falls in one of these three categories:
- Actual test code (classes that derive from unittest.TestCase)
- Utility classes/functions (code to simplify development of test code)
- Resources (accessory files required by some test). They are located in subdirectories named res situated inside the folders named test.
For a more complete description of the conventions on how to write tests with the Sardana Testing Framework, please refer to the [SEP5](http://sourceforge.net/p/sardana/wiki/SEP5/).
Sardana Test Framework provides tools for testing macros. These tools come from sardana.macroserver.macros.test module
Tests meant to be incorporated in the Sardana distribution must be portable. For this reason it is strongly encouraged to use only elements created by the sar_demo macro. Only in the case where this is not possible, one may create specific elements for a test; these elements must be removed at the end of the test execution (e.g. using the tearDown method).
The module sardana.macroserver.macros.test
provides utilities to simplify
the tests for macro execution and macro stop. Macro test classes can inherit
from RunMacroTestCase
,
RunStopMacroTestCase
or
BaseMacroTestCase
.
Another utility provided is the option to execute the same test with many different macro input parameters. This is done by decorating the test class with any of the decorators of the the macro tests family.
This decorator is provided by sardana.macroserver.macros.test
.
Specificities:
- Macros such as ‘lsm’ inherit from RunMacroTestCase as it is interesting to
test if the macros can be executed. Helper methods
( such as
RunMacroTestCase.macro_runs()
) can be overriden when programming new test cases. New helpers can be created as well. - Scan macros inherits from RunStopMacroTestCase as it is interesting to test both: if the macros can be executed and if they can be aborted.
For a more complete description of the conventions used when writing tests, see: http://sourceforge.net/p/sardana/wiki/SEP5/
For more information about unittest framework: http://docs.python.org/2/library/unittest.html
Run tests from command line¶
Running the whole Sardana test suite from command line can be done by going to the Sardana directory: <sardana_root>/sardana/test/.
And executing: python testsuite.py
- Executing a single test from command line is done by doing:
- python -m unittest test_name
Where test_name is the test module that has to be run.
- That can be done with more verbosity by indicating the option -v.
- python -m unittest -v test_name
Test-driven development example¶
In this section it is presented a practical example of how to code a macro by doing test-driven development thanks to the tools provided by the Sardana Test Framework.
Consider that we want to write a new macro named “sqrtmac” for calculating the square root of an input number. The “sqrtmac” specifications are:
- Its data must be given in the form {‘in’:x,’out’:s}
- Its output (‘out’) must be the square root of the input data (‘in’).
- Macro must raise an Exception of type ValueError if negative numbers are given as input.
First we design the tests according to the specifications considering the features that are required for the macro. For doing so we will need some imports in order to be able to use the base classes and decorators. In this case the important base class is RunMacroTestCase, and we import testRun and testFail to be used as decorators:
"""Tests for sqrt macro"""
import numpy as np
import unittest
from sardana.macroserver.macros.test import RunMacroTestCase, testRun, testFail
Now we will write a basic test, that will check the execution of the sqrtmac for a given input x = 12345.678. For doing so, we inherit from unittest and from RunMacroTestCase. In this implementation we will calculate in the test the sqrt of the input parameter and then, using assertEqual, we will verify that this value is equal to the output of the macro. The helper method macro_runs is used for executing the macro:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | """Tests for a macro calculating the sqrt of an input number"""
import numpy as np
import unittest
from sardana.macroserver.macros.test import RunMacroTestCase, testRun, testFail
class sqrtmacTest(RunMacroTestCase, unittest.TestCase):
"""Test of sqrt macro. It verifies that macro sqrt can be executed.
"""
macro_name = "sqrtmac"
def test_sqrtmac(self):
macro_params = [str(x)]
self.macro_runs(macro_params)
data=self.macro_executor.getData()
expected_output = 49
msg = 'Macro output does not equals the expected output'
self.assertEqual(data['in'] ,float(macro_params[0]), msg)
self.assertEqual(data['out'] ,expected_output, msg)
|
Now, two new tests are added thanks to the decorator and the helper functions. In this case we will use the decorator @testRun. The same test case can be launched with different sets of parameters. One decorator is used for each set of parameters.
One of the tests will run the sqrtmac macro for an input value of 9 and verify that the macro has been executed without problems.
Another test added will run the sqrt for an input of 2.25 and will verify its input and output values against the expected values which we pass to the decorator. A wait_timeout of 5s will be given; this means, that if the test does not finish within 5 seconds, the current test will give an error and the following test will be executed:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | """Tests for a macro calculating the sqrt of an input number"""
import numpy as np
import unittest
from sardana.macroserver.macros.test import RunMacroTestCase, testRun, testFail
@testRun(macro_params=['9'])
@testRun(macro_params=['2.25'], data={'in':2.25,'out':1.5}, wait_timeout=5)
class sqrtmacTest(RunMacroTestCase, unittest.TestCase):
"""Test of sqrt macro. It verifies that macro sqrt can be executed.
"""
macro_name = "sqrtmac"
def test_sqrtmac(self):
macro_params = [str(x)]
self.macro_runs(macro_params)
data=self.macro_executor.getData()
expected_output = 49
msg = 'Macro output does not equals the expected output'
self.assertEqual(data['in'] ,float(macro_params[0]), msg)
self.assertEqual(data['out'] ,expected_output, msg)
|
The following test implemented must check that the macro is raising an Exception if negative numbers are passed as input. The type of exception raised must be a ValueError. For developing this test we will use the decorator testFail which allows to test if a macro is raising an Exception before finishing its execution. The final implementation of our test file test_sqrt.py is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | """Tests for a macro calculating the sqrt of an input number"""
import numpy as np
import unittest
from sardana.macroserver.macros.test import RunMacroTestCase, testRun, testFail
@testRun(macro_params=['9'])
@testRun(macro_params=['2.25'], data={'in':2.25,'out':1.5}, wait_timeout=5)
@testFail(macro_params=['-3.0'], exception=ValueError, wait_timeout=5)
class sqrtmacTest(RunMacroTestCase, unittest.TestCase):
"""Test of sqrt macro. It verifies that macro sqrt can be executed.
"""
macro_name = "sqrtmac"
def test_sqrtmac(self):
macro_params = [str(x)]
self.macro_runs(macro_params)
data=self.macro_executor.getData()
expected_output = 49
msg = 'Macro output does not equals the expected output'
self.assertEqual(data['in'] ,float(macro_params[0]), msg)
self.assertEqual(data['out'] ,expected_output, msg)
|
Thanks to the test that we have designed precedently we can now implement the macro and check if it is developed according to the specifications.
We do a first implementation of the macro by calculating the square root of an input number. Then we will execute the test and analyze the results. The first implementation looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import numpy as np
from sardana.macroserver.macro import Macro, Type
class sqrtmac(Macro):
"""Macro sqrtmac"""
param_def = [ [ "value", Type.Float, 9,
"input value for which we want the square root"] ]
result_def = [ [ "result", Type.Float, None,
"square root of the input value"] ]
def run (self, n):
ret = np.sqrt(n)
return ret
|
An its ouput on the screen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | sardana/src/sardana/macroserver/macros/test> python -m unittest -v test_sqrtmac
test_sqrtmac (test_sqrtmac.sqrtmacTest) ... ERROR
test_sqrtmac_macro_fails (test_sqrtmac.sqrtmacTest)
Testing sqrtmac with macro_fails(macro_params=['-3.0'], exception=<type 'exceptions.ValueError'>, wait_timeout=5) ... FAIL
test_sqrtmac_macro_runs (test_sqrtmac.sqrtmacTest)
Testing sqrtmac with macro_runs(macro_params=['2.25'], wait_timeout=5, data={'out': 1.5, 'in': 2.25}) ... ERROR
test_sqrtmac_macro_runs_2 (test_sqrtmac.sqrtmacTest)
Testing sqrtmac with macro_runs(macro_params=['9']) ... ok
======================================================================
ERROR: test_sqrtmac (test_sqrtmac.sqrtmacTest)
----------------------------------------------------------------------
Traceback (most recent call last):
.
.
.
desc = Exception: Macro 'sqrtmac' does not produce any data
======================================================================
ERROR: test_sqrtmac_macro_runs (test_sqrtmac.sqrtmacTest)
Testing sqrtmac with macro_runs(macro_params=['2.25'], wait_timeout=5, data={'out': 1.5, 'in': 2.25})
----------------------------------------------------------------------
Traceback (most recent call last):
.
.
.
desc = Exception: Macro 'sqrtmac' does not produce any data
======================================================================
FAIL: test_sqrtmac_macro_fails (test_sqrtmac.sqrtmacTest)
Testing sqrtmac with macro_fails(macro_params=['-3.0'], exception=<type 'exceptions.ValueError'>, wait_timeout=5)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/siciliarep/tmp/mrosanes/workspace/GIT/projects/sardana/src/sardana/macroserver/macros/test/base.py", line 144, in newTest
return helper(**helper_kwargs)
File "/siciliarep/tmp/mrosanes/workspace/GIT/projects/sardana/src/sardana/macroserver/macros/test/base.py", line 271, in macro_fails
self.assertEqual(state, 'exception', msg)
AssertionError: Post-execution state should be "exception" (got "finish")
----------------------------------------------------------------------
Ran 4 tests in 0.977s
FAILED (failures=1, errors=2)
|
At this moment two tests are giving an error because ‘sqrtmac’ does not produce data, and one test is failing because the exception is not treat. The test that is giving ‘Ok’ is only testing that the macro can be executed.
The second step will be to set the input and output data of the macro and execute the test again:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import numpy as np
from sardana.macroserver.macro import Macro, Type
class sqrtmac(Macro):
"""Macro sqrtmac"""
param_def = [ [ "value", Type.Float, 9,
"input value for which we want the square root"] ]
result_def = [ [ "result", Type.Float, None,
"square root of the input value"] ]
def run (self, n):
ret = np.sqrt(n)
self.setData({'in':n,'out':ret})
return ret
|
An its ouput on the screen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | sardana/macroserver/macros/test> python -m unittest -v test_sqrtmac
test_sqrtmac (test_sqrtmac.sqrtmacTest) ... ok
test_sqrtmac_macro_fails (test_sqrtmac.sqrtmacTest)
Testing sqrtmac with macro_fails(macro_params=['-3.0'], exception=<type 'exceptions.ValueError'>, wait_timeout=5) ... FAIL
test_sqrtmac_macro_runs (test_sqrtmac.sqrtmacTest)
Testing sqrtmac with macro_runs(macro_params=['2.25'], wait_timeout=5, data={'out': 1.5, 'in': 2.25}) ... ok
test_sqrtmac_macro_runs_2 (test_sqrtmac.sqrtmacTest)
Testing sqrtmac with macro_runs(macro_params=['9']) ... ok
======================================================================
FAIL: test_sqrtmac_macro_fails (test_sqrtmac.sqrtmacTest)
Testing sqrtmac with macro_fails(macro_params=['-3.0'], exception=<type 'exceptions.ValueError'>, wait_timeout=5)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/siciliarep/tmp/mrosanes/workspace/GIT/projects/sardana/src/sardana/macroserver/macros/test/base.py", line 142, in newTest
return helper(**helper_kwargs)
File "/siciliarep/tmp/mrosanes/workspace/GIT/projects/sardana/src/sardana/macroserver/macros/test/base.py", line 267, in macro_fails
self.assertEqual(state, 'exception', msg)
AssertionError: Post-execution state should be "exception" (got "finish")
----------------------------------------------------------------------
Ran 4 tests in 0.932s
FAILED (failures=1)
|
As we can see, the test_sqrtmac_macro_fails is Failing, because the case of negative numbers is still not suppported. The rest of tests that are testing the execution and the expected output values are OK.
Finally we arrive to the complete implementation of the macro taking into account the Exception that should be raised if we enter a negative number as input parameter. For coding this macro test-driven development has been used:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import numpy as np
from sardana.macroserver.macro import Macro, Type
class sqrtmac(Macro):
"""Macro sqrtmac"""
param_def = [ [ "value", Type.Float, 9,
"input value for which we want the square root"] ]
result_def = [ [ "result", Type.Float, None,
"square root of the input value"] ]
def run (self, n):
if (n<0):
raise ValueError("Negative numbers are not accepted.")
ret = np.sqrt(n)
self.setData({'in':n,'out':ret})
return ret
|
An the output on the console after executing the test looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 | sardana/macroserver/macros/test> python -m unittest -v test_sqrtmac
test_sqrtmac (test_sqrtmac.sqrtmacTest) ... ok
test_sqrtmac_macro_fails (test_sqrtmac.sqrtmacTest)
Testing sqrtmac with macro_fails(macro_params=['-3.0'], exception=<type 'exceptions.ValueError'>, wait_timeout=5) ... ok
test_sqrtmac_macro_runs (test_sqrtmac.sqrtmacTest)
Testing sqrtmac with macro_runs(macro_params=['2.25'], wait_timeout=5, data={'out': 1.5, 'in': 2.25}) ... ok
test_sqrtmac_macro_runs_2 (test_sqrtmac.sqrtmacTest)
Testing sqrtmac with macro_runs(macro_params=['9']) ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.928s
OK
|
Sardana Unit Test Examples¶
test_ct
¶Tests for ct macros
Classes
-
class
CtTest
(*a, **kw)[source]¶ Test of ct macro. It verifies that macro ct can be executed. It inherits from RunStopMacroTestCase and from unittest.TestCase. It tests two executions of the ct macro with two different input parameters. Then it does another execution and it tests if the execution can be aborted.
-
macro_name
= 'ct'¶
-
test_list
¶Tests for list macros
Classes
-
class
LsTest
[source]¶ Base class for testing macros used to list elements. See
RunMacroTestCase
for requirements. LsTest use the lists of elem_type generated bySarDemoEnv
as reference for compare with the output of the tested ls macro.- LsTest provide the class member:
- elem_type (str): Type of the element to validate (mandatory).
Must be a valid type for
SarDemoEnv
class.
- It provides the helper method:
-
elem_type
= None¶
-
check_elements
(list1, list2)[source]¶ A helper method to evaluate if all elements of list1 are in list2. :params list1: (seq<str>) List of elements to evaluate. :params list2: (seq<str>) List of elements for validate.
-
macro_runs
(**kwargs)[source]¶ Reimplementation of macro_runs method for ls macros. It verifies that elements (elem_type) gotten by parsing the macro executor log output are in the correspondent list (elem_type) of SardanaEnv.
-
assertFinished
(msg)¶ Asserts that macro has finished.
-
door_name
= 'door/demo1/1'¶
-
macro_fails
(macro_params=None, wait_timeout=inf, exception=None)¶ Check that the macro fails to run for the given input parameters
Parameters: - macro_params – (seq<str>) input parameters for the macro
- wait_timeout – maximum allowed time for the macro to fail. By default infinite timeout is used.
- exception – (str or Exception) if given, an additional check of the type of the exception is done. (IMPORTANT: this is just a comparison of str representations of exception objects)
-
macro_name
= None¶
-
setUp
()¶ Preconditions: - Those from
BaseMacroTestCase
- the macro executor registers to all the log levels
-
tearDown
()¶ The macro_executor instance must be removed
test_scan
¶Tests for scan macros
Functions
-
parsing_log_output
(log_output)[source]¶ A helper method to parse log output of an executed scan macro. :params log_output: (seq<str>) Result of macro_executor.getLog(‘output’) (see description in
BaseMacroExecutor
).Returns: (seq<number>) The numeric data of a scan.
Classes
-
class
ANscanTest
[source]¶ Not yet implemented. Once implemented it will test anscan. See
RunStopMacroTestCase
for requirements.
-
class
DNscanTest
[source]¶ Not yet implemented. Once implemented it will test the macro dnscanc. See
ANscanTest
for requirements.
-
class
DNscancTest
[source]¶ Not yet implemented. Once implemented it will test the macro dnscanc. See
DNscanTest
for requirements.
-
class
AscanTest
(*a, **kw)[source]¶ Test of ascan macro. See
ANscanTest
for requirements. It verifies that macro ascan can be executed and stoped and tests the output of the ascan using data from log system and macro data.-
macro_name
= 'ascan'¶
-
macro_runs
(macro_params=None, wait_timeout=30.0)[source]¶ Reimplementation of macro_runs method for ascan macro. It verifies using double checking, with log output and data from the macro:
- The motor initial and final positions of the scan are the ones given as input.
- Intervals in terms of motor position between one point and the next one are equidistant.
-
-
class
DscanTest
(*a, **kw)[source]¶ Test of dscan macro. It verifies that macro dscan can be executed and stoped. See
DNscanTest
for requirements.-
macro_name
= 'dscan'¶
-
-
class
MeshTest
(*a, **kw)[source]¶ Test of mesh macro. It verifies that macro mesh can be executed and stoped. See
RunStopMacroTestCase
for requirements.-
macro_name
= 'mesh'¶
-
test_sardanavalue
¶Unit tests for sardanavalue module
Classes
Sardana API¶
APIs
Macro API reference¶
-
class
Macro
(*args, **kwargs)[source]¶ The Macro base class. All macros should inherit directly or indirectly from this class.
-
Abort
¶
-
All
= 'All'¶
-
BlockFinish
= '</BLOCK>'¶
-
BlockStart
= '<BLOCK>'¶
-
Fault
¶
-
Finished
¶
-
Init
¶
-
Pause
¶
-
Ready
¶
-
Running
¶
-
Stop
¶
-
addObj
(*args, **kwargs)[source]¶ Macro API. Adds the given object to the list of controlled objects of this macro. In practice it means that if a stop is executed the stop method of the given object will be called.
Parameters:
-
addObjs
(*args, **kwargs)[source]¶ Macro API. Adds the given objects to the list of controlled objects of this macro. In practice it means that if a stop is executed the stop method of the given object will be called.
Parameters: obj_list (sequence) – list of objects to be controlled
-
checkPoint
(*args, **kwargs)[source]¶ Macro API. Empty method that just performs a checkpoint. This can be used to check for the stop. Usually you won’t need to call this method
-
createExecMacroHook
(*args, **kwargs)[source]¶ Macro API. Creates a hook that executes the macro given as a sequence of strings where the first string is macro name and the following strings the macro parameters
Parameters: - par_str_sequence – the macro parameters
- parent_macro – the parent macro object. If None is given (default) then the parent macro is this macro
Returns: a ExecMacroHook object (which is a callable object)
-
createMacro
(*args, **kwargs)[source]¶ Macro API. Create a new macro and prepare it for execution Several different parameter formats are supported:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
# several parameters: self.createMacro('ascan', 'th', '0', '100', '10', '1.0') self.createMacro('ascan', 'th', 0, 100, 10, 1.0) th = self.getObj('th') self.createMacro('ascan', th, 0, 100, 10, 1.0) # a sequence of parameters: self.createMacro(['ascan', 'th', '0', '100', '10', '1.0') self.createMacro(('ascan', 'th', 0, 100, 10, 1.0)) th = self.getObj('th') self.createMacro(['ascan', th, 0, 100, 10, 1.0]) # a space separated string of parameters: self.createMacro('ascan th 0 100 10 1.0')
Parameters: pars – the command parameters as explained above Returns: a sequence of two elements: the macro object and the result of preparing the macro Return type: tuple
<Macro
, seq<obj>>
-
critical
(*args, **kwargs)[source]¶ Macro API. Record a critical message in this object’s logger. Accepted args and kwargs are the same as
logging.Logger.critical()
. Example:self.critical("this is a log message for macro %s", self.getName())
Parameters: - msg (
str
) – the message to be recorded - args – list of arguments
- kwargs – list of keyword arguments
- msg (
-
data
¶ macro data
-
debug
(*args, **kwargs)[source]¶ Macro API. Record a debug message in this object’s logger. Accepted args and kwargs are the same as
logging.Logger.debug()
. Example:self.debug("this is a log message for macro %s", self.getName())
Parameters: - msg (
str
) – the message to be recorded - args – list of arguments
- kw – list of keyword arguments
- msg (
-
description
¶ Unofficial Macro API. Alternative to
getDescription()
that does not throw StopException in case of a Stop. This should be called only internally by the Executor
-
door
¶ Unofficial Macro API. Alternative to
getDoorObj()
that does not throw StopException in case of a Stop. This should be called only internally
-
env
= ()¶
-
error
(*args, **kwargs)[source]¶ Macro API. Record an error message in this object’s logger. Accepted args and kwargs are the same as
logging.Logger.error()
. Example:self.error("this is a log message for macro %s", self.getName())
Parameters: - msg (
str
) – the message to be recorded - args – list of arguments
- kwargs – list of keyword arguments
- msg (
-
execMacro
(*args, **kwargs)[source]¶ Macro API. Execute a macro in this macro. The method only returns after the macro is completed or an exception is thrown. Several different parameter formats are supported:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
# several parameters: self.execMacro('ascan', 'th', '0', '100', '10', '1.0') self.execMacro('ascan', 'th', 0, 100, 10, 1.0) th = self.getObj('th') self.execMacro('ascan', th, 0, 100, 10, 1.0) # a sequence of parameters: self.execMacro(['ascan', 'th', '0', '100', '10', '1.0') self.execMacro(('ascan', 'th', 0, 100, 10, 1.0)) th = self.getObj('th') self.execMacro(['ascan', th, 0, 100, 10, 1.0]) # a space separated string of parameters: self.execMacro('ascan th 0 100 10 1.0')
Parameters: pars – the command parameters as explained above Returns: a macro object
-
execMacroObj
(*args, **kwargs)[source]¶ Macro API. Execute a macro in this macro. The method only returns after the macro is completed or an exception is thrown. This is a higher level version of runMacro method. It is the same as:
macro = self.prepareMacroObjs(name, *args, **kwargs) self.runMacro(macro) return macro
Parameters: - name (str) – name of the macro to be prepared
- args – list of parameter objects
- kwargs – list of keyword parameters
Returns: a macro object
-
executor
¶ Unofficial Macro API. Alternative to
getExecutor()
that does not throw StopException in case of a Stop. This should be called only internally
-
findObjs
(*args, **kwargs)[source]¶ Macro API. Gets the objects of the given type belonging to the given pool with the given names. The objects (if found) will automatically become controlled by the macro.
Parameters: - names – a string or a sequence of strings representing the names of the objects. Each string can be a regular expression
- type_class – the type of object (optional, default is All)
- subtype – a string representing the subtype [default: All] Ex.: if type_class is Type.ExpChannel, subtype could be ‘CTExpChannel’
- pool – the pool to which the object should belong [default: All]
- reserve – automatically reserve the object for this macro [default: True]
Returns: a list of objects or empty list if no compatible object is found
-
getAllDoorEnv
(*args, **kwargs)[source]¶ Macro API. Returns the enviroment for the door where the macro is running.
Returns: a dict
containing the environmentReturn type: dict
-
getAllEnv
(*args, **kwargs)[source]¶ Macro API. Returns the enviroment for the macro.
Returns: a dict
containing the environment for the macroReturn type: dict
-
getCommand
(*args, **kwargs)[source]¶ Macro API. Returns the string used to execute the macro. Ex.: ‘ascan M1 0 1000 100 0.8’
Returns: the macro command. Return type: str
-
getData
(*args, **kwargs)[source]¶ Macro API. Returns the data produced by the macro.
Raises: Exception if no data has been set before on this macro Returns: the data produced by the macro Return type: object
-
getDateString
(*args, **kwargs)[source]¶ Macro API. Helper method. Returns the current date in a string.
Parameters: time_format ( str
) – the format in which the date should be returned (optional, default value is ‘%a %b %d %H:%M:%S %Y’Returns: the current date Return type: str
-
getDescription
(*args, **kwargs)[source]¶ Macro API. Returns a string description of the macro.
Returns: the string description of the macro Return type: str
-
getDevice
(*args, **kwargs)[source]¶ Macro API. Helper method that returns the device for the given device name
Returns: the taurus device for the given device name Return type: TaurusDevice
-
getDoorName
(*args, **kwargs)[source]¶ Macro API. Returns the string with the name of the Door that invoked this macro.
Returns: the string with the name of the Door that invoked this macro. Return type: str
-
getDoorObj
(*args, **kwargs)[source]¶ Macro API. Returns the reference to the Door that invoked this macro.
Returns: the reference to the Door that invoked this macro. Rype: Door
-
getEnv
(*args, **kwargs)[source]¶ Macro API. Gets the local environment matching the given parameters:
- door_name and macro_name define the context where to look for the environment. If both are None, the global environment is used. If door name is None but macro name not, the given macro environment is used and so on...
- If key is None it returns the complete environment, otherwise key must be a string containing the environment variable name.
Raises: UnknownEnv
Parameters: Returns: a
dict
containing the environmentReturn type:
-
getExecutor
(*args, **kwargs)[source]¶ Macro API. Returns the reference to the object that invoked this macro. Usually is a MacroExecutor object.
Returns: the reference to the object that invoked this macro Return type: MacroExecutor
-
getGlobalEnv
(*args, **kwargs)[source]¶ Macro API. Returns the global environment.
Returns: a dict
containing the global environmentReturn type: dict
-
getID
(*args, **kwargs)[source]¶ Macro API. Returns this macro id
Returns: the macro id Return type: str
-
getMacroInfo
(*args, **kwargs)[source]¶ Macro API. Returns the corresponding
MacroClass
/MacroFunction
object.Parameters: macro_name ( str
) – a string with the desired macro name.Returns: a MacroClass
/MacroFunction
object or None if the macro with the given name was not foundReturn type: MacroClass
/MacroFunction
-
getMacroLib
(*args, **kwargs)¶ Macro API. Returns a
MacroLibrary
object for the given library name.Parameters: lib_name (str) – library name Returns: a macro library MacroLibrary
Return type: MacroLibrary
-
getMacroLibraries
(*args, **kwargs)[source]¶ Macro API. Returns a sequence of
MacroLibrary
objects for all known macros that obey the filter expression.Parameters: filter – a regular expression for the macro library [default: None meaning match all macro libraries) Returns: a sequence of MacroLibrary
objectsReturn type: seq< MacroLibrary
>
-
getMacroLibrary
(*args, **kwargs)[source]¶ Macro API. Returns a
MacroLibrary
object for the given library name.Parameters: lib_name (str) – library name Returns: a macro library MacroLibrary
Return type: MacroLibrary
-
getMacroLibs
(*args, **kwargs)¶ Macro API. Returns a sequence of
MacroLibrary
objects for all known macros that obey the filter expression.Parameters: filter – a regular expression for the macro library [default: None meaning match all macro libraries) Returns: a sequence of MacroLibrary
objectsReturn type: seq< MacroLibrary
>
-
getMacroNames
(*args, **kwargs)[source]¶ Macro API. Returns a list of strings containing the names of all known macros
return: a sequence of macro names rtype: seq< str
>
-
getMacroServer
(*args, **kwargs)[source]¶ Macro API. Returns the MacroServer for this macro
Returns: the MacroServer Return type: MacroServer
-
getMacroStatus
(*args, **kwargs)[source]¶ Macro API. Returns the current macro status. Macro status is a
dict
where keys are the strings:- id - macro ID (internal usage only)
- range - the full progress range of a macro (usually a
tuple
of two numbers (0, 100)) - state - the current macro state, a string which can have values start, step, stop and abort
- step - the current step in macro. Should be a value inside the allowed macro range
Returns: the macro status Return type: dict
-
getMacroThread
(*args, **kwargs)[source]¶ Macro API. Returns the python thread where this macro is running
Returns: the python thread where this macro is running Return type: threading.Thread
-
getMacroThreadID
(*args, **kwargs)[source]¶ Macro API. Returns the python thread id where this macro is running
Returns: the python thread id where this macro is running Return type: int
-
getMacros
(*args, **kwargs)[source]¶ Macro API. Returns a sequence of
MacroClass
/MacroFunction
objects for all known macros that obey the filter expression.Parameters: filter – a regular expression for the macro name (optional, default is None meaning match all macros) Returns: a sequence of MacroClass
/MacroFunction
objectsReturn type: seq< MacroClass
/MacroFunction
>
-
getManager
(*args, **kwargs)[source]¶ Macro API. Returns the manager for this macro (usually a MacroServer)
Returns: the MacroServer Return type: MacroServer
-
getMotion
(*args, **kwargs)[source]¶ Macro API. Returns a new Motion object containing the given elements.
Raises: Exception if no elements are defined or the elems is not recognized as valid, or an element is not found or an element appears more than once
Parameters: - elems – list of moveable object names
- motion_source – obj or list of objects containing moveable elements. Usually this is a Pool object or a list of Pool objects (optional, default is None, meaning all known pools will be searched for the given moveable items
- read_only – not used. Reserved for future use
- cache – not used. Reserved for future use
Returns: a Motion object
-
getName
(*args, **kwargs)[source]¶ Macro API. Returns this macro name
Returns: the macro name Return type: str
-
getObj
(*args, **kwargs)[source]¶ Macro API. Gets the object of the given type belonging to the given pool with the given name. The object (if found) will automatically become controlled by the macro.
Raises: MacroWrongParameterType if name is not a string
Raises: AttributeError if more than one matching object is found
Parameters: - name (
str
) – string representing the name of the object. Can be a regular expression - type_class – the type of object [default: All]
- subtype – a string representing the subtype [default: All] Ex.: if type_class is Type.ExpChannel, subtype could be ‘CTExpChannel’
- pool – the pool to which the object should belong [default: All]
- reserve – automatically reserve the object for this macro [default: True]
Returns: the object or None if no compatible object is found
- name (
-
getObjs
(*args, **kwargs)[source]¶ Macro API. Gets the objects of the given type belonging to the given pool with the given names. The objects (if found) will automatically become controlled by the macro.
Parameters: - names – a string or a sequence of strings representing the names of the objects. Each string can be a regular expression
- type_class – the type of object (optional, default is All). Example: Type.Motor, Type.ExpChannel
- subtype – a string representing the subtype (optional, default is All) Ex.: if type_class is Type.ExpChannel, subtype could be ‘CTExpChannel’
- pool – the pool to which the object should belong (optional, default is All)
- reserve – automatically reserve the object for this macro (optional, default is True)
Returns: a list of objects or empty list if no compatible object is found
-
getParameters
(*args, **kwargs)[source]¶ Macro API. Returns a the macro parameters. It returns a list containning the parameters with which the macro was executed
Returns: the macro parameters Return type: list
-
getParentMacro
(*args, **kwargs)[source]¶ Macro API. Returns the parent macro reference.
Returns: the parent macro reference or None if there is no parent macro Return type: Macro
-
getPools
(*args, **kwargs)[source]¶ Macro API. Returns the list of known device pools.
Returns: the list of known device pools Return type: seq<Pool>
-
getResult
()[source]¶ Unofficial Macro API. Returns the macro result object (if any)
Returns: the macro result object or None
-
getTangoFactory
(*args, **kwargs)[source]¶ Macro API. Helper method that returns the tango factory.
Returns: the tango factory singleton Return type: TangoFactory
-
classmethod
hasResult
()[source]¶ Unofficial Macro API. Returns True if the macro should return a result or False otherwise
Returns: True if the macro should return a result or False otherwise Return type: bool
-
hints
= {}¶
-
info
(*args, **kwargs)[source]¶ Macro API. Record an info message in this object’s logger. Accepted args and kwargs are the same as
logging.Logger.info()
. Example:self.info("this is a log message for macro %s", self.getName())
Parameters: - msg (
str
) – the message to be recorded - args – list of arguments
- kwargs – list of keyword arguments
- msg (
-
input
(*args, **kwargs)[source]¶ Macro API. If args is present, it is written to standard output without a trailing newline. The function then reads a line from input, converts it to a string (stripping a trailing newline), and returns that.
Depending on which type of application you are running, some of the keywords may have no effect (ex.: spock ignores decimals when a number is asked).
Recognized kwargs:
- data_type : [default: Type.String] specific input type. Can also specify a sequence of strings with possible values (use allow_multiple=True to say multiple values can be selected)
- key : [default: no default] variable/label to assign to this input
- unit: [default: no default] units (useful for GUIs)
- timeout : [default: None, meaning wait forever for input]
- default_value : [default: None, meaning no default value] When given, it must be compatible with data_type
- allow_multiple : [default: False] in case data_type is a sequence of values, allow multiple selection
- minimum : [default: None] When given, must be compatible with data_type (useful for GUIs)
- maximum : [default: None] When given, must be compatible with data_type (useful for GUIs)
- step : [default: None] When given, must be compatible with data_type (useful for GUIs)
- decimals : [default: None] When given, must be compatible with data_type (useful for GUIs)
Examples:
1 2 3 4 5 6
device_name = self.input("Which device name (%s)?", "tab separated") point_nb = self.input("How many points?", data_type=Type.Integer) calc_mode = self.input("Which algorithm?", data_type=["Average", "Integral", "Sum"], default_value="Average", allow_multiple=False)
-
interactive
= False¶
-
log
(*args, **kwargs)[source]¶ Macro API. Record a log message in this object’s logger. Accepted args and kwargs are the same as
logging.Logger.log()
. Example:self.debug(logging.INFO, "this is a info log message for macro %s", self.getName())
Parameters:
-
macro_server
¶ Macro API. Returns the MacroServer for this macro
Returns: the MacroServer Return type: MacroServer
-
macros
¶ Macro API. An object that contains all macro classes as members. With the returning object you can invoke other macros. Example:
m = self.macros.ascan('th', '0', '90', '10', '2') scan_data = m.data
-
manager
¶ Macro API. Returns the manager for this macro (usually a MacroServer)
Returns: the MacroServer Return type: MacroServer
-
on_abort
()[source]¶ Macro API. Hook executed when an abort occurs. Overwrite as necessary. Default implementation does nothing
-
on_pause
()[source]¶ Macro API. Hook executed when a pause occurs. Overwrite as necessary. Default implementation does nothing
-
on_stop
()[source]¶ Macro API. Hook executed when a stop occurs. Overwrite as necessary. Default implementation calls
on_abort()
-
output
(*args, **kwargs)[source]¶ Macro API. Record a log message in this object’s output. Accepted args and kwargs are the same as
logging.Logger.log()
. Example:self.output("this is a print for macro %s", self.getName())
Parameters: - msg (
str
) – the message to be recorded - args – list of arguments
- kwargs – list of keyword arguments
- msg (
-
outputBlock
(*args, **kwargs)[source]¶ Macro API. Sends an line tagged as a block to the output
Parameters: line (str) – line to be sent
-
outputDate
(*args, **kwargs)[source]¶ Macro API. Helper method. Outputs the current date into the output buffer
Parameters: time_format ( str
) – (str) the format in which the date should be returned (optional, default value is ‘%a %b %d %H:%M:%S %Y’
-
param_def
= []¶
-
parent_macro
¶ Unofficial Macro API. Alternative to getParentMacro that does not throw StopException in case of a Stop. This should be called only internally by the Executor
-
pause
(cb=None)[source]¶ Internal method. Pauses the macro execution. To be called by the Door running the macro to pause the current macro
-
pausePoint
(*args, **kwargs)[source]¶ Macro API. Will establish a pause point where called. If an external source as invoked a pause then, when this this method is called, it will be block until the external source calls resume. You may want to call this method if your macro takes a considerable time to execute and you may whish to pause it at some time. Example:
for i in range(10000): time.sleep(0.1) self.output("At step %d/10000", i) self.pausePoint()
Parameters: timeout ( float
) – timeout in seconds [default: None, meaning wait forever]
-
plot
(*args, **kwargs)[source]¶ Macro API. Sends the plot command to the client using the ‘RecordData’ DevEncoded attribute. The data is encoded using the pickle -> BZ2 codec.
Parameters: - args – the plotting args
- kwargs – the plotting keyword args
-
prepare
(*args, **kwargs)[source]¶ Macro API. Prepare phase. Overwrite as necessary. Default implementation does nothing
-
prepareMacro
(*args, **kwargs)[source]¶ Macro API. Prepare a new macro for execution Several different parameter formats are supported:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
# several parameters: executor.prepareMacro('ascan', 'th', '0', '100', '10', '1.0') executor.prepareMacro('ascan', 'th', 0, 100, 10, 1.0) th = self.getObj('th') executor.prepareMacro('ascan', th, 0, 100, 10, 1.0) # a sequence of parameters: executor.prepareMacro(['ascan', 'th', '0', '100', '10', '1.0') executor.prepareMacro(('ascan', 'th', 0, 100, 10, 1.0)) th = self.getObj('th') executor.prepareMacro(['ascan', th, 0, 100, 10, 1.0]) # a space separated string of parameters: executor._prepareMacro('ascan th 0 100 10 1.0')
Parameters: - args – the command parameters as explained above
- kwargs – keyword optional parameters for prepare
Returns: a sequence of two elements: the macro object and the result of preparing the macro
-
prepareMacroObj
(*args, **kwargs)[source]¶ Macro API. Prepare a new macro for execution
Parameters: - name (macro_name_or_klass) – name of the macro to be prepared or the macro class itself
- pars – list of parameter objects
- init_opts – keyword parameters for the macro constructor
- prepare_opts – keyword parameters for the macro prepare
Returns: a sequence of two elements: the macro object and the result of preparing the macro
-
print
(*args, **kwargs)[source]¶ Macro API. Prints a message. Accepted args and kwargs are the same as
print()
. Example:self.print("this is a print for macro", self.getName())
Note
you will need python >= 3.0. If you have python 2.x then you must include at the top of your file the statement:
from __future__ import print_function
-
pylab
¶
-
pyplot
¶
-
reloadLibrary
(*args, **kwargs)[source]¶ Macro API. Reloads the given library(=module) names
Raises: ImportError in case the reload process is not successfull Parameters: lib_name ( str
) – library(=module) nameReturns: the reloaded python module object
-
reloadMacro
(*args, **kwargs)[source]¶ Macro API. Reloads the module corresponding to the given macro name
Raises: MacroServerExceptionList in case the macro is unknown or the reload process is not successfull Parameters: macro_name ( str
) – macro name
-
reloadMacroLib
(*args, **kwargs)¶ Macro API. Reloads the given library(=module) names
Raises: MacroServerExceptionList in case the reload process is not successfull Parameters: lib_name ( str
) – library(=module) nameReturns: the MacroLibrary
for the reloaded libraryReturn type: MacroLibrary
-
reloadMacroLibraries
(*args, **kwargs)[source]¶ Macro API. Reloads the given library(=module) names
Raises: MacroServerExceptionList in case the reload process is not successfull for at least one lib param lib_names: a list of library(=module) names :type lib_name: seq<
str
>Returns: a sequence of MacroLibrary
objects for the reloaded librariesReturn type: seq< MacroLibrary
>
-
reloadMacroLibrary
(*args, **kwargs)[source]¶ Macro API. Reloads the given library(=module) names
Raises: MacroServerExceptionList in case the reload process is not successfull Parameters: lib_name ( str
) – library(=module) nameReturns: the MacroLibrary
for the reloaded libraryReturn type: MacroLibrary
-
reloadMacroLibs
(*args, **kwargs)¶ Macro API. Reloads the given library(=module) names
Raises: MacroServerExceptionList in case the reload process is not successfull for at least one lib param lib_names: a list of library(=module) names :type lib_name: seq<
str
>Returns: a sequence of MacroLibrary
objects for the reloaded librariesReturn type: seq< MacroLibrary
>
-
reloadMacros
(*args, **kwargs)[source]¶ Macro API. Reloads the modules corresponding to the given macro names.
Raises: MacroServerExceptionList in case the macro(s) are unknown or the reload process is not successfull Parameters: macro_names (sequence< str
>) – a list of macro names
-
report
(*args, **kwargs)[source]¶ Macro API. Record a log message in the sardana report (if enabled) with default level INFO. The msg is the message format string, and the args are the arguments which are merged into msg using the string formatting operator. (Note that this means that you can use keywords in the format string, together with a single dictionary argument.)
kwargs are the same as
logging.Logger.debug()
plus an optional level kwargs which has default value INFOExample:
self.report("this is an official report of macro %s", self.getName())
Parameters: - msg (
str
) – the message to be recorded - args – list of arguments
- kwargs – list of keyword arguments
- msg (
-
result_def
= []¶
-
resume
(cb=None)[source]¶ Internal method. Resumes the macro execution. To be called by the Door running the macro to resume the current macro
-
returnObj
(obj)[source]¶ Removes the given objects to the list of controlled objects of this macro.
Parameters: obj – object to be released from the control Return type: object
-
run
(*args)[source]¶ Macro API. Runs the macro. Overwrite MANDATORY! Default implementation raises RuntimeError.
Raises: RuntimeError
-
runMacro
(*args, **kwargs)[source]¶ Macro API. Runs the macro. This the lower level version of
execMacro()
. The method only returns after the macro is completed or an exception is thrown. It should be used instead of execMacro when some operation needs to be done between the macro preparation and the macro execution. Example:macro = self.prepareMacro("mymacro", "myparam") self.do_my_stuff_with_macro(macro) self.runMacro(macro)
Parameters: macro_obj – macro object Returns: macro result
-
sendRecordData
(*args, **kwargs)[source]¶ Macro API. Sends the given data to the RecordData attribute of the Door
Parameters: data – (sequence) the data to be sent
-
setData
(*args, **kwargs)[source]¶ Macro API. Sets the data for this macro
Parameters: data (object) – new data to be associated with this macro
-
setEnv
(*args, **kwargs)[source]¶ Macro API. Sets the environment key to the new value and stores it persistently.
Returns: a tuple
with the key and value objects storedReturn type: tuple
<str
, object>
-
setLogBlockFinish
(*args, **kwargs)[source]¶ Macro API. Specifies the end of a block of data. Basically it outputs the ‘/BLOCK’ tag
-
setLogBlockStart
(*args, **kwargs)[source]¶ Macro API. Specifies the begining of a block of data. Basically it outputs the ‘BLOCK’ tag
-
setResult
(result)[source]¶ Unofficial Macro API. Sets the result of this macro
Parameters: result – (object) the result for this macro
-
trace
(*args, **kwargs)[source]¶ Macro API. Record a trace message in this object’s logger.
Parameters: - msg – (str) the message to be recorded
- args – list of arguments
- kw – list of keyword arguments
-
traceback
(*args, **kwargs)[source]¶ Macro API. Logs the traceback with level TRACE on the macro logger.
-
unsetEnv
(*args, **kwargs)[source]¶ Macro API. Unsets the given environment variable.
Parameters: key ( str
) – the environment variable name
-
warning
(*args, **kwargs)[source]¶ Macro API. Record a warning message in this object’s logger. Accepted args and kwargs are the same as
logging.Logger.warning()
. Example:self.warning("this is a log message for macro %s", self.getName())
Parameters: - msg (
str
) – the message to be recorded - args – list of arguments
- kwargs – list of keyword arguments
- msg (
-
-
class
macro
(param_def=None, result_def=None, env=None, hints=None, interactive=None)[source]¶ Class designed to decorate a python function to transform it into a macro. Examples:
1 2 3 4 5 6 7
@macro() def my_macro1(self): self.output("Executing %s", self.getName()) @macro([ ["moveable", Type.Moveable, None, "motor to watch"] ]) def where_moveable(self, moveable): self.output("Moveable %s is at %s", moveable.getName(), moveable.getPosition())
Controller API reference¶
Controller
- Base API for all controller typesMotorController
- Motor controller APICounterTimerController
- Counter/Timer controller APIZeroDController
- 0D controller APIPseudoMotorController
- PseudoMotor controller APIPseudoCounterController
- PseudoCounter controller APIIORegisterController
- IORegister controller API
When writing a new controller you may need to specify extra attributes (per
controller or/and per axis) as well as extra properties. This chapter describes
how to describe the data type for each of this additional members.
Controller data type definition has the following equivalences. This means you
can use any of the given possibilities to describe a field data type. The
possibilities are ordered by preference (example: usage of int
is
preferred to “int” or “PyTango.DevLong”):
- for 0D data types:
- integer:
int
|DataType.Integer
| “int” | “integer” | “long” |long
| [ “PyTango.” ] “DevLong” - double:
float
|DataType.Double
| “double” | “float” | [ “PyTango.” ] “DevDouble” - string:
str
|DataType.String
| “str” | “string” | [ “PyTango.” ] “DevString” - boolean:
bool
|DataType.Boolean
| “bool” | “boolean” | [ “PyTango.” ] “DevBoolean”
- integer:
- for 1D data types:
- integer: (
int
,) | (DataType.Integer
,) | (“int”,) | (“integer”,) | (long
,) | (“long”,) | [ “PyTango.” ] “DevVarLongArray” | ([ “PyTango.” ] “DevLong”,) - double: (
float
,) | (DataType.Double
,) | (“double”,) | (“float”,) | [ “PyTango.” ] “DevVarDoubleArray” | ([ “PyTango.” ] “DevDouble”,) - string: (
str
,) | (DataType.String
,) | (“str”,) | (“string”,) | [ “PyTango.” ] “DevVarStringArray” | ([ “PyTango.” ] “DevString”,) - boolean: (
bool
,) | (DataType.Boolean
,) | (“bool”,) | (“boolean”,) | [ “PyTango.” ] “DevVarBooleanArray” | ([ “PyTango.” ] “DevBoolean”,)
- integer: (
Deprecated since version 1.0: [ “PyTango.” ] “Dev”<concrete type string> types are considered deprecated.
Note
when string, types are case insensitive. This means “long” is the same as “LONG”
Here is an example on how to define extra attributes per axis:
- EncoderSource: a scalar r/w string
- ReflectionMatrix: a 2D readable float with customized getter method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | from sardana import State, DataAccess
from sardana.pool.controller import MotorController, \
Type, Description, DefaultValue, Access, FGet, FSet
class MyMotorCtrl(MotorController):
axis_attributes = \
{
'EncoderSource' : { Type : str,
Description : 'motor encoder source', },
'ReflectionMatrix' : { Type : ( (float,), ),
Access : DataAccess.ReadOnly,
FGet : 'getReflectionMatrix', },
}
def getAxisExtraPar(self, axis, name):
name = name.lower()
if name == 'encodersource':
return self._encodersource[axis]
def setAxisPar(self, axis, name, value):
name = name.lower()
if name == 'encodersource':
self._encodersource[axis] = value
def getReflectionMatrix(self, axis):
return ( (1.0, 0.0), (0.0, 1.0) )
|
Motor API reference¶
The motor is one of the most used elements in sardana. A motor represents anything that can be changed (and can potentially take some time to do it).
This chapter explains the generic motor API in the context of sardana. In sardana there are, in fact, two Motor APIs. To better explain why, let’s consider the case were sardana server is running as a Sardana Tango device server:

Every motor in sardana is represented in the sardana kernel as a
PoolMotor
. The PoolMotor
API is not directly
accessible from outside the sardana server. This is a low level API
that is only accessbile to someone writing a server extension to sardana. At
the time of writing, the only available sardana server extension is Tango.
The second motor interface consists on the one provided by the server extension,
which is in this case the one provided by the Tango motor device interface:
Motor
. The Tango motor interface tries to
mimic the as closely as possible the PoolMotor
API.
See also
- Motor overview
- the motor overview
Motor
- the motor tango device API
A motor will have, at least, a state
, and a position
. The state
indicates at any time if the motor is stopped, in alarm or moving. The
position, indicates the current user position. Unless a motor
controller is specifically programmed not to, it’s motors will also have:
- limit switches
the three limit switches (home, upper and lower). Each switch is represented by a boolean value: False means inactive while True means active.
low level
PoolMotor
API.high level Tango Motor API: limit_switches tango attribute
- acceleration
motor acceleration (usually acceleration time in seconds, but it’s up to the motor controller class to decide)
- deceleration
motor deceleration (usually deceleration time in seconds, but it’s up to the motor controller class to decide)
- velocity
top velocity
- base rate
initial velocity
- dial position
the dial position
- offset
the offset to be applied in the motor position computation [default: 0.0]
- sign
the sign to be applied in the motor position computation [default: 1, possible values are (1, -1)]
- Steps per unit
This is the number of motor steps per user position [default: 1.0]
- backlash
If this is defined to be something different than 0, the motor will always stop the motion coming from the same mechanical direction. This means that it could be possible to ask the motor to go a little bit after the desired position and then to return to the desired position. The value is the number of steps the motor will pass the desired position if it arrives from the “wrong” direction. This is a signed value. If the sign is positive, this means that the authorized direction to stop the motion is the increasing motor position direction. If the sign is negative, this means that the authorized direction to stop the motion is the decreasing motor position direction.
- instability_time
This property defines the time in milliseconds that the software managing a motor movement will wait between it detects the end of the motion and the last motor position reading. It is typically used for motors that move mechanics which have an instability time after each motion.
The available operations are:
- start move absolute (user position)
starts to move the motor to the given absolute user position
- stop
- stops the motor in an orderly fashion
- abort
- stops the motor motion as fast as possible (possibly without deceleration time and loss of position)
On a sardana tango server, the motor state can be obtained by reading the state attribute or by executing the state command. The diagram shows the internal sequence of calls.

The motor’s current user position can be obtained by reading the position attribute. The diagram shows the internal sequence of calls.

The most useful thing to do with a motor is, of course, to move it. To move a motor to another absolute user position you have to write the value into the position attribute.

Before allowing a movement, some pre-conditions are automatically checked by tango (not represented in the diagram):
- motor is in a proper state;
- requested position is within the allowed motor boundaries (if defined)
Then, the dial position is calculated taking into account the offset, signal as well as a possible backlash.
Afterward, and because the motor may be part of a pseudo motor system, other pre-conditions are checked:
- is the final dial position (including backlash) within the motor boundaries (if defined)
- will the resulting motion end in an allowed position for all the pseudo motors that depend on this motor
After all pre-conditions are checked, the motor will deploy a motion job into the sardana kernel engine which will trigger a series of calls to the underlying motor controller.
The motor awaits for the PreStartOne()
to signal that the motion will be possible to return successfully from the move
request.
The following diagram shows the motion state machine of a motor. The black state transitions are the ones which can be triggered by a user. For simplicity, only the most relevant states involved in a motor motion are shown. Error states are omitted.
I/O register API reference¶
Todo
document I/O register API reference
See also
- I/O register overview
- the I/O register overview
IORegister
- the I/O register tango device API
Counter/Timer API reference¶
Todo
document Counter/Timer API reference
See also
- Counter/timer overview
- the counter/timer overview
CTExpChannel
- the counter/timer tango device API
0D channel API reference¶
Todo
document 0D channel API reference
See also
- 0D channel overview
- the 0D experiment channel overview
ZeroDExpChannel
- the 0D experiment channel tango device API
1D channel API reference¶
Todo
document 1D channel API reference
See also
- 1D channel overview
- the 1D experiment channel overview
OneDExpChannel
- the 1D experiment channel tango device API
2D channel API reference¶
Todo
document 2D channel API reference
See also
- 2D channel overview
- the 2D experiment channel overview
TwoDExpChannel
- the 2D experiment channel tango device API
Pseudo motor API reference¶
Todo
document pseudo-motor API reference
See also
- Pseudo motor overview
- the pseudo-motor overview
PseudoMotor
- the pseudo-motor tango device API
Pseudo counter API reference¶
Todo
document pseudo-counter API reference
See also
- Pseudo counter overview
- the pseudo-counter overview
PseudoCounter
- the pseudo-counter tango device API
Device Pool Tango API¶
Todo
Device Pool chapter is out of date. Need to update it and distribute chapters logically around the sardana documentation
This paper describes what could be the implementation of the Sardana device pool. This work is based on Jorg’s paper called “Reordered SPEC”. It is not at all a final version of this device pool. It is rather a first approach to define this pool more precisely and to help defining its features and the way it could be implemented.
The pool could be seen as a kind of intelligent Tango device container to control the experiment hardware. In a first approach, it requires that the hardware to be controlled is connected to the control computer or to external crate(s) connected to the control computer using bus coupler. It has two basic features which are:
- Hardware access using dynamically created/deleted Tango devices according to the experiment needs
- Management of some very common and well defined action regularly done on a beam line (scanning, motor position archiving....)
To achieve these two goals and to provide the user with a way to control its behavior, it is implemented as a Tango class with commands and attributes like any other Tango class.
Most of the times, it is possible to define a list of very common devices found in most of the experiments, a list of communication link used between the experiment hardware and the control computer(s) and some of the most commonly used protocol used on these communication links. Devices commonly used to drive an experiment are:
- Motor
- Group of motor
- Pseudo motor
- Counter/Timer
- Multi Channel Analyzer
- CCD cameras
- And some other that I don’t know
Communication link used to drive experiment devices are:
- Serial line
- GPIB
- Socket
- And some other that I don’t know (USB????)
Protocol used on the communication links are:
- Modbus
- Ans some other that I don’t know
Each of the controlled hardware (one motor, one pseudo-motor, one serial line device,...) will be driven by independent Tango classes. The pool device server will embed all these Tango classes together (statically linked). The pool Tango device is the “container interface” and allows the user to create/delete classical Tango devices which are instances of these embedded classes. This is summarized in the following drawing.

Therefore, the three main actions to control a new equipment using the pool will be (assuming the equipment is connected to the control computer via a serial line):
- Create the serial line Tango device with one of the Pool device command assigning it a name like “MyNewEquipment”.
- Connect to this newly created Tango device using its assigned name
- Send order or write/read data to/from the new equipment using for instance the WriteRead command of the serial line Tango device
When the experiment does not need this new equipment any more, the user can delete the serial line Tango device with another pool device command. Note that most of the time, creating Tango device means defining some device configuration parameters (Property in Tango language). The Tango wizard will be used to retrieve which properties have to be defined and will allow the user to set them on the fly. This means that all the Tango classes embedded within the Pool must have their wizard initialized.
From time to time, it could be useful to extend the list of Tango classes known by the device pool in case a new kind of equipment (not using the core hardware access) is added to the experiment. Starting with Tango 5.5 (and the associated Pogo), each Tango class has a method which allow the class to be dynamically loaded into a running process. This feature will be used to extend the pool feature. It has to be checked that it is possible for Tango Python class.

To achieve this feature, the pool Tango device will have commands to
Load a Tango class. This command will dynamically add two other commands and one attribute to the pool device Tango interface. These commands and the attribute are:
- Command: Create a device of the newly loaded class
- Command: Delete a device of the newly loaded class
- Attribute: Get the list of Tango devices instances of the newly created class
Unload a Tango class
Reload a Tango class
The following common actions regularly done on a beam line experiment will be done by the pool device server:
- Evaluating user constraint(s) before moving motor(s)
- Scanning
- Saving experiment data
- Experiment management
- Archiving motor positions
The motor interface is a first approach of what could be a complete motor interface. It is statically linked with the Pool device server and supports several attributes and commands. It is implemented in C++ and used a set of the so-called “controller” methods. The motor interface is always the same whatever the hardware is. This is the rule of the “controller” to access the hardware using the communication link supported by the motor controller hardware (network link, serial line...).

The controller code has a well-defined interface and can be written using Python or C++. In both cases, it will be dynamically loaded into the pool device server process.
The motor interface knows five states which are ON, MOVING, ALARM, FAULT and UNKNOWN. A motor device is in MOVING state when it is moving! It is in ALARM state when it has reached one of the limit switches and is in FAULT if its controller software is not available (impossible to load it) or if a fault is reported from the hardware controller. The motor is in the UNKNOWN state if an exception occurs during the communication between the pool and the hardware controller. When the motor is in ALARM state, its status will indicate which limit switches is active.
The motor interface supports 3 commands on top of the Tango classical Init, State and Status commands. These commands are summarized in the following table:
Command name | Input data type | Output data type |
---|---|---|
Abort | void | void |
SetPosition | Tango::DevDouble | void |
SaveConfig | void | void |
- Abort : It aborts a running motion. This command does not have input or output argument.
- SetPosition : Loads a position into controller. It has one input argument which is the new position value (a double). It is allowed only in the ON or ALARM states. The unit used for the command input value is the physical unit: millimeters or milli-radians. It is always an absolute position.
- SaveConfig : Write some of the motor parameters in database. Today, it writes the motor acceleration, deceleration, base_rate and velocity into database as motor device properties. It is allowed only in the ON or ALARM states
The classical Tango Init command destroys the motor and re-create it.
The motor interface supports several attributes which are summarized in the following table:
Name | Data type | Data format | Writable | Memorized | Ope/Expert |
---|---|---|---|---|---|
Position | Tango::DevDouble | Scalar | R/W | No * | Ope |
DialPosition | Tango::DevDouble | Scalar | R | No | Exp |
Offset | Tango::DevDouble | Scalar | R/W | Yes | Exp |
Acceleration | Tango::DevDouble | Scalar | R/W | No | Exp |
Base_rate | Tango::DevDouble | Scalar | R/W | No | Exp |
Deceleration | Tango::DevDouble | Scalar | R/W | No | Exp |
Velocity | Tango::DevDouble | Scalar | R/W | No | Exp |
Limit_Switches | Tango::DevBoolean | Spectrum | R | No | Exp |
SimulationMode | Tango::DevBoolean | Scalar | R | No | Exp |
Step_per_unit | Tango::DevDouble | Scalar | R/W | Yes | Exp |
Backlash | Tango::DevLong | Scalar | R/W | Yes | Exp |
Position : This is read-write scalar double attribute. With the classical Tango min and max_value attribute properties, it is easy to define authorized limit for this attribute. See the definition of the DialPosition and Offset attributes to get a precise definition of the meaning of this attribute. It is not allowed to read or write this attribute when the motor is in FAULT or UNKNOWN state. It is also not possible to write this attribute when the motor is already MOVING. The unit used for this attribute is the physical unit: millimeters or milli-radian. It is always an absolute position. The value of this attribute is memorized in the Tango database but not by the default Tango system memorization. See chapter XXX: Unknown inset LatexCommand ref{sub:Archiving-motor-position}: for details about motor position archiving.
DialPosition : This attribute is the motor dial position. The following formula links together the Position, DialPosition, Sign and Offset attributes:
Position = Sign * DialPosition + Offset
This allows to have the motor position centered around any position defined by the Offset attribute (classically the X ray beam position). It is a read only attribute. To set the motor position, the user has to use the Position attribute. It is not allowed to read this attribute when the motor is in FAULT or UNKNOWN mode. The unit used for this attribute is the physical unit: millimeters or milli-radian. It is also always an absolute position.
Offset : The offset to be applied in the motor position computation. By default set to 0. It is a memorized attribute. It is not allowed to read or write this attribute when the motor is in FAULT, MOVING or UNKNOWN mode.
Acceleration : This is an expert read-write scalar double attribute. This parameter value is written in database when the SaveConfig command is executed. It is not allowed to read or write this attribute when the motor is in FAULT or UNKNOWN state.
Deceleration : This is an expert read-write scalar double attribute. This parameter value is written in database when the SaveConfig command is executed. It is not allowed to read or write this attribute when the motor is in FAULT or UNKNOWN state.
Base_rate : This is an expert read-write scalar double attribute. This parameter value is written in database when the SaveConfig command is executed. It is not allowed to read or write this attribute when the motor is in FAULT or UNKNOWN state.
Velocity : This is an expert read-write scalar double attribute. This parameter value is written in database when the SaveConfig command is executed. It is not allowed to read or write this attribute when the motor is in FAULT or UNKNOWN state.
Limit_Switches : Three limit switches are managed by this attribute. Each of the switch are represented by a boolean value: False means inactive while True means active. It is a read only attribute. It is not possible to read this attribute when the motor is in UNKNOWN mode. It is a spectrum attribute with 3 values which are:
- Data[0] : The Home switch value
- Data[1] : The Upper switch value
- Data[2] : The Lower switch value
SimulationMode : This is a read only scalar boolean attribute. When set, all motion requests are not forwarded to the software controller and then to the hardware. When set, the motor position is simulated and is immediately set to the value written by the user. To set this attribute, the user has to used the pool device Tango interface. The value of the position, acceleration, deceleration, base_rate, velocity and offset attributes are memorized at the moment this attribute is set. When this mode is turned off, if the value of any of the previously memorized attributes has changed, it is reapplied to the memorized value. It is not allowed to read this attribute when the motor is in FAULT or UNKNOWN states.
Step_per_unit : This is the number of motor step per millimeter or per degree. It is a memorized attribute. It is not allowed to read or write this attribute when the motor is in FAULT or UNKNOWN mode. It is also not allowed to write this attribute when the motor is MOVING. The default value is 1.
Backlash : If this attribute is defined to something different than 0, the motor will always stop the motion coming from the same mechanical direction. This means that it could be possible to ask the motor to go a little bit after the desired position and then to return to the desired position. The attribute value is the number of steps the motor will pass the desired position if it arrives from the “wrong” direction. This is a signed value. If the sign is positive, this means that the authorized direction to stop the motion is the increasing motor position direction. If the sign is negative, this means that the authorized direction to stop the motion is the decreasing motor position direction. It is a memorized attribute. It is not allowed to read or write this attribute when the motor is in FAULT or UNKNOWN mode. It is also not allowed to write this attribute when the motor is MOVING. Some hardware motor controllers are able to manage this backlash feature. If it is not the case, the motor interface will implement this behavior.
All the motor devices will have the already described attributes but some hardware motor controller supports other features which are not covered by this list of pre-defined attributes. Using Tango dynamic attribute creation, a motor device may have extra attributes used to get/set the motor hardware controller specific features. The main characteristics of these extra attributes are :
- Name defined by the motor controller software (See next chapter)
- Data type is BOOLEAN, LONG, DOUBLE or STRING defined by the motor controller software (See next chapter)
- The data format is always Scalar
- The write type is READ or READ_WRITE defined by the motor controller software (See next chapter). If the write type is READ_WRITE, the attribute is memorized by the Tango layer
Each motor device has a set of properties. Five of these properties are automatically managed by the pool software and must not be changed by the user. These properties are named Motor_id, _Acceleration, _Velocity, _Base_rate and _Deceleration. The user properties are:
Property name | Default value |
---|---|
Sleep_before_last_read | 0 |
This property defines the time in milli-second that the software managing a motor movement will wait between it detects the end of the motion and the last motor position reading.
The simplest way to know if a motor is moving is to survey its state. If the motor is moving, its state will be MOVING. When the motion is over, its state will be back to ON (or ALARM if a limit switch has been reached). The pool motor interface allows client interested by motor state or motor limit switches value to use the Tango event system subscribing to motor state change event. As soon as a motor starts a motion, its state is changed to MOVING and an event is sent. As soon as the motion is over, the motor state is updated ans another event is sent. In the same way, as soon as a change in the limit switches value is detected, a change event is sent to client(s) which have subscribed to change event on the Limit_Switches attribute.
For each motor, the key attribute is its position. Special care has been taken on this attribute management. When the motor is not moving, reading the Position attribute will generate calls to the controller and therefore hardware access. When the motor is moving, its position is automatically read every 100 milli-seconds and stored in the Tango polling buffer. This means that a client reading motor Position attribute while the motor is moving will get the position from the Tango polling buffer and will not generate extra controller calls. It is also possible to get a motor position using the Tango event system. When the motor is moving, an event is sent to the registered clients when the change event criterion is true. By default, this change event criterion is set to be a difference in position of 5. It is tunable on a motor basis using the classical motor Position attribute abs_change property or at the pool device basis using its DefaultMotPos_AbsChange property. Anyway, not more than 10 events could be sent by second. Once the motion is over, the motor position is made unavailable from the Tango polling buffer and is read a last time after a tunable waiting time (Sleep_bef_last_read property). A forced change event with this value is sent to clients using events.
XXX: Unknown inset LatexCommand label{sub:The-Motor-Controller}:
Each controller code is built as a shared library or as a Python module which is dynamically loaded by the pool device the first time one controller using the shared library (or the module) is created. Each controller is uniquely defined by its name following the syntax:
<controller_file_name>.<controller_class_name>/<instance_name>
At controller creation time, the pool checks the controller unicity on its control system (defined by the TANGO_HOST). It is possible to write controller using either C++ or Python language. Even if a Tango device server is a multi-threaded process, every access to the same controller will be serialized by a monitor managed by the Motor interface. This monitor is attached to the controller class and not to the controller instance to handle cases where several instances of the same controller class is used. For Python controller, this monitor will also take care of taking/releasing the Python Global Interpreter Lock (GIL) before any call to the Python controller is executed.
For motor controller, a pre-defined set of methods has to be implemented in the class implementing the controller interface. These methods can be splitted in 6 different types which are:
- Methods to create/remove motor
- Methods to move motor(s)
- Methods to read motor(s) position
- Methods to get motor(s) state
- Methods to configure a motor
- Remaining methods.
These methods, their rules and their execution sequencing is detailed in the following sub-chapters. The motor controller software layer is also used to inform the upper level of the features supported by the underlying hardware. This is called the controller features . It is detailed in a following sub-chapter. Some controller may need some configuration data. This will be supported using Tango properties. This is detailed in a dedicated sub-chapter.
A controller feature is something that motor hardware controller is able to do or require on top of what has been qualified as the basic rules. Even if these features are common, not all the controllers implement them. Each of these common features are referenced by a pre- defined string. The controller code writer defined (from a pre-defined list) which of these features his hardware controller implements/requires. This list (a Python list or an array of C strings) has a well-defined name used by the upper layer software to retrieve it. The possible strings in this list are (case independent):
- CanDoBacklash : The hardware controller manages the motor backlash if the user defines one
- WantRounding : The hardware controller wants an integer number of step
- encoder : The hardware knows how to deal with encoder
- home : The hardware is able to manage home switch
- home_acceleration : It is possible to set the acceleration for motor homing
- home_method _ xxx : The hardware knows the home method called xxx
- home_method_yyy : The hardware knows the home method called yyy
The name of this list is simply: ctrl_features . If this list is not defined, this means that the hardware does not support/require any of the additional features. The Tango motor class will retrieve this list from the controller before the first motor belonging to this controller is created. As an example, we suppose that we have a pool with two classes of motor controller called Ctrl_A and Ctrl_B. The controllers features list are (in Python)
Controller A : ctrl_features = ['CanDoBacklash','encoder']
ControllerB : ctrl_features = ['WantRounding','home','home_method_xxx']
All motors devices belonging to the controller A will have the Encoder and Backlash features. For these motors, the backlash will be done by the motor controller hardware. All the motors belonging to the controller B will have the rounding, home and home_method features. For these motors, the backlash will be done by the motor interface code.
XXX: Unknown inset LatexCommand label{par:Specifying-the-motor}:
Some of the hardware motor controller will have features not defined in the features list or not accessible with a pre-defined feature. To provide an interface to these specific hardware features, the controller code can define extra attributes. Another list called : ctrl_extra_attributes is used to define them. This list (Python dictionary or an array of classical C strings) is used to define the name, data and read-write type of the Tango attribute which will be created to deal with these extra features. The attribute created for these controller extra features are all:
- Boolean, Long, Double or String
- Scalar
- Read or Read/Write (and memorized if Read/Write).
For Python classes (Python controller class), it is possible to define these extra attributes informations using a Python dictionary called ctrl_extra _ attributes . The extra attribute name is the dictionary element key. The dictionary element value is another dictionary with two members which are the extra attribute data type and the extra attribute read/write type. For instance, for our IcePap controller, this dictionary to defined one extra attribute called “SuperExtra” of data type Double which is also R/W will be:
ctrl_extra_attributes = { "SuperExtra" : { "Type" : "DevDouble", "R/W Type", "READ_WRITE" } }
For C++ controller class, the extra attributes are defined within an array of Controller::ExtraAttrInfo structures. The name of this array has to be <Ctrl_class_name>_ctrl_extra_attributes. Each Controller::ExtraAttrInfo structure has three elements which are all pointers to classical C string (const char *). These elements are:
- The extra attribute name
- The extra attribute data type
- The extra attribute R/W type
A NULL pointer defined the last extra attribute. The following is an example of extra attribute definition for a controller class called “DummyController”:
Controller::ExtraAttrInfo DummyController_ctrl_extra_attributes[] =
{ { "SuperExtra", "DevDouble", "Read_Write" }, NULL };
The string describing the extra attribute data type may have the following value (case independent):
- DevBoolean, DevLong, DevDouble or DevString (in Python, a preceding “PyTango.” is allowed)
The string describing the extra attribute R/W type may have the following value (case independent)
- Read or Read_Write (in Python, a preceding “PyTango.” is allowed)
Two methods are called when creating or removing motor from a controller. These methods are called AddDevice and DeleteDevice . The AddDevice method is called when a new motor belonging to the controller is created within the pool. The DeleteDevice method is called when a motor belonging to the controller is removed from the pool.
Four methods are used when a request to move motor(s) is executed. These methods are called PreStartAll , PreStartOne , StartOne and StartAll . The algorithm used to move one or several motors is the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /FOR/ Each controller(s) implied in the motion
- Call PreStartAll()
/END FOR/
/FOR/ Each motor(s) implied in the motion
- ret = PreStartOne(motor to move, new position)
- /IF/ ret is true
- Call StartOne(motor to move, new position)
- /END IF/
/END FOR/
/FOR/ Each controller(s) implied in the motion
- Call StartAll()
/END FOR/
|
The following array summarizes the rule of each of these methods:
Default action | Does nothing | Return true | Does nothing | Does nothing |
Externally called by | Writing the Position attribute | Writing the Position attribute | Writing the Position attribute | Writing the Position attribute |
Internally called | Once for each implied controller | For each implied motor | For each implied motor | Once for each implied controller |
Typical rule | Init internal data for motion | Check if motor motion is possible | Set new motor position in internal data | Send order to physical controller |
This algorithm covers the sophisticated case where a physical controller is able to move several motors at the same time. For some simpler controller, it is possible to implement only the StartOne() method. The default implementation of the three remaining methods is defined in a way that the algorithm works even in such a case.
Four methods are used when a request to read motor(s) position is received. These methods are called PreReadAll, PreReadOne, ReadAll and ReadOne. The algorithm used to read position of one or several motors is the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /FOR/ Each controller(s) implied in the reading
- Call PreReadAll()
/END FOR/
/FOR/ Each motor(s) implied in the reading
- PreReadOne(motor to read)
/END FOR/
/FOR/ Each controller(s) implied in the reading
- Call ReadAll()
/END FOR/
/FOR/ Each motor(s) implied in the reading
- Call ReadOne(motor to read)
/END FOR/
|
The following array summarizes the rule of each of these methods:
Default action | Does nothing | Does nothing | Does nothing | Print message on the screen and returns NaN. Mandatory for Python |
Externally called by | Reading the Position attribute | Reading the Position attribute | Reading the Position attribute | Reading the Position attribute |
Internally called | Once for each implied controller | For each implied motor | For each implied controller | Once for each implied motor |
Typical rule | Init internal data for reading | Memorize which motor has to be read | Send order to physical controller | Return motor position from internal data |
This algorithm covers the sophisticated case where a physical controller is able to read several motors positions at the same time. For some simpler controller, it is possible to implement only the ReadOne() method. The default implementation of the three remaining methods is defined in a way that the algorithm works even in such a case.
XXX: Unknown inset LatexCommand label{par:Methods-to-get-state}:
Four methods are used when a request to get motor(s) state is received. These methods are called PreStateAll, PreStateOne, StateAll and StateOne. The algorithm used to get state of one or several motors is the following :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /FOR/ Each controller(s) implied in the state getting
- Call PreStateAll()
/END FOR/
/FOR/ Each motor(s) implied in the state getting
- PreStateOne(motor to get state)
/END FOR/
/FOR/ Each controller(s) implied in the state getting
- Call StateAll()
/END FOR/
/FOR/ Each motor(s) implied in the getting state
- Call StateOne(motor to get state)
/END FOR/
|
The following array summarizes the rule of each of these methods:
Default action | Does nothing | Does nothing | Does nothing | Mandatory for Python |
Externally called by | Reading the motor state | Reading the motor state | Reading the motor state | Reading the motor state |
Internally called | Once for each implied controller | For each implied motor | For each implied controller | Once for each implied motor |
Typical rule | Init internal data for reading | Memorize which motor has to be read | Send order to physical controller | Return motor state from internal data |
This algorithm covers the sophisticated case where a physical controller is able to read several motors state at the same time. For some simpler controller, it is possible to implement only the StateOne() method. The default implementation of the three remaining methods is defined in a way that the algorithm works even in such a case.
The rule of these methods is to
- Get or Set motor parameter(s) with methods called GetPar() or SetPar()
- Get or Set motor extra feature(s) parameter with methods called GetExtraAttributePar() or SetExtraAttributePar()
The following table summarizes the usage of these methods:
Called by | Reading the Velocity, Acceleration, Base_rate, Deceleration and eventually Backlash attributes | Writing the Velocity, Acceleration, Base_rate, Deceleration, Step_per_unit and eventually Backlash attribute | Reading any of the extra attributes | Writing any of the extra attributes |
Rule | Get parameter from physical controller | Set parameter in physical controller | Get extra attribute value from the physical layer | Set additional attribute value in physical controller |
Please, note that the default implementation of the GetPar() prints a message on the screen and returns a NaN double value. The GetExtraAttributePar() default implementation also prints a message on the screen and returns a string set to “Pool_met_not_implemented”.
The rule of the remaining methods are to
- Load a new motor position in a controller with a method called DefinePosition()
- Abort a running motion with a method called AbortOne()
- Send a raw string to the controller with a method called SendToCtrl()
The following table summarizes the usage of these methods:
Called by | The motor SetPosition command | The motor Abort command | The Pool SendToController command |
Rule | Load a new motor position in controller | Abort a running motion | Send the input string to the controller and returns the controller answer |
XXX: Unknown inset LatexCommand label{par:Controller-properties}:
Each controller may have a set of properties to configure itself. Properties are defined at the controller class level but can be re-defined at the instance level. It is also possible to define a property default value. These default values are stored within the controller class code. If a default value is not adapted to specific object instance, it is possible to define a new property value which will be stored in the Tango database. Tango database allows storing data which are not Tango device property. This storage could be seen simply as a couple name/value. Naming convention for this kind of storage could be defined as:
controller_class->prop: value or controller_class/instance->prop: value
The calls necessary to retrieve/insert/update these values from/to the database already exist in the Tango core. The algorithm used to retrieve a property value is the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | - Property value = Not defined
/IF/ Property has a default value
- Property value = default value
/ENDIF/
/IF/ Property has a value defined in db at class level
- Property value = class db value
/ENDIF/
/IF/ Property has a value defined in db at instance level
- Property value = instance db value
/ENDIF/
/IF/ Property still not defined
- Error
/ENDIF/
|
As an example, the following array summarizes the result of this algorithm. The example is for an IcePap controller and the property is the port number (called port_number):
default value | 5000 | 5000 | 5000 | 5000 | |
class in DB | 5150 | 5150 | |||
inst. in DB | 5200 | 5250 | |||
Property value | 5000 | 5200 | 5150 | 5250 | Error |
- Case 1: The IcePap controller class defines one property called port_number and assigns it a default value of 5000
- Case 2 : An IcePap controller is created with an instance name “My_IcePap”. The property IcePap/My_IcePap->port_number has been set to 5200 in db
- Case 3: The hard coded value of 5000 for port number does not fulfill the need. A property called IcePap->port_number set to 5150 is defined in db.
- Case 4: We have one instance of IcePap called “My_IcePap” for which we have defined a property “IcePap/My_IcePap” set to 5250.
- Case 5: The IcePap controller has not defined a default value for the property.
In order to provide the user with a friendly interface, all the properties defined for a controller class have to have informations hard-coded into the controller class code. We need at least three informations and sometimes four for each property. They are:
- The property name (Mandatory)
- The property description (Mandatory)
- The property data type (Mandatory)
- The property default value (Optional)
With these informations, a graphical user interface is able to build at controller creation time a panel with the list of all the needed properties, their descriptions and eventually their default value. The user then have the possibility to re-define property value if the default one is not valid for his usage. This is the rule of the graphical panel to store the new value into the Tango database. The supported data type for controller property are:
Property data type | String to use in property definition |
---|---|
Boolean | DevBoolean |
Long | DevLong |
Double | DevDouble |
String | DevString |
Boolean array | DevVarBooleanArray |
Long array | DevVarLongArray |
Double array | DevVarDoubleArray |
String array | DevVarStringArray |
For Python classes (Python controller class), it is possible to define these properties informations using a Python dictionary called class_prop . The property name is the dictionary element key. The dictionary element value is another dictionary with two or three members which are the property data type, the property description and an optional default value. If the data type is an array, the default value has to be defined in a Python list or tuple. For instance, for our IcePap port number property, this dictionary will be
class_prop = { "port_number" : { "Type" : "DevLong", "Description",
"Port on which the IcePap software server is listening", "DefaultValue" : 5000 } }
For C++ controller class, the properties are defined within an array of Controller::PropInfo structures. The name of this array has to be <Ctrl_class_name>_class_prop. Each Controller::PropInfo structure has four elements which are all pointers to classical C string (const char *). These elements are:
- The property name
- The property description
- The property data type
- The property default value (NULL if not used)
A NULL pointer defined the last property. The following is an example of property definition for a controller class called “DummyController”:
1 2 3 4 5 | Controller::PropInfo DummyController_class_prop[] =
{{"The prop","The first CPP property","DevLong","12"},
{"Another_Prop","The second CPP property","DevString",NULL},
{"Third_Prop","The third CPP property","DevVarLongArray","11,22,33"},
NULL};
|
The value of these properties is passed to the controller at controller instance creation time using a constructor parameter. In Python, this parameter is a dictionnary and the base class of the controller class will create one object attribute for each property. In our Python example, the controller will have an attribute called “port_number” with its value set to 5000. In C++, the controller contructor receives a vector of Controller::Properties structure. Each Controller::Properties structure has two elements which are:
The property name as a C++ string
- The property value in a PropData structure. This PropData structure has four elements which are
- A C++ vector of C++ bool type
- A C++ vector of C++ long type
- A C++ vector of C++ double type
- A C++ vector of C++ string.
Only the vector corresponding to the property data type has a size different than 0. If the property is an array, the vector has as many elements as the property has.
Each controller has to have a property defining the maximum number of device it supports. This is a mandatory requirement. Therefore, in Python this property is simply defined by setting the value of a controller data member called MaxDevice which will be taken as the default value for the controller. In C++, you have to define a global variable called <Ctrl_class_name>_MaxDevice. The management of the number of devices created using a controller (limited by this property) will be completely done by the pool software. The information related to this property is automatically added as first element in the information passed to the controller at creation time. The following is an example of the definition of this MaxDevice property in C++ for a controller class called “DummyController”
long DummyController_MaxDevice = 16;
For C++, the controller code is implemented as a set of classes: A base class called Controller and a class called MotorController which inherits from Controller. Finally, the user has to write its controller class which inherits from MotorController.
XXX: Unknown layout Subparagraph: The Controller class XXX: XXX: Unknown inset LatexCommand label{sub:The-Cpp-Controller-class}: This class defined two pure virtual methods, seven virtual methods and some data types. The methods defined in this class are:
void Controller::AddDevice (long axe_number) Pure virtual
void Controller::DeleteDevice (long axe_number) Pure virtual
void Controller::PreStateAll () The default implementation does nothing
void Controller::PreStateOne (long idx_number) The default implementation does nothing. The parameter is the device index in the controller
void Controller::StateAll () The default implementation does nothing
void Controller::StateOne (long idx_number,CtrlState *ptr) Read a device state. The CtrlState data type is a structure with two elements which are:
- A long dedicated to return device state (format ??)
- A string used in case the motor is in FAULT and the controller is able to return a string describing the fault.
string Controller::SendToCtrl (string in_string) Send the input string to the controller without interpreting it and returns the controller answer
Controller::CtrlData Controller::GetExtraAttributePar (long idx_number,string &extra_attribute_name) Get device extra attribute value. The name of the extra attribute is passed as the second argument of the method. The default definition of this method prints a message on the screen and returns a string set to “Pool_meth_not_implemented”. The CtrlData data type is a structure with the following elements
- A data type enumeration called data_type describing which of the following element is valid (BOOLEAN, LONG, DOUBLE or STRING)
- A boolean data called bo_data for boolean transfer
- A long data called lo_data for long transfer
- A double data called db_data for double transfer
- A C++ string data called str_data for string transfer
void Controller::SetExtraAttributePar (long idx_number, string &extra_attribute_name, Controller::CtrlData &extra_attribute_value) Set device extra attribute value.
It also has one data member which is the controller instance name with one method to return it
- string & Controller::get_name (): Returns the controller instance name
XXX: Unknown layout Subparagraph: The MotorController class This class defined twelve virtual methods with default implementation. The virtual methods declared in this class are:
void MotorController::PreStartAll () The default implementation does nothing.
bool MotorController::PreStartOne (long axe_number, double wanted_position) The default implementation returns True.
void MotorController::StartOne (long axe_number, double wanted_position) The default implementation does nothing.
void MotorController::StartAll () Start the motion. The default implementation does nothing.
void MotorController::PreReadAll () The default implementation does nothing.
void MotorController::PreReadOne (long axe_number) The default implementation does nothing.
void MotorController::ReadAll () The default implementation does nothing.
double MotorController::ReadOne (long axe_number) Read a position. The default implementation does nothing.
void MotorController::AbortOne (long axe_number) Abort a motion. The default implementation does nothing.
void MotorController::DefinePosition (long axe_number, double new_position) Load a new position. The default implementation does nothing.
Controller::CtrlData MotorController::GetPar (long axe_number, string &par_name) Get motor parameter value. The CtrlData data type is a structure with the following elements
- A data type enumeration called data_type describing which of the following element is valid (BOOLEAN, LONG, DOUBLE or STRING)
- A boolean data called bo_data for boolean transfer
- A long data called lo_data for long transfer
- A double data called db_data for double transfer
- A C++ string data called str_data for string transfer
A motor controller has to handle four or five different possible values for the “par_name” parameter which are:
- Acceleration
- Deceleration
- Velocity
- Base_rate
- Backlash which has to be handled only for controller which has the backlash feature
The default definition of this method prints a message on the screen and returns a NaN double value.
void MotorController::SetPar (long axe_number, string &par_name, Controller::CtrlData &par_value) Set motor parameter value. The default implementation does nothing. A motor controller has to handle five or six different value for the “par_name” parameter which are:
- Acceleration
- Deceleration
- Velocity
- Base_rate
- Step_per_unit
- Backlash which has to be handled only for controller which has the backlash feature
The description of the CtrlData type is given in the documentation of the GetPar() method. The default definition of this method does nothing
This class has only one constructor which is
- MotorController::MotorController (const char *) Constructor of the MotorController class with the controller name as instance name
Please, note that this class defines a structure called MotorState which inherits from the Controller::CtrlState and which has a data member:
- A long describing the motor limit switches state (bit 0 for the Home switch, bit 1 for Upper Limit switch and bit 2 for the Lower Limit switch)
This structure is used in the StateOne() method.
XXX: Unknown layout Subparagraph: The user controller class XXX: XXX: Unknown inset LatexCommand label{par:The-user-controller}: The user has to implement the remaining pure virtual methods (AddDevice and DeleteDevice) and has to re-define virtual methods if the default implementation does not cover his needs. The controller code has to define two global variables which are:
- Motor_Ctrl_class_name (for Motor controller). This is an array of classical C strings terminated by a NULL pointer. Each array element is the name of a Motor controller class defined in this file.
- <CtrlClassName>_MaxDevice . This variable is a long defining the maximum number of device that the controller hardware can support.
On top of that, a controller code has to define a C function (defined as “extern C”) which is called by the pool to create instance(s) of the controller class. This function has the following definition:
Controller * **_create_<Controller class name>** (const char \*ctrl_instance_name,vector<Controller::Properties> &props)
For instance, for a controller class called DummyController, the name of this function has to be: _create_DummyController(). The parameters passed to this function are:
- The forth parameter given to the pool during the CreateController command (the instance name).
- A reference to a C++ vector with controller properties as defined in XXX: Unknown inset LatexCommand ref{par:Controller-properties}:
The rule of this C function is to create one instance of the user controller class passing it the arguments it has received. The following is an example of these definitions
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //
// Methods of the DummyController controller
//
....
const char *Motor_Ctrl_class_name[] = {"DummyController",NULL};
long DummyController_MaxDevice = 16;
extern "C" {
Controller *_create_DummyController(const char *inst,vector<Controller::Properties> &prop)
{
return new DummyController(inst,prop);
}
}
|
On top of these mandatory definitions, you can define a controller documentation string, controller properties, controller features and controller extra features. The documentation string is the first element of the array returned by the Pool device GetControllerInfo command as detailed in XXX: Unknown inset LatexCommand ref{ite:GetControllerInfo:}: . It has to be defined as a classical C string (const char *) with a name like <Ctrl_class_name>_doc. The following is an example of a controller C++ code defining all these elements.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | //
// Methods of the DummyController controller
//
....
const char *Motor_Ctrl_class_name[] = {"DummyController",NULL};
const char *DummyController_doc = "This is the C++ controller for the DummyController class";
long DummyController_MaxDevice = 16;
char *DummyController_ctrl_extra_features_list[] = {{"Extra_1","DevLong","Read_Write"},
{"Super_2","DevString","Read"},
NULL};
char *DummyController_ctrl_features[] = {"WantRounding","CanDoBacklash",NULL};
Controller::PropInfo DummyController_class_prop[] =
{{"The prop","The first CPP property","DevLong","12"},
{"Another_Prop","The second CPP property","DevString",NULL},
{"Third_Prop","The third CPP property","DevVarLongArray","11,22,33"},
NULL};
extern "C" {
Controller *_create_DummyController(const char *inst,vector<Controller::Properties> &prop)
{
return new DummyController(inst,prop);
}
}
|
The principle is exactly the same than the one used for C++ controller but we don’t have pure virtual methods with a compiler checking if they are defined at compile time. Therefore, it is the pool software which checks that the following methods are defined within the controller class when the controller module is loaded (imported):
- AddDevice
- DeleteDevice
- StartOne or StartAll method
- ReadOne method
- StateOne method
With Python controller, there is no need for function to create controller class instance. With the help of the Python C API, the pool device is able to create the needed instances. Note that the StateOne() method does not have the same signature for Python controller.
tuple Stat e One (self,axe_number) Get a motor state. The method has to return a tuple with two or three elements which are:
- The motor state (as defined by Tango)
- The limit switch state (integer with bit 0 for Home switch, bit 1 for Upper switch and bit 2 for Lower switch)
- A string describing the motor fault if the controller has this feature.
A Python controller class has to inherit from a class called MotorController . This does not add any feature but allow the pool software to realize that this class is a motor controller.
XXX: Unknown layout Subparagraph: A minimum controller code The following is an example of the minimum code structure needed to write a Python controller :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 1 import socket
2 import PyTango
3 import MotorController
4
5 class MinController(MotorController.MotorController):
6
7 #
8 # Some controller definitions
9 #
10
11 MaxDevice = 1
12
13 #
14 # Controller methods
15 #
16
17 def __init__(self,inst,props):
18 MotorController.MotorController.__init__(self,inst,props)
19 self.inst_name = inst
20 self.socket_connected = False
21 self.host = "the_host"
22 self.port = 1111
23
24 #
25 # Connect to the icepap
26 #
27
28 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
29 self.sock.connect(self.host, self.port)
30 self.socket_connected = True
31
32 print "PYTHON -> Connected to", self.host, " on port", self.port
33
34
35 def AddDevice(self,axis):
36 print "PYTHON -> MinController/",self.inst_name,": In AddDevice method for axis",axis
37
38 def DeleteDevice(self,axis):
39 print "PYTHON -> MinController/",self.inst_name,": In DeleteDevice method for axis",axis
40
41 def StateOne(self,axis):
42 print "PYTHON -> MinController/",self.inst_name,": In StateOne method for axis",axis
43 tup = (PyTango.DevState.ON,0)
44 return tup
45
46 def ReadOne(self,axis):
47 print "PYTHON -> MinController/",self.inst_name,": In ReadOne method for axis",axis
48 self.sock.send("Read motor position")
49 pos = self.sock.recv(1024)
50 return pos
51
52 def StartOne(self,axis,pos):
53 print "PYTHON -> MinController/",self.inst_name,": In StartOne method for axis",axis," with pos",pos
54 self.sock.send("Send motor to position pos")
|
Line 11: Definition of the mandatory MaxDevice property set to 1 in this minimum code Line 17-32: The IcePapController constructor code Line 35-36: The AddDevice method Line 38-39: The DeleteDevice method Line 41-44: The StateOne method Line 46-50: The ReadOne method reading motor position from the hardware controller Line 52-54: The StartOne method writing motor position at position pos
XXX: Unknown layout Subparagraph: A full features controller code The following is an example of the code structure needed to write a full features Python controller :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | 1 import socket
2 import PyTango
3 import MotorController
4
5 class IcePapController(MotorController.MotorController)
6 "This is an example of a Python motor controller class"
7 #
8 # Some controller definitions
9 #
10
11 MaxDevice = 128
12 ctrl_features = ['CanDoBacklash']
13 ctrl_extra_attributes = {'IceAttribute':{'Type':'DevLong','R/W Type':'READ_WRITE'}}
14 class_prop = {'host':{'Type':'DevString','Description':"The IcePap controller
15 host name",'DefaultValue':"IcePapHost"},
16 'port':{'Type':'DevLong','Description':"The port on which the
17 IcePap software is listenning",'DefaultValue':5000}}
18
19 #
20 # Controller methods
21 #
22
23 def __init__(self,inst,props):
24 MotorController.MotorController.__init__(self,inst,props)
25 self.inst_name = inst
26 self.socket_connected = False
27
28 #
29 # Connect to the icepap
30 #
31
32 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
33 self.sock.connect(self.host, self.port)
34 self.socket_connected = True
35
36 print "PYTHON -> Connected to", self.host, " on port", self.port
37
38
39 def AddDevice(self,axis):
40 print "PYTHON -> IcePapController/",self.inst_name,": In AddDevice method for axis",axis
41
42 def DeleteDevice(self,axis):
43 print "PYTHON -> IcePapController/",self.inst_name,": In DeleteDevice method for axis",axis
44
45 def PreReadAll(self):
46 print "PYTHON -> IcePapController/",self.inst_name,": In PreReadAll method"
47 self.read_pos = []
48 self.motor_to_read = []
49
50 def PreReadOne(self,axis):
51 print "PYTHON -> IcePapController/",self.inst_name,": In PreReadOne method for axis",axis
52 self.motor_to_read.append(axis)
53
54 def ReadAll(self):
55 print "PYTHON -> IcePapController/",self.inst_name,": In ReadAll method"
56 self.sock.send("Read motors in the motor_to_read list")
57 self.read_pos = self.sock.recv(1024)
58
59 def ReadOne(self,axis):
60 print "PYTHON -> IcePapController/",self.inst_name,": In ReadOne method for axis",axis
61 return read_pos[axis]
62
63 def PreStartAll(self):
64 print "PYTHON -> IcePapController/",self.inst_name,": In PreStartAll method"
65 self.write_pos = []
66 self.motor_to_write = []
67
68 def PreStartOne(self,axis,pos):
69 print "PYTHON -> IcePapController/",self.inst_name,": In PreStartOne method for axis",axis," with pos",pos
70 return True
71
72 def StartOne(self,axis,pos):
73 print "PYTHON -> IcePapController/",self.inst_name,": In StartOne method for axis",axis," with pos",pos
74 self.write_pos.append(pos)
75 self.motor_to_write(axis)
76
77 def StartAll(self):
78 print "PYTHON -> IcePapController/",self.inst_name,": In StartAll method"
79 self.sock.send("Write motors in the motor_to_write list at position in the write_pos list"
80
81 def PreStateAll(self):
82 print "PYTHON -> IcePapController/",self.inst_name,": In PreStateAll method"
83 self.read_state = []
84 self.motor_to_get_state = []
85
86 def PreStateOne(self,axis):
87 print "PYTHON -> IcePapController/",self.inst_name,": In PreStateOne method for axis",axis
88 self.motor_to_get_state.append(axis)
89
90 def StateAll(self):
91 print "PYTHON -> IcePapController/",self.inst_name,": In StateAll method"
92 self.sock.send("Read motors state for motor(s) in the motor_to_get_state list")
93 self.read_state = self.sock.recv(1024)
94
95 def StateOne(self,axis):
96 print "PYTHON -> IcePapController/",self.inst_name,": In StateOne method for axis",axis
97 one_state = [read_state[axis]]
98 return one_state
99
100 def SetPar(self,axis,name,value):
101 if name == 'Acceleration'
102 print "Setting acceleration to",value
103 elif name == 'Deceleration'
104 print "Setting deceleartion to",value
105 elif name == 'Velocity'
106 print "Setting velocity to",value
107 elif name == 'Base_rate'
108 print "Setting base_rate to",value
109 elif name == 'Step_per_unit'
110 print "Setting step_per_unit to",value
111 elif name == 'Backlash'
112 print "Setting backlash to",value
113
114 def GetPar(self,axis,name):
115 ret_val = 0.0
116 if name == 'Acceleration'
117 print "Getting acceleration"
118 ret_val = 12.34
119 elif name == 'Deceleration'
120 print "Getting deceleration"
121 ret_val = 13.34
122 elif name == 'Velocity'
123 print "Getting velocity"
124 ret_val = 14.34
125 elif name == 'Base_rate'
126 print "Getting base_rate"
127 ret_val = 15.34
128 elif name == 'Backlash'
129 print "Getting backlash"
130 ret_val = 123
131 return ret_val
132
133 def SetExtraAttributePar(self,axis,name,value):
134 if name == 'IceAttribute'
135 print "Setting IceAttribute to",value
136
137 def GetExtraAttributePar(self,axis,name):
138 ret_val = 0.0
139 if name == 'IceAttribute'
140 print "Getting IceAttribute"
141 ret_val = 12.34
142 return ret_val
143
144 def AbortOne(self,axis):
145 print "PYTHON -> IcePapController/",self.inst_name,": Aborting motion for axis:",axis
146
147 def DefinePosition(self,axis,value):
148 print "PYTHON -> IcePapController/",self.inst_name,": Defining position for axis:",axis
149
150 def __del__(self):
151 print "PYTHON -> IcePapController/",self.inst_name,": Aarrrrrg, I am dying"
152
153 def SendToCtrl(self,in_str)
154 print "Python -> MinController/",self.inst_name,": In SendToCtrl method"
155 self.sock.send("The input string")
156 out_str = self.sock.recv(1024)
157 return out_str
|
Line 6 : Definition of the Python DocString which will also be used for the first returned value of the Pool device GetControllerInfo command. See chapter XXX: Unknown inset LatexCommand ref{ite:GetControllerInfo:}: to get all details about this command. Line 11: Definition of the mandatory MaxDevice property set to 128 Line 12: Definition of the pre-defined feature supported by this controller. In this example, only the backlash Line 13: Definition of one controller extra feature called IceFeature Line 14-17: Definition of 2 properties called host and port Line 23-36: The IcePapController constructor code. Note that the object attribute host and port automatically created by the property management are used on line 32 Line 39-40: The AddDevice method Line 42-43: The DeleteDevice method Line 45-48: The PreReadAll method which clears the 2 list read_pos and motor_to_read Line 50-52: The PreReadOne method. It stores which method has to be read in the motor_to_read list Line 54-57: The ReadAll method. It send the request to read motor positions to the controller and stores the result in the internal read_pos list Line 59-61: The ReadOne method returning motor position from the internal read_pos list Line 63-66: The PreStartAll method which clears 2 internal list called write_pos and motor_to_write Line 68-70: The PreStartOne method Line 72-75: The StartOne method which appends in the write_pos and motor_to_write list the new motor position and the motor number which has to be moved Line 77-79: The StartAll method sending the request to the controller Line 81-84: The PreStateAll method which clears 2 internal list called read_state and motor_to_get_state Line 86-88: The PreStateOne method Line 90-93: The StateAll method sending the request to the controller Line 95-98: The StateOne method returning motor state from the internal read_state list Line 100-112: The SetPar method managing the acceleration, deceleration, velocity, base_rate and backlash attributes (because defined in line 11) Line 114-131: The GetPar method managing the same 5 parameters plus the step_per_unit Line 133-135: The SetExtraAttributePar method for the controller extra feature defined at line 12 Line 137-142: The GetExtraAttributePar method for controller extra feature Line 144-145: The AbortOne method Line 147-148: The DefinePosition method Line 153-157: The SendToCtrl method
Four data types and two read_write modes are available for the attribute associated with controller features. The possible data type are:
- BOOLEAN
- LONG
- DOUBLE
- STRING
The read_write modes are:
- READ
- READ_WRITE
All the attributes created to deal with controller features and defined as READ_WRITE will be memorized attributes. This means that the attribute will be written with the memorized value just after the device creation by the Tango layer. The definition of a controller features means defining three elements which are the feature name, the feature data type and the feature read_write mode. It uses a C++ structure called MotorFeature with three elements which are a C string (const char *) for the feature name and two enumeration for the feature data type and feature read_write mode. All the available features are defined as an array of these structures in a file called MotorFeatures.h
When you create a motor (a new one or at Pool startup time), the calls executed on the controller depend if a command “SaveConfig” has already been executed for this motor. If the motor is new and the command SaveConfig has never been executed for this motor, the following controller methods are called:
- The AddDevice() method
- The SetPar() method for the Step_per_unit parameter
- The GetPar() method for the Velocity parameter
- The GetPar() method for the Acceleration parameter
- The GetPar() method for the Deceleration parameter
- The GetPar() method for the Base_rate parameter
If the motor is not new and if a SaveConfig command has been executed on this motor, during Pool startup sequence, the motor will be created and the following controller methods will be called:
- The AddDevice() method
- The SetPar() method for the Step_per_unit parameter
- The SetPar() method for the Velocity parameter
- The SetPar() method for the Acceleration parameter
- The SetPar() method for the Deceleration parameter
- The SetPar() method for the Base_rate parameter
- The SetExtraAttributePar() method for each of the memorized motor extra attributes
The motor group interface allows the user to move several motor(s) at the same time. It supports several attributes and commands. It is implemented in C++ and is mainly a set of controller methods call or individual motor call. The motor group interface is statically linked with the Pool device server. When creating a group, the user can define as group member three kinds of elements which are :
- A simple motor
- Another already created group
- A pseudo-motor
Nevertheless, it is not possible to have several times the same physical motor within a group. Therefore, each group has a logical structure (the one defined by the user when the group is created) and a physical structure (the list of physical motors really used in the group).
The motor group interface knows four states which are ON, MOVING, ALARM and FAULT. A motor group device is in MOVING state when one of the group element is in MOVING state. It is in ALARM state when one of the motor is in ALARM state (The underlying motor has reached one of the limit switches). A motor group device is in FAULT state as long as any one of the underlying motor is in FAULT state.
The motor interface supports 1 command on top of the Tango Init, State and Status command. This command is summarized in the following table:
Command name | Input data type | Output data type |
---|---|---|
Abort | void | void |
- Abort : It aborts a running motion. This command does not have input or output argument. It aborts the motion of the motor(s) member of the group which are still moving while the command is received.
The motor group supports the following attributes:
Name | Data type | Data format | Writable |
---|---|---|---|
Position | Tango::DevVarDoubleStringArray | Spectrum | R/W |
- P osition : This is a read/write spectrum of double attribute. Each spectrum element is the position of one motor. The order of this array is the order used when the motor group has been created. The size of this spectrum has to be the size corresponding to the motor number when the group is created. For instance, for a group created with 2 motors, another group of 3 motors and one pseudo-motor, the size of this spectrum when written has to be 6 ( 2 + 3 + 1)
Each motor group has 6 properties. Five of them are automatically managed by the pool software and must not be changed by the user. These properties are called Motor_group_id, Pool_device, Motor_list, User_group_elt and Pos_spectrum_dim_x. The last property called Sleep_bef_last_read is a user property.This user property is:
Property name | Default value |
---|---|
Sleep_before_last_read | 0 |
It defines the time in milli-second that the software managing a motor group motion will wait between it detects the end of the motion of the last group element and the last group motors position reading.
The simplest way to know if a motor group is moving is to survey its state. If the group is moving, its state will be MOVING. When the motion is over, its state will be back to ON. The pool motor interface allows client interested by group state to use the Tango event system subscribing to motor group state change event. As soon as a group starts a motion, its state is changed to MOVING and an event is sent. As soon as the motion is over, the group state is updated ans another event is sent. Events will also be sent to each motor element of the group when they start moving and when they stop. These events could be sent before before the group state change event is sent in case of group motion with different motor motion for each group member.
For each motor group, the key attribute is its position. Special care has been taken on this attribute management. When the motor group is not moving (None of the motor are moving), reading the Position attribute will generate calls to the controller(s) and therefore hardware access. When the motor group is moving (At least one of its motor is moving), its position is automatically read every 100 milli- seconds and stored in the Tango polling buffer. This means that a client reading motor group Position attribute while the group is moving will get the position from the Tango polling buffer and will not generate extra controller calls. It is also possible to get a group position using the Tango event system. When the group is moving, an event is sent to the registered clients when the change event criterion is true. By default, this change event criterion is set to be a difference in position of 5. It is tunable on a group basis using the classical group Position attribute “abs_change” property or at the pool device basis using its DefaultMotGrpPos_AbsChange property. Anyway, not more than 10 events could be sent by second. Once the motion is over (None of the motors within the group are moving), the group position is made unavailable from the Tango polling buffer and is read a last time after a tunable waiting time (Sleep_bef_last_read property). A forced change event with this value is sent to clients using events.
In order to allow pool client software to be entirely event based, some kind of polling has to be done on each motor to inform them on state change which are not related to motor motion. To achieve this goal, one internally managed motor group is created. Each pool motor is a member of this group. The Tango polling thread polls the state command of this group (Polling period tunable with the pool Ghostgroup_PollingPeriod property). The code of this group state command detects change in every motor state and send a state change event on the corresponding motor. This motor group is not available to client and is even not defined in the Tango database. This is why it is called the ghost group.
The pseudo motor interface acts like an abstraction layer for a motor or a set of motors allowing the user to control the experiment by means of an interface which is more meaningful to him(her).
Each pseudo motor is represented by a C++ written tango device whose interface allows for the control of a single position (scalar value).
In order to translate the motor positions into pseudo positions and vice versa, calculations have to be performed. The device pool provides a python API class that can be overwritten to provide new calculations.
The pseudo motor interface knows four states which are ON, MOVING, ALARM and FAULT. A pseudo motor device is in MOVING state when at least one motor is in MOVING state. It is in ALARM state when one of the motor is in ALARM state (The underlying motor has reached one of the limit switches. A pseudo motor device is in FAULT state as long as any one of the underlying motor is in FAULT state).
The pseudo motor interface supports 1 command on top of the Tango Init, State and Status commands. This command is summarized in the following table:
Command name | Input data type | Output data type |
---|---|---|
Abort | void | void |
- Abort : It aborts a running movement. This command does not have input or output argument. It aborts the movement of the motor(s) member of the pseudo motor which are still moving while the command is received.
The pseudo motor supports the following attributes:
Name | Data type | Data format | Writable |
---|---|---|---|
Position | Tango::DevDouble | Scalar | R/W |
- Position : This is read-write scalar double attribute. With the classical Tango min and max_value, it is easy to define authorized limit for this attribute. It is not allowed to read or write this attribute when the pseudo motor is in FAULT or UNKNOWN state. It is also not possible to write this attribute when the motor is already MOVING.
This chapter describes how to write a valid python pseudo motor system class.
Before writing the first python pseudo motor class for your device pool two checks must be performed:
- The device pool PoolPath property must exist and must point to the directory which will contain your python pseudo motor module. The syntax of this PseudoPath property is the same used in the PATH or PYTHONPATH environment variables. Please see XXX: Unknown inset LatexCommand ref{sub:PoolPath}: for more information on setting this property
- A PseudoMotor.py file is part of the device pool distribution and is located in <device pool home dir>/py_pseudo. This directory must be in the PYTHONPATH environment variable or it must be part of the PoolPath device pool property metioned above
A correct pseudo motor system class must obey the following rules:
the python class PseudoMotor of the PseudoMotor module must be imported into the current namespace by using one of the python import statements:
from PseudoMotor import * import PseudoMotor or from PseudoMotor import PseudoMotor or
the pseudo motor system class being written must be a subclass of the PseudoMotor class (see example below)
the class variable motor_roles must be set to be a tuple of text descriptions containing each motor role description. It is crucial that all necessary motors contain a textual description even if it is an empty one. This is because the number of elements in this tuple will determine the number of required motors for this pseudo motor class. The order in which the roles are defined is also important as it will determine the index of the motors in the pseudo motor system.
the class variable pseudo_motor_roles must be set if the pseudo motor class being written represents more than one pseudo motor. The order in which the roles are defined will determine the index of the pseudo motors in the pseudo motor system. If the pseudo motor class represents only one pseudo motor then this operation is optional. If omitted the value will of pseudo_motor_roles will be set to:
if the pseudo motor class needs some special parameters then the class variable parameters must be set to be a dictionary of <parameter name> : { <property> : <value> } values where:
<parameter name> - is a string representing the name of the parameter
<property> - is one of the following mandatory properties: ‘Description’, ‘Type’. The ‘Default Value’ property is optional.
<value> - is the corresponding value of the property. The ‘Description’ can contain any text value. The ‘Type’ must be one of available Tango property data types and ‘Default Value’ must be a string containning a valid value for the corresponding ‘Type’ value.
the pseudo motor class must implement a calc_pseudo method with the following signature:
number = calc_pseudo(index, physical_pos, params = None)
The method will receive as argument the index of the pseudo motor for which the pseudo position calculation is requested. This number refers to the index in the pseudo_motor_roles class variable.
The physical_pos is a tuple containing the motor positions.
The params argument is optional and will contain a dictionary of <parameter name> : <value>.
The method body should contain a code to translate the given motor positions into pseudo motor positions.
The method will return a number representing the calculated pseudo motor position.
the pseudo motor class must implement a calc_physical method with the following signature:
number = calc_physical(index, pseudo_pos, params = None)
The method will receive as argument the index of the motor for which the physical position calculation is requested. This number refers to the index in the motor_roles class variable.
The pseudo_pos is a tuple containing the pseudo motor positions.
The params argument is optional and will contain a dictionary of <parameter name> : <value>.
The method body should contain a code to translate the given pseudo motor positions into motor positions.
The method will return a number representing the calculated motor position.
Optional implementation of calc_all_pseudo method with the following signature:
()/[]/number = calc_all_pseudo(physical_pos,params = None)
The method will receive as argument a physical_pos which is a tuple of motor positions.
The params argument is optional and will contain a dictionary of <parameter name> : <value>.
The method will return a tuple or a list of calculated pseudo motor positions. If the pseudo motor class represents a single pseudo motor then the return value could be a single number.
Optional implementation of calc_all_physical method with the following signature:
()/[]/number = calc_all_physical(pseudo_pos, params = None)
The method will receive as argument a pseudo_pos which is a tuple of pseudo motor positions.
The params argument is optional and will contain a dictionary of <parameter name> : <value>.
The method will return a tuple or a list of calculated motor positions. If the pseudo motor class requires a single motor then the return value could be a single number.
Note: The default implementation calc_all_physical and calc_all_pseudo methods will call calc_physical and calc_pseudo for each motor and physical motor respectively. Overwriting the default implementation should only be done if a gain in performance can be obtained.
One of the most basic examples is the control of a slit. The slit has two blades with one motor each. Usually the user doesn’t want to control the experiment by directly handling these two motor positions since their have little meaning from the experiments perspective.

Instead, it would be more useful for the user to control the experiment by means of changing the gap and offset values. Pseudo motors gap and offset will provide the necessary interface for controlling the experiments gap and offset values respectively.
The calculations that need to be performed are:
The corresponding python code would be:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 01 class Slit(PseudoMotor):
02 """A Slit system for controlling gap and offset pseudo motors."""
04
05 pseudo_motor_roles = ("Gap", "Offset")
06 motor_roles = ("Motor on blade 1", "Motor on blade 2")
07
08 def calc_physical(self,index,pseudo_pos,params = None):
09 half_gap = pseudo_pos[0]/2.0
10 if index == 0:
11 return -pseudo_pos[1] + half_gap
12 else
13 return pseudo_pos[1] + half_gap
14
15 def calc_pseudo(self,index,physical_pos,params = None):
16 if index == 0:
17 return physical_pos[1] + physical_pos[0]
18 else:
19 return (physical_pos[1] - physical_pos[0])/2.0
|
The following diagram shows the sequence of operations performed when the position is requested from the gap pseudo motor:

The following diagram shows the sequence of operations performed when a new position is written to the gap pseudo motor:

The Counter/Timer interface is statically linked with the Pool device server and supports several attributes and commands. It is implemented in C++ and used a set of the so-called “controller” methods. The Counter/Timer interface is always the same whatever the hardware is. This is the rule of the “controller” to access the hardware using the communication link supported by the hardware (network link, Serial line...).
The controller code has a well-defined interface and can be written using Python or C++. In both cases, it will be dynamically loaded into the pool device server process.
The Counter/Timer interface knows four states which are ON, MOVING, FAULT and UNKNOWN. A Counter/Timer device is in MOVING state when it is counting! It is in FAULT if its controller software is not available (impossible to load it), if a fault is reported from the hardware controller or if the controller software returns an unforeseen state. The device is in the UNKNOWN state if an exception occurs during the communication between the pool and the hardware controller.
The Counter/Timer interface supports 2 commands on top of the Tango classical Init, State and Status commands. These commands are summarized in the following table:
Command name | Input data type | Output data type |
---|---|---|
Start | void | void |
Stop | void | void |
- Start : When the device is used as a counter, this commands allows the counter to start counting. When it is used as a timer, this command starts the timer. This command changes the device state from ON to MOVING. It is not allowed to execute this command if the device is already in the MOVING state.
- Stop : When the device is used as a counter, this commands stops the counter. When it is used as a timer, this command stops the timer. This commands changes the device state from MOVING to ON. It is a no action command if this command is received and the device is not in the MOVING state.
The Counter/Timer interface supports several attributes which are summarized in the following table:
Name | Data type | Data format | Writable | Memorized | Ope/Expert |
---|---|---|---|---|---|
Value | Tango::DevDouble | Scalar | R/W | No | Ope |
SimulationMode | Tango::DevBoolean | Scalar | R | No | Ope |
- Value : This is read-write scalar double attribute. Writing the value is used to clear (or to preset) a counter or to set a timer time. For counter, reading the value allows the user to get the count number. For timer, the read value is the elapsed time since the timer has been started. After the acquisition, the value stays unchanged until a new count/time is started. For timer, the unit of this attribute is the second.
- SimulationMode : This is a read only scalar boolean attribute. When set, all the counting/timing requests are not forwarded to the software controller and then to the hardware. When set, the device Value is always 0. To set this attribute, the user has to used the pool device Tango interface. It is not allowed to read this attribute when the device is in FAULT or UNKNOWN states.
Each Counter/Timer device has one property which is automatically managed by the pool software and must not be changed by the user. This property is named Channel_id.
The CounterTimer controller follows the same principles already explained for the Motor controller in chapter XXX: Unknown inset LatexCommand ref{sub:The-Motor-Controller}:
For Counter/Timer, the pre-defined set of methods which has to be implemented can be splitted in 7 different types which are:
- Methods to create/remove counter/timer experiment channel
- Methods to get channel(s) state
- Methods to read channel(s)
- Methods to load channel(s)
- Methods to start channel(s)
- Methods to configure a channel
- Remaining method
Not defined yet
The definition is the same than the one defined for Motor controller and explained in chapter XXX: Unknown inset LatexCommand ref{par:Specifying-the-motor}:
Two methods are called when creating or removing counter/timer channel from a controller. These methods are called AddDevice and DeleteDevice . The AddDevice method is called when a new channel belonging to the controller is created within the pool. The DeleteDevice method is called when a channel belonging to the controller is removed from the pool.
These methods follow the same definition than the one defined for Motor controller which are detailed in chapter XXX: Unknown inset LatexCommand ref{par:Methods-to-get-state}: .
Four methods are used when a request to read channel(s) value is received. These methods are called PreReadAll, PreReadOne, ReadAll and ReadOne. The algorithm used to read value of one or several channels is the following :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /FOR/ Each controller(s) implied in the reading
- Call PreReadAll()
/END FOR/
/FOR/ Each channel(s) implied in the reading
- PreReadOne(channel to read)
/END FOR/
/FOR/ Each controller(s) implied in the reading
- Call ReadAll()
/END FOR/
/FOR/ Each channel(s) implied in the reading
- Call ReadOne(channel to read)
/END FOR/
|
The following array summarizes the rule of each of these methods:
Default action | Does nothing | Does nothing | Does nothing | Print message on the screen and returns NaN. Mandatory for Python |
Externally called by | Reading the Value attribute | Reading the Value attribute | Reading the Value attribute | Reading the Value attribute |
Internally called | Once for each implied controller | For each implied channel | For each implied controller | Once for each implied channel |
Typical rule | Init internal data for reading | Memorize which channel has to be read | Send order to physical controller | Return channel value from internal data |
This algorithm covers the sophisticated case where a physical controller is able to read several channels positions at the same time. For some simpler controller, it is possible to implement only the ReadOne() method. The default implementation of the three remaining methods is defined in a way that the algorithm works even in such a case.
Four methods are used when a request to load channel(s) value is received. These methods are called PreLoadAll, PreLoadOne, LoadAll and LoadOne. The algorithm used to load value in one or several channels is the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /FOR/ Each controller(s) implied in the loading
- Call PreLoadAll()
/END FOR/
/FOR/ Each channel(s) implied in the loading
- ret = PreLoadOne(channel to load,new channel value)
- /IF/ ret is true
- Call LoadOne(channel to load, new channel value)
- /END IF/
/END FOR/
/FOR/ Each controller(s) implied in the loading
- Call LoadAll()
/END FOR/
|
The following array summarizes the rule of each of these methods:
Default action | Does nothing | Returns true | Does nothing | Does nothing |
Externally called by | Writing the Value attribute | Writing the Value attribute | Writing the Value attribute | Writing the Value attribute |
Internally called | Once for each implied controller | For each implied channel | For each implied channel | Once for each implied controller |
Typical rule | Init internal data for loading | Check if counting is possible | Set new channel value in internal data | Send order to physical controller |
This algorithm covers the sophisticated case where a physical controller is able to write several channels positions at the same time. For some simpler controller, it is possible to implement only the LoadOne() method. The default implementation of the three remaining methods is defined in a way that the algorithm works even in such a case.
Four methods are used when a request to start channel(s) is received. These methods are called PreStartAllCT, PreStartOneCT, StartAllCT and StartOneCT. The algorithm used to start one or several channels is the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /FOR/ Each controller(s) implied in the starting
- Call PreStartAllCT()
/END FOR/
/FOR/ Each channel(s) implied in the starting
- ret = PreStartOneCT(channel to start)
- /IF/ ret is true
- Call StartOneCT(channel to start)
- /END IF/
/END FOR/
/FOR/ Each controller(s) implied in the starting
- Call StartAllCT()
/END FOR/
|
The following array summarizes the rule of each of these methods:
Default action | Does nothing | Returns true | Does nothing | Does nothing |
Externally called by | The Start command | The Start command | The Start command | The Start command |
Internally called | Once for each implied controller | For each implied channel | For each implied channel | Once for each implied controller |
Typical rule | Init internal data for starting | Check if starting is possible | Set new channel value in internal data | Send order to physical controller |
This algorithm covers the sophisticated case where a physical controller is able to write several channels positions at the same time. For some simpler controller, it is possible to implement only the StartOneCT() method. The default implementation of the three remaining methods is defined in a way that the algorithm works even in such a case.
The rule of these methods is to
- Get or Set channel extra attribute(s) parameter with methods called GetExtraAttributePar() or SetExtraAttributePar()
The following table summarizes the usage of these methods:
Called by | Reading any of the extra attributes | Writing any of the extra attributes |
Rule | Get extra attribute value from the physical layer | Set additional attribute value in physical controller |
The GetExtraAttributePar() default implementation returns a string set to “Pool_meth_not_implemented”.
The rule of the remaining methods is to
- Send a raw string to the controller with a method called SendToCtrl()
- Abort a counting counter/timer with a method called AbortOne()
The following table summarizes the usage of this method:
Called by | The Pool SendToController command | The Stop CounterTimer command |
Rule | Send the input string to the controller and returns the controller answer | Abort a running count |
property)
The definition is the same than the one defined for Motor controller and explained in chapter XXX: Unknown inset LatexCommand ref{par:Controller-properties}:
For C++, the controller code is implemented as a set of classes: A base class called Controller and a class called CoTiController which inherits from Controller. Finally, the user has to write its controller class which inherits from CoTiController. The Controller class has already been detailed in XXX: Unknown inset LatexCommand ref{sub:The-Cpp-Controller-class}: .
XXX: Unknown layout Subparagraph: The CoTiController class The CoTiController class defines thirteen virtual methods which are:
- void CoTiController::PreReadAll () The default implementation does nothing
- void CoTiController::PreReadOne (long idx_to_read) The default implementation does nothing
- void CoTiController::ReadAll () The default implementation does nothing
- double CoTiController::ReadOne (long idx_to_read) The default implementation prints a message on the screen and return a NaN value
- void CoTiController::PreLoadAll () The default implementation does nothing
- bool CoTiController::PreLoadOne (long idx_to_load,double new_value) The default implementation returns true
- void CoTiController::LoadOne (long idx_to_load,double new_value) The default implementation does nothing
- void CoTiController::LoadAll () The default implementation does nothing
- void CoTiController::PreStartAllCT () The default implementation does nothing
- bool CoTiController::PreStartOneCT (long idx_to_start) The default implementation returns true
- void CoTiController::StartOneCT (long idx_to_start) The default implementation does nothing
- void CoTiController::StartAllCT () The default implementation does nothing
- void CoTiController::AbortOne (long idx_to_abort) The default implementation does nothing
This class has one constructor which is
- CoTiController::CoTiController (const char *) Constructor of the CoTiController class with the controller instance name as parameter
XXX: Unknown layout Subparagraph: The user controller class The user has to implement the remaining pure virtual methods (AddDevice and DeleteDevice) and has to re-define virtual methods if the default implementation does not cover his needs. The controller code has to define two global variables which are:
- CounterTimer_Ctrl_class_name : This is an array of classical C strings terminated by a NULL pointer. Each array element is the name of a Counter Timer Channel controller defined in the file.
- <CtrlClassName>_MaxDevice : Idem motor controller definition
On top of that, a controller code has to define a C function to create the controller object. This is similar to the Motor controller definition which is documented in XXX: Unknown inset LatexCommand ref{par:The-user-controller}:
The principle is exactly the same than the one used for C++ controller but we don’t have pure virtual methods with a compiler checking if they are defined at compile time. Therefore, it is the pool software which checks that the following methods are defined within the controller class when the controller module is loaded (imported):
- AddDevice
- DeleteDevice
- ReadOne method
- StateOne method
- StartOneCT or StartAllCT method
- LoadOne or LoadAll method
With Python controller, there is no need for function to create controller class instance. With the help of the Python C API, the pool device is able to create the needed instances. Note that the StateOne() method does not have the same signature for Python controller.
tuple Stat e One (self,idx_number) Get a channel state. The method has to return a tuple with one or two elements which are:
- The channel state (as defined by Tango)
- A string describing the motor fault if the controller has this feature.
A Python controller class has to inherit from a class called CounterTimerController . This does not add any feature but allows the pool software to realize that this class is a Counter Timer Channel controller.
A timer using the Unix getitimer() and setitimer() system calls is provided. It is a Counter/Timer C++ controller following the definition of the previous chapter. Therefore, the device created using this controller will have the Tango interface as the one previously described.
The Unix Timer controller shared library is called UxTimer.so and the Controlller class is called UnixTimer . This controller is foresee to have only one device (MaxDevice = 1)
The ZeroDExpChannel is used to access any kind of device which returns a scalar value and which are not counter or timer. Very often (but not always), this is a commercial measurement equipment connected to a GPIB bus. In order to have a precise as possible measurement, an acquisition loop is implemented for these ZeroDExpChannel device. This acquisition loop will simply read the data from the hardware as fast as it can (only “sleeping” 20 mS between each reading) and a computation is done on the resulting data set to return only one value. Three types of computation are foreseen. The user selects which one he needs with an attribute. The time during which this acquisition loop will get data is also defined by an attribute
The ZeroDExpChannel interface is statically linked with the Pool device server and supports several attributes and commands. It is implemented in C++ and used a set of the so-called “controller” methods. The ZeroDExpChannel interface is always the same whatever the hardware is. This is the rule of the “controller” to access the hardware using the communication link supported by the hardware (network link, GPIB...).
The controller code has a well-defined interface and can be written using Python or C++. In both cases, it will be dynamically loaded into the pool device server process.
The ZeroDExpChannel interface knows five states which are ON, MOVING, ALARM, FAULT and UNKNOWN. A ZeroDExpChannel device is in MOVING state when it is acquiring data! It is in ALARM state when at least one error has occured during the last acquisition. It is in FAULT if its controller software is not available (impossible to load it), if a fault is reported from the hardware controller or if the controller software returns an unforeseen state. The device is in the UNKNOWN state if an exception occurs during the communication between the pool and the hardware controller.
The ZeroDExpChannel interface supports 2 commands on top of the Tango classical Init, State and Status commands. These commands are summarized in the following table:
Command name | Input data type | Output data type |
---|---|---|
Start | void | void |
Stop | void | void |
- Start : Start the acquisition for the time defined by the attribute CumulatedTime. If the CumulatedTime attribute value is 0, the acquisition will not automatically stop until a Stop command is received. This command changes the device state from ON to MOVING. It is not allowed to execute this command if the device is already in the MOVING state.
- Stop : Stop the acquisition. This commands changes the device state from MOVING to ON. It is a no action command if this command is received and the device is not in the MOVING state.
The ZeroDExpChannel interface supports several attributes which are summarized in the following table:
Name | Data type | Data format | Writable | Memorized | Ope/Expert |
---|---|---|---|---|---|
Value | Tango::DevDouble | Scalar | R | No | Ope |
CumulatedValue | Tango::DevDouble | Scalar | R | No | Ope |
CumulationTime | Tango::DevDouble | Scalar | R/W | Yes | Ope |
CumulationType | Tango::DevLong | Scalar | R/W | Yes | Ope |
CumulatedPointsNumber | Tango::DevLong | Scalar | R | No | Ope |
CumulatedPointsError | Tango::DevLong | Scalar | R | No | Ope |
SimulationMode | Tango::DevBoolean | Scalar | R | No | Ope |
Value : This is read scalar double attribute. This is the live value reads from the hardware through the controller
CumulatedValue : This is a read scalar double attribute. This is the result of the data acquisition after the computation defined by the CumulationType attribute has been applied. This value is 0 until an acquisition has been started. After an acquisition, the attribute value stays unchanged until the next acquisition is started. If during the acquisition some error(s) has been received while reading the data, the attribute quality factor will be set to ALARM
CumulationTime : This is a read-write scalar double and memorized attribute. This is the acquisition time in seconds. The acquisition will automatically stops after this CumulationTime. Very often, reading the hardware device to get one data is time-consuming and it is not possible to read the hardware a integer number of times within this CumulationTime. A device property called StopIfNoTime (see XXX: Unknown inset LatexCommand ref{ite:StopIfNoTime:-A-boolean}: ) allows the user to tune the acquisition loop.
CumulationType : This a read-write scalar long and memorized attribute. Defines the computation type done of the values gathered during the acquisition. Three type of computation are supported:
- Sum: The CumulatedValue attribute is the sum of all the data read during the acquisition. This is the default type.
- Average: The CumulatedValue attribute is the average of all the data read during the acquisition
- Integral: The CumulatedValue attribute is a type of the integral of all the data read during the acquisition
CumulatedPointsNumber : This is a read scalar long attribute. This is the number of data correctly read during the acquisition. The attribute value is 0 until an acquisition has been started and stay unchanged between the end of the acquisition and the start of the next one.
CumulatedPointsError : This is a read scalar long attribute. This is the number of times it was not possible to read the data from the hardware due to error(s). The property ContinueOnError allows the user to define what to do in case of error. The attribute value is 0 until an acquisition has been started and stay unchanged between the end of the acquisition and the start of the next one.
SimulationMode : This is a read only scalar boolean attribute. When set, all the acquisition requests are not forwarded to the software controller and then to the hardware. When set, the device Value, CumulatedValue, CumulatedPointsNumber and CumulatedPointsError are always 0. To set this attribute, the user has to used the pool device Tango interface. The value of the CumulationTime and CumulationType attributes are memorized at the moment this attribute is set. When this mode is turned off, if the value of any of the previously memorized attributes has changed, it is reapplied to the memorized value. It is not allowed to read this attribute when the device is in FAULT or UNKNOWN states.
Each ZeroDExpChannel device has a set of properties. One of these properties is automatically managed by the pool software and must not be changed by the user. This property is named Channel_id. The user properties are:
Property name | Default value |
---|---|
StopIfNoTime | true |
ContinueOnError | true |
- XXX: Unknown inset LatexCommand label{ite:StopIfNoTime:-A-boolean}: StopIfNoTime : A boolean property. If this property is set to true, the acquisition loop will check before acquiring a new data that it has enough time to do this. To achieve this, the acquisition loop measures the time needed by the previous data read and checks that the actual time plus the acquisition time is still less than the CumulationTime. If not, the acquisition stops. When this property is set to false, the acquisition stops when the acquisition time is greater or equal than the CumulationTime
- ContinueOnError : A boolean property. If this property is set to true (the default), the acquisition loop continues reading the data even after an error has been received when trying to read data. If it is false, the acquisition stops as soon as an error is detected when trying to read data from the hardware.
The simplest way to know if a Zero D Experiment Channel is acquiring data is to survey its state. If the device is acquiring data, its state will be MOVING. When the acquisition is over, its state will be back to ON. The pool ZeroDExpChannel interface allows client interested by Experiment Channel state value to use the Tango event system subscribing to channel state change event. As soon as a channel starts an acquisition, its state is changed to MOVING and an event is sent. As soon as the acquisition is over (for one reason or another), the channel state is updated and another event is sent.
Reading the ZeroDExpChannel CumulatedValue attribute
During an acquisition, events with CumulatedValue attribute are sent from the device server to the interested clients. The acquisition loop will periodically read this event and fire an event. The first and the last events fired during the acquisition loop do not check the change event criteria. The other during the acquisition loop check the change event criteria
The ZeroDExpChannel controller follows the same principles already explained for the Motor controller in chapter XXX: Unknown inset LatexCommand ref{sub:The-Motor-Controller}:
For Zero Dimension Experiment Channel, the pre-defined set of methods which has to be implemented can be splitted in 5 different types which are:
- Methods to create/remove zero dimension experiment channel
- Methods to get channel(s) state
- Methods to read channel(s)
- Methods to configure a channel
- Remaining method
Not defined yet
The definition is the same than the one defined for Motor controller and explained in chapter XXX: Unknown inset LatexCommand ref{par:Specifying-the-motor}:
Two methods are called when creating or removing experiment channel from a controller. These methods are called AddDevice and DeleteDevice . The AddDevice method is called when a new channel belonging to the controller is created within the pool. The DeleteDevice method is called when a channel belonging to the controller is removed from the pool.
These methods follow the same definition than the one defined for Motor controller which are detailed in chapter XXX: Unknown inset LatexCommand ref{par:Methods-to-get-state}: .
Four methods are used when a request to read channel(s) value is received. These methods are called PreReadAll, PreReadOne, ReadAll and ReadOne. The algorithm used to read value of one or several channels is the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /FOR/ Each controller(s) implied in the reading
- Call PreReadAll()
/END FOR/
/FOR/ Each channel(s) implied in the reading
- PreReadOne(channel to read)
/END FOR/
/FOR/ Each controller(s) implied in the reading
- Call ReadAll()
/END FOR/
/FOR/ Each channel(s) implied in the reading
- Call ReadOne(channel to read)
/END FOR/
|
The following array summarizes the rule of each of these methods:
Default action | Does nothing | Does nothing | Does nothing | Print message on the screen and returns NaN. Mandatory for Python |
Externally called by | Reading the Value attribute | Reading the Value attribute | Reading the Value attribute | Reading the Value attribute |
Internally called | Once for each implied controller | For each implied channel | For each implied controller | Once for each implied channel |
Typical rule | Init internal data for reading | Memorize which channel has to be read | Send order to physical controller | Return channel value from internal data |
This algorithm covers the sophisticated case where a physical controller is able to read several channels positions at the same time. For some simpler controller, it is possible to implement only the ReadOne() method. The default implementation of the three remaining methods is defined in a way that the algorithm works even in such a case.
The rule of these methods is to
- Get or Set channel extra attribute(s) parameter with methods called GetExtraAttributePar() or SetExtraAttributePar()
The following table summarizes the usage of these methods:
Called by | Reading any of the extra attributes | Writing any of the extra attributes |
Rule | Get extra attribute value from the physical layer | Set additional attribute value in physical controller |
The GetExtraAttributePar() default implementation returns a string set to “Pool_meth_not_implemented”.
The rule of the remaining method is to
- Send a raw string to the controller with a method called SendToCtrl()
The following table summarizes the usage of this method:
Called by | The Pool SendToController command |
Rule | Send the input string to the controller and returns the controller answer |
The definition is the same than the one defined for Motor controller and explained in chapter XXX: Unknown inset LatexCommand ref{par:Controller-properties}:
For C++, the controller code is implemented as a set of classes: A base class called Controller and a class called ZeroDController which inherits from Controller. Finally, the user has to write its controller class which inherits from ZeroDController. The Controller class has already been detailed in XXX: Unknown inset LatexCommand ref{sub:The-Cpp-Controller-class}: .
XXX: Unknown layout Subparagraph: The ZeroDController class The ZeroDController class defines four virtual methods which are:
- void ZeroDController::PreReadAll () The default implementation does nothing
- void ZeroDController::PreReadOne (long idx_to_read) The default implementation does nothing
- void ZeroDController::ReadAll () The default implementation does nothing
- double ZeroDController::ReadOne (long idx_to_read) The default implementation prints a message on the screen and return a NaN value
This class has one constructor which is
- ZeroDController::ZeroDController (const char *) Constructor of the ZeroDController class with the controller instance name as parameter
XXX: Unknown layout Subparagraph: The user controller class The user has to implement the remaining pure virtual methods (AddDevice and DeleteDevice) and has to re-define virtual methods if the default implementation does not cover his needs. The controller code has to define two global variables which are:
- ZeroDExpChannel_Ctrl_class_name : This is an array of classical C strings terminated by a NULL pointer. Each array element is the name of a ZeroDExpChannel controller defined in the file.
- <CtrlClassName>_MaxDevice : Idem motor controller definition
On top of that, a controller code has to define a C function to create the controller object. This is similar to the Motor controller definition which is documented in XXX: Unknown inset LatexCommand ref{par:The-user-controller}:
The principle is exactly the same than the one used for C++ controller but we don’t have pure virtual methods with a compiler checking if they are defined at compile time. Therefore, it is the pool software which checks that the following methods are defined within the controller class when the controller module is loaded (imported):
- AddDevice
- DeleteDevice
- ReadOne method
- StateOne method
With Python controller, there is no need for function to create controller class instance. With the help of the Python C API, the pool device is able to create the needed instances. Note that the StateOne() method does not have the same signature for Python controller.
tuple Stat e One (self,idx_number) Get a channel state. The method has to return a tuple with one or two elements which are:
- The channel state (as defined by Tango)
- A string describing the motor fault if the controller has this feature.
A Python controller class has to inherit from a class called ZeroDController . This does not add any feature but allows the pool software to realize that this class is a Zero D Experiment Channel controller.
To be filled in
To be filled in
The measurement group interface allows the user to access several data acquisition channels at the same time. It is implemented as a C++ Tango device that is statically linked with the Pool device server. It supports several attributes and commands.
The measurement group is the key interface to be used when getting data. The Pool can have several measurement groups but only one will be ‘in use’ at a time. When creating a measurement group, the user can define four kinds of channels which are:
- Counter/Timer
- ZeroDExpChannel
- OneDExpChannel
- TwoDExpChannel
In order to properly use the measurement group, one of the channels has to be defined as the timer or the monitor. It is not possible to have several times the same channel in a measurement group. It is also not possible to create two measurement groups with exactly the same channels.
The measurement group interface knows five states which are ON, MOVING, ALARM, FAULT. A group is in MOVING state when it is acquiring data (which means that the timer/monitor channel is in MOVING state). A STANDBY state means that the group is not the current active group of the Pool it belongs to. An ON state means that the group is ready to be used. ALARM means that no timer or monitor are defined for the group. If at least one of the channels reported a FAULT by the controller(s) of that(those) channel(s), the group will be in FAULT state.
The measurement group interface supports three commands on top of the Tango Init, State and Status commands. These commands are summarized in the following table:
Command name | Input data type | Output data type |
---|---|---|
Start | void | void |
Abort | void | void |
AddExpChannel | String | void |
RemoveExpChannel | String | void |
- Start : When the device is in timer mode (Integration_time attribute > 0), it will start counting on all channels at the same time until the timer channel reaches a value of the Integration_time attribute. When the device in in monitor mode (Integration_count attribute > 0), it will start counting on all channels at the same time until de monitor channel reaches the value of the Integration_count attribute. For more details on setting the acquisition mode see XXX: Unknown inset LatexCommand ref{Measurement Group: The attributes}: . This command will change the device state to MOVING. It will not be allowed to execute this command if the device is already in MOVING state. This command does not have any input or output arguments. The state will change from MOVING to ON only when the last channel reports that its acquisition has finished.
- Abort : It aborts the running data acquisition. It will stop each channel member of the measurement group. This command does not have any input or output arguments.
- AddExpChannel : adds a new experiment channel to the measurement group. The given string argument must be a valid experiment channel in the pool and must not be one of the channels of the measurement group. An event will be sent on the corresponding attribute representing the list of channels in the measurement group. For example, if the given channel is a Counter/Timer channel, then an event will be sent for the attribute “Counters “(See below for a list of attributes in the measurement group).
- RemoveExpChannel : removes the given channel from the measurement group. The given string argument must be a valid experiment channel in the measurement group. If the channel to be deleted is the current Timer/Monitor then the value for the corresponding attribute will be set to “Not Initialized “and an event will be sent. An event will be sent on the corresponding attribute representing the list of channels in the measurement group.
The attributes
A measurement group will support 8+n (n being the number of channels)
attributes summarized in the following table:
========================================= ================ ===================== ======== ========= ==========
Name Data type Data format Writable Memorized Ope/Expert
========================================= ================ ===================== ======== ========= ==========
Integration_time Tango::DevDouble Scalar R/W Yes Ope
Integration_count Tango::DevLong Scalar R/W Yes Ope
Timer Tango::DevString Scalar R/W Yes Ope
Monitor Tango::DevString Scalar R/W Yes Ope
Counters Tango::DevString Spectrum R No Ope
ZeroDExpChannels Tango::DevString Spectrum R No Ope
OneDExpChannels Tango::DevString Spectrum R No Ope
TwoDExpChannels Tango::DevString Spectrum R No Ope
<channel_name >_Value Tango::DevDouble Scalar/Spectrum/Image R No Ope
========================================= ================ ===================== ======== ========= ==========
- Integration_time : The group timer integration time. Setting this value to >0 will set the measurement group acquisition mode to timer. It will force Integration_count attribute to 0 (zero). It will also exclude the current Timer channel from the list of Counters. Units are in seconds.
- Integration_count : The group monitor count value. Setting this value to >0 will set the measurement group acquisition mode change to monitor. It will force Integration_time attribute to 0 (zero).
- Timer : The name of the channel used as a Timer. A “Not Initialized “value means no timer is defined
- Monitor : The name of the channel used as a Monitor. A “Not Initialized “value means no timer is defined
- Counter : The list of counter names in the group
- ZeroDExpChannels : The list of 0D Experiment channel names in the group
- OneDExpChannels : The list of 1D Experiment channel names in the group
- TwoDExpChannels : The list of 2D Experiment channel names in the group
- <channel_name
>_Value : (with
) attributes dynamically created (one for each channel) which will contain the corresponding channel Value(for Counter/Timer, 1D or 2DExpChannels), CumulatedValue(for 0DExpChannels). For Counter/Timers and 0DExpChannels the data format will be Scalar. For 1DExpChannels it will be Spectrum and for 2DExpChannels it will be Image.
Each measurement group has five properties. All of them are managed automatically by the pool software and must not be changed by the user. These properties are called Measurement_group_id, Pool_device, CT_List, ZeroDExpChannel_List, OneDExpChannel_List, TwoDExpChannel_List.
Checking operation mode
Currently, the measurement group supports two operation modes. The table below shows how to determine the current mode for a measurement group.
mode | Integration_time | Integration_count |
---|---|---|
Timer | >0.0 | 0 |
Monitor | 0.0 | >0 |
Undef | 0.0 | 0 |
‘Undef’ means no valid values are defined in Integration_time and in Integration_count. You will not be able to execute the Start command in this mode.
The simplest way to know if a measurement group is acquiring data is to survey its state. If a measurement group is acquiring data its state will be MOVING. When the data acquisition is over, its state will change back to ON. The data acquisition is over when the measurement group detects that all channels finished acquisition (their state changed from MOVING to ON).The pool group interface allows clients interested in group state to use the Tango event system subscribing to measurement group state change event. As soon as a group starts acquiring data, its state is changed to MOVING and an event is sent. A new event will be sent when the data acquisition ends. Events will also be sent to each channel of the group when they start acquiring data and when they stop.
For each measurement group there is a set of key dynamic attributes
representing the value of each channel in the group. They are named
<channel_name >_Value. Special care has been taken on the management of these
attributes with distinct behavior depending on the type of channel the
attribute represents (Counter/Timer, 0D, 1D or 2D channel).
A Counter/Timer Value is represented by a scalar read-only double attribute. When the measurement group is not taking data, reading the counter/timer value will generate calls to the controller and therefore hardware access. When the group is taking data (master channel is moving), the value of a counter/timer is read every 100 miliseconds and stored in the Tango polling buffer. This means that a client reading the value of the channel while the group is moving will get the value from the Tango polling buffer and will not generate exra controller calls. It is also possible to get the value using the Tango event system. When the group is moving, an event is sent to the registered clients when the change event criteria is true. This is applicable for each Counter/Timer channel in the group. By default, this change event criterion is set to be an absolute difference in the value of 5.0. It is tunable by attribute using the classical “abs_change “property or the pool device basis using its defaultCtGrpVal_AbsChange property. Anyway, not more than 10 events could be sent by second. Once the data acquisition is over, the value is made unavailable from the Tango polling buffer and is read a last time. A forced change event is sent to clients using events.
A ZeroDExpChannel CumulatedValue is represented by a scalar read-only double attribute. Usually a ZeroDChannel represents the value of a single device (ex.: multimeter). Therefore, has hardware access cannot be optimized for a group of devices, reading the value on the measurement group device attribute has exactly the same behavior as reading it directly on the CumulatedValue attribute of the ZeroDChannel device (see XXX: Unknown inset LatexCommand ref{par:Reading-the-ZeroDExpChannel}: ).
To be filled in
To be filled in
Measurement group devices can often contain many channels. Client
applications often request channel values for the set (or subset) of
channels in a group. Read requests for these channel values through
the <channel_name >_Value attributes of a measurement group should be done by clients in
groups as often as possible. This can be achieved by using the client
Tango API call read_attributes on a DeviceProxy object. This will
ensure maximum performance by minimizing hardware access since the
measurement group can order channel value requests per controller thus
avoiding unecessary calls to the hardware.
Measurement group operation mode can be checked/set through the Integration_time and Integration_count (see XXX: Unknown inset LatexCommand ref{measurement group:Checking-operation-modes}: ). Setting the Integration_time to >0.0 will make the data acquisition (initiated by the invoking the Start command) finish when the channel defined in the Timer attribute reaches the value of Integration_time. Setting the Integration_count to >0 will make the data acquisition (initiated by the invoking the Start command) finish when the channel defined in the Monitor attribute reaches the value of Integration_count.
In either case, the measurement group will NOT assume that the master channel(timer/monitor) is able to stop all the other channels in the group, so it will force a Stop on these channels as soon as it detects that the master has finished. This is the case of the UnixTimer channel which itself has no knowledge of the channels involved and therefore is not able to stop them directly.
Integration_time, Integration_count, timer and monitor are memorized attributes. This means that the configuration values of these attributes are stored in the database. The next time the Pool starts the values are restored. This is done in order to reduce Pool configuration at startup to the minimum.
In order to allow pool client software to be entirely event based, some kind of polling has to be done on each channel to inform them on state change which are not related to data acquisition. To achieve this goal, one internally managed measurement group is created. Each pool channel (counter/timer, 0D, 1D or 2D experiment channel) is a member of this group. The Tango polling thread polls the state command of this group (Polling period tunable with the pool Ghostgroup_PollingPeriod property). The code of this group state command detects change in every channel state and send a state change event on the corresponding channel. This measurment group is not available to client and is even not defined in the Tango database. This is why it is called the ghost measurement group.
To be filled in
To be filled in
To be filled in
Two types of constraint are identified.
Simple constraint: This type of constraint is valid only for motor motion. It limits motor motion. This in not the limit switches which are a hardware protection. It’s a software limit. This type of constraint is managed by the min_value and max_value property of the motor Position Tango attribute. Tango core will refused to write the attribute (Position) if outside the limits set by these min_value and max_value attribute properties. These values are set on motor Position attribute in physical unit. Warning : The backlash has to be taken into account in the management of this limit. In order to finish the motion always coming from the same direction, sometimes the motor has to go a little bit after the wanted position and then returns to the desired position. The limit value has to take the backlash value into account. If the motor backlash attribute is modified, it will also change the Position limit value.
User constraint: This kind of constraint is given to the user to allow him to write constraint macros which will be executed to allow or disallow an action to be done on one object. In the pool case, the object is a writable attribute and the action is writing the attribute. Therefore, the following algorithm is used when writing an attribute with constraint:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /IF/ Simple constraint set
/IF/ New value outside limits
- Throw an exception
/ENDIF/
/ENDIF/
/IF/ Some user constraint associated to this attribute
/FOR/ All the user constraint
- Evaluate the constraint
/IF/ The constraint evaluates to False
- Throw an exception
/ENDIF/
/ENDFOR/
/ENDIF/
- Write the attribute
|
The first part of this algorithm is part of the Tango core. The second part will be coded in the Pool Tango classes and in a first phase will be available only for the Position attribute of the Motor class.
When the user creates a constraint, he has to provide to the pool the following information:
- The name of the object to which the constraint belongs. It is the name of the writable Tango attribute (actually only a motor position attribute.
A user constraint will be written using the Python language. It has to be a Python class with a constructor and a “Evaluate” method. This class has to inherit from a class called PoolConstraint. This will allow the pool software to dynamically discover that this class is a pool constraint. The class may define the depending attributes/devices. A depending attribute/device is an object used to evaluate if the constraint is true or false. The depending attributes have to be defined in a list called depending_attr_list . Each element in this list is a dictionnary with up to 2 elements which are the description of the depending attribute and eventually a default value. The depending devices have to be defined in a list called depending_dev_list which follow the same syntax than the depending_attr_list. A constraint may also have properties as defined in XXX: Unknown inset LatexCommand ref{par:Controller-properties}: . The constructor will receive three input arguments which are:
- A list with the depending attribute name
- A list with the depending device name
- A dictionnary (name:value) with the properties definition
One rule of the constructor is to build the connection with these Tango objects and to keep them in the instance. The Evaluate method will evaluate the constraint and will return true or false. It receives as input argument a list with the result of a read_attribute call executed on all the depending attributes.
Five pool device commands and two attribute allow the management of these constraints. The commands are CreateConstraint , DeleteConstraint , EvaluateContraint, GetConstraintClassInfo and GetConstraint . The attributes are called ConstraintList and ConstraintClassList . They are all detailed in chapters XXX: Unknown inset LatexCommand ref{sub:Device-pool-commands}: and XXX: Unknown inset LatexCommand ref{sub:Device-pool-attributes}: . The following is an example of a user constraint
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 1 import PyTango
2
3 class MyConstraint(PoolConstraint):
4
5 depending_attr_list = [{'DefaultValue':"first_mot/position",
6 'Description':"X position"},
7 {'DefaultValue':"second_mot/position",
8 'Description':"Z position"},
9 {'DefaultValue':"first_mot/velocity",
10 'Description':"X position speed"}]
11
11 depending_dev_list = [{'DefaultValue':"first_dev",
12 'Description':"Air pressure device"}]
13
14 inst_prop = {'MyProp':{'Type':PyTango.DevLong,'Description':'The psi constant',
15 'DefaultValue',1234}}
16
17 def __init__(self,attr_list,dev_list,prop_dict)
18 self.air_device = PyTango.DeviceProxy(dev_list[0])
19 self.const = prop_dict["MyProp"]
20
21 def Evaluate(self,att_value):
22 if att_value[0].value > (xxx * self.const)
23 return False
24 elif att_value[1].value > yyy
25 return False
26 elif att_value[2].value > zzz
27 return False
28 elif self.air_device.state() == PyTango.FAULT
29 return False
30 return True
|
Line 3 : The class inherits from the PoolConstraint class Line 5-10: Definition of the depending attributes Line 11-12: Definition of the depending devices Line 14-15: Definition of a constraint property Line 17-19: The constructor Line 21-30: The Evaluate method
XXX: Unknown inset LatexCommand label{sub:Archiving-motor-position}:
It is not possible to archive motor position using the Tango memorized attribute feature because Tango writes the attribute value into the database just after it has been set by the user. In case of motors which need some time to go to the desired value and which from time to time do not go exactly to the desired value (for always possible to have position which is a integer number of motor steps), it is more suited to store the motor position at the end of the motion. To achieve this, the pool has a command (called ArchieveMotorPosition ) which will store new motor positions into the database. This command will be polled by the classical Tango polling thread in order to execute it regularly. The algorithm used by this command is the following:
1 2 3 4 5 6 7 8 9 10 | - Read motors position for all motors which are not actually moving
- /FOR/ all motors
- /IF/ The new position just read is different than the old one
- Mark the motor as storable
- /ENDIF/
- /ENDFOR/
- Store in DB position of all storable motors
- Memorize motors position
|
In order to minimize the number of calls done on the Tango database, we need to add to the Tango database software the ability to store x properties of one attribute of y devices into the database in one call (or may be simply the same property of one attribute of several device).
To be filled in
To be filled in
The pool is implemented as a C++ Tango device server and therefore supports a set of commands/attributes. It has several attributes to get object (motor, pseudo-motor, controller) list. These lists are managed as attributes in order to have events on them when a new object (motor, controller...) is created/deleted.
XXX: Unknown inset LatexCommand label{sub:Device-pool-commands}:
On top of the three classical Tango commands (State, Status and Init), the pool device supports the commands summarized in the following table:
Device type | Name | Input data type | Output data type |
---|---|---|---|
related | InitController | Tango::DevString | void |
commands | ReloadControllerCode SendToController | Tango::DevString Tango::DevVarStringArray | void Tango::DevString |
Motor | CreateMotor | Tango::DevVarLongStringArray | void |
related commands | DeleteMotor | Tango::DevString | void |
Motor group | CreateMotorGroup | Tango::DevVarStringArray | void |
related commands | DeleteMotorGroup GetPseudoMotorInfo | Tango::DevString Tango::DevVarStringArray | void Tango::DevVarStringArray |
Pseudo motor | CreatePseudoMotor | Tango::DevVarStringArray | void |
related commands | DeletePseudoMotor ReloadPseudoMotorCode GetConstraintClassInfo CreateConstraint | Tango::DevString Tango::DevString Tango::DevString Tango::DevVarStringArray | void void Tango::DevVarStringArray void |
User Constraint | DeleteConstraint | Tango::DevString | void |
related | EvaluateConstraint | Tango::DevString | Tango::DevBoolean |
commands | GetConstraint ReloadConstraintCode | Tango::DevString Tango::DevString | Tango::DevVarLongArray void |
Experiment Channel | CreateExpChannel | Tango::DevVarStringArray | void |
related commands | DeleteExpChannel | Tango::DevString | void |
Measurement group | CreateMeasurementGroup | Tango::DevVarStringArray | void |
related commands | DeleteMeasurementGroup | Tango::DevString | void |
Dyn loaded Tango | LoadTangoClass | ||
class related | UnloadTangoClass | ||
commands | ReloadTangoClass | ||
Dyn. created | CreateXXX | ||
commands | DeleteXXX | ||
Miscellaneous | ArchiveMotorPosition | void | void |
CreateController : This command creates a controller object. It has four arguments (all strings) which are:
The controller device type: Actually three types are supported as device type. They are:
- “Motor” (case independent) for motor device
- “CounterTimer” (case independent) for counter timer device
- “ZeroDExpChannel” (case independent) for zero dimension experiment channel device
Controller code file name: For C++ controller, this is the name of the controller shared library file. For Python controller, this is the name of the controller module. This parameter is only a file name, not a path. The path is automatically taken from the pool device PooPath property. It is not necessary to change your LD_LIBRARY_PATH or PYTHONPATH environment variable. Everything is taken from the PoolPath property.
Controller class name: This is the name of the class implementing the controller. This class has to be implemented within the controller shared library or Python module passed as previous argument
Instance name: It is a string which allows the device pool to deal with several instance of the same controller class. The pool checks that this name is uniq within a control system.
The list of created controllers is kept in one of the pool device property and at next startup time, all controllers will be automatically re-created. If you have several pool device within a control system (the same TANGO_HOST), it is not possible to have two times the same controller defines on different pool device. Even if the full controller name is <Controller file name>.<Controller class name>/<Instance name>, each created controller has an associated name which is:
<Instance name>
which has to be used when the controller name is requested. This name is case independent.
DeleteController : This command has only one input argument which is the controller name (as defined previously). It is not possible to delete a controller with attached device(s). You first have to delete controller’s device(s).
InitController : This command is used to (re)-initialize a controller if the controller initialization done at pool startup time has failed. At startup time, the device pool creates controller devices even if the controller initialization has failed. All controller devices are set to the FAULT state. This command will try to re-create the controller object and if successful, send an “Init” command to every controller devices. Its input argument is the controller name.
GetControllerInfo : This command has three or four input parameters which are: XXX: Unknown inset LatexCommand label{ite:GetControllerInfo:}:
- The controller device type
- The controller code file name: For C++ controller, this is the name of the controller shared library file. For Python controller, this is the name of the controller module. This parameter is only a file name, not a path. The path is automatically taken from the pool device PooPath property.
- The controller class name: This is the name of the class implementing the controller. This class has to be implemented within the controller shared library or Python module passed as previous argument
- The controller instance name: This parameter is optional. If you do not specify it, the command will return information concerning controller properties as defined at the class level. If you defined it, the command will return information concerning controller properties for this specific controller instance.
It returns to the caller all the informations related to controller properties as defined in the controller code and/or in the Tango database. The following format is used to return these informations:
The string describing the controller (or an empty string if not defined)
Number of controller properties
For each property:
- The property name
- The property data type
- The property description
- The property default value (Empty string if not defined)
ReloadControllerCode : The controller code is contains in a shared library dynamically loaded or in a Python module. The aim of this command is to unlink the pool to the shared library and to reload it (or Reload the Python module). The command argument is a string which is the controller file name as defined for the CreateController command. For motor controller, it is not possible to do this command if one of the motor attached to controller(s) using the code within the file is actually moving. All motor(s) attached to every controller(s) using this file is switched to FAULT state during this command execution. Once the code is reloaded, an “Init” command is sent to every controller devices.
SendToController : Send data to a controller. The first element of the input argument array is the controller name. The second one is the string to be sent to the controller. This command returns the controller answer or an empty string is the controller does not have answer.
CreateMotor : This command creates a new motor. It has three arguments which are:
- The motor name (a string). This is a Tango device alias. It is not allowed to have ‘/’ character within this name. It is a case independent name.
- The motor controller name (a string)
- The axe number within the controller
The motor is created as a Tango device and automatically registered in the database. At next startup time, all motors will be automatically re-created. A Tango name is assigned to every motor. This name is a Tango device name (3 fields) and follow the syntax:
motor/controller_instance_name/axe_number
in lower case letters.
DeleteMotor : This command has only one argument which is the motor name as given in the first argument of the CreateMotor command. The device is automatically unregistered from the Tango database and is not accessible any more even for client already connected to it.
CreateMotorGroup : This command creates a new motor group. It has N arguments which are:
- The motor group name (a string). This is a Tango device alias. It is not allowed to have ‘/’ character within this name. It is a case independent name.
- The list of motor element of the group (motor name or another group name or pseudo-motor name)
The motor group is created as a Tango device and automatically registered in the database. At next startup time, all motor groups will be automatically re-created. A Tango name is assigned to every motor group. This name is a Tango device name (3 fields) and follow the syntax:
mg/ds_instance_name/motor_group_name
in lower case letters.
DeleteMotorGroup : This command has only one argument which is the motor group name as given in the first argument of the CreateMotorGroup command. The device is automatically unregistered from the Tango database and is not accessible any more even for client already connected to it. This command is not allowed if another motor group is using the motor group to be deleted.
GetPseudoMotorInfo : XXX: Unknown inset LatexCommand label{sub:GetPseudoMotorClassInfo}: : This command has one input argument (a string):
<module_name>.<class_name>
The command returns a list of strings representing the pseudo motor system information with the following meaning:
pseudo_info[0] - textual description of the pseudo motor class.
pseudo_info[1] - (=M) the number of motors required by this pseudo motor class.
pseudo_info[2] - (=N) the number of pseudo motors that the pseudo motor system aggregates.
pseudo_info[3] - the number of parameters required by the pseudo motor system.
pseudo_info[4..N+4] - the textual description of the roles of the N motors.
pseudo_info[N+5..N+M+5] - the textual description of the roles of the M pseudo motors.
pseudo_info[N+M+6..N+M+P+6] - the textual description of the P parameters.
example :
GetPseudoMotorInfo('PseudoLib.Slit')
could have as a return:
1 2 3 4 5 6 7 8 | ["A Slit system for controlling gap and offset pseudo motors.",
"2",
"2",
"0",
"Motor on blade 1",
"Motor on blade 2",
"Gap",
"Offset"]
|
CreatePseudoMotor :This command has a variable number of input arguments (all strings):
- the python file which contains the pseudo motor python code.
- the class name representing the pseudo motor system.
- the N pseudo motor names. These will be the pseudo motor alias for the corresponding pseudo motor tango devices.
- the M motor names. These names are the existing tango motor alias.
N and M must conform to the class name information. See XXX: Unknown inset LatexCommand ref{sub:GetPseudoMotorClassInfo}: to find how to get class information.
For each given pseudo motor name a Tango pseudo motor device is created and automatically registered in the database. At next startup time, all pseudo motors will be automatically re- created. A Tango name is assigned to every pseudo motor. This name is a Tango device name (3 fields) and follow the syntax:
pm/python_module_name.class_name/pseudo_motor_name
For each Tango pseudo motor device the device pool will also create a corresponding alias named pseudo_motor_name.
If a motor group Tango device with the given motor names doesn’t exist then the device pool will also create a motor group with the following name:
mg/tango_device_server_instance_name/_pm_<internal motor group number>
This motor group is built for internal Pool usage. It is not intended that the pseudo motor is accessed directly through this motor group. However, if needed elsewhere, it can be accessed as the usual motor group without any special restrictions.
example:
CreatePseudoMotor(‘PseudoLib.py’,’Slit’,’gap01’,’offset01’,’blade01’,’ blade02’)
DeletePseudoMotor : This command has only one argument which is the pseudo motor identifier. The device is automatically unregistered from the Tango database and is not accessible any more even for client already connected to it. This command is not allowed if a motor group is using the pseudo motor to be deleted.
ReloadPseudoMotorCode :The calculation code is contains in a dynamically loaded Python module. The aim of this command is to reload the Python module. The command argument is a string which is the python module as defined for the CreatePseudoMotor and GetPseudoMotorInfo commands. It is not possible to do this command if one of the motor attached to pseudo motor system(s) using code within the file is actually moving. All pseudo motor(s) using this file are switched to FAULT state during this command execution.
CreateExpChannel : This command creates a new experiment channel. It has three arguments which are:
- The experiment channel name (a string). This is a Tango device alias. It is not allowed to have ‘/’ character within this name. It is a case independent name.
- The experiment channel controller name (a string)
- The index number within the controller
The experiment channel is created as a Tango device and automatically registered in the database. At next startup time, all created experiment channels will be automatically re-created. A Tango name is assigned to every experiment channel. This name is a Tango device name (3 fields) and follow the syntax:
expchan/controller_instance_name/index_number
in lower case letters. The precise type of the experiment channel (Counter/Timer, ZeroD, OneD...) is retrieved by the pool device from the controller given as command second parameter.
DeleteExpChannel : This command has only one argument which is the experiment channel name as given in the first argument of the CreateExpChannel command. The device is automatically unregistered from the Tango database and is not accessible any more even for client already connected to it.
GetConstraintClassInfo : This command has one input parameter (a string) which is the constraint class name. It returns to the caller all the information related to constraint dependencies and to constraint properties as defined in the constraint code. The following format is used to return properties:
Depending attributes number
- Depending attribute name
- Depending attribute description
Depending devices number
- Depending device name
- Depending device description
Class property number
- Class property name
- Class property description
- Class property default value (Set to “NotDef” if not defined)
Instance property number
- Instance property name
- Instance property description
- Instance property default value (Set to “NotDef” if not defined)
CreateMeasurementGroup : This command creates a new measurement group. It has N arguments which are:
- The measurement group name (a string). This is a Tango device alias. It is not allowed to have ‘/’ character within this name. It is a case independent name.
- The list of channel elements of the group (Counter/Timer, 0D, 1D or 2D experiment channel)
The measurement group is created as a Tango device and automatically registered in the database. At next startup time, all measurement groups will be automatically re-created. A Tango name is assigned to every measurement group. This name is a Tango device name (3 fields) and follow the syntax:
mntgrp/ds_instance_name/measurement_group_name
in lower case letters.
DeleteMeasurementGroup : This command has only one argument which is the measurement group name as given in the first argument of the CreateMeasurementGroup command. The device is automatically unregistered from the Tango database and is not accessible any more even for client already connected to it.
AddConstraint : This command creates a user constraint object. It has several arguments (all strings) which are:
- Constraint code file name: The name of the constraint module. This parameter is only a file name, not a path. The path is automatically taken from the pool PooPath property.
- Constraint class name: This is the name of the class implementing the controller. This class has to be implemented within the controller shared library or Python module passed as previous argument
- Instance name: It is a string which allows the device pool to deal with several instance of the same controller class.
- The object to which the constraint belongs. It has to be a writable attribute name (actually only a motor position)
- The list of depending objects. (Variable length list which may be empty)
The list of created constraints is kept in one of the pool device property and at next startup time, all constraints will be automatically re-created. It is possible to create several constraint on the same object. They will be executed in the order of their creation. Each created constraint has a associated name which is:
<Constraint class name>/<Instance name>
- DeleteConstraint : This command has only one argument which is the constraint name as define previously.
- EvaluateConstraint : This command has only one argument which is the constraint name. It runs the “evaluate” method of the constraint and sends the return value to the caller
- GetConstraint : The input parameter of this command is the name of a Tango object. Actually, it has to be the name of one of the motor Position attribute. The command returns the list of Constraint ID attached to this object.
- ReloadConstraintCode : The constraint code is contains in a Python module. The aim of this command is to reload the Python module. The command argument is a string which is the constraint file name as defined for theAddConstraint command. All object(s) using this constraint are switched to FAULT state during this command execution.
- LoadTangoClass :
- UnloadTangoClass :
- ReloadTangoClass :
- CreateXXX :
- DeleteXXX:
- ArchiveMotorPosition : Send new motor(s) position to the database. This command will be polled with a default polling period of 10 seconds.
The classical Tango Init command destroys all constructed controller(s) and re-create them reloading their code. Then, it sends an “Init” command to every controlled objects (motor, pseudo-motor and motor group) belonging to the pool device. Motor(s) are switched to FAULT state when controller are destroyed.
The pool device knows only two states which are ON and ALARM. The pool device is in ALARM state if one of its controller failed during its initialization phase. It is in ON state when all controllers are correctly constructed. In case the pool device in in ALARM state, its status indicates which controller is faulty.
XXX: Unknown inset LatexCommand label{sub:Device-pool-attributes}:
The device pool supports the following attributes:
Name | Data type | Data format | Writable |
---|---|---|---|
ControllerList | Tango::DevString | Spectrum | R |
ControllerClassList | Tango::DevString | Spectrum | R |
MotorList | Tango::DevString | Spectrum | R |
MotorGroupList | Tango::DevString | Spectrum | R |
PseudoMotorList | Tango::DevString | Spectrum | R |
PseudoMotorClassList | Tango::DevString | Spectrum | R |
ExpChannelList | Tango::DevString | Spectrum | R |
MeasurementGroupList | Tango::DevString | Spectrum | R |
ConstraintList | Tango::DevString | Spectrum | R |
ConstraintClassList | Tango::DevString | Spectrum | R |
SimulationMode | Tango::DevBoolean | Scalar | R/W |
XXXList | Tango::DevString | Spectrum | R |
ControllerList : This is a read only spectrum string attribute. Each spectrum element is the name of one controller following the syntax:
<instance_name> - <Ctrl file>.<controller_class_name/instance_name> - <Device type> <Controller language> Ctrl (<Ctrl file>)
ControllerClassList : This is a read only spectrum string attribute. Each spectrum element is the name of one of the available controller class that the user can create. To build this list, the pool device server is using a property called PoolPath which defines the path where all files containing controller code should be (Python and C++ controllers). The syntax used for this PoolPath property is similar to the syntax used for Unix PATH environment variable (list of absolute path separated by the ”:” character). Each returned string has the following syntax:
Type: <Ctrl dev type> - Class: <Ctrl class name> - File: <Abs ctrl file path>
MotorList : This is a read only spectrum string attribute. Each spectrum element is the name of one motor known by this pool. The syntax is:
<Motor name> (<Motor tango name>)
MotorGroupList : This is a read only spectrum string attribute. Each spectrum element is the name of one motor group known by this pool. The syntax is:
<Motor group name> (<Motor group tango name>) Motor list: <List of group members> (<List of physical motors in the group>)
The last information is displayed only if the physical group structure differs from the logical one (pseudo-motor or other group used as group member)
PseudoMotorList :This is a read only spectrum string attribute. Each spectrum element is the name of one motor known by this pool. The syntax is:
<pseudo motor name> (<pseudo motor tango name>) Motor List: <motor name>1,...,<motor name>M
ExpChannelList : This is a read only spectrum string attribute. Each spectrum element is the name of one experiment channel known by this pool. The syntax is:
<Exp Channel name> (<Channel tango name>) <Experiment channel type>
The string describing the experiment channel type may be:
- Counter/Timer Experiment Channel
- Zero D Experiment Channel
MeasurementGroupList : This is a read only spectrum string attribute. Each spectrum element is the name of one measurement group known by the pool. The syntax is:
<Measurement group name> (<Measurement group tango name>) Experiment Channel list: <List of group members>
PseudoMotorClassList :This is a read only spectrum string attribute. Each spectrum element is the name of a valid Pseudo python system class. The syntax is:
<python module name>.<python class name>
. The python files to be found depend on the current value of the pool path. See XXX: Unknown inset LatexCommand ref{sub:PoolPath}:
ConstraintClassList : This is a read only spectrum string attribute. Each spectrum element is the name of one of the available constraint class that the user can create. To build this list, the pool device server is using a property called PoolPath which defines the path where all files containing constraint code should be. The syntax used for this property is similar to the syntax used for Unix PATH environment variable (list of absolute path separated by the ”:” character). To find constraint classes, the pool will look into all Python files (those with a .py suffix) for classes definition which inherit from a base class called PoolConstraint .
ConstraintList : This is a read only spectrum string attribute. each spectrum element is one of the constraint actually registered in the pool. The syntax of each string is:
<Constraint class name/instance name> - <associated to> - <depending on attribute(s) - <depending on device(s)>
SimulationMode : This is a read-write scalar boolean attribute. If set to true, all the pool device(s) are switched to Simulation mode. This means that all commands received by pool device(s) will not be forwarded to the associated controllers.
XXXList :
The pool device supports the following property:
Property name | Property data type | Default value |
---|---|---|
PoolPath | String | |
DefaultMotPos_AbsChange | Double | 5 |
DefaultMotGrpPos_AbsChange | Double | 5 |
DefaultCtVal_AbsChange | Double | 5 |
DefaultZeroDVal_AbsChange | Double | 5 |
DefaultCtGrpVal_AbsChange | Double | 5 |
DefaultZeroDGrpVal_AbsChange | Double | 5 |
GhostGroup_PollingPeriod | String | 5000 |
MotThreadLoop_SleepTime | Long | 10 |
NbStatePerRead | Long | 10 |
ZeroDNbReadPerEvent | Long | 5 |
- PoolPath : XXX: Unknown inset LatexCommand label{sub:PoolPath}: The path (same syntax than the Unix PATH environment variable) where the pool software is able to locate Controller software, Pseudo-motor software or Constraint software for both Python or C++ languages
- DefaultMotPos_AbsChange : The default value used to trigger change event when the position attribute is changing (the associated motor is moving). This property has a hard-coded default value set to 5
- DefaultMotGrpPos_AbsChange : The default value used to trigger change event when the group device position attribute is changing. This property has a hard-coded default value set to 5
- DefaultCtVal_AbsChange : The default value used to trigger change event when the counter/timer attribute is changing (the counter is counting or the timer is timing). This property has a hard-coded default value set to 5
- DefaultZeroDVal_AbsChange : The default value used to trigger change event when the Zero Dimension Experiment Channel is acquiring data. This property has a hard-coded default value set to 5
- DefaultCtGrpVal_AbsChange : The default value used to trigger change event when the counter/timer attribute(s) of a measurement group is(are) changing (the counter is counting or the timer is timing). This property has a hard-coded default value set to 5
- DefaultZeroDGrpVal_AbsChange : The default value used to trigger change event when the Zero Dimension Experiment Channel(s) of a measurement group is(are) acquiring data. This property has a hard-coded default value set to 5
- GhostGroup_PollingPeriod : The ghost motor/measurement group polling period in mS. This property has a default value of 5000 (5 sec)
- MotThreadLoop_SleepTime : The time (in mS) during which the motion thread will sleep between two consecutive motor state request. The default value is 10
- NbStatePerRead : The number of motor state request between each position attribute reading done by the motion thread. The default value is 10. This means that during a motion, the motor position is read by the thread every 100 mS (10 * 10)
- ZeroDNbReadPerEvent : The number of times the Zero D Experiment Channel value is read by the acquisition thread between firing a change event. The event will be effectively fired to the interested clients according to the CumulatedValue attribute “Absolute Change” property value.
- Controller : An internally managed property which allow the pool device to remember which controller has been created.
This chapter gives details on what has to be done to create device using the device pool in order to check the work to be done by a Sardana configuration tool.
The following is the action list which has to be done when you want to create a new motor:
Display the list of all the controller the pool already has.
Select one of this controller
If the user selects a new controller
- Read the attribute ControllerClassList to get the list of Controller installed in your system
- Select one of the controller class
- With the GetControllerInfo command, get the list of controller properties
- Give a controller instance name
- Display and eventually change the controller properties (if any)
- Create the controller object using the CreateController pool command
Give a motor name and a motor axis number in the selected controller
Create the motor with the CreateMotor pool command
Read the attribute list of the newly created motor
Display and eventually change the motor attributes related to motor features and eventually to extra-features
The following is the action list which has to be done when creating a motor group
- Give a name to the motor group
- Display the list of all registered motors (attribute MotorList), all registered motor groups (attribute MotorGroupList), all registered pseudo motors (attribute PseudoMotorList) and select those which have to be member of the group.
- Create the group (command CreateMotorGroup)
The following is the action list which has to be done when you want to create a new pseudo motor:
Display the list of all available pseudo motor system classes and select one of them
- if there is no proper pseudo system class write one in Python
- update the PoolPath Pool property if necessary
Get the selected pseudo motor system class information
Give names to the pseudo motors involved in the selected pseudo motor system
Create the motor(s) which are involved (if they have are not created yet: See XXX: Unknown inset LatexCommand ref{sub:Creating-motor}: ) and assign the coresponding roles
Create the pseudo motor system (command CreatePseudoMotor)
The following is the action list which has to be done when you want to create a new user constraint:
Display the list of all the constraint the pool already has.
Select one of this constraint
If the user selects a new constraint
Read the attribute ConstraintClassList to get the list of Constraint installed in your system
Select one of the constraint class
With the GetConstraintClassInfo command, get the list of constraint dependencies and properties
Give a constraint instance name
If it is the first constraint of this class
- Display and eventually change the constraint class properties (if any)
Display and eventually change the constraint depending attribute (if any)
Display and eventually change the constraint depending device (if any)
Display and eventually change the constraint instance properties (if any)
Create the constraint object using the CreateConstraint pool command
This chapter gives some details on some part of the pool implementation in order to clarify reader ideas
Moving a motor means writing its Position attribute. In Tango, it is already splitted in two actions which are:
- Call a Motor class method called “is_allowed”
- Call a Motor class method called “write_Position”
The second method will be executed only if the first one returns true. The move order is sent to the motor (via the controller) in the code of the second method.
The code implemented in this method follow the algorithm:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | - /IF/ There are any Pseudo Motor using the motor
- /FOR/ All these Pseudo Motors
- /IF/ They have some limits defined
- Compute new Pseudo Motor position if motor moved to the desired value
- /IF/ The computed value is outside the authorized window
- Return False
- /ENDIF/
- /ENDIF/
- /ENDFOR/
- /ENDIF/
- /IF/ There are some user constraint attached to the motor
- /FOR/ Each user constraint
- /IF/ The constraint has some depending attribute(s)
- Read these attributes
- /ENDIF/
- /IF/ If the execution of the contraint "Evaluate" method returns False
- Return False
- /ENDIF/
- /ENDFOR/
- /ENDIF/
- Return True
|
The code implemented in this method follows the algorithm:
1 2 3 4 5 6 7 | - Compute the dial position from the user position
- /IF/ A backlash is defined for this motor and the controller does not manage it
- Update motor desired position according to motion direction and backlash value
- /ENDIF/
- Start a thread sending it which motor has to move to which position
- Wait for thread acknowledge
- Return to caller
|
The motion thread will execute the following algorithm:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | - /FOR/ Each controller(s) implied in the motion
- Lock the controller object
- Call PreStartAll()
- /ENDFOR/
- /FOR/ Each motor(s) implied in the motion
- ret = PreStartOne(motor to move, new position)
- /IF/ ret is true
- Call StartOne(motor to move, new position)
- /ELSE/
- Inform write_Position that an error occurs
- Send acknowledge to write_Position method
- /ENDIF/
- /ENDFOR/
- /FOR/ Each motor(s) implied in the motion
- Set motor state to MOVING and send a Tango_ event to the requesting client
- /ENDFOR/
- /FOR/ Each controller(s) implied in the motion
- Call StartAll()
- Unlock the controller object
- /ENDFOR/
- Send acknowledge to the write_Position method
- /WHILE/ One of the motor state is MOVING (From controller)
- Sleep for 10 mS
- /IF/ One of the motor implied in the motion is not moving any more
- /IF/ This motor has backlash and the motion is in the "wrong" direction
- Ask for a backlash motion in the other direction
(Easy to write, not as easy to do...)
- /ENDIF/
- Send a Tango_ event on the state attribute to the requesting client
- Leave the loop
- /ENDIF/
- /IF/ it is time to read the motor position
- Read the motor position
- Send a change event on the Position attribute to the requested client if
the change event criterion is true
- /ENDIF/
- /ENDWHILE/
- Sleep for the time defined by the motor (group) Sleep_bef_last_read property
- Read the motor position
- Send a forced change event on the Position attribute to the requesting client
with the value set to the one just read
|
Data aquisition is triggered by invoking a Start command on the measurement group. The code implemented implements the following algorithm.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /IF/ in timer mode
- Write CumulationTime on all 0D channels with Integration_time value
/ELIF/ in monitor mode
- Write CumulationTime on all 0D channels with 0(zero) value
/ENDIF/
/FOR/ Each 0D channel implied in the data aquisition
- Load configuration
/END FOR/
- Start a CounterTimer thread with channels involved, master channel and the proper value to be set on it
- Wait for CounterTimer thread acknowledge
/FOR/ Each 0D channel implied in the data aquisition
- Send Start command
/END FOR/
- Return to caller
|
The Counter/Timer thread will execute the following algorithm:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | - Calculate the list of controllers involved and determine which controller has the master channel
/FOR/ Each channel(s) implied in the data aquisition
- Lock the channel object
/END FOR/
/FOR/ Each controller(s) implied in the data acquisition
- Lock the controller object
/END FOR/
/FOR/ Each channel(s) implied in the data acquisition
- Load configuration
/END FOR/
- Load the master channel - timer(monitor) - with the integration time(count)
/FOR/ Each controller(s) implied in the data acquisition
- Call PreStartAllCT()
/END FOR/
/FOR/ Each channel(s), except for the master channel, implied in the data acquisition,
- Call PreStartOneCT(channel)
- Call StartOneCT(channel)
/END FOR/
/FOR/ Each controller(s) implied in the data aquisition
- Call StartAllCT()
/END FOR/
- Call PreStartAllCT() on the controller which contains the master channel
- Call PreStartOneCT(master channel)
- Call StartOneCT(master channel)
- Call StartAllCT() on the controller which contains the master channel
/FOR/ Each controller(s) implied in the data aquisition
- Unlock the controller object
/END FOR/
/FOR/ Each channel(s) implied in the data aquisition
- Unlock the channel object
/END FOR/
- Send acknowledge to the Start method
/WHILE/ master channel state is MOVING (From controller)
- Sleep for 10 * sleepTime mS
/IF/ If master channel is not moving any more
- Stop all channels
- Send a Tango event on the state attribute to the requesting client
- Leave the loop
/ENDIF/
/IF/ it is time to read the channel values
- Read the channel values
- Send a change event on each value attribute to the requested client if
the change event criterion is true
/ENDIF/
/ENDWHILE/
- Read the channel values
- Send a forced change event on each value attribute to the requesting client
with the value set to the one just read
|
Macro Server¶
Todo
document this chapter
sardana
¶
This package provides the sardana library
Packages
pool
¶This is the main device pool module
Modules
controller
¶This module contains the definition of the Controller base classes
Constants
-
Type
= 'type'¶ Constant data type (to be used as a key in the definition of
axis_attributes
orctrl_attributes
)
-
Access
= 'r/w type'¶ Constant data access (to be used as a key in the definition of
axis_attributes
orctrl_attributes
)
-
Description
= 'description'¶ Constant description (to be used as a key in the definition of
axis_attributes
orctrl_attributes
)
-
DefaultValue
= 'defaultvalue'¶ Constant default value (to be used as a key in the definition of
axis_attributes
orctrl_attributes
)
-
FGet
= 'fget'¶ Constant for getter function (to be used as a key in the definition of
axis_attributes
orctrl_attributes
)
-
FSet
= 'fset'¶ Constant for setter function (to be used as a key in the definition of
axis_attributes
orctrl_attributes
)
-
Memorize
= 'memorized'¶ Constant memorize (to be used as a key in the definition of
axis_attributes
orctrl_attributes
) Possible values for this key areMemorized
,MemorizedNoInit
andNotMemorized
-
Memorized
= 'true'¶ Constant memorized (to be used as a value in the
Memorize
field definition inaxis_attributes
orctrl_attributes
)
-
MemorizedNoInit
= 'true_without_hard_applied'¶ Constant memorize but not write at initialization (to be used as a value in the
Memorize
field definition inaxis_attributes
orctrl_attributes
)
-
NotMemorized
= 'false'¶ Constant not memorize (to be used as a value in the
Memorize
field definition inaxis_attributes
orctrl_attributes
)
-
MaxDimSize
= 'maxdimsize'¶ Constant MaxDimSize (to be used as a key in the definition of
axis_attributes
orctrl_attributes
)
Interfaces
Classes
-
class
Readable
[source]¶ Bases:
object
A Readable interface. A controller for which it’s axis are ‘readable’ (like a motor, counter or 1D for example) should implement this interface
-
PreReadAll
()[source]¶ Controller API. Override if necessary. Called to prepare a read of the value of all axis. Default implementation does nothing.
-
PreReadOne
(axis)[source]¶ Controller API. Override if necessary. Called to prepare a read of the value of a single axis. Default implementation does nothing.
Parameters: axis (int) – axis number
-
ReadAll
()[source]¶ Controller API. Override if necessary. Called to read the value of all selected axis Default implementation does nothing.
-
ReadOne
(axis)[source]¶ Controller API. Override is MANDATORY! Default implementation raises
NotImplementedError
Parameters: axis (int) – axis number Returns: the axis value Return type: object
-
-
class
Startable
[source]¶ Bases:
object
A Startable interface. A controller for which it’s axis are ‘startable’ (like a motor, for example) should implement this interface
-
PreStartAll
()[source]¶ Controller API. Override if necessary. Called to prepare a start of all axis (whatever pre-start means). Default implementation does nothing.
-
PreStartOne
(axis, value)[source]¶ Controller API. Override if necessary. Called to prepare a start of the given axis (whatever pre-start means). Default implementation returns True.
Parameters: Returns: True means a successfull pre-start or False for a failure
Return type:
-
StartOne
(axis, value)[source]¶ Controller API. Override if necessary. Called to do a start of the given axis (whatever start means). Default implementation raises
NotImplementedError
Parameters:
-
-
class
Stopable
[source]¶ Bases:
object
A Stopable interface. A controller for which it’s axis are ‘stoppable’ (like a motor, for example) should implement this interface
-
AbortOne
(axis)[source]¶ Controller API. Override is MANDATORY! Default implementation raises
NotImplementedError
. Aborts one of the axisParameters: axis (int) – axis number
-
AbortAll
()[source]¶ Controller API. Override if necessary. Aborts all active axis of this controller. Default implementation calls
AbortOne()
on each active axis.New in version 1.0.
-
-
class
Loadable
[source]¶ Bases:
object
A Loadable interface. A controller for which it’s axis are ‘loadable’ (like a counter, 1D or 2D for example) should implement this interface
-
PreLoadAll
()[source]¶ Controller API. Override if necessary. Called to prepare loading the integration time / monitor value. Default implementation does nothing.
-
PreLoadOne
(axis, value)[source]¶ Controller API. Override if necessary. Called to prepare loading the master channel axis with the integration time / monitor value. Default implementation returns True.
Parameters: Returns: True means a successfull PreLoadOne or False for a failure
Return type:
-
LoadAll
()[source]¶ Controller API. Override if necessary. Called to load the integration time / monitor value. Default implementation does nothing.
-
LoadOne
(axis, value)[source]¶ Controller API. Override is MANDATORY! Called to load the integration time / monitor value. Default implementation raises
NotImplementedError
.Parameters:
-
-
class
Controller
(inst, props, *args, **kwargs)[source]¶ Bases:
object
Base controller class. Do NOT inherit from this class directly
Parameters: -
class_prop
= {}¶ Deprecated since version 1.0: use
ctrl_properties
instead
-
ctrl_extra_attributes
= {}¶ Deprecated since version 1.0: use
axis_attributes
instead
-
ctrl_properties
= {}¶ A
dict
containing controller properties where:key : (
str
) controller property namevalue :
dict
with with threestr
keys (“type”, “description” and “defaultvalue” case insensitive):- for
Type
, value is one of the values described in Data Type definition - for
Description
, value is astr
description of the property. if is not given it defaults to empty string. - for
DefaultValue
, value is a python object or None if no default value exists for the property.
- for
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13
from sardana.pool.controller import MotorController, \ Type, Description, DefaultValue class MyCtrl(MotorController): ctrl_properties = \ { 'host' : { Type : str, Description : "host name" }, 'port' : { Type : int, Description : "port number", DefaultValue: 5000 } }
-
ctrl_attributes
= {}¶ A
dict
containning controller extra attributes where:key : (
str
) controller attribute namevalue :
dict
withstr
possible keys: “type”, “r/w type”, “description”, “fget”, “fset” and “maxdimsize” (case insensitive):for
Type
, value is one of the values described in Data Type definitionfor
Access
, value is one ofDataAccess
(“read” or “read_write” (case insensitive) strings are also accepted) [default: ReadWrite]for
Description
, value is astr
description of the attribute [default: “” (empty string)]for
FGet
, value is astr
with the method name for the attribute getter [default: “get”<controller attribute name>]for
FSet
, value is astr
with the method name for the attribute setter. [default, ifAccess
= “read_write”: “set”<controller attribute name>]for
DefaultValue
, value is a python object or None if no default value exists for the attribute. If given, the attribute is set when the controller is first created.for
Memorize
, value is astr
with possible values:Memorized
,MemorizedNoInit
andNotMemorized
[default:Memorized
]New in version 1.1.
- for
MaxDimSize
, value is atuple
with possible values: - for scalar must be an empty tuple ( () or [] ) [default: ()]
- for 1D arrays a sequence with one value (example: (1024,)) [default: (2048,)]
- for 1D arrays a sequence with two values (example: (1024, 1024)) [default: (2048, 2048)]
New in version 1.1.
- for
New in version 1.0.
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
from sardana.pool.controller import PseudoMotorController, \ Type, Description, DefaultValue, DataAccess class HKLCtrl(PseudoMotorController): ctrl_attributes = \ { 'ReflectionMatrix' : { Type : ( (float,), ), Description : "The reflection matrix", Access : DataAccess.ReadOnly, FGet : 'getReflectionMatrix', }, } def getReflectionMatrix(self): return ( (1.0, 0.0), (0.0, 1.0) )
-
axis_attributes
= {}¶ A
dict
containning controller extra attributes for each axis where:key : (
str
) axis attribute namevalue :
dict
with threestr
keys (“type”, “r/w type”, “description” case insensitive):for
Type
, value is one of the values described in Data Type definitionfor
Access
, value is one ofDataAccess
(“read” or “read_write” (case insensitive) strings are also accepted)for
Description
, value is astr
description of the attributefor
DefaultValue
, value is a python object or None if no default value exists for the attribute. If given, the attribute is set when the axis is first created.for
Memorize
, value is astr
with possible values:Memorized
,MemorizedNoInit
andNotMemorized
[default:Memorized
]New in version 1.1.
- for
MaxDimSize
, value is atuple
with possible values: - for scalar must be an empty tuple ( () or [] ) [default: ()]
- for 1D arrays a sequence with one value (example: (1024,)) [default: (2048,)]
- for 1D arrays a sequence with two values (example: (1024, 1024)) [default: (2048, 2048)]
New in version 1.1.
- for
New in version 1.0.
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
from sardana.pool.controller import MotorController, \ Type, Description, DefaultValue, DataAccess class MyMCtrl(MotorController): axis_attributes = \ { 'EncoderSource' : { Type : str, Description : 'motor encoder source', }, } def getAxisPar(self, axis, name): name = name.lower() if name == 'encodersource': return self._encodersource[axis] def setAxisPar(self, axis, name, value): name = name.lower() if name == 'encodersource': self._encodersource[axis] = value
-
standard_axis_attributes
= {}¶ A
dict
containing the standard attributes present on each axis device
-
_findAPIVersion
()[source]¶ Internal. By default return the Pool Controller API version of the pool where the controller is running
-
AddDevice
(axis)[source]¶ Controller API. Override if necessary. Default implementation does nothing.
Parameters: axis (int) – axis number
-
DeleteDevice
(axis)[source]¶ Controller API. Override if necessary. Default implementation does nothing.
Parameters: axis (int) – axis number
-
inst_name
¶ Controller API. The controller instance name.
Deprecated since version 1.0: use
GetName()
instead
-
GetName
()[source]¶ Controller API. The controller instance name.
Returns: the controller instance name Return type: str New in version 1.0.
-
GetAxisName
(axis)[source]¶ Controller API. The axis name.
Returns: the axis name Return type: str New in version 1.0.
-
PreStateAll
()[source]¶ Controller API. Override if necessary. Called to prepare a read of the state of all axis. Default implementation does nothing.
-
PreStateOne
(axis)[source]¶ Controller API. Override if necessary. Called to prepare a read of the state of a single axis. Default implementation does nothing.
Parameters: axis (int) – axis number
-
StateAll
()[source]¶ Controller API. Override if necessary. Called to read the state of all selected axis. Default implementation does nothing.
-
StateOne
(axis)[source]¶ Controller API. Override is MANDATORY. Called to read the state of one axis. Default implementation raises
NotImplementedError
.
-
SetCtrlPar
(parameter, value)[source]¶ Controller API. Override if necessary. Called to set a parameter with a value. Default implementation sets this object member named ‘_’+parameter with the given value.
New in version 1.0.
-
GetCtrlPar
(parameter)[source]¶ Controller API. Override if necessary. Called to set a parameter with a value. Default implementation returns the value contained in this object’s member named ‘_’+parameter.
New in version 1.0.
-
SetAxisPar
(axis, parameter, value)[source]¶ Controller API. Override is MANDATORY. Called to set a parameter with a value on the given axis. Default implementation calls deprecated
SetPar()
which, by default, raisesNotImplementedError
.New in version 1.0.
-
GetAxisPar
(axis, parameter)[source]¶ Controller API. Override is MANDATORY. Called to get a parameter value on the given axis. Default implementation calls deprecated
GetPar()
which, by default, raisesNotImplementedError
.New in version 1.0.
-
SetAxisExtraPar
(axis, parameter, value)[source]¶ Controller API. Override if necessary. Called to set a parameter with a value on the given axis. Default implementation calls deprecated
SetExtraAttributePar()
which, by default, raisesNotImplementedError
.New in version 1.0.
-
GetAxisExtraPar
(axis, parameter)[source]¶ Controller API. Override if necessary. Called to get a parameter value on the given axis. Default implementation calls deprecated
GetExtraAttributePar()
which, by default, raisesNotImplementedError
.New in version 1.0.
-
SetPar
(axis, parameter, value)[source]¶ Controller API. Called to set a parameter with a value on the given axis. Default implementation raises
NotImplementedError
.Deprecated since version 1.0: use
SetAxisPar()
instead
-
GetPar
(axis, parameter)[source]¶ Controller API. Called to get a parameter value on the given axis. Default implementation raises
NotImplementedError
.Deprecated since version 1.0: use
GetAxisPar()
instead
-
SetExtraAttributePar
(axis, parameter, value)[source]¶ Controller API. Called to set a parameter with a value on the given axis. Default implementation raises
NotImplementedError
.Deprecated since version 1.0: use
SetAxisExtraPar()
instead
-
GetExtraAttributePar
(axis, parameter)[source]¶ Controller API. Called to get a parameter value on the given axis. Default implementation raises
NotImplementedError
.Deprecated since version 1.0: use
GetAxisExtraPar()
instead
-
GetAxisAttributes
(axis)[source]¶ Controller API. Override if necessary. Returns a dictionary of all attributes per axis. Default implementation returns a new
dict
with the standard attributes plus theaxis_attributes
Parameters: axis (int) – axis number Returns: a dict containing attribute information as defined in axis_attributes
New in version 1.0.
-
SendToCtrl
(stream)[source]¶ Controller API. Override if necessary. Sends a string to the controller. Default implementation raises
NotImplementedError
.Parameters: stream (str) – stream to be sent Returns: any relevant information e.g. response of the controller Return type: str
-
-
class
PseudoController
(inst, props, *args, **kwargs)[source]¶ Bases:
sardana.pool.controller.Controller
Base class for all pseudo controllers.
-
class
MotorController
(inst, props, *args, **kwargs)[source]¶ Bases:
sardana.pool.controller.Controller
,sardana.pool.controller.Startable
,sardana.pool.controller.Stopable
,sardana.pool.controller.Readable
Base class for a motor controller. Inherit from this class to implement your own motor controller for the device pool.
A motor controller should support these axis parameters:
- acceleration
- deceleration
- velocity
- base_rate
- step_per_unit
These parameters are configured through the
GetAxisPar()
/SetAxisPar()
API (in version <1.0 the methods were calledGetPar()
/SetPar()
. DefaultGetAxisPar()
andSetAxisPar()
still callGetPar()
andSetPar()
respectively in order to maintain backward compatibility).-
NoLimitSwitch
= 0¶ A constant representing no active switch.
-
HomeLimitSwitch
= 1¶ A constant representing an active home switch. You can OR two or more switches together. For example, to say both upper and lower limit switches are active:
limit_switches = self.HomeLimitSwitch | self.LowerLimitSwitch
-
UpperLimitSwitch
= 2¶ A constant representing an active upper limit switch. You can OR two or more switches together. For example, to say both upper and lower limit switches are active:
limit_switches = self.UpperLimitSwitch | self.LowerLimitSwitch
-
LowerLimitSwitch
= 4¶ A constant representing an active lower limit switch. You can OR two or more switches together. For example, to say both upper and lower limit switches are active:
limit_switches = self.UpperLimitSwitch | self.LowerLimitSwitch
-
standard_axis_attributes
= {'Acceleration': {'type': <type 'float'>, 'description': 'Acceleration time (s)'}, 'DialPosition': {'type': <type 'float'>, 'description': 'Dial Position'}, 'Velocity': {'type': <type 'float'>, 'description': 'Velocity'}, 'Position': {'type': <type 'float'>, 'description': 'Position'}, 'Step_per_unit': {'type': <type 'float'>, 'description': 'Steps per unit'}, 'Base_rate': {'type': <type 'float'>, 'description': 'Base rate'}, 'Sign': {'type': <type 'float'>, 'description': 'Sign'}, 'Limit_switches': {'type': (<type 'bool'>,), 'description': "This attribute is the motor limit switches state. It's an array with 3 \nelements which are:\n0 - The home switch\n1 - The upper limit switch\n2 - The lower limit switch\nFalse means not active. True means active"}, 'Deceleration': {'type': <type 'float'>, 'description': 'Deceleration time (s)'}, 'Offset': {'type': <type 'float'>, 'description': 'Offset'}, 'Backlash': {'type': <type 'float'>, 'description': 'Backlash'}}¶ A
dict
containing the standard attributes present on each axis device
-
GetAxisAttributes
(axis)[source]¶ Motor Controller API. Override if necessary. Returns a sequence of all attributes per axis. Default implementation returns a
dict
containning:- Position
- DialPosition
- Offset
- Sign
- Step_per_unit
- Acceleration
- Deceleration
- Base_rate
- Velocity
- Backlash
- Limit_switches
plus all attributes contained in
axis_attributes
Note
Normally you don’t need to Override this method. You just implement the class member
axis_attributes
. Typically, you will need to Override this method in two cases:- certain axes contain a different set of extra attributes
which cannot be simply defined in
axis_attributes
- some axes (or all) don’t implement a set of standard moveable parameters (ex.: if a motor controller is created to control a power supply, it may have a position (current) and a velocity (ramp speed) but it may not have acceleration)
Parameters: axis (int) – axis number Returns: a dict containing attribute information as defined in axis_attributes
New in version 1.0.
-
class
PseudoMotorController
(inst, props, *args, **kwargs)[source]¶ Bases:
sardana.pool.controller.PseudoController
Base class for a pseudo motor controller. Inherit from this class to implement your own pseudo motor controller for the device pool.
Every Pseudo Motor implementation must be a subclass of this class. Current procedure for a correct implementation of a Pseudo Motor class:
- mandatory:
- define the class level attributes
pseudo_motor_roles
,motor_roles
- write
CalcPseudo()
method - write
CalcPhysical()
method.
- define the class level attributes
- optional:
- write
CalcAllPseudo()
andCalcAllPhysical()
if great performance gain can be achived
- write
-
pseudo_motor_roles
= ()¶ a sequence of strings describing the role of each pseudo motor axis in this controller
-
motor_roles
= ()¶ a sequence of strings describing the role of each motor in this controller
-
standard_axis_attributes
= {'Position': {'type': <type 'float'>, 'description': 'Position'}}¶ A
dict
containing the standard attributes present on each axis device
-
CalcAllPseudo
(physical_pos, curr_pseudo_pos)[source]¶ Pseudo Motor Controller API. Override if necessary. Calculates the positions of all pseudo motors that belong to the pseudo motor system from the positions of the physical motors. Default implementation does a loop calling
PseudoMotorController.calc_pseudo()
for each pseudo motor role.Parameters: - physical_pos (sequence<float>) – a sequence containing physical motor positions
- curr_pseudo_pos (sequence<float>) – a sequence containing the current pseudo motor positions
Returns: a sequece of pseudo motor positions (one for each pseudo motor role)
Return type: sequence<float>
New in version 1.0.
-
CalcAllPhysical
(pseudo_pos, curr_physical_pos)[source]¶ Pseudo Motor Controller API. Override if necessary. Calculates the positions of all motors that belong to the pseudo motor system from the positions of the pseudo motors. Default implementation does a loop calling
PseudoMotorController.calc_physical()
for each motor role.Parameters: - pseudo_pos (sequence<float>) – a sequence containing pseudo motor positions
- curr_physical_pos (sequence<float>) – a sequence containing the current physical motor positions
Returns: a sequece of motor positions (one for each motor role)
Return type: sequence<float>
New in version 1.0.
-
CalcPseudo
(axis, physical_pos, curr_pseudo_pos)[source]¶ Pseudo Motor Controller API. Override is MANDATORY. Calculate pseudo motor position given the physical motor positions
Parameters: - axis (int) – the pseudo motor role axis
- physical_pos (sequence<float>) – a sequence containing motor positions
- curr_pseudo_pos (sequence<float>) – a sequence containing the current pseudo motor positions
Returns: a pseudo motor position corresponding to the given axis pseudo motor role
Return type: New in version 1.0.
-
CalcPhysical
(axis, pseudo_pos, curr_physical_pos)[source]¶ Pseudo Motor Controller API. Override is MANDATORY. Calculate physical motor position given the pseudo motor positions.
Parameters: - axis (int) – the motor role axis
- pseudo_pos (sequence<float>) – a sequence containing pseudo motor positions
- curr_physical_pos (sequence<float>) – a sequence containing the current physical motor positions
Returns: a motor position corresponding to the given axis motor role
Return type: New in version 1.0.
-
calc_all_pseudo
(physical_pos)[source]¶ Pseudo Motor Controller API. Override if necessary. Calculates the positions of all pseudo motors that belong to the pseudo motor system from the positions of the physical motors. Default implementation does a loop calling
PseudoMotorController.calc_pseudo()
for each pseudo motor role.Parameters: physical_pos (sequence<float>) – a sequence of physical motor positions Returns: a sequece of pseudo motor positions (one for each pseudo motor role) Return type: sequence<float> Deprecated since version 1.0: implement
CalcAllPseudo()
instead
-
calc_all_physical
(pseudo_pos)[source]¶ Pseudo Motor Controller API. Override if necessary. Calculates the positions of all motors that belong to the pseudo motor system from the positions of the pseudo motors. Default implementation does a loop calling
PseudoMotorController.calc_physical()
for each motor role.Parameters: pseudo_pos (sequence<float>) – a sequence of pseudo motor positions Returns: a sequece of motor positions (one for each motor role) Return type: sequence<float> Deprecated since version 1.0: implement
CalcAllPhysical()
instead
-
calc_pseudo
(axis, physical_pos)[source]¶ Pseudo Motor Controller API. Override is MANDATORY. Calculate pseudo motor position given the physical motor positions
Parameters: - axis (int) – the pseudo motor role axis
- physical_pos (sequence<float>) – a sequence of motor positions
Returns: a pseudo motor position corresponding to the given axis pseudo motor role
Return type: Deprecated since version 1.0: implement
CalcPseudo()
instead
-
calc_physical
(axis, pseudo_pos)[source]¶ Pseudo Motor Controller API. Override is MANDATORY. Calculate physical motor position given the pseudo motor positions.
Parameters: - axis (int) – the motor role axis
- pseudo_pos (sequence<float>) – a sequence of pseudo motor positions
Returns: a motor position corresponding to the given axis motor role
Return type: Deprecated since version 1.0: implement
CalcPhysical()
instead
-
GetMotor
(index_or_role)[source]¶ Returns the motor for a given role/index.
Warning
- Use with care: Executing motor methods can be dangerous!
- Since the controller is built before any element (including motors), this method will FAIL when called from the controller constructor
Parameters: index_or_role (int or str) – index number or role name Returns: Motor object for the given role/index Return type: PoolMotor
-
GetPseudoMotor
(index_or_role)[source]¶ Returns the pseudo motor for a given role/index.
Warning
- Use with care: Executing pseudo motor methods can be dangerous!
- Since the controller is built before any element (including pseudo motors), this method will FAIL when called from the controller constructor
Parameters: index_or_role (int or str) – index number or role name Returns: PseudoMotor object for the given role/index Return type: PoolPseudoMotor
-
class
CounterTimerController
(inst, props, *args, **kwargs)[source]¶ Bases:
sardana.pool.controller.Controller
,sardana.pool.controller.Readable
,sardana.pool.controller.Startable
,sardana.pool.controller.Stopable
,sardana.pool.controller.Loadable
Base class for a counter/timer controller. Inherit from this class to implement your own counter/timer controller for the device pool.
A counter timer controller should support these controller parameters:
- timer
- monitor
- trigger_type
-
standard_axis_attributes
= {'Value': {'type': <type 'float'>, 'description': 'Value'}}¶ A
dict
containing the standard attributes present on each axis device
-
PreStartAllCT
()[source]¶ Counter/Timer Controller API. Override if necessary. Called to prepare an acquisition of all selected axis. Default implementation does nothing.
Deprecated since version 1.0: use
PreStartAll()
instead
-
PreStartOneCT
(axis)[source]¶ Counter/Timer Controller API. Override if necessary. Called to prepare an acquisition a single axis. Default implementation returns True.
Parameters: axis (int) – axis number Returns: True means a successfull PreStartOneCT or False for a failure Return type: bool Deprecated since version 1.0: use
PreStartOne()
instead
-
StartOneCT
(axis)[source]¶ Counter/Timer Controller API. Override if necessary. Called to start an acquisition of a selected axis. Default implementation does nothing.
Parameters: axis (int) – axis number Deprecated since version 1.0: use
StartOne()
instead
-
StartAllCT
()[source]¶ Counter/Timer Controller API. Override is MANDATORY! Called to start an acquisition of a selected axis. Default implementation raises
NotImplementedError
.Deprecated since version 1.0: use
StartAll()
instead
-
PreStartAll
()[source]¶ Controller API. Override if necessary. Called to prepare a write of the position of all axis. Default implementation calls deprecated
PreStartAllCT()
which, by default, does nothing.New in version 1.0.
-
PreStartOne
(axis, value=None)[source]¶ Controller API. Override if necessary. Called to prepare a write of the position of a single axis. Default implementation calls deprecated
PreStartOneCT()
which, by default, returns True.Parameters: Returns: True means a successfull pre-start or False for a failure
Return type: New in version 1.0.
-
StartOne
(axis, value=None)[source]¶ Controller API. Override if necessary. Called to write the position of a selected axis. Default implementation calls deprecated
StartOneCT()
which, by default, does nothing.Parameters:
-
StartAll
()[source]¶ Controller API. Override is MANDATORY! Default implementation calls deprecated
StartAllCT()
which, by default, raisesNotImplementedError
.
-
class
ZeroDController
(inst, props, *args, **kwargs)[source]¶ Bases:
sardana.pool.controller.Controller
,sardana.pool.controller.Readable
,sardana.pool.controller.Stopable
Base class for a 0D controller. Inherit from this class to implement your own 0D controller for the device pool.
-
class
OneDController
(inst, props, *args, **kwargs)[source]¶ Bases:
sardana.pool.controller.Controller
,sardana.pool.controller.Readable
,sardana.pool.controller.Startable
,sardana.pool.controller.Stopable
,sardana.pool.controller.Loadable
Base class for a 1D controller. Inherit from this class to implement your own 1D controller for the device pool.
New in version 1.2.
-
standard_axis_attributes
= {'Value': {'maxdimsize': (16384,), 'type': (<type 'float'>,), 'description': 'Value'}}¶
-
GetAxisPar
(axis, parameter)[source]¶ Controller API. Override is MANDATORY. Called to get a parameter value on the given axis. If parameter == ‘data_source’, default implementation returns None, meaning let sardana decide the proper URI for accessing the axis value. Otherwise, default implementation calls deprecated
GetPar()
which, by default, raisesNotImplementedError
.New in version 1.2.
-
-
class
TwoDController
(inst, props, *args, **kwargs)[source]¶ Bases:
sardana.pool.controller.Controller
,sardana.pool.controller.Readable
,sardana.pool.controller.Startable
,sardana.pool.controller.Stopable
,sardana.pool.controller.Loadable
Base class for a 2D controller. Inherit from this class to implement your own 2D controller for the device pool.
-
standard_axis_attributes
= {'Value': {'maxdimsize': (4096, 4096), 'type': ((<type 'float'>,),), 'description': 'Value'}}¶
-
GetAxisPar
(axis, parameter)[source]¶ Controller API. Override is MANDATORY. Called to get a parameter value on the given axis. If parameter == ‘data_source’, default implementation returns None, meaning let sardana decide the proper URI for accessing the axis value. Otherwise, default implementation calls deprecated
GetPar()
which, by default, raisesNotImplementedError
.New in version 1.2.
-
-
class
PseudoCounterController
(inst, props, *args, **kwargs)[source]¶ Bases:
sardana.pool.controller.Controller
Base class for a pseudo counter controller. Inherit from this class to implement your own pseudo counter controller for the device pool.
Every Pseudo Counter implementation must be a subclass of this class. Current procedure for a correct implementation of a Pseudo Counter class:
- mandatory:
- define the class level attributes
counter_roles
, - write
Calc()
method
- define the class level attributes
-
pseudo_counter_roles
= ()¶ a sequence of strings describing the role of each pseudo counter axis in this controller
-
counter_roles
= ()¶ a sequence of strings describing the role of each counter in this controller
-
standard_axis_attributes
= {'Value': {'type': <type 'float'>, 'description': 'Value'}}¶ A
dict
containing the standard attributes present on each axis device
-
Calc
(axis, values)[source]¶ Pseudo Counter Controller API. Override is MANDATORY. Calculate pseudo counter position given the counter values.
Parameters: - axis (int) – the pseudo counter role axis
- values (sequence<float>) – a sequence containing current values of underlying elements
Returns: a pseudo counter value corresponding to the given axis pseudo counter role
Return type: New in version 1.0.
-
calc
(axis, values)[source]¶ Pseudo Counter Controller API. Override is MANDATORY. Calculate pseudo counter value given the counter values.
Parameters: - axis (int) – the pseudo counter role axis
- values (sequence<float>) – a sequence containing current values of underlying elements
Returns: a pseudo counter value corresponding to the given axis pseudo counter role
Return type: Deprecated since version 1.0: implement
Calc()
instead
-
CalcAll
(values)[source]¶ Pseudo Counter Controller API. Override if necessary. Calculates all pseudo counter values from the values of counters. Default implementation does a loop calling
PseudoCounterController.Calc()
for each pseudo counter role.Parameters: values (sequence<float>) – a sequence containing current values of underlying elements Returns: a sequece of pseudo counter values (one for each pseudo counter role) Return type: sequence<float> New in version 1.2.
-
class
IORegisterController
(inst, props, *args, **kwargs)[source]¶ Bases:
sardana.pool.controller.Controller
,sardana.pool.controller.Readable
Base class for a IORegister controller. Inherit from this class to implement your own IORegister controller for the device pool.
-
predefined_values
= ()¶ Deprecated since version 1.0: use
axis_attributes
instead
-
pool
¶This module contains the main pool class
Functions
|
Classes
-
class
Pool
(full_name, name=None)[source]¶ Bases:
sardana.pool.poolcontainer.PoolContainer
,sardana.pool.poolobject.PoolObject
,sardana.sardanamanager.SardanaElementManager
,sardana.sardanamanager.SardanaIDManager
The central pool class.
-
Default_MotionLoop_StatesPerPosition
= 10¶ Default value representing the number of state reads per position read during a motion loop
-
Default_MotionLoop_SleepTime
= 0.01¶ Default value representing the sleep time for each motion loop
-
Default_AcqLoop_StatesPerValue
= 10¶ Default value representing the number of state reads per value read during a motion loop
-
Default_AcqLoop_SleepTime
= 0.01¶ Default value representing the sleep time for each acquisition loop
-
Default_DriftCorrection
= True¶
-
init_remote_logging
(host=None, port=None)[source]¶ Initializes remote logging.
Parameters: - host (str) – host name [default: None, meaning use the machine host name
as returned by
socket.gethostname()
]. - port – port number [default: None, meaning use
logging.handlers.DEFAULT_TCP_LOGGING_PORT
- host (str) – host name [default: None, meaning use the machine host name
as returned by
-
motion_loop_sleep_time
¶ motion sleep time (s)
-
motion_loop_states_per_position
¶ Number of State reads done before doing a position read in the motion loop
-
acq_loop_sleep_time
¶ acquisition sleep time (s)
-
acq_loop_states_per_value
¶ Number of State reads done before doing a value read in the acquisition loop
-
drift_correction
¶ drift correction
-
monitor
¶
-
ctrl_manager
¶
-
poolacquisition
¶This module is part of the Python Pool libray. It defines the class for an acquisition
Classes
-
class
PoolCTAcquisition
(main_element, name='CTAcquisition', slaves=None)[source]¶ Bases:
sardana.pool.poolaction.PoolAction
-
start_action
(*args, **kwargs)[source]¶ Prepares everything for acquisition and starts it.
Param: config
-
in_acquisition
(states)[source]¶ Determines if we are in acquisition or if the acquisition has ended based on the current unit trigger modes and states returned by the controller(s)
Parameters: states (dict<PoolElement, State>) – a map containing state information as returned by read_state_info Returns: returns True if in acquisition or False otherwise Return type: bool
-
poolaction
¶This module is part of the Python Pool libray. It defines the class for an abstract action over a set of pool elements
Functions
Classes
-
get_thread_pool
()[source]¶ Returns the global pool of threads for Sardana
Returns: the global pool of threads object Return type: taurus.core.util.ThreadPool
-
class
PoolAction
(main_element, name='GlobalAction')[source]¶ Bases:
taurus.core.util.log.Logger
A generic class to handle any type of operation (like motion or acquisition)
-
get_main_element
()[source]¶ Returns the main element for this action
Returns: sardana.pool.poolelement.PoolElement
-
main_element
¶ Returns the main element for this action
Returns: sardana.pool.poolelement.PoolElement
-
pool
¶ Returns the pool object for thi action
Returns: sardana.pool.pool.Pool
-
add_element
(element)[source]¶ Adds a new element to this action.
Parameters: element (sardana.pool.poolelement.PoolElement) – the new element to be added
-
remove_element
(element)[source]¶ Removes an element from this action. If the element is not part of this action, a ValueError is raised.
Parameters: element (sardana.pool.poolelement.PoolElement) – the new element to be removed Raises: ValueError
-
get_elements
(copy_of=False)[source]¶ Returns a sequence of all elements involved in this action.
Parameters: copy_of (bool) – If False (default) the internal container of elements is returned. If True, a copy of the internal container is returned instead Returns: a sequence of all elements involved in this action. Return type: seq<sardana.pool.poolelement.PoolElement>
-
get_pool_controller_list
()[source]¶ Returns a list of all controller elements involved in this action.
Returns: a list of all controller elements involved in this action. Return type: list<sardana.pool.poolelement.PoolController>
-
get_pool_controllers
()[source]¶ Returns a dict of all controller elements involved in this action.
Returns: a dict of all controller elements involved in this action. Return type: dict<sardana.pool.poolelement.PoolController, seq<sardana.pool.poolelement.PoolElement>>
-
is_running
()[source]¶ Determines if this action is running or not
Returns: True if action is running or False otherwise Return type: bool
-
start_action
(*args, **kwargs)[source]¶ Start procedure for this action. Default implementation raises NotImplementedError
Raises: NotImplementedError
-
set_finish_hook
(hook)[source]¶ Attaches/Detaches a finish hook
Parameters: hook (callable or None) – a callable object or None
-
finish_action
()[source]¶ Finishes the action execution. If a finish hook is defined it safely executes it. Otherwise nothing happens
-
was_stopped
()[source]¶ Determines if the action has been stopped from outside
Returns: True if action has been stopped from outside or False otherwise Return type: bool
-
was_aborted
()[source]¶ Determines if the action has been aborted from outside
Returns: True if action has been aborted from outside or False otherwise Return type: bool
-
was_action_interrupted
()[source]¶ Determines if the action has been interruped from outside (either from an abort or a stop).
Returns: True if action has been interruped from outside or False otherwise Return type: bool
-
action_loop
()[source]¶ Action loop for this action. Default implementation raises NotImplementedError
Raises: NotImplementedError
-
read_state_info
(ret=None, serial=False)[source]¶ Reads state information of all elements involved in this action
Parameters: Returns: a map containing state information per element
Return type: dict<sardana.pool.poolelement.PoolElement, stateinfo>
-
raw_read_state_info
(ret=None, serial=False)[source]¶ Unsafe. Reads state information of all elements involved in this action
Parameters: Returns: a map containing state information per element
Return type: dict<sardana.pool.poolelement.PoolElement, stateinfo>
-
read_value
(ret=None, serial=False)[source]¶ Reads value information of all elements involved in this action
Parameters: Returns: a map containing value information per element
Return type: dict<:class:~`sardana.pool.poolelement.PoolElement`, (value object, Exception or None)>
-
raw_read_value
(ret=None, serial=False)[source]¶ Unsafe. Reads value information of all elements involved in this action
Parameters: Returns: a map containing value information per element
Return type: dict<:class:~`sardana.pool.poolelement.PoolElement,
sardana.sardanavalue.SardanaValue
>
-
read_value_loop
(ret=None, serial=False)[source]¶ Reads value information of all elements involved in this action
Parameters: Returns: a map containing value information per element
Return type: dict<:class:~`sardana.pool.poolelement.PoolElement`, (value object, Exception or None)>
-
raw_read_value_loop
(ret=None, serial=False)[source]¶ Unsafe. Reads value information of all elements involved in this action
Parameters: Returns: a map containing value information per element
Return type: dict<:class:~`sardana.pool.poolelement.PoolElement,
sardana.sardanavalue.SardanaValue
>
-
poolbasechannel
¶This module is part of the Python Pool library. It defines the base classes for experiment channels
Classes
-
class
PoolBaseChannel
(**kwargs)[source]¶ Bases:
sardana.pool.poolelement.PoolElement
-
ValueAttributeClass
¶ alias of
Value
-
AcquisitionClass
¶
-
get_value_attribute
()[source]¶ Returns the value attribute object for this experiment channel
Returns: the value attribute Return type: SardanaAttribute
-
acquisition
¶ acquisition object
-
read_value
()[source]¶ Reads the channel value from hardware.
Returns: a SardanaValue
containing the channel valueReturn type: SardanaValue
-
put_value
(value, propagate=1)[source]¶ Sets a value.
Parameters: - value (
SardanaValue
) – the new value - propagate (int) – 0 for not propagating, 1 to propagate, 2 propagate with priority
- value (
-
get_value
(cache=True, propagate=1)[source]¶ Returns the channel value.
Parameters: Returns: the channel value
Return type:
-
set_value
(value)[source]¶ Starts an acquisition on this channel
Parameters: value ( Number
) – the value to count
-
value
¶ channel value
-
poolbaseobject
¶This module is part of the Python Pool library. It defines the base classes for Pool object
Classes
-
class
PoolBaseObject
(**kwargs)[source]¶ Bases:
sardana.sardanabase.SardanaBaseObject
The Pool most abstract object.
-
get_pool
()[source]¶ Return the
sardana.pool.pool.Pool
which owns this pool object.Returns: the pool which owns this pool object. Return type: sardana.pool.pool.Pool
-
pool
¶ reference to the
sardana.pool.pool.Pool
-
poolcontainer
¶This module is part of the Python Pool libray. It defines the base classes for a pool container element
Classes
poolcontroller
¶This module is part of the Python Pool library. It defines the base classes for
Classes
-
class
PoolController
(**kwargs)[source]¶ Bases:
sardana.pool.poolcontroller.PoolBaseController
Controller class mediator for sardana controller plugins
-
ctrl
¶ actual controller object
-
ctrl_info
¶ controller information object
-
set_operator
(operator)[source]¶ Defines the current operator object for this controller. For example, in acquisition, it should be a
PoolMeasurementGroup
object.Parameters: operator (object) – the new operator object
-
operator
¶ current controller operator
-
raw_read_axis_states
(axes=None, ctrl_states=None)[source]¶ Unsafe method. Reads the state for the given axes. If axes is None, reads the state of all active axes.
Parameters: axes (seq<int> or None) – the list of axis to get the state. Default is None meaning all active axis in this controller Returns: a tuple of two elements: a map containing the controller state information for each axis and a boolean telling if an error occured Return type: dict<PoolElement, state info>, bool
-
read_axis_states
(pool_ctrl, *args, **kwargs)[source]¶ Reads the state for the given axes. If axes is None, reads the state of all active axes.
Parameters: axes (seq<int> or None) – the list of axis to get the state. Default is None meaning all active axis in this controller Returns: a map containing the controller state information for each axis Return type: dict<PoolElement, state info>
-
raw_read_axis_values
(axes=None, ctrl_values=None)[source]¶ Unsafe method. Reads the value for the given axes. If axes is None, reads the value of all active axes.
Parameters: axes (seq<int> or None) – the list of axis to get the value. Default is None meaning all active axis in this controller Returns: a map containing the controller value information for each axis Return type: dict<PoolElement, SardanaValue>
-
read_axis_values
(pool_ctrl, *args, **kwargs)[source]¶ Reads the value for the given axes. If axes is None, reads the value of all active axes.
Parameters: axes (seq<int> or None) – the list of axis to get the value. Default is None meaning all active axis in this controller Returns: a map containing the controller value information for each axis Return type: dict<PoolElement, SardanaValue>
-
stop
(pool_ctrl, *args, **kwargs)¶
-
stop_axes
(pool_ctrl, *args, **kwargs)[source]¶ Stops the given axes. If axes is None, stops all active axes.
Parameters: axes (seq<int> or None) – the list of axis to stop. Default is None meaning all active axis in this controller
-
stop_elements
(pool_ctrl, *args, **kwargs)[source]¶ Stops the given elements. If axes is None, stops all active axes.
Parameters: elements – the list of elements to stop. Default is None meaning all active axis in this controller
-
abort_axes
(pool_ctrl, *args, **kwargs)[source]¶ Aborts the given axes. If axes is None, aborts all active axes.
Parameters: axes (seq<int> or None) – the list of axis to abort. Default is None meaning all active axis in this controller
-
abort_elements
(pool_ctrl, *args, **kwargs)[source]¶ Aborts the given elements. If axes is None, aborts all active axes.
Parameters: elements – the list of elements to abort. Default is None meaning all active axis in this controller
-
abort
(pool_ctrl, *args, **kwargs)¶
-
poolcontrollermanager
¶This module is part of the Python Pool library. It defines the class which controls finding, loading/unloading of device pool controller plug-ins.
Classes
-
class
ControllerManager
[source]¶ Bases:
taurus.core.util.singleton.Singleton
,taurus.core.util.log.Logger
The singleton class responsible for managing controller plug-ins.
-
DEFAULT_CONTROLLER_DIRECTORIES
= ('poolcontrollers',)¶
-
setControllerPath
(controller_path, reload=True)[source]¶ Registers a new list of controller directories in this manager.
Parameters: controller_path (seq<str>) – a sequence of absolute paths where this manager should look for controllers Warning
as a consequence all the controller modules will be reloaded. This means that if any reference to an old controller object was kept it will refer to an old module (which could possibly generate problems of type class A != class A).
-
getControllerPath
()[source]¶ Returns the current sequence of absolute paths used to look for controllers.
Returns: sequence of absolute paths Return type: seq<str>
-
getOrCreateControllerLib
(lib_name, controller_name=None)[source]¶ Gets the exiting controller lib or creates a new controller lib file. If name is not None, a controller template code for the given controller name is appended to the end of the file.
Parameters: Returns: a sequence with three items: full_filename, code, line number line number is 0 if no controller is created or n representing the first line of code for the given controller name.
Return type: tuple<str, str, int>
-
setControllerLib
(lib_name, code)[source]¶ Creates a new controller library file with the given name and code. The new module is imported and becomes imediately available.
Parameters:
-
createControllerLib
(lib_name, path=None)[source]¶ Creates a new empty controller library (python module)
-
reloadController
(controller_name, path=None)[source]¶ Reloads the module corresponding to the given controller name
Raises: sardana.pool.poolexception.UnknownController
in case the controller is unknown orImportError
if the reload process is not successfullParameters: - controller_name (str) – controller class name
- path (seq<str>) – a list of absolute path to search for libraries [default: None, meaning the current ControllerPath will be used]
-
reloadControllers
(controller_names, path=None)[source]¶ Reloads the modules corresponding to the given controller names
Raises: sardana.pool.poolexception.UnknownController
in case the controller is unknown orImportError
if the reload process is not successfulParameters: - controller_names (seq<str>) – a list of controller class names
- path (seq<str>) – a list of absolute path to search for libraries [default: None, meaning the current ControllerPath will be used]
-
reloadControllerLibs
(module_names, path=None, reload=True)[source]¶ Reloads the given library(=module) names
Raises: sardana.pool.poolexception.UnknownController
in case the controller is unknown orImportError
if the reload process is not successfulParameters: - module_names (seq<str>) – a list of module names
- path (seq<str>) – a list of absolute path to search for libraries [default: None, meaning the current ControllerPath will be used]
-
reloadControllerLib
(module_name, path=None, reload=True)[source]¶ Reloads the given library(=module) names
Raises: sardana.pool.poolexception.UnknownController
in case the controller is unknown orImportError
if the reload process is not successfulParameters: - module_name (str) – controller library name (=python module name)
- path (seq<str>) – a list of absolute path to search for libraries [default: None, meaning the current ControllerPath will be used]
Returns: the ControllerLib object for the reloaded controller lib
Return type:
-
poolcountertimer
¶This module is part of the Python Pool library. It defines the base classes for CounterTimer
Classes
pooldefs
¶This file contains the basic pool definitions.
Constants
-
ControllerAPI
= 1.1¶ A constant defining the controller API version currently supported
poolelement
¶This module is part of the Python Pool library. It defines the base classes for
Classes
-
class
PoolBaseElement
(**kwargs)[source]¶ Bases:
sardana.pool.poolobject.PoolObject
A Pool object that besides the name, reference to the pool, ID, full_name and user_full_name has:
- _simulation_mode : boolean telling if in simulation mode
- _state : element state
- _status : element status
-
lock
(blocking=True)[source]¶ Acquires the this element lock
Parameters: blocking (bool) – whether or not to block if lock is already acquired [default: True]
-
get_simulation_mode
(cache=True, propagate=1)[source]¶ Returns the simulation mode for this object.
Parameters: Returns: the current simulation mode
Return type:
-
simulation_mode
¶ element simulation mode
-
get_state
(cache=True, propagate=1)[source]¶ Returns the state for this object. If cache is True (default) it returns the current state stored in cache (it will force an update if cache is empty). If propagate > 0 and if the state changed since last read, it will propagate the state event to all listeners.
Parameters: Returns: the current object state
Return type: sardana.State
-
inspect_state
()[source]¶ Looks at the current cached value of state
Returns: the current object state Return type: sardana.State
-
state
¶ element state
-
inspect_status
()[source]¶ Looks at the current cached value of status
Returns: the current object status Return type: str
-
get_status
(cache=True, propagate=1)[source]¶ Returns the status for this object. If cache is True (default) it returns the current status stored in cache (it will force an update if cache is empty). If propagate > 0 and if the status changed since last read, it will propagate the status event to all listeners.
Parameters: Returns: the current object status
Return type:
-
status
¶ element status
-
calculate_state_info
(status_info=None)[source]¶ Transforms the given state information. This specific base implementation transforms the given state,status tuple into a state, new_status tuple where new_status is “self.name is state plus the given status. It is assumed that the given status comes directly from the controller status information.
Parameters: status_info (tuple<State, str>) – given status information [default: None, meaning use current state status. Returns: a transformed state information Return type: tuple<State, str>
-
class
PoolElement
(**kwargs)[source]¶ Bases:
sardana.pool.poolbaseelement.PoolBaseElement
A Pool element is an Pool object which is controlled by a controller. Therefore it contains a _ctrl_id and a _axis (the id of the element in the controller).
-
axis
¶ element axis
-
controller
¶ element controller
-
controller_id
¶ element controller id
-
instrument
¶ element instrument
-
poolexception
¶This module is part of the Python Pool libray. It defines the base classes for pool exceptions
Classes
poolexternal
¶This module is part of the Python Pool libray. It defines the base classes for external objects to the pool (like tango objects)
Functions
Classes
poolgroupelement
¶This module is part of the Python Pool library. It defines the base classes for
Classes
-
class
PoolBaseGroup
(**kwargs)[source]¶ Bases:
sardana.pool.poolcontainer.PoolContainer
-
get_user_element_ids
()[source]¶ Returns the sequence of user element IDs
Returns: the sequence of user element IDs Return type: sequence< int
>
-
user_element_ids
¶ Returns the sequence of user element IDs
Returns: the sequence of user element IDs Return type: sequence< int
>
-
get_user_elements
()[source]¶ Returns the sequence of user elements
Returns: the sequence of user elements Return type: sequence< PoolElement
>
-
get_user_elements_attribute_iterator
()[source]¶ Returns an iterator over the main attribute of each user element.
Returns: an iterator over the main attribute of each user element. Return type: iter< SardanaAttribute
>
-
get_user_elements_attribute
()¶ Returns an iterator over the main attribute of each user element.
Returns: an iterator over the main attribute of each user element. Return type: iter< SardanaAttribute
>
-
get_user_elements_attribute_sequence
()[source]¶ Returns a sequence of main attribute of each user element.
In loops use preferably
get_user_elements_attribute_iterator()
for performance and memory reasons.Returns: a sequence of main attribute of each user element. Return type: sequence< SardanaAttribute
>
-
get_user_elements_attribute_map
()[source]¶ Returns a dictionary of main attribute of each user element.
Returns: a dictionary of main attribute of each user element. Return type: dict< PoolElement
,SardanaAttribute
>
-
get_physical_elements
()[source]¶ Returns a dictionary or physical elements where key is a controller object and value is a sequence of pool elements
Returns: a dictionary of physical elements Return type: dict< PoolElement
>
-
get_physical_elements_iterator
()[source]¶ Returns an iterator over the physical elements.
Warning
The order is non deterministic.
Returns: an iterator over the physical elements. Return type: iter< PoolElement
>
-
get_physical_elements_attribute_iterator
()[source]¶ Returns an iterator over the main attribute of each physical element.
Warning
The order is non deterministic.
Returns: an iterator over the main attribute of each physical element. Return type: iter< SardanaAttribute
>
-
poolinstrument
¶This module is part of the Python Pool library. It defines the base classes for instrument
Classes
poolioregister
¶This module is part of the Python Pool libray. It defines the base classes for
Classes
-
class
PoolIORegister
(**kwargs)[source]¶ Bases:
sardana.pool.poolelement.PoolElement
-
get_value_attribute
()[source]¶ Returns the value attribute object for this IO register
Returns: the value attribute Return type: SardanaAttribute
-
read_value
()[source]¶ Reads the IO register value from hardware.
Returns: a SardanaValue
containing the IO register valueReturn type: SardanaValue
-
put_value
(value, propagate=1)[source]¶ Sets a value.
Parameters: - value (
SardanaValue
) – the new value - propagate (int) – 0 for not propagating, 1 to propagate, 2 propagate with priority
- value (
-
set_write_value
(w_value, timestamp=None, propagate=1)[source]¶ Sets a new write value for the IO registere
Parameters:
-
value
¶ ioregister value
-
poolmeasurementgroup
¶This module is part of the Python Pool library. It defines the base classes for
Classes
-
class
PoolMeasurementGroup
(**kwargs)[source]¶ Bases:
sardana.pool.poolgroupelement.PoolGroupElement
-
DFT_DESC
= 'General purpose measurement group'¶
-
load_configuration
(force=False)[source]¶ Loads the current configuration to all involved controllers
-
timer
¶
-
integration_time
¶ the current integration time
-
monitor_count
¶ the current monitor count
-
acquisition_mode
¶ the current acquisition mode
-
acquisition
¶ acquisition object
-
poolmetacontroller
¶This module is part of the Python Pool libray. It defines the base classes for
Classes
-
class
ControllerLibrary
(**kwargs)[source]¶ Bases:
sardana.sardanameta.SardanaLibrary
Object representing a python module containning controller classes. Public members:
module - reference to python module
f_path - complete (absolute) path and filename
f_name - filename (including file extension)
path - complete (absolute) path
name - module name (without file extension)
controller_list - list<ControllerClass>
- exc_info - exception information if an error occured when loading
the module
-
add_controller
(meta_class)¶ Adds a new :class:~`sardana.sardanameta.SardanaClass` to this library.
Parameters: meta_class (:class:~`sardana.sardanameta.SardanaClass`) – the meta class to be added to this library
-
get_controller
(meta_class_name)¶ Returns a :class:~`sardana.sardanameta.SardanaClass` for the given meta class name or None if the meta class does not exist in this library.
Parameters: meta_class_name (str) – the meta class name Returns: a meta class or None Return type: :class:~`sardana.sardanameta.SardanaClass`
-
get_controllers
()¶ Returns a sequence of the meta classes that belong to this library.
Returns: a sequence of meta classes that belong to this library Return type: seq<:class:~`sardana.sardanameta.SardanaClass`>
-
has_controller
(meta_class_name)¶ Returns True if the given meta class name belongs to this library or False otherwise.
Parameters: meta_class_name (str) – the meta class name Returns: True if the given meta class name belongs to this library or False otherwise Return type: bool
-
controllers
¶
-
class
ControllerClass
(**kwargs)[source]¶ Bases:
sardana.sardanameta.SardanaClass
Object representing a python controller class. Public members:
- name - class name
- klass - python class object
- lib - ControllerLibrary object representing the module where the controller is.
-
controller_class
¶
-
gender
¶
-
model
¶
-
organization
¶
Constants
-
CONTROLLER_TEMPLATE
= 'class @controller_name@(@controller_type@):\n """@controller_name@ description."""\n\n'¶ String containing template code for a controller class
-
CTRL_TYPE_MAP
= {<_mock._Mock object at 0x7f23ac6c4850>: <class 'sardana.pool.poolcontroller.PoolController'>, <_mock._Mock object at 0x7f23ac6c4a90>: <class 'sardana.pool.poolcontroller.PoolController'>, <_mock._Mock object at 0x7f23ac6c46d0>: <class 'sardana.pool.poolcontroller.PoolPseudoMotorController'>, <_mock._Mock object at 0x7f23ac6c4310>: <class 'sardana.pool.poolcontroller.PoolController'>, <_mock._Mock object at 0x7f23ac6c4d50>: <class 'sardana.pool.poolcontroller.PoolController'>, <_mock._Mock object at 0x7f23ac6c4c90>: <class 'sardana.pool.poolcontroller.PoolPseudoCounterController'>, <_mock._Mock object at 0x7f23ac6c4190>: <class 'sardana.pool.poolcontroller.PoolController'>}¶ a dictionary dict<
ElementType
, class> mapping element type enumeration with the corresponding controller pool class (PoolController
or sub-class of it).
-
TYPE_MAP
= {<_mock._Mock object at 0x7f23ac6c4050>: ('MeasurementGroup', 'MeasurementGroup', <class 'sardana.pool.poolmeasurementgroup.PoolMeasurementGroup'>, 'mntgrp/{pool_name}/{name}', None), <_mock._Mock object at 0x7f23ac6c40d0>: ('PseudoCounter', 'ExpChannel', <class 'sardana.pool.poolpseudocounter.PoolPseudoCounter'>, 'pc/{ctrl_name}/{axis}', <class 'sardana.pool.controller.PseudoCounterController'>), <_mock._Mock object at 0x7f23ac6c48d0>: ('Controller', 'Controller', {<_mock._Mock object at 0x7f23ac6c4850>: <class 'sardana.pool.poolcontroller.PoolController'>, <_mock._Mock object at 0x7f23ac6c4a90>: <class 'sardana.pool.poolcontroller.PoolController'>, <_mock._Mock object at 0x7f23ac6c46d0>: <class 'sardana.pool.poolcontroller.PoolPseudoMotorController'>, <_mock._Mock object at 0x7f23ac6c4310>: <class 'sardana.pool.poolcontroller.PoolController'>, <_mock._Mock object at 0x7f23ac6c4d50>: <class 'sardana.pool.poolcontroller.PoolController'>, <_mock._Mock object at 0x7f23ac6c4c90>: <class 'sardana.pool.poolcontroller.PoolPseudoCounterController'>, <_mock._Mock object at 0x7f23ac6c4190>: <class 'sardana.pool.poolcontroller.PoolController'>}, 'controller/{klass}/{name}', <class 'sardana.pool.controller.Controller'>), <_mock._Mock object at 0x7f23ac6c4d10>: ('PseudoMotor', 'Motor', <class 'sardana.pool.poolpseudomotor.PoolPseudoMotor'>, 'pm/{ctrl_name}/{axis}', <class 'sardana.pool.controller.PseudoMotorController'>), <_mock._Mock object at 0x7f23ac6c4d90>: ('ZeroDExpChannel', 'ExpChannel', <class 'sardana.pool.poolzerodexpchannel.Pool0DExpChannel'>, 'expchan/{ctrl_name}/{axis}', <class 'sardana.pool.controller.ZeroDController'>), <_mock._Mock object at 0x7f23ac6c4250>: ('MotorGroup', 'MotorGroup', <class 'sardana.pool.poolmotorgroup.PoolMotorGroup'>, 'mg/{pool_name}/{name}', None), <_mock._Mock object at 0x7f23ac6c4690>: ('OneDExpChannel', 'ExpChannel', <class 'sardana.pool.poolonedexpchannel.Pool1DExpChannel'>, 'expchan/{ctrl_name}/{axis}', <class 'sardana.pool.controller.OneDController'>), <_mock._Mock object at 0x7f23ac6c42d0>: ('CTExpChannel', 'ExpChannel', <class 'sardana.pool.poolcountertimer.PoolCounterTimer'>, 'expchan/{ctrl_name}/{axis}', <class 'sardana.pool.controller.CounterTimerController'>), <_mock._Mock object at 0x7f23ac6c4710>: ('Instrument', 'Instrument', <class 'sardana.pool.poolinstrument.PoolInstrument'>, '{full_name}', None), <_mock._Mock object at 0x7f23ac6c4290>: ('Motor', 'Motor', <class 'sardana.pool.poolmotor.PoolMotor'>, 'motor/{ctrl_name}/{axis}', <class 'sardana.pool.controller.MotorController'>), <_mock._Mock object at 0x7f23ac7f4e90>: ('IORegister', 'IORegister', <class 'sardana.pool.poolioregister.PoolIORegister'>, 'ioregister/{ctrl_name}/{axis}', <class 'sardana.pool.controller.IORegisterController'>), <_mock._Mock object at 0x7f23ac6c4bd0>: ('TwoDExpChannel', 'ExpChannel', <class 'sardana.pool.pooltwodexpchannel.Pool2DExpChannel'>, 'expchan/{ctrl_name}/{axis}', <class 'sardana.pool.controller.TwoDController'>)}¶ dictionary dict<
ElementType
,tuple
> where tuple is a sequence:- type string representation
- family
- internal pool class
- automatic full name
- controller class
-
TYPE_MAP_OBJ
= {<_mock._Mock object at 0x7f23ac6c4250>: <sardana.pool.poolmetacontroller.TypeData object at 0x7f23acddc6d0>, <_mock._Mock object at 0x7f23ac6c4710>: <sardana.pool.poolmetacontroller.TypeData object at 0x7f23acddc950>, <_mock._Mock object at 0x7f23ac6c4690>: <sardana.pool.poolmetacontroller.TypeData object at 0x7f23acddc810>, <_mock._Mock object at 0x7f23ac6c48d0>: <sardana.pool.poolmetacontroller.TypeData object at 0x7f23acddc850>, <_mock._Mock object at 0x7f23ac6c40d0>: <sardana.pool.poolmetacontroller.TypeData object at 0x7f23ac84eb90>, <_mock._Mock object at 0x7f23ac6c4d10>: <sardana.pool.poolmetacontroller.TypeData object at 0x7f23acddc890>, <_mock._Mock object at 0x7f23ac6c42d0>: <sardana.pool.poolmetacontroller.TypeData object at 0x7f23acddc550>, <_mock._Mock object at 0x7f23ac6c4290>: <sardana.pool.poolmetacontroller.TypeData object at 0x7f23acddc8d0>, <_mock._Mock object at 0x7f23ac6c4d90>: <sardana.pool.poolmetacontroller.TypeData object at 0x7f23acddc350>, <_mock._Mock object at 0x7f23ac7f4e90>: <sardana.pool.poolmetacontroller.TypeData object at 0x7f23acddc990>, <_mock._Mock object at 0x7f23ac6c4bd0>: <sardana.pool.poolmetacontroller.TypeData object at 0x7f23acb51710>, <_mock._Mock object at 0x7f23ac6c4050>: <sardana.pool.poolmetacontroller.TypeData object at 0x7f23ac626f50>}¶ dictionary dict<
ElementType
,TypeData
>
poolmonitor
¶This file contains the pool monitor class
Classes
poolmotion
¶This module is part of the Python Pool libray. It defines the class for a motion
Classes
-
class
PoolMotionItem
(moveable, position, dial_position, do_backlash, backlash, instability_time=None)[source]¶ Bases:
sardana.pool.poolaction.PoolActionItem
An item involved in the motion. Maps directly to a motor object
-
moveable
¶
-
-
class
PoolMotion
(main_element, name='GlobalMotion')[source]¶ Bases:
sardana.pool.poolaction.PoolAction
This class manages motion actions
-
start_action
(*args, **kwargs)[source]¶ kwargs[‘items’] is a dict<moveable, (pos, dial, do_backlash, backlash)
-
action_loop
¶
-
Enumerations
-
MotionState
= <taurus.core.util.enumeration.Enumeration object>¶
poolmotor
¶This module is part of the Python Pool libray. It defines the base classes for
Classes
-
class
PoolMotor
(**kwargs)[source]¶ Bases:
sardana.pool.poolelement.PoolElement
An internal Motor object. NOT part of the official API. Accessing this object from a controller plug-in may lead to undetermined behavior like infinite recursion.
-
inspect_limit_switches
()[source]¶ returns the current (cached value of the limit switches
Returns: the current limit switches flags
-
get_limit_switches
(cache=True, propagate=1)[source]¶ Returns the motor limit switches state.
Parameters: Returns: the motor limit switches state
Return type:
-
limit_switches
¶ motor limit switches
-
instability_time
¶ motor instability time
-
backlash
¶ motor backlash
-
offset
¶ motor offset
-
sign
¶ motor sign
-
step_per_unit
¶ motor steps per unit
-
acceleration
¶ motor acceleration
-
deceleration
¶ motor deceleration
-
base_rate
¶ motor base rate
-
velocity
¶ motor velocity
-
get_position_attribute
()[source]¶ Returns the position attribute object for this motor
Returns: the position attribute Return type: SardanaAttribute
-
get_position
(cache=True, propagate=1)[source]¶ Returns the user position.
Parameters: Returns: the user position
Return type:
-
set_position
(position)[source]¶ Moves the motor to the specified user position
Parameters: position ( Number
) – the user position to move to
-
set_write_position
(w_position, timestamp=None, propagate=1)[source]¶ Sets a new write value for the user position.
Parameters:
-
read_dial_position
()[source]¶ Reads the dial position from hardware.
Returns: a SardanaValue
containing the dial positionReturn type: SardanaValue
-
put_dial_position
(dial_position_value, propagate=1)[source]¶ Sets a new dial position.
Parameters: - dial_position_value (
SardanaValue
) – the new dial position value - propagate (int) – 0 for not propagating, 1 to propagate, 2 propagate with priority
- dial_position_value (
-
get_dial_position_attribute
()[source]¶ Returns the dial position attribute object for this motor
Returns: the dial position attribute Return type: SardanaAttribute
-
get_dial_position
(cache=True, propagate=1)[source]¶ Returns the dial position.
Parameters: Returns: the dial position
Return type:
-
position
¶ motor user position
-
dial_position
¶ motor dial position
-
motion
¶ motion object
-
poolmotorgroup
¶This module is part of the Python Pool library. It defines the base classes for
Classes
-
class
PoolMotorGroup
(**kwargs)[source]¶ Bases:
sardana.pool.poolgroupelement.PoolGroupElement
-
get_position
(cache=True, propagate=1)[source]¶ Returns the user position.
Parameters: Returns: the user position
Return type:
-
set_position
(positions)[source]¶ Moves the motor group to the specified user positions
Parameters: positions (sequence< Number
>) – the user positions to move to
-
set_write_position
(w_position, timestamp=None, propagate=1)[source]¶ Sets a new write value for the user position.
Parameters:
-
position
¶ motor group positions
-
motion
¶ motion object
-
poolmoveable
¶This module is part of the Python Pool libray. It defines the base classes for moveable elements
Classes
poolobject
¶This module is part of the Python Pool library. It defines the base classes for Pool object
Classes
-
class
PoolObject
(**kwargs)[source]¶ Bases:
sardana.sardanabase.SardanaObjectID
,sardana.pool.poolbaseobject.PoolBaseObject
A Pool object that besides the name and reference to the pool has:
- _id : the internal identifier
poolonedexpchannel
¶This module is part of the Python Pool library. It defines the base classes for OneDExpChannel
Classes
poolpseudocounter
¶This module is part of the Python Pool library. It defines the PoolPseudoCounter class
Classes
-
class
PoolPseudoCounter
(**kwargs)[source]¶ Bases:
sardana.pool.poolbasegroup.PoolBaseGroup
,sardana.pool.poolbasechannel.PoolBaseChannel
A class representing a Pseudo Counter in the Sardana Device Pool
-
ValueAttributeClass
¶ alias of
Value
-
AcquisitionClass
= None¶
-
siblings
¶ the siblings for this pseudo counter
-
get_physical_values
(cache=True, propagate=1)[source]¶ Get value for underlying elements.
Parameters: Returns: the physical value
Return type: dict <PoolElement,
SardanaAttribute
>
-
get_siblings_values
(use=None)[source]¶ Get the last values for all siblings.
Parameters: use (dict <PoolElement, SardanaValue
>) – the already calculated values. If a sibling is in this dictionary, the value stored here is used insteadReturns: a dictionary with siblings values Return type: dict <PoolElement, value(float?) >
-
get_value
(cache=True, propagate=1)[source]¶ Returns the pseudo counter value.
Parameters: Returns: the pseudo counter value
Return type:
-
value
¶ pseudo counter value
-
poolpseudomotor
¶Classes
-
class
PoolPseudoMotor
(**kwargs)[source]¶ Bases:
sardana.pool.poolbasegroup.PoolBaseGroup
,sardana.pool.poolelement.PoolElement
A class representing a Pseudo Motor in the Sardana Device Pool
-
drift_correction
¶ drift correction
-
siblings
¶ the siblings for this pseudo motor
-
get_physical_positions
(cache=True, propagate=1)[source]¶ Get positions for underlying elements.
Parameters: Returns: the physical positions
Return type: dict <PoolElement,
SardanaAttribute
>
-
get_siblings_positions
(use=None, write_pos=True)[source]¶ Get the last positions for all siblings. If write_pos is True and a sibling has already been moved before, it’s last write position is used. Otherwise its read position is used instead.
Parameters: - use (dict <PoolElement,
SardanaValue
>) – the already calculated positions. If a sibling is in this dictionary, the position stored here is used instead - write_pos (bool) – determines if should try to use the last set point [default: True]
Returns: a dictionary with siblings write positions
Return type: dict <PoolElement, position(float?) >
- use (dict <PoolElement,
-
get_position
(cache=True, propagate=1)[source]¶ Returns the user position.
Parameters: Returns: the user position
Return type:
-
set_position
(position)[source]¶ Moves the motor to the specified user position
Parameters: position ( Number
) – the user position to move to
-
set_write_position
(w_position, timestamp=None, propagate=1)[source]¶ Sets a new write value for the user position.
Parameters:
-
position
¶ pseudo motor position
-
motion
¶ motion object
-
pooltwodexpchannel
¶This module is part of the Python Pool library. It defines the base classes for TwoDExpChannel
Classes
poolzerodexpchannel
¶This module is part of the Python Pool library. It defines the base classes for ZeroDExpChannel
Classes
-
class
Pool0DExpChannel
(**kwargs)[source]¶ Bases:
sardana.pool.poolbasechannel.PoolBaseChannel
-
ValueAttributeClass
¶ alias of
Value
-
AcquisitionClass
¶
-
accumulation
¶
-
get_accumulated_value_attribute
()[source]¶ Returns the accumulated value attribute object for this 0D.
Returns: the accumulated value attribute Return type: SardanaAttribute
-
get_current_value_attribute
()[source]¶ Returns the current value attribute object for this 0D.
Returns: the current value attribute Return type: SardanaAttribute
-
get_accumulated_value
()[source]¶ Gets the accumulated value for this 0D.
Returns: a SardanaValue
containing the 0D valueReturn type: SardanaAttribute
Raises: Exception if no acquisition has been done yet on this 0D
-
read_current_value
()[source]¶ Reads the 0D value from hardware.
Returns: a SardanaValue
containing the counter valueReturn type: SardanaValue
-
put_current_value
(value, propagate=1)[source]¶ Sets a value.
Parameters: - value (
SardanaValue
) – the new value - propagate (int) – 0 for not propagating, 1 to propagate, 2 propagate with priority
- value (
-
get_current_value
(cache=True, propagate=1)[source]¶ Returns the counter value.
Returns: the 0D accumulated value Return type: SardanaAttribute
-
current_value
¶ 0D value
-
accumulated_value
¶ 0D value
-
value_buffer
¶
-
time_buffer
¶
-
Classes
Constants
macroserver
¶This is the main macro server module
Modules
macros
¶-
class
scan.
a2scan
¶ two-motor scan. a2scan scans two motors, as specified by motor1 and motor2. Each motor moves the same number of intervals with starting and ending positions given by start_pos1 and final_pos1, start_pos2 and final_pos2, respectively. The step size for each motor 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.
-
class
scan.
a2scanc
¶ two-motor continuous scan
-
class
scan.
a3scan
¶ three-motor scan . a3scan scans three motors, as specified by motor1, motor2 and motor3. Each motor moves the same number of intervals with starting and ending positions given by start_pos1 and final_pos1, start_pos2 and final_pos2, start_pos3 and final_pos3, respectively. The step size for each motor 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.
-
class
scan.
a3scanc
¶ three-motor continuous scan
-
class
scan.
a4scan
¶ four-motor scan . a4scan scans four motors, as specified by motor1, motor2, motor3 and motor4. Each motor moves the same number of intervals with starting and ending positions given by start_posN and final_posN (for N=1,2,3,4). The step size for each motor 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.
-
class
scan.
a4scanc
¶ four-motor continuous scan
-
class
scan.
amultiscan
¶ Multiple motor scan. amultiscan scans N motors, as specified by motor1, motor2,...,motorN. Each motor moves the same number of intervals with starting and ending positions given by start_posN and final_posN (for N=1,2,...). The step size for each motor 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.
-
class
scan.
ascan
¶ 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.
-
class
scan.
ascanc
¶ Do an absolute continuous scan of the specified motor. ascanc scans one motor, as specified by motor.
-
class
scan.
ascanh
¶ 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.
-
class
demo.
clear_sar_demo
¶ Undoes changes done with sar_demo
-
class
expert.
commit_ctrllib
¶ Puts the contents of the given data in a file inside the pool
-
class
standard.
ct
¶ Count for the specified time on the active measurement group
-
class
scan.
d2scan
¶ two-motor scan relative to the starting position. d2scan scans two motors, as specified by motor1 and motor2. Each motor moves the same number of intervals. If each motor is at a position X before the scan begins, it will be scanned from X+start_posN to X+final_posN (where N is one of 1,2). The step size for each motor 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.
-
class
scan.
d2scanc
¶ continuous two-motor scan relative to the starting positions
-
class
scan.
d3scan
¶ three-motor scan . d3scan scans three motors, as specified by motor1, motor2 and motor3. Each motor moves the same number of intervals. If each motor is at a position X before the scan begins, it will be scanned from X+start_posN to X+final_posN (where N is one of 1,2,3) The step size for each motor 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.
-
class
scan.
d3scanc
¶ continuous three-motor scan
-
class
scan.
d4scan
¶ four-motor scan relative to the starting positions a4scan scans four motors, as specified by motor1, motor2, motor3 and motor4. Each motor moves the same number of intervals. If each motor is at a position X before the scan begins, it will be scanned from X+start_posN to X+final_posN (where N is one of 1,2,3,4). The step size for each motor 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. Upon termination, the motors are returned to their starting positions.
-
class
scan.
d4scanc
¶ continuous four-motor scan relative to the starting positions
-
class
expert.
defctrl
¶ Creates a new controller ‘role_prop’ is a sequence of roles and/or properties. - A role is defined as <role name>=<role value> (only applicable to pseudo controllers) - A property is defined as <property name> <property value>
If both roles and properties are supplied, all roles must come before properties. All controller properties that don’t have default values must be given.
Example of creating a motor controller (with a host and port properties):
[1]: defctrl SuperMotorController myctrl host homer.springfield.com port 5000
Example of creating a Slit pseudo motor (sl2t and sl2b motor roles, Gap and Offset pseudo motor roles):
[1]: defctrl Slit myslit sl2t=mot01 sl2b=mot02 Gap=gap01 Offset=offset01
-
class
expert.
defelem
¶ Creates an element on a controller with an axis
-
class
expert.
defm
¶ Creates a new motor in the active pool
-
class
expert.
defmeas
¶ Create a new measurement group. First channel in channel_list MUST be an internal sardana channel. At least one channel MUST be a Counter/Timer (by default, the first Counter/Timer in the list will become the master).
-
class
scan.
dmultiscan
¶ Multiple motor scan relative to the starting positions. dmultiscan scans N motors, as specified by motor1, motor2,...,motorN. Each motor moves the same number of intervals If each motor is at a position X before the scan begins, it will be scanned from X+start_posN to X+final_posN (where N is one of 1,2,...) The step size for each motor 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.
-
class
scan.
dscan
¶ motor scan relative to the starting position. dscan scans one motor, as specified by motor. If motor motor is at a position X before the scan begins, it will be scanned from X+start_pos to X+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.
-
class
scan.
dscanc
¶ continuous motor scan relative to the starting position.
-
class
env.
dumpenv
¶ Dumps the complete environment
-
class
expert.
edctrl
¶ Returns the contents of the library file which contains the given controller code.
-
class
expert.
edctrllib
¶ Returns the contents of the given library file
-
class
scan.
fscan
¶ N-dimensional scan along user defined paths. The motion path for each motor is defined through the evaluation of a user-supplied function that is evaluated as a function of the independent variables. -independent variables are supplied through the indepvar string. The syntax for indepvar is “x=expresion1,y=expresion2,...” -If no indep vars need to be defined, write ”!” or “*” or “None” -motion path for motor is generated by evaluating the corresponding function ‘func’ -Count time is given by integ_time. If integ_time is a scalar, then the same integ_time is used for all points. If it evaluates as an array (with same length as the paths), fscan will assign a different integration time to each acquisition point. -If integ_time is positive, it specifies seconds and if negative, specifies monitor counts.
IMPORTANT Notes: -no spaces are allowed in the indepvar string. -all funcs must evaluate to the same number of points
EXAMPLE: fscan x=[1,3,5,7,9],y=arange(5) motor1 x**2 motor2 sqrt(y*x-3) 0.1
-
class
communication.
get
¶ Reads and outputs the data from the communication channel
-
class
env.
load_env
¶ Read environment variables from config_env.xml file
-
class
lists.
ls0d
¶ Lists all 0D experiment channels
-
class
lists.
ls1d
¶ Lists all 1D experiment channels
-
class
lists.
ls2d
¶ Lists all 2D experiment channels
-
class
lists.
lsa
¶ Lists all existing objects
-
class
lists.
lscom
¶ Lists all communication channels
-
class
lists.
lsct
¶ Lists all Counter/Timers
-
class
lists.
lsctrl
¶ Lists all existing controllers
-
class
lists.
lsctrllib
¶ Lists all existing controller classes
-
class
lists.
lsdef
¶ List all macro definitions
-
class
env.
lsenv
¶ Lists the environment
-
class
lists.
lsexp
¶ Lists all experiment channels
-
class
lists.
lsi
¶ Lists all existing instruments
-
class
lists.
lsior
¶ Lists all IORegisters
-
class
lists.
lsm
¶ Lists all motors
-
class
lists.
lsmac
¶ Lists existing macros
-
class
lists.
lsmaclib
¶ Lists existing macro libraries.
-
class
lists.
lsmeas
¶ List existing measurement groups
-
class
lists.
lspc
¶ Lists all pseudo counters
-
class
lists.
lspm
¶ Lists all existing motors
-
class
env.
lsvo
¶ Lists the view options
-
class
mca.
mca_start
¶ Starts an mca
-
class
mca.
mca_stop
¶ Stops an mca
-
class
scan.
mesh
¶ 2d grid scan . The mesh scan traces out a grid using motor1 and motor2. The first motor scans from m1_start_pos to m1_final_pos using the specified number of intervals. The second motor similarly scans from m2_start_pos to m2_final_pos. Each point is counted for for integ_time seconds (or monitor counts, if integ_time is negative). The scan of motor1 is done at each point scanned by motor2. That is, the first motor scan is nested within the second motor scan.
-
class
scan.
meshc
¶ 2d grid scan. scans continuous
-
class
standard.
mstate
¶ Prints the state of a motor
-
class
standard.
mv
¶ Move motor(s) to the specified position(s)
-
class
standard.
mvr
¶ Move motor(s) relative to the current position(s)
-
class
expert.
prdef
¶ Returns the the macro code for the given macro name.
-
class
communication.
put
¶ Sends a string to the communication channel
-
class
standard.
pwa
¶ Show all motor positions in a pretty table
-
class
standard.
pwm
¶ Show the position of the specified motors in a pretty table
-
class
ioregister.
read_ioreg
¶ Reads an output register
-
class
expert.
addmaclib
¶ Loads a new macro library.
Warning
Keep in mind that macros from the new library can override macros already present in the system.
-
class
expert.
rellib
¶ Reloads the given python library code from the macro server filesystem.
Warning
use with extreme care! Accidentally reloading a system module or an installed python module may lead to unpredictable behavior
Warning
Prior to the Sardana version 1.6.0 this macro was successfully reloading python libraries located in the MacroPath. The MacroPath is not a correct place to locate your python libraries. They may be successfully loaded on the MacroServer startup, but this can not be guaranteed. In order to use python libraries within your macro code, locate them in either of valid system PYTHONPATH or MacroServer’s PythonPath property (of the host where MacroServer runs). In order to achieve the previous behavior, just configure the the same directory in both system PYTHONPATH (or MacroServer’s PythonPath) and MacroPath.
Note
if python module is used by any macro, don’t forget to reload the corresponding macros afterward so the changes take effect.
-
class
expert.
relmac
¶ Reloads the given macro code from the macro server filesystem. Attention: All macros inside the same file will also be reloaded.
-
class
expert.
relmaclib
¶ Reloads the given macro library code from the macro server filesystem.
-
class
standard.
report
¶ Logs a new record into the message report system (if active)
-
class
demo.
sar_demo
¶ Sets up a demo environment. It creates many elements for testing
-
class
expert.
sar_info
¶ Prints details about the given sardana object
-
class
scan.
scanhist
¶ Shows scan history information. Give optional parameter scan number to display details about a specific scan
-
class
expert.
send2ctrl
¶ Sends the given data directly to the controller
-
class
env.
senv
¶ Sets the given environment variable to the given value
-
class
sequence.
sequence
¶ This macro executes a sequence of macros. As a parameter it receives a string which is a xml structure. These macros which allow hooks can nest another sequence (xml structure). In such a case, this macro is executed recursively.
-
class
standard.
set_lim
¶ Sets the software limits on the specified motor hello
-
class
standard.
set_lm
¶ Sets the dial limits on the specified motor
-
class
standard.
set_pos
¶ Sets the position of the motor to the specified value
-
class
standard.
set_user_pos
¶ Sets the USER position of the motor to the specified value (by changing OFFSET and keeping DIAL)
-
class
standard.
settimer
¶ Defines the timer channel for the active measurement group
-
class
env.
setvo
¶ Sets the given view option to the given value
-
class
standard.
uct
¶ Count on the active measurement group and update
-
class
expert.
udefctrl
¶ Deletes an existing controller
-
class
expert.
udefelem
¶ Deletes an existing element
-
class
expert.
udefmeas
¶ Deletes an existing measurement group
-
class
standard.
umv
¶ Move motor(s) to the specified position(s) and update
-
class
standard.
umvr
¶ Move motor(s) relative to the current position(s) and update
-
class
env.
usenv
¶ Unsets the given environment variable
-
class
env.
usetvo
¶ Resets the value of the given view option
-
class
standard.
wa
¶ Show all motor positions
-
class
standard.
wm
¶ Show the position of the specified motors.
-
class
ioregister.
write_ioreg
¶ Writes a value to an input register
-
class
standard.
wu
¶ Show all user motor positions
-
class
standard.
wum
¶ Show the user position of the specified motors.
macroserver
¶Functions
Classes
-
class
MacroServer
(full_name, name=None, macro_path=None, environment_db=None)[source]¶ Bases:
sardana.macroserver.mscontainer.MSContainer
,sardana.macroserver.msbase.MSObject
,sardana.sardanamanager.SardanaElementManager
,sardana.sardanamanager.SardanaIDManager
msbase
¶This module is part of the Python MacroServer libray. It defines the base classes for MacroServer object
Functions
Classes
-
class
MSBaseObject
(**kwargs)[source]¶ Bases:
sardana.sardanabase.SardanaBaseObject
The MacroServer most abstract object.
-
class
MSObject
(**kwargs)[source]¶ Bases:
sardana.sardanabase.SardanaObjectID
,sardana.macroserver.msbase.MSBaseObject
A macro server object that besides the name and reference to the macro server base object has:
- _id : the internal identifier
mscontainer
¶This module is part of the Python Macro Server libray. It defines the base classes for a macro server container element
Functions
Classes
msdoor
¶This module contains the class definition for the macro server door
Functions
Classes
-
class
MSDoor
(**kwargs)[source]¶ Bases:
sardana.macroserver.msbase.MSObject
Sardana door object
msenvmanager
¶This module contains the class definition for the MacroServer environment manager
Functions
Classes
-
class
EnvironmentManager
(macro_server, environment_db=None)[source]¶ Bases:
sardana.macroserver.msmanager.MacroServerManager
The MacroServer environment manager class. It is designed to be a singleton for the entire application.
msexception
¶This module contains the class definition for the MacroServer environment manager
Functions
Classes
msmacromanager
¶This module contains the class definition for the MacroServer macro manager
Functions
Classes
msmetamacro
¶This module contains the class definition for the MacroServer meta macro information
Functions
Classes
-
class
MacroLibrary
(**kwargs)[source]¶ Bases:
sardana.sardanameta.SardanaLibrary
Object representing a python module containing macro classes and/or macro functions. Public members:
module - reference to python module
file_path - complete (absolute) path (with file name at the end)
file_name - file name (including file extension)
path - complete (absolute) path
name - (=module name) module name (without file extension)
macros - dict<str, MacroClass>
- exc_info - exception information if an error occurred when loading
the module
-
get_macro
(meta_name)¶ Returns a :class:~`sardana.sardanameta.SardanaCode` for the given meta name or None if the meta does not exist in this library.
Parameters: meta_name (str) – the meta name (class, function) Returns: a meta or None Return type: :class:~`sardana.sardanameta.SardanaCode`
-
get_macros
()¶ Returns a sequence of the meta (class and functions) that belong to this library.
Returns: a sequence of meta (class and functions) that belong to this library Return type: seq<:class:~`sardana.sardanameta.SardanaCode`>
-
has_macro
(meta_name)¶ Returns True if the given meta name belongs to this library or False otherwise.
Parameters: meta_name (str) – the meta name Returns: True if the given meta (class or function) name belongs to this library or False otherwise Return type: bool
-
has_macros
()¶ Returns True if any meta object exists in the library or False otherwise.
Returns: True if any meta object (class or function) exists in the library or False otherwise Return type: bool
-
add_macro_class
(meta_class)¶ Adds a new :class:~`sardana.sardanameta.SardanaClass` to this library.
Parameters: meta_class (:class:~`sardana.sardanameta.SardanaClass`) – the meta class to be added to this library
-
get_macro_class
(meta_class_name)¶ Returns a :class:~`sardana.sardanameta.SardanaClass` for the given meta class name or None if the meta class does not exist in this library.
Parameters: meta_class_name (str) – the meta class name Returns: a meta class or None Return type: :class:~`sardana.sardanameta.SardanaClass`
-
get_macro_classes
()¶ Returns a sequence of the meta classes that belong to this library.
Returns: a sequence of meta classes that belong to this library Return type: seq<:class:~`sardana.sardanameta.SardanaClass`>
-
has_macro_class
(meta_class_name)¶ Returns True if the given meta class name belongs to this library or False otherwise.
Parameters: meta_class_name (str) – the meta class name Returns: True if the given meta class name belongs to this library or False otherwise Return type: bool
-
add_macro_function
(meta_function)¶ Adds a new :class:~`sardana.sardanameta.SardanaFunction` to this library.
Parameters: meta_function (:class:~`sardana.sardanameta.SardanaFunction`) – the meta function to be added to this library
-
get_macro_function
(meta_function_name)¶ Returns a :class:~`sardana.sardanameta.SardanaFunction` for the given meta function name or None if the meta function does not exist in this library.
Parameters: meta_function_name (str) – the meta function name Returns: a meta function or None Return type: :class:~`sardana.sardanameta.SardanaFunction`
-
get_macro_functions
()¶ Returns a sequence of the meta functions that belong to this library.
Returns: a sequence of meta functions that belong to this library Return type: seq<:class:~`sardana.sardanameta.SardanaFunction`>
-
class
Parameterizable
[source]¶ Bases:
object
Helper class to handle parameter and result definition for a
MacroClass
or aMacroFunction
-
class
MacroClass
(**kwargs)[source]¶ Bases:
sardana.sardanameta.SardanaClass
,sardana.macroserver.msmetamacro.Parameterizable
-
class
MacroFunction
(**kwargs)[source]¶ Bases:
sardana.sardanameta.SardanaFunction
,sardana.macroserver.msmetamacro.Parameterizable
msparameter
¶This module contains the definition of the macroserver parameters for macros
Functions
Classes
mstypemanager
¶This module contains the definition of the macroserver data type manager
Functions
Classes
tango
¶Modules
core
¶Modules
SardanaDevice
¶Generic Sardana Tango device module
Classes
-
class
SardanaDevice
(dclass, name)[source]¶ Bases:
PyTango.Device_4Impl
,taurus.core.util.log.Logger
SardanaDevice represents the base class for all Sardana
PyTango.DeviceImpl
classes-
init
(name)[source]¶ initialize the device once in the object lifetime. Override when necessary but always call the method from your super class
Parameters: name (str) – device name
-
alias
¶ the device alias name
-
get_full_name
()[source]¶ Returns the device full name in format dbname:dbport/<domain>/<family>/<member>
Returns: this device full name Return type: str
-
init_device
()[source]¶ Initialize the device. Called during startup after
init()
and every time the tangoInit
command is executed. Override when necessary but always call the method from your super class
-
init_device_nodb
()[source]¶ Internal method. Initialize the device when tango database is not being used (example: in demos)
-
delete_device
()[source]¶ Clean the device. Called during shutdown and every time the tango
Init
command is executed. Override when necessary but always call the method from your super class
-
set_change_events
(evts_checked, evts_not_checked)[source]¶ Helper method to set change events on attributes
Parameters: - evts_checked (seq<
str
>) – list of attribute names to activate change events programatically with tango filter active - evts_not_checked (seq<
str
>) – list of attribute names to activate change events programatically with tango filter inactive. Use this with care! Attributes configured with no change event filter may potentially generated a lot of events!
- evts_checked (seq<
-
initialize_dynamic_attributes
()[source]¶ Initialize dynamic attributes. Default implementation does nothing. Override when necessary.
-
get_event_thread_pool
()[source]¶ Return the
ThreadPool
used by sardana to send tango events.Returns: the sardana ThreadPool
Return type: ThreadPool
-
get_attribute_by_name
(attr_name)[source]¶ Gets the attribute for the given name.
Parameters: attr_name (str) – attribute name Returns: the attribute object Return type: Attribute
-
get_wattribute_by_name
(attr_name)[source]¶ Gets the writable attribute for the given name.
Parameters: attr_name (str) – attribute name Returns: the attribute object Return type: WAttribute
-
get_database
()[source]¶ Helper method to return a reference to the current tango database
Returns: the Tango database Return type: Database
-
set_attribute
(attr, value=None, w_value=None, timestamp=None, quality=None, error=None, priority=1, synch=True)[source]¶ Sets the given attribute value. If timestamp is not given, now is used as timestamp. If quality is not given VALID is assigned. If error is given an error event is sent (with no value and quality INVALID). If priority is > 1, the event filter is temporarily disabled so the event is sent for sure. If synch is set to True, wait for fire event to finish
Parameters: - attr (
PyTango.Attribute
) – the tango attribute - value (object) – the value to be set (not mandatory if setting an error) [default: None]
- w_value – the write value to be set (not mandatory) [default: None, meaning maintain current write value]
- timestamp (float or
PyTango.TimeVal
) – the timestamp associated with the operation [default: None, meaning use now as timestamp] - quality (
PyTango.AttrQuality
) – attribute quality [default: None, meaning VALID] - error (
PyTango.DevFailed
) – a tango DevFailed error or None if not an error [default: None] - priority (int) – event priority [default: 1, meaning normal priority]. If priority is > 1, the event filter is temporarily disabled so the event is sent for sure. The event filter is restored to the previous value
- synch – If synch is set to True, wait for fire event to finish. If False, a job is sent to the sardana thread pool and the method returns immediately [default: True]
- attr (
-
set_attribute_push
(attr, value=None, w_value=None, timestamp=None, quality=None, error=None, priority=1, synch=True)[source]¶ Synchronous internal implementation of
set_attribute()
(synch is passed to this method because it might need to know if it is being executed in a synchronous or asynchronous context).
-
calculate_tango_state
(ctrl_state, update=False)[source]¶ Calculate tango state based on the controller state.
Parameters: Returns: the corresponding tango state
Return type:
-
-
class
SardanaDeviceClass
(name)[source]¶ Bases:
PyTango.DeviceClass
SardanaDeviceClass represents the base class for all Sardana
PyTango.DeviceClass
classes-
class_property_list
= {}¶ Sardana device class properties definition
See also
-
device_property_list
= {}¶ Sardana device properties definition
See also
-
cmd_list
= {}¶ Sardana device command definition
See also
-
attr_list
= {}¶ Sardana device attribute definition
See also
-
write_class_property
()[source]¶ Write class properties
ProjectTitle
,Description
,doc_url
,InheritedFrom
and__icon
-
dyn_attr
(dev_list)[source]¶ Invoked to create dynamic attributes for the given devices. Default implementation calls
SardanaDevice.initialize_dynamic_attributes()
for each deviceParameters: dev_list ( PyTango.DeviceImpl
) – list of devices
-
pool
¶Modules
Pool
¶Classes
-
class
Pool
(cl, name)[source]¶ Bases:
PyTango.Device_4Impl
,taurus.core.util.log.Logger
-
ElementsCache
= None¶
-
pool
¶
-
delete_device
¶
-
init_device
¶
-
is_ControllerLibList_allowed
(req_type)¶
-
is_ControllerClassList_allowed
(req_type)¶
-
is_ControllerList_allowed
(req_type)¶
-
is_InstrumentList_allowed
(req_type)¶
-
is_ExpChannelList_allowed
(req_type)¶
-
is_AcqChannelList_allowed
(req_type)¶
-
is_MotorGroupList_allowed
(req_type)¶
-
is_MotorList_allowed
(req_type)¶
-
is_MeasurementGroupList_allowed
(req_type)¶
-
is_IORegisterList_allowed
(req_type)¶
-
is_ComChannelList_allowed
(req_type)¶
-
CreateController
(argin)[source]¶ Tango command to create controller.
Parameters: argin (list<str>) – Must give either:
- A JSON encoded dict as first string with:
- mandatory keys: ‘type’, ‘library’, ‘klass’ and ‘name’ (values are strings).
- optional keys:
- ‘properties’: a dict with keys being property names and values the property values
- ‘roles’: a dict with keys being controller roles and values being element names. (example: { ‘gap’ : ‘motor21’, ‘offset’ : ‘motor55’ }). Only applicable of pseudo controllers
- a sequence of strings: <type>, <library>, <class>, <name> [, <role_name>’=’<element name>] [, <property name>, <property value>]
Examples:
1 2 3 4 5 6 7
data = dict(type='Motor', library='DummyMotorController', klass='DummyMotorController', name='my_motor_ctrl_1') pool.CreateController([json.dumps(data)]) pool.CreateController(['Motor', 'DummyMotorController', 'DummyMotorController', 'my_motor_ctrl_2'])
Returns: None
-
CreateInstrument
(argin)[source]¶ Tango command to create instrument.
Parameters: argin (list<str>) – Must give either:
- A JSON encoded dict as first string with:
- mandatory keys: ‘full_name’, ‘klass’ (values are strings).
- a sequence of strings: <full_name>, <class>
Examples:
pool.CreateInstrument(['/OH', 'NXhutch']) pool.CreateInstrument(['/OH/Mono', 'NXmonochromator']) pool.CreateInstrument(['/EH', 'NXhutch']) pool.CreateInstrument(['/EH/Pilatus', 'NXdetector'])
Returns: None
-
CreateElement
(argin)[source]¶ Tango command to create element (motor, counter/timer, 0D, 1D, 2D, IORegister).
Parameters: argin (list<str>) – Must give either:
- A JSON encoded dict as first string with:
- mandatory keys: ‘type’, ‘ctrl_name’, ‘axis’, ‘name’ (values are strings).
- optional keys:
- ‘full_name’ : a string representing the full tango device name
- a sequence of strings: <type>, <ctrl_name>, <axis>, <name> [, <full_name>]
Examples:
1 2 3 4 5
data = dict(type='Motor', ctrl_name='my_motor_ctrl_1', axis='4', name='theta', full_name='BL99/EH/THETA') pool.CreateElement([json.dumps(data)]) pool.CreateElement(['Motor', 'my_motor_ctrl_1', '1', 'phi', 'BL99/EH/PHI'])
Returns: None
-
CreateMotorGroup
(argin)[source]¶ Tango command to create motor group.
Parameters: argin (list<str>) – Must give either:
- A JSON encoded dict as first string with:
- mandatory keys: ‘name’, ‘elements’ (with value being a list of moveables)
- optional keys:
- ‘full_name’: with value being a full tango device name
- a sequence of strings: <motor group name> [, <element> ]”
Examples:
data = dict(name='diffrac_motor_group', elements=['theta', 'theta2', 'phi']) pool.CreateMotorGroup([json.dumps(data)]) pool.CreateMotorGroup(['diffrac_mg', 'theta', 'theta2' ])
Returns: None
-
CreateMeasurementGroup
(argin)[source]¶ Tango command to create measurement group.
Parameters: argin (list<str>) – Must give either:
- A JSON encoded dict as first string with:
- mandatory keys: ‘name’, ‘elements’ (with value being a list of acquirables)”
- optional keys:
- ‘full_name’: with value being a full tango device name
- a sequence of strings: <motor group name> [, <element> ]”
An acquirable is either a sardana element (counter/timer, 0D, 1D, 2D, motor) or a tango attribute (ex: sys/tg_test/1/short_spectrum_ro)
Examples:
data = dict(name='my_exp_01', elements=['timer', 'C1', 'sys/tg_test/1/double_scalar']) pool.CreateMeasurementGroup([json.dumps(data)]) pool.CreateMeasurementGroup(['my_exp_02', 'timer', 'CCD1', 'sys/tg_test/1/short_spectrum_ro'])
Returns: None
-
DeleteElement
(name)[source]¶ Tango command to delete element.
Parameters: argin (str) – name of element to be deleted Returns: None
-
GetControllerClassInfo
(names)[source]¶ Tango command to get detailed information about a controller class.
Parameters: argin (str) – Must give either:
- A JSON encoded list of controller class names
- a controller class name
Examples:
data = "DummyMotorController", "DummyCounterTimerController" pool.GetControllerClassInfo(json.dumps(data)) pool.GetControllerClassInfo("DummyMotorController")
Returns: a JSON encoded string describing the controller class
Return type: str
-
ReloadControllerLib
(lib_name)[source]¶ Tango command to reload the controller library code.
Parameters: argin (str) – the controller library name (without extension) Returns: None
-
PoolDevice
¶Generic Tango Pool Device base classes
Classes
-
class
PoolDevice
(dclass, name)[source]¶ Bases:
sardana.tango.core.SardanaDevice.SardanaDevice
Base Tango Pool device class
-
ExtremeErrorStates
= (<_mock._Mock object at 0x7f23ab9129d0>, <_mock._Mock object at 0x7f23ab912610>)¶ list of extreme error states
-
BusyStates
= (<_mock._Mock object at 0x7f23ab912f10>, <_mock._Mock object at 0x7f23ab912d10>)¶ list of busy states
-
BusyRetries
= 3¶ Maximum number of retries in a busy state
-
init
(name)[source]¶ initialize the device once in the object lifetime. Override when necessary but always call the method from your super class
Parameters: name (str) – device name
-
pool_device
¶ The tango pool device
-
pool
¶ The sardana pool object
-
get_element
()[source]¶ Returns the underlying pool element object
Returns: the underlying pool element object Return type: PoolElement
-
set_element
(element)[source]¶ Associates this device with the sardana element
Parameters: element ( PoolElement
) – the sardana element
-
element
¶ The underlying sardana element
-
init_device
()[source]¶ Initialize the device. Called during startup after
init()
and every time the tangoInit
command is executed. Override when necessary but always call the method from your super class
-
delete_device
()[source]¶ Clean the device. Called during shutdown and every time the tango
Init
command is executed. Override when necessary but always call the method from your super class
-
is_Abort_allowed
()[source]¶ Returns True if it is allowed to execute the tango abort command
Returns: True if it is allowed to execute the tango abort command or False otherwise Return type: bool
-
is_Stop_allowed
()[source]¶ Returns True if it is allowed to execute the tango stop command
Returns: True if it is allowed to execute the tango stop command or False otherwise Return type: bool
-
get_dynamic_attributes
()[source]¶ Returns the standard dynamic and fully dynamic attributes for this device. The return is a tuple of two dictionaries:
- standard attributes: caseless dictionary with key being the attribute name and value is a tuple of attribute name(str), tango information, attribute information
- dynamic attributes: caseless dictionary with key being the attribute name and value is a tuple of attribute name(str), tango information, attribute information
- tango information
- seq<
CmdArgType
,AttrDataFormat
,AttrWriteType
> - attribute information
- attribute information as returned by the sardana controller
Returns: the standard dynamic and fully dynamic attributes Return type: seq< CaselessDict
,CaselessDict
>
-
remove_unwanted_dynamic_attributes
(new_std_attrs, new_dyn_attrs)[source]¶ Removes unwanted dynamic attributes from previous device creation
-
add_dynamic_attribute
(attr_name, data_info, attr_info, read, write, is_allowed)[source]¶ Adds a single dynamic attribute
Parameters: - attr_name (str) – the attribute name
- data_info (seq<
CmdArgType
,AttrDataFormat
,AttrWriteType
>) – tango attribute information - attr_info – attribute information
- read – read method for the attribute
- write – write method for the attribute
- is_allowed – is allowed method
-
add_standard_attribute
(attr_name, data_info, attr_info, read, write, is_allowed)[source]¶ Adds a single standard dynamic attribute
Parameters: - attr_name (str) – the attribute name
- data_info (seq<
CmdArgType
,AttrDataFormat
,AttrWriteType
>) – tango attribute information - attr_info – attribute information
- read – read method for the attribute
- write – write method for the attribute
- is_allowed – is allowed method
-
read_DynamicAttribute
(attr)[source]¶ Generic read dynamic attribute. Default implementation raises
NotImplementedError
Parameters: attr ( Attribute
) – attribute to be readRaises: NotImplementedError
-
write_DynamicAttribute
(attr)[source]¶ Generic write dynamic attribute. Default implementation raises
NotImplementedError
Parameters: attr ( Attribute
) – attribute to be writtenRaises: NotImplementedError
-
is_DynamicAttribute_allowed
(req_type)[source]¶ Generic is dynamic attribute allowed. Default implementation calls
_is_allowed()
Parameters: req_type – request type
-
dev_state
()[source]¶ Calculates and returns the device state. Called by Tango on a read state request.
Returns: the device state Return type: DevState
-
dev_status
()[source]¶ Calculates and returns the device status. Called by Tango on a read status request.
Returns: the device status Return type: str
-
wait_for_operation
()[source]¶ Waits for an operation to finish. It uses the maxumum number of retries. Sleeps 0.01s between retries.
Raises: Exception
in case of a timeout
-
-
class
PoolDeviceClass
(name)[source]¶ Bases:
sardana.tango.core.SardanaDevice.SardanaDeviceClass
Base Tango Pool Device Class class
-
class_property_list
= {}¶ Sardana device class properties definition
See also
-
device_property_list
= {'Force_HW_Read': [<_mock._Mock object at 0x7f23b160db90>, 'Force a hardware read of value even when in operation (motion/acquisition', False], 'Id': [<_mock._Mock object at 0x7f23b160dd10>, 'Internal ID', 0]}¶ Sardana device properties definition
See also
-
cmd_list
= {'Restore': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Abort': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Stop': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']]}¶ Sardana device command definition
See also
-
attr_list
= {}¶ Sardana device attribute definition
See also
-
standard_attr_list
= {}¶
-
-
class
PoolElementDevice
(dclass, name)[source]¶ Bases:
sardana.tango.pool.PoolDevice.PoolDevice
Base Tango Pool Element Device class
-
init_device
()[source]¶ Initialize the device. Called during startup after
init()
and every time the tangoInit
command is executed. Override when necessary but always call the method from your super class
-
read_Instrument
(attr)[source]¶ Read the value of the
Instrument
tango attribute. Returns the instrument full name or empty string if this element doesn’t belong to any instrumentParameters: attr ( Attribute
) – tango instrument attribute
-
write_Instrument
(attr)[source]¶ Write the value of the
Instrument
tango attribute. Sets a new instrument full name or empty string if this element doesn’t belong to any instrument. The instrument must have been previously created.Parameters: attr ( Attribute
) – tango instrument attribute
-
get_dynamic_attributes
()[source]¶ Override of
PoolDevice.get_dynamic_attributes
. Returns the standard dynamic and fully dynamic attributes for this device. The return is a tuple of two dictionaries:- standard attributes: caseless dictionary with key being the attribute name and value is a tuple of attribute name(str), tango information, attribute information
- dynamic attributes: caseless dictionary with key being the attribute name and value is a tuple of attribute name(str), tango information, attribute information
- tango information
- seq<
CmdArgType
,AttrDataFormat
,AttrWriteType
> - attribute information
- attribute information as returned by the sardana controller
Returns: the standard dynamic and fully dynamic attributes Return type: seq< CaselessDict
,CaselessDict
>
-
read_DynamicAttribute
(attr)[source]¶ Read a generic dynamic attribute. Calls the controller of this element to get the dynamic attribute value
Parameters: attr ( Attribute
) – tango attribute
-
write_DynamicAttribute
(attr)[source]¶ Write a generic dynamic attribute. Calls the controller of this element to get the dynamic attribute value
Parameters: attr ( Attribute
) – tango attribute
-
-
class
PoolElementDeviceClass
(name)[source]¶ Bases:
sardana.tango.pool.PoolDevice.PoolDeviceClass
Base Tango Pool Element Device Class class
-
device_property_list
= {'Instrument_id': [<_mock._Mock object at 0x7f23b160dd10>, 'Controller ID', [0]], 'Ctrl_id': [<_mock._Mock object at 0x7f23b160dd10>, 'Controller ID', [0]], 'Force_HW_Read': [<_mock._Mock object at 0x7f23b160db90>, 'Force a hardware read of value even when in operation (motion/acquisition', False], 'Id': [<_mock._Mock object at 0x7f23b160dd10>, 'Internal ID', 0], 'Axis': [<_mock._Mock object at 0x7f23b160dd10>, 'Axis in the controller', [0]]}¶ Sardana device properties definition
See also
-
attr_list
= {'Instrument': [[<_mock._Mock object at 0x7f23b160dd90>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'Display level': <_mock._Mock object at 0x7f23ab912e50>, 'label': 'Instrument'}], 'SimulationMode': [[<_mock._Mock object at 0x7f23b160db90>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'label': 'Simulation mode'}]}¶ Sardana device attribute definition
See also
-
cmd_list
= {'Restore': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Abort': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Stop': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']]}¶
-
-
class
PoolGroupDeviceClass
(name)[source]¶ Bases:
sardana.tango.pool.PoolDevice.PoolDeviceClass
Base Tango Pool Group Device Class class
-
device_property_list
= {'Elements': [<_mock._Mock object at 0x7f23b1611150>, 'elements in the group', []], 'Force_HW_Read': [<_mock._Mock object at 0x7f23b160db90>, 'Force a hardware read of value even when in operation (motion/acquisition', False], 'Id': [<_mock._Mock object at 0x7f23b160dd10>, 'Internal ID', 0]}¶ Sardana device properties definition
See also
-
cmd_list
= {'Restore': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Abort': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Stop': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']]}¶ Sardana device command definition
See also
-
attr_list
= {'ElementList': [[<_mock._Mock object at 0x7f23b160dd90>, <_mock._Mock object at 0x7f23b1611410>, <_mock._Mock object at 0x7f23b1611310>, 4096]]}¶ Sardana device attribute definition
See also
-
Controller
¶Classes
-
class
ControllerClass
(name)[source]¶ Bases:
sardana.tango.pool.PoolDevice.PoolDeviceClass
-
class_property_list
= {}¶
-
device_property_list
= {'Role_ids': [<_mock._Mock object at 0x7f23b1611090>, '', []], 'Force_HW_Read': [<_mock._Mock object at 0x7f23b160db90>, 'Force a hardware read of value even when in operation (motion/acquisition', False], 'Klass': [<_mock._Mock object at 0x7f23b160dd90>, '', None], 'Type': [<_mock._Mock object at 0x7f23b160dd90>, '', None], 'Id': [<_mock._Mock object at 0x7f23b160dd10>, 'Internal ID', 0], 'Library': [<_mock._Mock object at 0x7f23b160dd90>, '', None]}¶
-
cmd_list
= {'CreateElement': [[<_mock._Mock object at 0x7f23b1611150>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'DeleteElement': [[<_mock._Mock object at 0x7f23b160dd90>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Abort': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Stop': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Restore': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']]}¶
-
attr_list
= {'LogLevel': [[<_mock._Mock object at 0x7f23b160dcd0>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'label': 'Log level', 'Display level': <_mock._Mock object at 0x7f23ab3a2690>, 'Memorized': 'true'}], 'ElementList': [[<_mock._Mock object at 0x7f23b160dd90>, <_mock._Mock object at 0x7f23b1611410>, <_mock._Mock object at 0x7f23b1611310>, 4096]]}¶
-
Motor
¶The sardana tango motor module
Classes
-
class
Motor
(dclass, name)[source]¶ Bases:
sardana.tango.pool.PoolDevice.PoolElementDevice
The tango motor device class. This class exposes through a tango device the sardana motor (
PoolMotor
).The states
The motor interface knows five states which are ON, MOVING, ALARM, FAULT and UNKNOWN. A motor device is in MOVING state when it is moving! It is in ALARM state when it has reached one of the limit switches and is in FAULT if its controller software is not available (impossible to load it) or if a fault is reported from the hardware controller. The motor is in the UNKNOWN state if an exception occurs during the communication between the pool and the hardware controller. When the motor is in ALARM state, its status will indicate which limit switches is active.
The commands
The motor interface supports 3 commands on top of the Tango classical Init, State and Status commands. These commands are summarized in the following table:
Command name Input data type Output data type Stop void void Abort void void DefinePosition Tango::DevDouble void SaveConfig void void - Stop : It stops a running motion. This command does not have input or output argument.
- Abort : It aborts a running motion. This command does not have input or output argument.
- DefinePosition : Loads a position into controller. It has one input argument which is the new position value (a double). It is allowed only in the ON or ALARM states. The unit used for the command input value is the physical unit: millimeters or milli-radians. It is always an absolute position.
- SaveConfig : Write some of the motor parameters in database. Today, it writes the motor acceleration, deceleration, base_rate and velocity into database as motor device properties. It is allowed only in the ON or ALARM states
The classical Tango Init command destroys the motor and re-create it.
The attributes
The motor interface supports several attributes which are summarized in the following table:
Name Data type Data format Writable Memorized Operator/Expert Position Tango::DevDouble Scalar R/W No * Operator DialPosition Tango::DevDouble Scalar R No Expert Offset Tango::DevDouble Scalar R/W Yes Expert Acceleration Tango::DevDouble Scalar R/W No Expert Base_rate Tango::DevDouble Scalar R/W No Expert Deceleration Tango::DevDouble Scalar R/W No Expert Velocity Tango::DevDouble Scalar R/W No Expert Limit_Switches Tango::DevBoolean Spectrum R No Expert SimulationMode Tango::DevBoolean Scalar R No Expert Step_per_unit Tango::DevDouble Scalar R/W Yes Expert Backlash Tango::DevLong Scalar R/W Yes Expert Position : This is read-write scalar double attribute. With the classical Tango min and max_value attribute properties, it is easy to define authorized limit for this attribute. See the definition of the DialPosition and Offset attributes to get a precise definition of the meaning of this attribute. It is not allowed to read or write this attribute when the motor is in FAULT or UNKNOWN state. It is also not possible to write this attribute when the motor is already MOVING. The unit used for this attribute is the physical unit: millimeters or milli-radian. It is always an absolute position .
DialPosition : This attribute is the motor dial position. The following formula links together the Position, DialPosition, Sign and Offset attributes:
Position = Sign * DialPosition + Offset
This allows to have the motor position centered around any position defined by the Offset attribute (classically the X ray beam position). It is a read only attribute. To set the motor position, the user has to use the Position attribute. It is not allowed to read this attribute when the motor is in FAULT or UNKNOWN mode. The unit used for this attribute is the physical unit: millimeters or milli-radian. It is also always an absolute position.
Offset : The offset to be applied in the motor position computation. By default set to 0. It is a memorized attribute. It is not allowed to read or write this attribute when the motor is in FAULT, MOVING or UNKNOWN mode.
Acceleration : This is an expert read-write scalar double attribute. This parameter value is written in database when the SaveConfig command is executed. It is not allowed to read or write this attribute when the motor is in FAULT or UNKNOWN state.
Deceleration : This is an expert read-write scalar double attribute. This parameter value is written in database when the SaveConfig command is executed. It is not allowed to read or write this attribute when the motor is in FAULT or UNKNOWN state.
Base_rate : This is an expert read-write scalar double attribute. This parameter value is written in database when the SaveConfig command is executed. It is not allowed to read or write this attribute when the motor is in FAULT or UNKNOWN state.
Velocity : This is an expert read-write scalar double attribute. This parameter value is written in database when the SaveConfig command is executed. It is not allowed to read or write this attribute when the motor is in FAULT or UNKNOWN state.
Limit_Switches : Three limit switches are managed by this attribute. Each of the switch are represented by a boolean value: False means inactive while True means active. It is a read only attribute. It is not possible to read this attribute when the motor is in UNKNOWN mode. It is a spectrum attribute with 3 values which are:
- Data[0] : The Home switch value
- Data[1] : The Upper switch value
- Data[2] : The Lower switch value
SimulationMode : This is a read only scalar boolean attribute. When set, all motion requests are not forwarded to the software controller and then to the hardware. When set, the motor position is simulated and is immediately set to the value written by the user. To set this attribute, the user has to used the pool device Tango interface. The value of the position, acceleration, deceleration, base_rate, velocity and offset attributes are memorized at the moment this attribute is set. When this mode is turned off, if the value of any of the previously memorized attributes has changed, it is reapplied to the memorized value. It is not allowed to read this attribute when the motor is in FAULT or UNKNOWN states.
Step_per_unit : This is the number of motor step per millimeter or per degree. It is a memorized attribute. It is not allowed to read or write this attribute when the motor is in FAULT or UNKNOWN mode. It is also not allowed to write this attribute when the motor is MOVING. The default value is 1.
Backlash : If this attribute is defined to something different than 0, the motor will always stop the motion coming from the same mechanical direction. This means that it could be possible to ask the motor to go a little bit after the desired position and then to return to the desired position. The attribute value is the number of steps the motor will pass the desired position if it arrives from the “wrong” direction. This is a signed value. If the sign is positive, this means that the authorized direction to stop the motion is the increasing motor position direction. If the sign is negative, this means that the authorized direction to stop the motion is the decreasing motor position direction. It is a memorized attribute. It is not allowed to read or write this attribute when the motor is in FAULT or UNKNOWN mode. It is also not allowed to write this attribute when the motor is MOVING. Some hardware motor controllers are able to manage this backlash feature. If it is not the case, the motor interface will implement this behavior.
All the motor devices will have the already described attributes but some hardware motor controller supports other features which are not covered by this list of pre-defined attributes. Using Tango dynamic attribute creation, a motor device may have extra attributes used to get/set the motor hardware controller specific features. These are the attributes specified on the controller with
axis_attribues
.The properties
- Sleep_before_last_read : This property exposes the motor instability time. It defines the time in milli-second that the software managing a motor movement will wait between it detects the end of the motion and the last motor position reading.
Getting motor state and limit switches using event
The simplest way to know if a motor is moving is to survey its state. If the motor is moving, its state will be MOVING. When the motion is over, its state will be back to ON (or ALARM if a limit switch has been reached). The pool motor interface allows client interested by motor state or motor limit switches value to use the Tango event system subscribing to motor state change event. As soon as a motor starts a motion, its state is changed to MOVING and an event is sent. As soon as the motion is over, the motor state is updated ans another event is sent. In the same way, as soon as a change in the limit switches value is detected, a change event is sent to client(s) which have subscribed to change event on the Limit_Switches attribute.
Reading the motor position attribute
For each motor, the key attribute is its position. Special care has been taken on this attribute management. When the motor is not moving, reading the Position attribute will generate calls to the controller and therefore hardware access. When the motor is moving, its position is automatically read every 100 milli-seconds and stored in the Tango polling buffer. This means that a client reading motor Position attribute while the motor is moving will get the position from the Tango polling buffer and will not generate extra controller calls. It is also possible to get a motor position using the Tango event system. When the motor is moving, an event is sent to the registered clients when the change event criterion is true. By default, this change event criterion is set to be a difference in position of 5. It is tunable on a motor basis using the classical motor Position attribute abs_change property or at the pool device basis using its DefaultMotPos_AbsChange property. Anyway, not more than 10 events could be sent by second. Once the motion is over, the motor position is made unavailable from the Tango polling buffer and is read a last time after a tunable waiting time (Sleep_bef_last_read property). A forced change event with this value is sent to clients using events.
-
motor
¶
-
delete_device
¶
-
init_device
¶
-
is_Position_allowed
(req_type)¶
-
is_Acceleration_allowed
(req_type)¶
-
is_Deceleration_allowed
(req_type)¶
-
is_Base_rate_allowed
(req_type)¶
-
is_Velocity_allowed
(req_type)¶
-
is_Offset_allowed
(req_type)¶
-
is_DialPosition_allowed
(req_type)¶
-
is_Step_per_unit_allowed
(req_type)¶
-
is_Backlash_allowed
(req_type)¶
-
is_Sign_allowed
(req_type)¶
-
is_Limit_switches_allowed
(req_type)¶
-
class
MotorClass
(name)[source]¶ Bases:
sardana.tango.pool.PoolDevice.PoolElementDeviceClass
-
class_property_list
= {}¶
-
device_property_list
= {'Instrument_id': [<_mock._Mock object at 0x7f23b160dd10>, 'Controller ID', [0]], 'Ctrl_id': [<_mock._Mock object at 0x7f23b160dd10>, 'Controller ID', [0]], '_Velocity': [<_mock._Mock object at 0x7f23b160dbd0>, '', -1], '_Base_rate': [<_mock._Mock object at 0x7f23b160dbd0>, '', -1], 'Force_HW_Read': [<_mock._Mock object at 0x7f23b160db90>, 'Force a hardware read of value even when in operation (motion/acquisition', False], 'Sleep_bef_last_read': [<_mock._Mock object at 0x7f23b160dcd0>, 'Number of mS to sleep before the last read during a motor movement', 0], '_Deceleration': [<_mock._Mock object at 0x7f23b160dbd0>, '', -1], '_Acceleration': [<_mock._Mock object at 0x7f23b160dbd0>, '', -1], 'Id': [<_mock._Mock object at 0x7f23b160dd10>, 'Internal ID', 0], 'Axis': [<_mock._Mock object at 0x7f23b160dd10>, 'Axis in the controller', [0]]}¶
-
cmd_list
= {'Restore': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Abort': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'DefinePosition': [[<_mock._Mock object at 0x7f23b160dbd0>, 'New position'], [<_mock._Mock object at 0x7f23b1611250>, '']], 'MoveRelative': [[<_mock._Mock object at 0x7f23b160dbd0>, 'amount to move'], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Stop': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'SaveConfig': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']]}¶
-
attr_list
= {'Instrument': [[<_mock._Mock object at 0x7f23b160dd90>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'Display level': <_mock._Mock object at 0x7f23ab912e50>, 'label': 'Instrument'}], 'SimulationMode': [[<_mock._Mock object at 0x7f23b160db90>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'label': 'Simulation mode'}]}¶
-
standard_attr_list
= {'Acceleration': [[<_mock._Mock object at 0x7f23b160dbd0>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'Memorized': 'true'}], 'DialPosition': [[<_mock._Mock object at 0x7f23b160dbd0>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611310>], {'Display level': <_mock._Mock object at 0x7f23aa947350>, 'label': 'Dial position'}], 'Position': [[<_mock._Mock object at 0x7f23b160dbd0>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'abs_change': '1.0'}], 'Velocity': [[<_mock._Mock object at 0x7f23b160dbd0>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'Memorized': 'true'}], 'Step_per_unit': [[<_mock._Mock object at 0x7f23b160dbd0>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'label': 'Steps p/ unit', 'Display level': <_mock._Mock object at 0x7f23aa947810>, 'Memorized': 'true'}], 'Base_rate': [[<_mock._Mock object at 0x7f23b160dbd0>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'label': 'Base rate', 'Memorized': 'true'}], 'Sign': [[<_mock._Mock object at 0x7f23b160dd50>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'Display level': <_mock._Mock object at 0x7f23aa947590>, 'Memorized': 'true'}], 'Limit_switches': [[<_mock._Mock object at 0x7f23b160db90>, <_mock._Mock object at 0x7f23b1611410>, <_mock._Mock object at 0x7f23b1611310>, 3], {'description': "This attribute is the motor limit switches state. It's an array with 3 \nelements which are:\n0 - The home switch\n1 - The upper limit switch\n2 - The lower limit switch\nFalse means not active. True means active", 'label': 'Limit switches (H,U,L)'}], 'Deceleration': [[<_mock._Mock object at 0x7f23b160dbd0>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'Memorized': 'true'}], 'Offset': [[<_mock._Mock object at 0x7f23b160dbd0>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'Display level': <_mock._Mock object at 0x7f23aa947950>, 'Memorized': 'true'}], 'Backlash': [[<_mock._Mock object at 0x7f23b160dcd0>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'Display level': <_mock._Mock object at 0x7f23aa947b10>, 'Memorized': 'true'}]}¶
-
IORegister
¶Classes
-
class
IORegisterClass
(name)[source]¶ Bases:
sardana.tango.pool.PoolDevice.PoolElementDeviceClass
-
class_property_list
= {}¶
-
device_property_list
= {'Instrument_id': [<_mock._Mock object at 0x7f23b160dd10>, 'Controller ID', [0]], 'Ctrl_id': [<_mock._Mock object at 0x7f23b160dd10>, 'Controller ID', [0]], 'Force_HW_Read': [<_mock._Mock object at 0x7f23b160db90>, 'Force a hardware read of value even when in operation (motion/acquisition', False], 'Id': [<_mock._Mock object at 0x7f23b160dd10>, 'Internal ID', 0], 'Axis': [<_mock._Mock object at 0x7f23b160dd10>, 'Axis in the controller', [0]]}¶
-
cmd_list
= {'Start': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Abort': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Stop': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Restore': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']]}¶
-
attr_list
= {'Instrument': [[<_mock._Mock object at 0x7f23b160dd90>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'Display level': <_mock._Mock object at 0x7f23ab912e50>, 'label': 'Instrument'}], 'SimulationMode': [[<_mock._Mock object at 0x7f23b160db90>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'label': 'Simulation mode'}]}¶
-
standard_attr_list
= {'Value': [[<_mock._Mock object at 0x7f23b160dcd0>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'Memorized': 'true_without_hard_applied'}]}¶
-
CTExpChannel
¶Classes
-
class
CTExpChannelClass
(name)[source]¶ Bases:
sardana.tango.pool.PoolDevice.PoolElementDeviceClass
-
class_property_list
= {}¶
-
device_property_list
= {'Instrument_id': [<_mock._Mock object at 0x7f23b160dd10>, 'Controller ID', [0]], 'Ctrl_id': [<_mock._Mock object at 0x7f23b160dd10>, 'Controller ID', [0]], 'Force_HW_Read': [<_mock._Mock object at 0x7f23b160db90>, 'Force a hardware read of value even when in operation (motion/acquisition', False], 'Id': [<_mock._Mock object at 0x7f23b160dd10>, 'Internal ID', 0], 'Axis': [<_mock._Mock object at 0x7f23b160dd10>, 'Axis in the controller', [0]]}¶
-
cmd_list
= {'Start': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Abort': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Stop': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Restore': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']]}¶
-
attr_list
= {'Instrument': [[<_mock._Mock object at 0x7f23b160dd90>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'Display level': <_mock._Mock object at 0x7f23ab912e50>, 'label': 'Instrument'}], 'SimulationMode': [[<_mock._Mock object at 0x7f23b160db90>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'label': 'Simulation mode'}]}¶
-
standard_attr_list
= {'Value': [[<_mock._Mock object at 0x7f23b160dbd0>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611310>], {'abs_change': '1.0'}]}¶
-
ZeroDExpChannel
¶Classes
-
class
ZeroDExpChannel
(dclass, name)[source]¶ Bases:
sardana.tango.pool.PoolDevice.PoolElementDevice
-
zerod
¶
-
delete_device
¶
-
init_device
¶
-
is_Value_allowed
(req_type)¶
-
is_CurrentValue_allowed
(req_type)¶
-
is_CumulationType_allowed
(req_type)¶
-
is_ValueBuffer_allowed
(req_type)¶
-
is_TimeBuffer_allowed
(req_type)¶
-
-
class
ZeroDExpChannelClass
(name)[source]¶ Bases:
sardana.tango.pool.PoolDevice.PoolElementDeviceClass
-
class_property_list
= {}¶
-
device_property_list
= {'Instrument_id': [<_mock._Mock object at 0x7f23b160dd10>, 'Controller ID', [0]], 'Ctrl_id': [<_mock._Mock object at 0x7f23b160dd10>, 'Controller ID', [0]], 'Force_HW_Read': [<_mock._Mock object at 0x7f23b160db90>, 'Force a hardware read of value even when in operation (motion/acquisition', False], 'Id': [<_mock._Mock object at 0x7f23b160dd10>, 'Internal ID', 0], 'Axis': [<_mock._Mock object at 0x7f23b160dd10>, 'Axis in the controller', [0]]}¶
-
cmd_list
= {'Start': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Abort': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Stop': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Restore': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']]}¶
-
attr_list
= {'Instrument': [[<_mock._Mock object at 0x7f23b160dd90>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'Display level': <_mock._Mock object at 0x7f23ab912e50>, 'label': 'Instrument'}], 'CumulationType': [[<_mock._Mock object at 0x7f23b160dd90>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'label': 'Cumulation Type', 'Display level': <_mock._Mock object at 0x7f23a9b0c110>, 'Memorized': 'true'}], 'SimulationMode': [[<_mock._Mock object at 0x7f23b160db90>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'label': 'Simulation mode'}], 'ValueBuffer': [[<_mock._Mock object at 0x7f23b160dbd0>, <_mock._Mock object at 0x7f23b1611410>, <_mock._Mock object at 0x7f23b1611310>, 16384]], 'TimeBuffer': [[<_mock._Mock object at 0x7f23b160dbd0>, <_mock._Mock object at 0x7f23b1611410>, <_mock._Mock object at 0x7f23b1611310>, 16384]]}¶
-
standard_attr_list
= {'Value': [[<_mock._Mock object at 0x7f23b1611250>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611310>], {'abs_change': '1.0'}]}¶
-
OneDExpChannel
¶Classes
-
class
OneDExpChannelClass
(name)[source]¶ Bases:
sardana.tango.pool.PoolDevice.PoolElementDeviceClass
-
class_property_list
= {}¶
-
device_property_list
= {'Instrument_id': [<_mock._Mock object at 0x7f23b160dd10>, 'Controller ID', [0]], 'Ctrl_id': [<_mock._Mock object at 0x7f23b160dd10>, 'Controller ID', [0]], 'Force_HW_Read': [<_mock._Mock object at 0x7f23b160db90>, 'Force a hardware read of value even when in operation (motion/acquisition', False], 'Id': [<_mock._Mock object at 0x7f23b160dd10>, 'Internal ID', 0], 'Axis': [<_mock._Mock object at 0x7f23b160dd10>, 'Axis in the controller', [0]]}¶
-
cmd_list
= {'Start': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Abort': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Stop': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Restore': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']]}¶
-
attr_list
= {'Instrument': [[<_mock._Mock object at 0x7f23b160dd90>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'Display level': <_mock._Mock object at 0x7f23ab912e50>, 'label': 'Instrument'}], 'DataSource': [[<_mock._Mock object at 0x7f23b160dd90>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611310>]], 'SimulationMode': [[<_mock._Mock object at 0x7f23b160db90>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'label': 'Simulation mode'}]}¶
-
standard_attr_list
= {'Value': [[<_mock._Mock object at 0x7f23b1611250>, <_mock._Mock object at 0x7f23b1611290>, <_mock._Mock object at 0x7f23b1611310>, 16384], {'abs_change': '1.0'}]}¶
-
TwoDExpChannel
¶Classes
-
class
TwoDExpChannelClass
(name)[source]¶ Bases:
sardana.tango.pool.PoolDevice.PoolElementDeviceClass
-
class_property_list
= {}¶
-
device_property_list
= {'Instrument_id': [<_mock._Mock object at 0x7f23b160dd10>, 'Controller ID', [0]], 'Ctrl_id': [<_mock._Mock object at 0x7f23b160dd10>, 'Controller ID', [0]], 'Force_HW_Read': [<_mock._Mock object at 0x7f23b160db90>, 'Force a hardware read of value even when in operation (motion/acquisition', False], 'Id': [<_mock._Mock object at 0x7f23b160dd10>, 'Internal ID', 0], 'Axis': [<_mock._Mock object at 0x7f23b160dd10>, 'Axis in the controller', [0]]}¶
-
cmd_list
= {'Start': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Abort': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Stop': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Restore': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']]}¶
-
attr_list
= {'Instrument': [[<_mock._Mock object at 0x7f23b160dd90>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'Display level': <_mock._Mock object at 0x7f23ab912e50>, 'label': 'Instrument'}], 'DataSource': [[<_mock._Mock object at 0x7f23b160dd90>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611310>]], 'SimulationMode': [[<_mock._Mock object at 0x7f23b160db90>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'label': 'Simulation mode'}]}¶
-
standard_attr_list
= {'Value': [[<_mock._Mock object at 0x7f23b1611250>, <_mock._Mock object at 0x7f23b1611290>, <_mock._Mock object at 0x7f23b1611310>, 4096, 4096], {'abs_change': '1.0'}]}¶
-
PseudoMotor
¶Classes
-
class
PseudoMotor
(dclass, name)[source]¶ Bases:
sardana.tango.pool.PoolDevice.PoolElementDevice
-
pseudo_motor
¶
-
delete_device
¶
-
init_device
¶
-
CalcPseudo
(physical_positions)[source]¶ Returns the pseudo motor position for the given physical positions
-
CalcPhysical
(pseudo_position)[source]¶ Returns the physical motor positions for the given pseudo motor position assuming the current pseudo motor write positions for all the other sibling pseudo motors
-
CalcAllPhysical
(pseudo_positions)[source]¶ Returns the physical motor positions for the given pseudo motor position(s)
-
CalcAllPseudo
(physical_positions)[source]¶ Returns the pseudo motor position(s) for the given physical positions
-
is_Position_allowed
(req_type)¶
-
-
class
PseudoMotorClass
(name)[source]¶ Bases:
sardana.tango.pool.PoolDevice.PoolElementDeviceClass
-
class_property_list
= {}¶
-
device_property_list
= {'Instrument_id': [<_mock._Mock object at 0x7f23b160dd10>, 'Controller ID', [0]], 'Ctrl_id': [<_mock._Mock object at 0x7f23b160dd10>, 'Controller ID', [0]], 'Elements': [<_mock._Mock object at 0x7f23b1611150>, 'elements used by the pseudo', []], 'Force_HW_Read': [<_mock._Mock object at 0x7f23b160db90>, 'Force a hardware read of value even when in operation (motion/acquisition', False], 'Id': [<_mock._Mock object at 0x7f23b160dd10>, 'Internal ID', 0], 'DriftCorrection': [<_mock._Mock object at 0x7f23b160db90>, 'Locally apply drift correction on pseudo motors. Default is the current global drift correction in the Pool Device', None], 'Axis': [<_mock._Mock object at 0x7f23b160dd10>, 'Axis in the controller', [0]]}¶
-
cmd_list
= {'Restore': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'CalcAllPseudo': [[<_mock._Mock object at 0x7f23b160df50>, 'physical positions'], [<_mock._Mock object at 0x7f23b160df50>, 'pseudo positions']], 'CalcPhysical': [[<_mock._Mock object at 0x7f23b160dbd0>, 'pseudo position'], [<_mock._Mock object at 0x7f23b160df50>, 'physical positions']], 'Stop': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Abort': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'MoveRelative': [[<_mock._Mock object at 0x7f23b160dbd0>, 'amount to move'], [<_mock._Mock object at 0x7f23b1611250>, '']], 'CalcAllPhysical': [[<_mock._Mock object at 0x7f23b160df50>, 'pseudo positions'], [<_mock._Mock object at 0x7f23b160df50>, 'physical positions']], 'CalcPseudo': [[<_mock._Mock object at 0x7f23b160df50>, 'physical positions'], [<_mock._Mock object at 0x7f23b160dbd0>, 'pseudo position']]}¶
-
standard_attr_list
= {'Position': [[<_mock._Mock object at 0x7f23b160dbd0>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611390>], {'abs_change': '1.0', 'label': 'Position'}]}¶
-
PseudoCounter
¶Classes
-
class
PseudoCounterClass
(name)[source]¶ Bases:
sardana.tango.pool.PoolDevice.PoolElementDeviceClass
-
class_property_list
= {}¶
-
device_property_list
= {'Instrument_id': [<_mock._Mock object at 0x7f23b160dd10>, 'Controller ID', [0]], 'Ctrl_id': [<_mock._Mock object at 0x7f23b160dd10>, 'Controller ID', [0]], 'Elements': [<_mock._Mock object at 0x7f23b1611150>, 'elements used by the pseudo', []], 'Force_HW_Read': [<_mock._Mock object at 0x7f23b160db90>, 'Force a hardware read of value even when in operation (motion/acquisition', False], 'Id': [<_mock._Mock object at 0x7f23b160dd10>, 'Internal ID', 0], 'Axis': [<_mock._Mock object at 0x7f23b160dd10>, 'Axis in the controller', [0]]}¶
-
cmd_list
= {'Abort': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'Restore': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'CalcAllPseudo': [[<_mock._Mock object at 0x7f23b160df50>, 'physical positions'], [<_mock._Mock object at 0x7f23b160df50>, 'pseudo counter values']], 'Stop': [[<_mock._Mock object at 0x7f23b1611250>, ''], [<_mock._Mock object at 0x7f23b1611250>, '']], 'CalcPseudo': [[<_mock._Mock object at 0x7f23b160df50>, 'physical values'], [<_mock._Mock object at 0x7f23b160dbd0>, 'pseudo counter']]}¶
-
standard_attr_list
= {'Value': [[<_mock._Mock object at 0x7f23b160dbd0>, <_mock._Mock object at 0x7f23b16113d0>, <_mock._Mock object at 0x7f23b1611310>]]}¶
-
macroserver
¶Modules
macroexecutor
¶Functions
Classes
|
|
-
class
TangoMacroExecutor
(door_name=None)[source]¶ Macro executor implemented using Tango communication with the Door device
-
createCommonBuffer
()¶ Create a common buffer, where all the registered logs will be stored.
-
getCommonBuffer
()¶ - Get common buffer.
- Method getCommonBuffer can only be used if at least one buffer exists.
Returns: (seq<str>) list of strings with messages from all log levels See also
-
getExceptionStr
()¶ Get macro exception type representation (None if the macro state is not exception).
Returns: (str)
-
getLog
(log_level)¶ Get log messages.
Parameters: log_level – (str) string indicating the log level Returns: (seq<str>) list of strings with log messages
-
getResult
()¶ Get macro result.
Returns: (seq<str>) list of strings with Result messages
-
getState
()¶ Get macro execution state.
Returns: (str)
-
getStateBuffer
()¶ Get buffer (history) of macro execution states.
Returns: (seq<str>)
-
log_levels
= ['debug', 'output', 'info', 'warning', 'critical', 'error']¶
-
registerAll
()¶ Register for macro result, all log levels and common buffer.
-
registerLog
(log_level)¶ Start registering log messages.
Parameters: log_level – (str) string indicating the log level
-
registerResult
()¶ Register for macro result
-
run
(macro_name, macro_params=None, sync=True, timeout=inf)¶ Execute macro.
Parameters: - macro_name – (string) name of macro to be executed
- macro_params – (list<string>) macro parameters (default is macro_params=None for macros without parameters or with the default values)
- sync – (bool) whether synchronous or asynchronous call (default is sync=True)
- timeout –
- (float) timeout (in s) that will be passed to the wait
- method, in case of synchronous execution
In asyncrhonous execution method
wait()
has to be explicitly called.
-
stop
(started_event_timeout=3.0)¶ Stop macro execution. Execute macro in synchronous way before using this method.
Parameters: started_event_timeout – (float) waiting timeout for started event
-
unregisterAll
()¶ Unregister macro result, all log levels and common buffer.
-
unregisterLog
(log_level)¶ Stop registering log messages.
Parameters: log_level – (str) string indicating the log level
-
unregisterResult
()¶ Unregister macro result.
-
wait
(timeout=inf)¶ Wait until macro is done. Use it in asynchronous executions.
Parameters: timeout – (float) waiting timeout (in s)
-
Modules
sardanadefs
¶This module contains the most generic sardana constants and enumerations
Constants
-
EpsilonError
= 1e-16¶ maximum difference between two floats so that they are considered equal
-
InvalidId
= 0¶ A constant representing an invalid ID
-
InvalidAxis
= 0¶ A constant representing an invalid axis
-
TYPE_ELEMENTS
= set([<_mock._Mock object at 0x7f23b145d610>, <_mock._Mock object at 0x7f23b145d650>, <_mock._Mock object at 0x7f23b145d810>, <_mock._Mock object at 0x7f23b145d690>, <_mock._Mock object at 0x7f23b145d6d0>, <_mock._Mock object at 0x7f23b145d710>, <_mock._Mock object at 0x7f23b145d750>, <_mock._Mock object at 0x7f23b145d790>, <_mock._Mock object at 0x7f23b145d7d0>, <_mock._Mock object at 0x7f23b145d850>])¶ a set containning all “controllable” element types. Constant values belong to
ElementType
-
TYPE_GROUP_ELEMENTS
= set([<_mock._Mock object at 0x7f23b145d890>, <_mock._Mock object at 0x7f23b145d8d0>])¶ a set containing all group element types. Constant values belong to
ElementType
-
TYPE_MOVEABLE_ELEMENTS
= set([<_mock._Mock object at 0x7f23b145d910>, <_mock._Mock object at 0x7f23b145d950>, <_mock._Mock object at 0x7f23b145d990>])¶ a set containing the type of elements which are moveable. Constant values belong to
ElementType
-
TYPE_PHYSICAL_ELEMENTS
= set([<_mock._Mock object at 0x7f23b145da10>, <_mock._Mock object at 0x7f23b145da50>, <_mock._Mock object at 0x7f23b145da90>, <_mock._Mock object at 0x7f23b145dad0>, <_mock._Mock object at 0x7f23b145db10>, <_mock._Mock object at 0x7f23b145db50>, <_mock._Mock object at 0x7f23b145d9d0>])¶ a set containing the possible types of physical elements. Constant values belong to
ElementType
-
TYPE_ACQUIRABLE_ELEMENTS
= set([<_mock._Mock object at 0x7f23b145dc10>, <_mock._Mock object at 0x7f23b145dc50>, <_mock._Mock object at 0x7f23b145dc90>, <_mock._Mock object at 0x7f23b145dcd0>, <_mock._Mock object at 0x7f23b145dd10>, <_mock._Mock object at 0x7f23b145dd50>, <_mock._Mock object at 0x7f23b145dd90>, <_mock._Mock object at 0x7f23b145db90>, <_mock._Mock object at 0x7f23b145dbd0>])¶ a set containing the possible types of acquirable elements. Constant values belong to
ElementType
-
TYPE_PSEUDO_ELEMENTS
= set([<_mock._Mock object at 0x7f23b145dfd0>, <_mock._Mock object at 0x7f23b145f050>])¶ a set containing the possible types of pseudo elements. Constant values belong to
ElementType
-
SardanaServer
= SardanaServer()¶ the global object containing the SardanaServer information
Enumerations
-
ServerRunMode
= <taurus.core.util.enumeration.Enumeration object>¶
-
State
= <taurus.core.util.enumeration.Enumeration object>¶
-
DataType
= <taurus.core.util.enumeration.Enumeration object>¶
-
DataFormat
= <taurus.core.util.enumeration.Enumeration object>¶
-
DataAccess
= <taurus.core.util.enumeration.Enumeration object>¶
-
ElementType
= <taurus.core.util.enumeration.Enumeration object>¶
-
Interface
= <taurus.core.util.enumeration.Enumeration object>¶
-
Interfaces
= {<_mock._Mock object at 0x7f23b146c810>: set([<_mock._Mock object at 0x7f23b146ca10>, <_mock._Mock object at 0x7f23b146ca50>]), <_mock._Mock object at 0x7f23b1468050>: set([<_mock._Mock object at 0x7f23b1468290>]), <_mock._Mock object at 0x7f23b146a410>: set([<_mock._Mock object at 0x7f23b146a610>, <_mock._Mock object at 0x7f23b146a650>]), <_mock._Mock object at 0x7f23b146a090>: set([<_mock._Mock object at 0x7f23b146a350>]), <_mock._Mock object at 0x7f23b145f0d0>: set([<_mock._Mock object at 0x7f23b145f150>]), <_mock._Mock object at 0x7f23b145f910>: set([<_mock._Mock object at 0x7f23b145fb90>, <_mock._Mock object at 0x7f23b145fbd0>]), <_mock._Mock object at 0x7f23b146c590>: set([<_mock._Mock object at 0x7f23b146c850>]), <_mock._Mock object at 0x7f23b146c190>: set([]), <_mock._Mock object at 0x7f23b146c9d0>: set([<_mock._Mock object at 0x7f23b146cc10>, <_mock._Mock object at 0x7f23b146cc50>]), <_mock._Mock object at 0x7f23b145f210>: set([<_mock._Mock object at 0x7f23b145f490>]), <_mock._Mock object at 0x7f23b1468250>: set([<_mock._Mock object at 0x7f23b1468450>]), <_mock._Mock object at 0x7f23b1468a90>: set([<_mock._Mock object at 0x7f23b1468d10>, <_mock._Mock object at 0x7f23b1468d50>]), <_mock._Mock object at 0x7f23b146a310>: set([<_mock._Mock object at 0x7f23b146a490>, <_mock._Mock object at 0x7f23b146a450>]), <_mock._Mock object at 0x7f23b145fb50>: set([<_mock._Mock object at 0x7f23b145fd50>]), <_mock._Mock object at 0x7f23b1468890>: set([<_mock._Mock object at 0x7f23b1468ad0>]), <_mock._Mock object at 0x7f23b146ce90>: set([<_mock._Mock object at 0x7f23b15e90d0>]), <_mock._Mock object at 0x7f23b146c3d0>: set([<_mock._Mock object at 0x7f23b146c610>, <_mock._Mock object at 0x7f23b146c5d0>]), <_mock._Mock object at 0x7f23b146c350>: set([<_mock._Mock object at 0x7f23b146c410>]), <_mock._Mock object at 0x7f23b1468410>: set([<_mock._Mock object at 0x7f23b1468590>]), <_mock._Mock object at 0x7f23b145f450>: set([<_mock._Mock object at 0x7f23b145f650>]), <_mock._Mock object at 0x7f23b146ac90>: set([<_mock._Mock object at 0x7f23b146add0>]), <_mock._Mock object at 0x7f23b1468cd0>: set([<_mock._Mock object at 0x7f23b1468ed0>]), <_mock._Mock object at 0x7f23b145fd10>: set([<_mock._Mock object at 0x7f23b145ff50>]), <_mock._Mock object at 0x7f23b1468550>: set([<_mock._Mock object at 0x7f23b14687d0>]), <_mock._Mock object at 0x7f23b146ad90>: set([<_mock._Mock object at 0x7f23b146af10>, <_mock._Mock object at 0x7f23b146aed0>]), <_mock._Mock object at 0x7f23b146cd90>: set([<_mock._Mock object at 0x7f23b146ced0>]), <_mock._Mock object at 0x7f23b146a5d0>: set([<_mock._Mock object at 0x7f23b146a8d0>]), <_mock._Mock object at 0x7f23b146aa50>: set([]), <_mock._Mock object at 0x7f23b145f610>: set([<_mock._Mock object at 0x7f23b145f790>]), <_mock._Mock object at 0x7f23b145f110>: set([<_mock._Mock object at 0x7f23b145f250>]), <_mock._Mock object at 0x7f23b146a890>: set([<_mock._Mock object at 0x7f23b146aa90>]), <_mock._Mock object at 0x7f23b1468e90>: set([<_mock._Mock object at 0x7f23b146a110>, <_mock._Mock object at 0x7f23b146a0d0>]), <_mock._Mock object at 0x7f23b146cbd0>: set([<_mock._Mock object at 0x7f23b146cdd0>]), <_mock._Mock object at 0x7f23b145ff10>: set([<_mock._Mock object at 0x7f23b1468090>]), <_mock._Mock object at 0x7f23b146c090>: set([<_mock._Mock object at 0x7f23b146c1d0>]), <_mock._Mock object at 0x7f23b145f750>: set([<_mock._Mock object at 0x7f23b145f950>]), <_mock._Mock object at 0x7f23b146ae90>: set([<_mock._Mock object at 0x7f23b146c0d0>]), <_mock._Mock object at 0x7f23b1468790>: set([<_mock._Mock object at 0x7f23b1468910>, <_mock._Mock object at 0x7f23b14688d0>]), <_mock._Mock object at 0x7f23b146ac10>: set([<_mock._Mock object at 0x7f23b146acd0>])}¶ a dictionary containing the direct interfaces supported by each type (
dict
<sardana.sardanadefs.Interface
,set
<sardana.sardanadefs.Interface
> >)
-
InterfacesExpanded
= {<_mock._Mock object at 0x7f23b146c810>: set([<_mock._Mock object at 0x7f23b146cb90>, <_mock._Mock object at 0x7f23b146ca90>, <_mock._Mock object at 0x7f23b146cad0>, <_mock._Mock object at 0x7f23b146cb50>, <_mock._Mock object at 0x7f23b146cb10>]), <_mock._Mock object at 0x7f23b1468050>: set([<_mock._Mock object at 0x7f23b1468310>, <_mock._Mock object at 0x7f23b14683d0>, <_mock._Mock object at 0x7f23b1468390>, <_mock._Mock object at 0x7f23b14682d0>, <_mock._Mock object at 0x7f23b1468350>]), <_mock._Mock object at 0x7f23b146a410>: set([<_mock._Mock object at 0x7f23b146a810>, <_mock._Mock object at 0x7f23b146a850>, <_mock._Mock object at 0x7f23b146a690>, <_mock._Mock object at 0x7f23b146a6d0>, <_mock._Mock object at 0x7f23b146a710>, <_mock._Mock object at 0x7f23b146a750>, <_mock._Mock object at 0x7f23b146a790>, <_mock._Mock object at 0x7f23b146a7d0>]), <_mock._Mock object at 0x7f23b146a090>: set([<_mock._Mock object at 0x7f23b146a390>, <_mock._Mock object at 0x7f23b146a3d0>]), <_mock._Mock object at 0x7f23b145f0d0>: set([<_mock._Mock object at 0x7f23b145f190>, <_mock._Mock object at 0x7f23b145f1d0>]), <_mock._Mock object at 0x7f23b145f910>: set([<_mock._Mock object at 0x7f23b145fc10>, <_mock._Mock object at 0x7f23b145fcd0>, <_mock._Mock object at 0x7f23b145fc50>, <_mock._Mock object at 0x7f23b145fc90>]), <_mock._Mock object at 0x7f23b146c590>: set([<_mock._Mock object at 0x7f23b146c950>, <_mock._Mock object at 0x7f23b146c890>, <_mock._Mock object at 0x7f23b146c990>, <_mock._Mock object at 0x7f23b146c8d0>, <_mock._Mock object at 0x7f23b146c910>]), <_mock._Mock object at 0x7f23b146c190>: set([<_mock._Mock object at 0x7f23b146c390>]), <_mock._Mock object at 0x7f23b146c9d0>: set([<_mock._Mock object at 0x7f23b146cc90>, <_mock._Mock object at 0x7f23b146cd50>, <_mock._Mock object at 0x7f23b146ccd0>, <_mock._Mock object at 0x7f23b146cd10>]), <_mock._Mock object at 0x7f23b145f210>: set([<_mock._Mock object at 0x7f23b145f510>, <_mock._Mock object at 0x7f23b145f5d0>, <_mock._Mock object at 0x7f23b145f4d0>, <_mock._Mock object at 0x7f23b145f590>, <_mock._Mock object at 0x7f23b145f550>]), <_mock._Mock object at 0x7f23b1468250>: set([<_mock._Mock object at 0x7f23b1468490>, <_mock._Mock object at 0x7f23b14684d0>, <_mock._Mock object at 0x7f23b1468510>]), <_mock._Mock object at 0x7f23b1468a90>: set([<_mock._Mock object at 0x7f23b1468d90>, <_mock._Mock object at 0x7f23b1468e50>, <_mock._Mock object at 0x7f23b1468dd0>, <_mock._Mock object at 0x7f23b1468e10>]), <_mock._Mock object at 0x7f23b146a310>: set([<_mock._Mock object at 0x7f23b146a510>, <_mock._Mock object at 0x7f23b146a4d0>, <_mock._Mock object at 0x7f23b146a590>, <_mock._Mock object at 0x7f23b146a550>]), <_mock._Mock object at 0x7f23b145fb50>: set([<_mock._Mock object at 0x7f23b145fe10>, <_mock._Mock object at 0x7f23b145fe50>, <_mock._Mock object at 0x7f23b145fe90>, <_mock._Mock object at 0x7f23b145fed0>, <_mock._Mock object at 0x7f23b145fd90>, <_mock._Mock object at 0x7f23b145fdd0>]), <_mock._Mock object at 0x7f23b1468890>: set([<_mock._Mock object at 0x7f23b1468c10>, <_mock._Mock object at 0x7f23b1468c50>, <_mock._Mock object at 0x7f23b1468c90>, <_mock._Mock object at 0x7f23b1468b10>, <_mock._Mock object at 0x7f23b1468b50>, <_mock._Mock object at 0x7f23b1468b90>, <_mock._Mock object at 0x7f23b1468bd0>]), <_mock._Mock object at 0x7f23b146ce90>: set([<_mock._Mock object at 0x7f23b15e9210>, <_mock._Mock object at 0x7f23b15e9250>, <_mock._Mock object at 0x7f23b15e9290>, <_mock._Mock object at 0x7f23b15e9110>, <_mock._Mock object at 0x7f23b15e9150>, <_mock._Mock object at 0x7f23b15e9190>, <_mock._Mock object at 0x7f23b15e91d0>]), <_mock._Mock object at 0x7f23b146c3d0>: set([<_mock._Mock object at 0x7f23b146c650>, <_mock._Mock object at 0x7f23b146c690>, <_mock._Mock object at 0x7f23b146c6d0>, <_mock._Mock object at 0x7f23b146c710>, <_mock._Mock object at 0x7f23b146c750>, <_mock._Mock object at 0x7f23b146c790>, <_mock._Mock object at 0x7f23b146c7d0>]), <_mock._Mock object at 0x7f23b146c350>: set([<_mock._Mock object at 0x7f23b146c490>, <_mock._Mock object at 0x7f23b146c550>, <_mock._Mock object at 0x7f23b146c450>, <_mock._Mock object at 0x7f23b146c510>, <_mock._Mock object at 0x7f23b146c4d0>]), <_mock._Mock object at 0x7f23b1468410>: set([<_mock._Mock object at 0x7f23b1468610>, <_mock._Mock object at 0x7f23b1468650>, <_mock._Mock object at 0x7f23b1468690>, <_mock._Mock object at 0x7f23b14686d0>, <_mock._Mock object at 0x7f23b1468710>, <_mock._Mock object at 0x7f23b1468750>, <_mock._Mock object at 0x7f23b14685d0>]), <_mock._Mock object at 0x7f23b145f450>: set([<_mock._Mock object at 0x7f23b145f690>, <_mock._Mock object at 0x7f23b145f6d0>, <_mock._Mock object at 0x7f23b145f710>]), <_mock._Mock object at 0x7f23b146ac90>: set([<_mock._Mock object at 0x7f23b146ae10>, <_mock._Mock object at 0x7f23b146ae50>]), <_mock._Mock object at 0x7f23b1468cd0>: set([<_mock._Mock object at 0x7f23b1468f10>, <_mock._Mock object at 0x7f23b1468fd0>, <_mock._Mock object at 0x7f23b146a050>, <_mock._Mock object at 0x7f23b1468f50>, <_mock._Mock object at 0x7f23b1468f90>]), <_mock._Mock object at 0x7f23b145fd10>: set([<_mock._Mock object at 0x7f23b145ff90>, <_mock._Mock object at 0x7f23b145ffd0>]), <_mock._Mock object at 0x7f23b1468550>: set([<_mock._Mock object at 0x7f23b1468810>, <_mock._Mock object at 0x7f23b1468850>]), <_mock._Mock object at 0x7f23b146ad90>: set([<_mock._Mock object at 0x7f23b146af90>, <_mock._Mock object at 0x7f23b146c050>, <_mock._Mock object at 0x7f23b146af50>, <_mock._Mock object at 0x7f23b146afd0>]), <_mock._Mock object at 0x7f23b146cd90>: set([<_mock._Mock object at 0x7f23b146cf10>, <_mock._Mock object at 0x7f23b146cfd0>, <_mock._Mock object at 0x7f23b15e9050>, <_mock._Mock object at 0x7f23b146cf50>, <_mock._Mock object at 0x7f23b146cf90>]), <_mock._Mock object at 0x7f23b146a5d0>: set([<_mock._Mock object at 0x7f23b146a9d0>, <_mock._Mock object at 0x7f23b146a910>, <_mock._Mock object at 0x7f23b146a950>, <_mock._Mock object at 0x7f23b146aa10>, <_mock._Mock object at 0x7f23b146a990>]), <_mock._Mock object at 0x7f23b146aa50>: set([<_mock._Mock object at 0x7f23b146ac50>]), <_mock._Mock object at 0x7f23b145f610>: set([<_mock._Mock object at 0x7f23b145f890>, <_mock._Mock object at 0x7f23b145f810>, <_mock._Mock object at 0x7f23b145f7d0>, <_mock._Mock object at 0x7f23b145f8d0>, <_mock._Mock object at 0x7f23b145f850>]), <_mock._Mock object at 0x7f23b145f110>: set([<_mock._Mock object at 0x7f23b145f410>, <_mock._Mock object at 0x7f23b145f290>, <_mock._Mock object at 0x7f23b145f2d0>, <_mock._Mock object at 0x7f23b145f310>, <_mock._Mock object at 0x7f23b145f350>, <_mock._Mock object at 0x7f23b145f390>, <_mock._Mock object at 0x7f23b145f3d0>]), <_mock._Mock object at 0x7f23b146a890>: set([<_mock._Mock object at 0x7f23b146ab90>, <_mock._Mock object at 0x7f23b146ab10>, <_mock._Mock object at 0x7f23b146aad0>, <_mock._Mock object at 0x7f23b146abd0>, <_mock._Mock object at 0x7f23b146ab50>]), <_mock._Mock object at 0x7f23b1468e90>: set([<_mock._Mock object at 0x7f23b146a210>, <_mock._Mock object at 0x7f23b146a250>, <_mock._Mock object at 0x7f23b146a290>, <_mock._Mock object at 0x7f23b146a2d0>, <_mock._Mock object at 0x7f23b146a150>, <_mock._Mock object at 0x7f23b146a190>, <_mock._Mock object at 0x7f23b146a1d0>]), <_mock._Mock object at 0x7f23b146cbd0>: set([<_mock._Mock object at 0x7f23b146ce10>, <_mock._Mock object at 0x7f23b146ce50>]), <_mock._Mock object at 0x7f23b145ff10>: set([<_mock._Mock object at 0x7f23b1468210>, <_mock._Mock object at 0x7f23b14680d0>, <_mock._Mock object at 0x7f23b1468110>, <_mock._Mock object at 0x7f23b1468150>, <_mock._Mock object at 0x7f23b1468190>, <_mock._Mock object at 0x7f23b14681d0>]), <_mock._Mock object at 0x7f23b146c090>: set([<_mock._Mock object at 0x7f23b146c210>, <_mock._Mock object at 0x7f23b146c2d0>, <_mock._Mock object at 0x7f23b146c310>, <_mock._Mock object at 0x7f23b146c250>, <_mock._Mock object at 0x7f23b146c290>]), <_mock._Mock object at 0x7f23b145f750>: set([<_mock._Mock object at 0x7f23b145fa10>, <_mock._Mock object at 0x7f23b145fa50>, <_mock._Mock object at 0x7f23b145fa90>, <_mock._Mock object at 0x7f23b145fad0>, <_mock._Mock object at 0x7f23b145fb10>, <_mock._Mock object at 0x7f23b145f990>, <_mock._Mock object at 0x7f23b145f9d0>]), <_mock._Mock object at 0x7f23b146ae90>: set([<_mock._Mock object at 0x7f23b146c110>, <_mock._Mock object at 0x7f23b146c150>]), <_mock._Mock object at 0x7f23b1468790>: set([<_mock._Mock object at 0x7f23b1468a10>, <_mock._Mock object at 0x7f23b1468990>, <_mock._Mock object at 0x7f23b1468950>, <_mock._Mock object at 0x7f23b1468a50>, <_mock._Mock object at 0x7f23b14689d0>]), <_mock._Mock object at 0x7f23b146ac10>: set([<_mock._Mock object at 0x7f23b146ad10>, <_mock._Mock object at 0x7f23b146ad50>])}¶ a dictionary containing the all interfaces supported by each type. (
dict
<sardana.sardanadefs.Interface
,set
<sardana.sardanadefs.Interface
> >)
-
INTERFACES
= {'ParameterType': (set(['Meta']), 'A generic macro server parameter type'), 'ZeroDExpChannel': (set(['ExpChannel']), 'A 0D experimental channel'), 'MacroServer': (set(['MacroServerElement']), 'A MacroServer'), 'Constraint': (set(['PoolObject']), 'A constraint'), 'Acquirable': (set(['PoolElement']), 'An acquirable element'), 'TwoDExpChannel': (set(['ExpChannel']), 'A 2D experimental channel'), 'MacroLibrary': (set(['MacroServerObject', 'Library']), 'A macro server library'), 'IORegister': (set(['Acquirable']), 'An IO register'), 'Element': (set(['Object']), 'A generic sardana element'), 'ExpChannel': (set(['Acquirable']), 'A generic experimental channel'), 'Instrument': (set(['PoolElement']), 'An instrument'), 'MacroCode': (set(['MacroServerObject']), 'A macro server macro code'), 'OneDExpChannel': (set(['ExpChannel']), 'A 1D experimental channel'), 'PoolObject': (set(['Object']), 'A Pool object'), 'MacroClass': (set(['MacroCode', 'Class']), 'A macro server macro class'), 'PseudoCounter': (set(['ExpChannel']), 'A pseudo counter'), 'PoolElement': (set(['PoolObject', 'Element']), 'A Pool element'), 'MeasurementGroup': (set(['PoolElement']), 'A measurement group'), 'PseudoMotor': (set(['Acquirable', 'Moveable']), 'A pseudo motor'), 'Function': (set(['Object']), 'A generic sardana function'), 'MacroServerElement': (set(['MacroServerObject', 'Element']), 'A generic macro server element'), 'Macro': (set(['MacroFunction', 'MacroClass']), 'A macro server macro'), 'Door': (set(['MacroServerElement']), 'A macro server door'), 'MotorGroup': (set(['PoolElement']), 'A motor group'), 'Object': (set([]), 'A generic sardana object'), 'Library': (set(['Object']), 'A generic sardana library'), 'MacroServerObject': (set(['Object']), 'A generic macro server object'), 'ControllerLibrary': (set(['PoolObject', 'Library']), 'A controller library'), 'External': (set(['Object']), 'An external object'), 'Controller': (set(['PoolElement']), 'A controller'), 'Meta': (set([]), 'A generic sardana meta object'), 'ComChannel': (set(['PoolElement']), 'A communication channel'), 'Motor': (set(['Acquirable', 'Moveable']), 'a motor'), 'Moveable': (set(['PoolElement']), 'A moveable element'), 'MacroFunction': (set(['Function', 'MacroCode']), 'A macro server macro function'), 'ControllerClass': (set(['Class', 'PoolObject']), 'A controller class'), 'Class': (set(['Object']), 'A generic sardana class'), 'Pool': (set(['PoolElement']), 'A Pool'), 'CTExpChannel': (set(['ExpChannel']), 'A counter/timer experimental channel')}¶ a dictionary containing the direct interfaces supported by each type (
dict
<str
,tuple
<set
<str
,str
>>>)
-
INTERFACES_EXPANDED
= {'Acquirable': (set(['PoolElement', 'Object', 'Acquirable', 'PoolObject', 'Element']), 'An acquirable element'), 'IORegister': (set(['PoolObject', 'PoolElement', 'Acquirable', 'Object', 'IORegister', 'Element']), 'An IO register'), 'Instrument': (set(['Instrument', 'PoolElement', 'Object', 'PoolObject', 'Element']), 'An instrument'), 'PoolElement': (set(['PoolElement', 'Object', 'PoolObject', 'Element']), 'A Pool element'), 'MacroClass': (set(['MacroCode', 'Object', 'Class', 'MacroClass', 'MacroServerObject']), 'A macro server macro class'), 'Function': (set(['Function', 'Object']), 'A generic sardana function'), 'Door': (set(['MacroServerElement', 'Object', 'Door', 'MacroServerObject', 'Element']), 'A macro server door'), 'Object': (set(['Object']), 'A generic sardana object'), 'Controller': (set(['PoolElement', 'Controller', 'Object', 'PoolObject', 'Element']), 'A controller'), 'External': (set(['Object', 'External']), 'An external object'), 'MacroServer': (set(['MacroServerElement', 'Object', 'MacroServerObject', 'MacroServer', 'Element']), 'A MacroServer'), 'MacroLibrary': (set(['MacroLibrary', 'Object', 'MacroServerObject', 'Library']), 'A macro server library'), 'MacroFunction': (set(['Function', 'MacroCode', 'MacroFunction', 'Object', 'MacroServerObject']), 'A macro server macro function'), 'MacroServerObject': (set(['Object', 'MacroServerObject']), 'A generic macro server object'), 'ComChannel': (set(['PoolElement', 'Object', 'PoolObject', 'ComChannel', 'Element']), 'A communication channel'), 'ZeroDExpChannel': (set(['ExpChannel', 'ZeroDExpChannel', 'PoolObject', 'Acquirable', 'PoolElement', 'Object', 'Element']), 'A 0D experimental channel'), 'Constraint': (set(['Object', 'PoolObject', 'Constraint']), 'A constraint'), 'Macro': (set(['Function', 'Macro', 'Object', 'Class', 'MacroCode', 'MacroFunction', 'MacroServerObject', 'MacroClass']), 'A macro server macro'), 'TwoDExpChannel': (set(['ExpChannel', 'PoolObject', 'Acquirable', 'PoolElement', 'TwoDExpChannel', 'Object', 'Element']), 'A 2D experimental channel'), 'Moveable': (set(['PoolElement', 'Object', 'Moveable', 'PoolObject', 'Element']), 'A moveable element'), 'Element': (set(['Object', 'Element']), 'A generic sardana element'), 'ExpChannel': (set(['ExpChannel', 'PoolObject', 'PoolElement', 'Acquirable', 'Object', 'Element']), 'A generic experimental channel'), 'MacroCode': (set(['MacroCode', 'Object', 'MacroServerObject']), 'A macro server macro code'), 'OneDExpChannel': (set(['ExpChannel', 'OneDExpChannel', 'PoolObject', 'Acquirable', 'PoolElement', 'Object', 'Element']), 'A 1D experimental channel'), 'PoolObject': (set(['Object', 'PoolObject']), 'A Pool object'), 'PseudoCounter': (set(['ExpChannel', 'PoolObject', 'PseudoCounter', 'Acquirable', 'PoolElement', 'Object', 'Element']), 'A pseudo counter'), 'MeasurementGroup': (set(['PoolElement', 'Object', 'PoolObject', 'MeasurementGroup', 'Element']), 'A measurement group'), 'PseudoMotor': (set(['Acquirable', 'Object', 'Element', 'PoolObject', 'Moveable', 'PoolElement', 'PseudoMotor']), 'A pseudo motor'), 'ParameterType': (set(['ParameterType', 'Meta']), 'A generic macro server parameter type'), 'MacroServerElement': (set(['MacroServerElement', 'Object', 'MacroServerObject', 'Element']), 'A generic macro server element'), 'MotorGroup': (set(['PoolElement', 'MotorGroup', 'Object', 'PoolObject', 'Element']), 'A motor group'), 'Library': (set(['Object', 'Library']), 'A generic sardana library'), 'Class': (set(['Object', 'Class']), 'A generic sardana class'), 'ControllerLibrary': (set(['ControllerLibrary', 'Object', 'PoolObject', 'Library']), 'A controller library'), 'Meta': (set(['Meta']), 'A generic sardana meta object'), 'Pool': (set(['PoolElement', 'Object', 'PoolObject', 'Pool', 'Element']), 'A Pool'), 'Motor': (set(['Acquirable', 'Object', 'Element', 'PoolObject', 'Motor', 'Moveable', 'PoolElement']), 'a motor'), 'CTExpChannel': (set(['ExpChannel', 'PoolObject', 'CTExpChannel', 'Acquirable', 'PoolElement', 'Object', 'Element']), 'A counter/timer experimental channel'), 'ControllerClass': (set(['ControllerClass', 'Object', 'Class', 'PoolObject']), 'A controller class')}¶ a dictionary containing the all interfaces supported by each type (
dict
<str
,set
<str
> >)
Functions
-
from_dtype_str
(dtype)[source]¶ Transforms the given dtype parameter (string/
DataType
or None) into a tuple of two elements (str,DataFormat
) where the first element is a string with a simplified data type.Parameters: dtype (str or None or DataType
) – the data type to be transformedReturns: a tuple <str, DataFormat
> for the given dtypeReturn type: tuple<str, DataFormat
>
-
from_access_str
(access)[source]¶ Transforms the given access parameter (string or
DataAccess
) into a simplified data access string.Parameters: dtype (str) – the access to be transformed Returns: a simple string for the given access Return type: str
-
to_dtype_dformat
(data)[source]¶ Transforms the given data parameter (string/ or sequence of string or sequence of sequence of string/
DataType
) into a tuple of two elements (DataType
,DataFormat
).Parameters: data (str or seq<str> or seq<seq<str>>) – the data information to be transformed Returns: a tuple < DataType
,DataFormat
> for the given dataReturn type: tuple< DataType
,DataFormat
>
-
to_daccess
(daccess)[source]¶ Transforms the given access parameter (string or None) into a
DataAccess
. If None is given returnsDataAccess.ReadWrite
Parameters: dtype (str) – the access to be transformed Returns: a DataAccess
for the given accessReturn type: DataAccess
sardanabase
¶This module is part of the Python Sardana library. It defines the base classes for Sardana object
Classes
-
class
SardanaBaseObject
(**kwargs)[source]¶ The Sardana most abstract object. It contains only two members:
- _manager : a weak reference to the manager (pool or ms) where it belongs
- _name : the name
- _full_name : the name (usually a tango device name, but can be anything else.)
-
get_manager
()[source]¶ Return the
sardana.Manager
which owns this sardana object.Returns: the manager which owns this pool object. Return type: sardana.Manager
-
get_name
()[source]¶ Returns this sardana object name
Returns: this sardana object name Return type: str
-
get_full_name
()[source]¶ Returns this sardana object full name
Returns: this sardana object full name Return type: str
-
get_type
()[source]¶ Returns this sardana object type.
Returns: this sardana object type Return type: ElementType
-
get_parent
()[source]¶ Returns this pool object parent.
Returns: this objects parent Return type: SardanaBaseObject
-
get_parent_name
()[source]¶ Returns this sardana object parent’s name.
Returns: this objects parent Return type: str
-
get_frontend
()[source]¶ Returns this sardana frontend object or None if no frontend is registered
Returns: this objects frontend Return type: object
-
get_interfaces
()[source]¶ Returns the set of interfaces this object implements.
Returns: The set of interfaces this object implements. Return type: class:set < sardana.sardanadefs.Interface
>
-
get_interface
()[source]¶ Returns the interface this object implements.
Returns: The interface this object implements. Return type: sardana.sardanadefs.Interface
-
get_interface_names
()[source]¶ Returns a sequence of interface names this object implements.
Returns: The sequence of interfaces this object implements. Return type: sequence< str
>
-
manager
¶ reference to the
sardana.Manager
-
name
¶ object name
-
full_name
¶ object full name
-
frontend
¶ the object frontend
-
Critical
= 50¶
-
Debug
= 10¶
-
DftLogLevel
= 20¶
-
DftLogMessageFormat
= '%(threadName)-14s %(levelname)-8s %(asctime)s %(name)s: %(message)s'¶
-
Error
= 40¶
-
Fatal
= 50¶
-
Info
= 20¶
-
Trace
= 5¶
-
Warning
= 30¶
-
add_listener
(listener)¶ Adds a new listener for this object.
Parameters: listener – a listener
-
are_events_blocked
()¶
-
block_events
()¶
-
flush_queue
()¶
-
has_listeners
()¶ Returns True if anybody is listening to events from this object
Returns: True is at least one listener is listening or False otherwise
-
log_level
= 20¶
-
queue_event
(event_type, event_value, listeners=None)¶
-
remove_listener
(listener)¶ Removes an existing listener for this object.
Parameters: listener – the listener to be removed Returns: True is succeeded or False otherwise
-
root_inited
= True¶
-
unblock_events
()¶
sardanacontainer
¶This module is part of the Python Pool libray. It defines the base classes for a pool container element
Classes
-
class
SardanaContainer
[source]¶ A container class for sardana elements
-
add_element
(e)[source]¶ Adds a new
pool.PoolObject
to this containerParameters: e ( pool.PoolObject
) – the pool element to be added
-
remove_element
(e)[source]¶ Removes the
pool.PoolObject
from this containerParameters: e ( pool.PoolObject
) – the pool object to be removedThrow: KeyError
-
get_element_id_map
()[source]¶ Returns a reference to the internal pool object ID map
Returns: the internal pool object ID map Return type: dict<id, pool.PoolObject>
-
get_element_name_map
()[source]¶ Returns a reference to the internal pool object name map
Returns: the internal pool object name map Return type: dict<str, pool.PoolObject>
-
get_element_type_map
()[source]¶ Returns a reference to the internal pool object type map
Returns: the internal pool object type map Return type: dict<pool.ElementType, dict<id, pool.PoolObject>>
-
get_element
(**kwargs)[source]¶ Returns a reference to the requested pool object
Parameters: kwargs – if key ‘id’ given: search by ID else if key ‘full_name’ given: search by full name else if key ‘name’ given: search by name Returns: the pool object Return type: pool.PoolObject Throw: KeyError
-
get_element_by_name
(name, **kwargs)[source]¶ Returns a reference to the requested pool object
Parameters: name (str) – pool object name Returns: the pool object Return type: pool.PoolObject Throw: KeyError
-
get_element_by_full_name
(full_name, **kwargs)[source]¶ Returns a reference to the requested pool object
Parameters: name (str) – pool object full name Returns: the pool object Return type: pool.PoolObject Throw: KeyError
-
get_element_by_id
(id, **kwargs)[source]¶ Returns a reference to the requested pool object
Parameters: id (int) – pool object ID Returns: the pool object Return type: pool.PoolObject Throw: KeyError
-
get_elements_by_type
(t)[source]¶ Returns a list of all pool objects of the given type
Parameters: t (pool.ElementType) – element type Returns: list of pool objects Return type: seq<pool.PoolObject>
-
get_element_names_by_type
(t)[source]¶ Returns a list of all pool object names of the given type
Parameters: t (pool.ElementType) – element type Returns: list of pool object names Return type: seq<str>
-
sardanaevent
¶This module is part of the Python Pool libray. It defines the base classes for pool event mechanism
Classes
-
class
EventGenerator
(max_queue_len=10, listeners=None)[source]¶ A class capable of generating events to their listeners
-
add_listener
(listener)[source]¶ Adds a new listener for this object.
Parameters: listener – a listener
-
remove_listener
(listener)[source]¶ Removes an existing listener for this object.
Parameters: listener – the listener to be removed Returns: True is succeeded or False otherwise
-
sardanamodulemanager
¶This module is part of the Python Sardana library. It defines the base classes for module manager
Classes
-
class
ModuleManager
[source]¶ This class handles python module loading/reloading and unloading.
-
loadModule
(module_name, path=None)[source]¶ Loads the given module name. If the module has been already loaded into this python interpreter, nothing is done.
Parameters: - module_name (str) – the module to be loaded.
- path (seq<str> or None) – list of paths to look for modules [default: None]
Returns: python module
Raises: ImportError
-
Critical
= 50¶
-
Debug
= 10¶
-
DftLogLevel
= 20¶
-
DftLogMessageFormat
= '%(threadName)-14s %(levelname)-8s %(asctime)s %(name)s: %(message)s'¶
-
Error
= 40¶
-
Fatal
= 50¶
-
Info
= 20¶
-
Trace
= 5¶
-
Warning
= 30¶
-
log_level
= 20¶
-
root_inited
= True¶
-
sardanameta
¶This module is part of the Python Sardana libray. It defines the base classes for MetaLibrary and MetaClass
Classes
-
class
SardanaLibrary
(**kwargs)[source]¶ Object representing a python module containing sardana classes. Public members:
module - reference to python module
file_path - complete (absolute) path (with file name at the end)
file_name - file name (including file extension)
path - complete (absolute) path
name - (=module name) module name (without file extension)
meta_classes - dict<str, SardanMetaClass>
- exc_info - exception information if an error occurred when loading
the module
-
name
¶ object name
-
description
= '<Undocumented>'¶
-
code
¶ Returns a sequence of sourcelines corresponding to the module code.
Returns: list of source code lines Return type: list<str>
-
add_meta_class
(meta_class)[source]¶ Adds a new :class:~`sardana.sardanameta.SardanaClass` to this library.
Parameters: meta_class (:class:~`sardana.sardanameta.SardanaClass`) – the meta class to be added to this library
-
get_meta_class
(meta_class_name)[source]¶ Returns a :class:~`sardana.sardanameta.SardanaClass` for the given meta class name or None if the meta class does not exist in this library.
Parameters: meta_class_name (str) – the meta class name Returns: a meta class or None Return type: :class:~`sardana.sardanameta.SardanaClass`
-
get_meta_classes
()[source]¶ Returns a sequence of the meta classes that belong to this library.
Returns: a sequence of meta classes that belong to this library Return type: seq<:class:~`sardana.sardanameta.SardanaClass`>
-
has_meta_class
(meta_class_name)[source]¶ Returns True if the given meta class name belongs to this library or False otherwise.
Parameters: meta_class_name (str) – the meta class name Returns: True if the given meta class name belongs to this library or False otherwise Return type: bool
-
add_meta_function
(meta_function)[source]¶ Adds a new :class:~`sardana.sardanameta.SardanaFunction` to this library.
Parameters: meta_function (:class:~`sardana.sardanameta.SardanaFunction`) – the meta function to be added to this library
-
get_meta_function
(meta_function_name)[source]¶ Returns a :class:~`sardana.sardanameta.SardanaFunction` for the given meta function name or None if the meta function does not exist in this library.
Parameters: meta_function_name (str) – the meta function name Returns: a meta function or None Return type: :class:~`sardana.sardanameta.SardanaFunction`
-
get_meta_functions
()[source]¶ Returns a sequence of the meta functions that belong to this library.
Returns: a sequence of meta functions that belong to this library Return type: seq<:class:~`sardana.sardanameta.SardanaFunction`>
-
has_meta_function
(meta_function_name)[source]¶ Returns True if the given meta function name belongs to this library or False otherwise.
Parameters: meta_function_name (str) – the meta function name Returns: True if the given meta function name belongs to this library or False otherwise Return type: bool
-
get_meta
(meta_name)[source]¶ Returns a :class:~`sardana.sardanameta.SardanaCode` for the given meta name or None if the meta does not exist in this library.
Parameters: meta_name (str) – the meta name (class, function) Returns: a meta or None Return type: :class:~`sardana.sardanameta.SardanaCode`
-
has_meta
(meta_name)[source]¶ Returns True if the given meta name belongs to this library or False otherwise.
Parameters: meta_name (str) – the meta name Returns: True if the given meta (class or function) name belongs to this library or False otherwise Return type: bool
-
has_metas
()[source]¶ Returns True if any meta object exists in the library or False otherwise.
Returns: True if any meta object (class or function) exists in the library or False otherwise Return type: bool
-
get_metas
()[source]¶ Returns a sequence of the meta (class and functions) that belong to this library.
Returns: a sequence of meta (class and functions) that belong to this library Return type: seq<:class:~`sardana.sardanameta.SardanaCode`>
-
get_name
()[source]¶ Returns the module name for this library (same as :meth:~sardana.sardanameta.SardanaLibrary.get_module_name).
Returns: the module name Return type: str
-
get_module_name
()[source]¶ Returns the module name for this library (same as :meth:~sardana.sardanameta.SardanaLibrary.get_name).
Returns: the module name Return type: str
-
get_module
()[source]¶ Returns the python module for this library.
Returns: the python module Return type: object
-
get_description
()[source]¶ Returns the this library documentation or “<Undocumented>” if no documentation exists.
Returns: this library documentation or None Return type: str
-
get_code
()[source]¶ Returns a sequence of sourcelines corresponding to the module code.
Returns: list of source code lines Return type: list<str>
-
get_file_path
()[source]¶ Returns the file path for this library. On posix systems is something like: /abs/path/filename.py
Returns: this library file path Return type: str
-
get_file_name
()[source]¶ Returns the file name for this library. On posix systems is something like: filename.py
Returns: this library file name Return type: str
-
has_errors
()[source]¶ Returns True if this library has syntax errors or False otherwise.
Returns: True if this library has syntax errors or False otherwise Return type: bool
-
set_error
(exc_info)[source]¶ Sets the error information for this library
Parameters: exc_info (tuple<type, value, traceback>) – error information. It must be an object similar to the one returned by sys.exc_info()
-
get_error
()[source]¶ Gets the error information for this library or None if no error exists
Returns: error information. An object similar to the one returned by sys.exc_info()
Return type: tuple<type, value, traceback>
-
serialize
(*args, **kwargs)[source]¶ Returns a serializable object describing this object.
Returns: a serializable dict Return type: dict
-
Critical
= 50¶
-
Debug
= 10¶
-
DftLogLevel
= 20¶
-
DftLogMessageFormat
= '%(threadName)-14s %(levelname)-8s %(asctime)s %(name)s: %(message)s'¶
-
Error
= 40¶
-
Fatal
= 50¶
-
Info
= 20¶
-
Trace
= 5¶
-
Warning
= 30¶
-
add_listener
(listener)¶ Adds a new listener for this object.
Parameters: listener – a listener
-
are_events_blocked
()¶
-
block_events
()¶
-
fire_event
(event_type, event_value, listeners=None, protected=True)¶
-
flush_queue
()¶
-
frontend
¶ the object frontend
-
full_name
¶ object full name
-
get_frontend
()¶ Returns this sardana frontend object or None if no frontend is registered
Returns: this objects frontend Return type: object
-
get_full_name
()¶ Returns this sardana object full name
Returns: this sardana object full name Return type: str
-
get_interface
()¶ Returns the interface this object implements.
Returns: The interface this object implements. Return type: sardana.sardanadefs.Interface
-
get_interface_names
()¶ Returns a sequence of interface names this object implements.
Returns: The sequence of interfaces this object implements. Return type: sequence< str
>
-
get_interfaces
()¶ Returns the set of interfaces this object implements.
Returns: The set of interfaces this object implements. Return type: class:set < sardana.sardanadefs.Interface
>
-
get_manager
()¶ Return the
sardana.Manager
which owns this sardana object.Returns: the manager which owns this pool object. Return type: sardana.Manager
-
get_parent
()¶ Returns this pool object parent.
Returns: this objects parent Return type: SardanaBaseObject
-
get_parent_name
()¶ Returns this sardana object parent’s name.
Returns: this objects parent Return type: str
-
get_type
()¶ Returns this sardana object type.
Returns: this sardana object type Return type: ElementType
-
has_listeners
()¶ Returns True if anybody is listening to events from this object
Returns: True is at least one listener is listening or False otherwise
-
log_level
= 20¶
-
manager
¶ reference to the
sardana.Manager
-
queue_event
(event_type, event_value, listeners=None)¶
-
remove_listener
(listener)¶ Removes an existing listener for this object.
Parameters: listener – the listener to be removed Returns: True is succeeded or False otherwise
-
root_inited
= True¶
-
serialized
(*args, **kwargs)¶
-
str
(*args, **kwargs)¶
-
unblock_events
()¶
-
class
SardanaClass
(**kwargs)[source]¶ Object representing a python class.
-
Critical
= 50¶
-
Debug
= 10¶
-
DftLogLevel
= 20¶
-
DftLogMessageFormat
= '%(threadName)-14s %(levelname)-8s %(asctime)s %(name)s: %(message)s'¶
-
Error
= 40¶
-
Fatal
= 50¶
-
Info
= 20¶
-
Trace
= 5¶
-
Warning
= 30¶
-
add_listener
(listener)¶ Adds a new listener for this object.
Parameters: listener – a listener
-
are_events_blocked
()¶
-
block_events
()¶
-
code
¶ Returns a tuple (sourcelines, firstline) corresponding to the definition of this code object. sourcelines is a list of source code lines. firstline is the line number of the first source code line.
-
code_object
¶
-
description
= '<Undocumented>'¶
-
file_name
¶ Returns the file name for the library where this class is. On posix systems is something like: filename.py
Returns: the file name for the library where this class is Return type: str
-
file_path
¶ Returns the file path for for the library where this class is. On posix systems is something like: /abs/path/filename.py
Returns: the file path for for the library where this class is Return type: str
-
fire_event
(event_type, event_value, listeners=None, protected=True)¶
-
flush_queue
()¶
-
frontend
¶ the object frontend
-
full_name
¶ object full name
-
get_brief_description
(max_chars=60)¶
-
get_code
()¶ Returns a tuple (sourcelines, firstline) corresponding to the definition of the controller class. sourcelines is a list of source code lines. firstline is the line number of the first source code line.
-
get_frontend
()¶ Returns this sardana frontend object or None if no frontend is registered
Returns: this objects frontend Return type: object
-
get_full_name
()¶ Returns this sardana object full name
Returns: this sardana object full name Return type: str
-
get_interface
()¶ Returns the interface this object implements.
Returns: The interface this object implements. Return type: sardana.sardanadefs.Interface
-
get_interface_names
()¶ Returns a sequence of interface names this object implements.
Returns: The sequence of interfaces this object implements. Return type: sequence< str
>
-
get_interfaces
()¶ Returns the set of interfaces this object implements.
Returns: The set of interfaces this object implements. Return type: class:set < sardana.sardanadefs.Interface
>
-
get_manager
()¶ Return the
sardana.Manager
which owns this sardana object.Returns: the manager which owns this pool object. Return type: sardana.Manager
-
get_parent
()¶ Returns this pool object parent.
Returns: this objects parent Return type: SardanaBaseObject
-
get_parent_name
()¶ Returns this sardana object parent’s name.
Returns: this objects parent Return type: str
-
get_type
()¶ Returns this sardana object type.
Returns: this sardana object type Return type: ElementType
-
has_listeners
()¶ Returns True if anybody is listening to events from this object
Returns: True is at least one listener is listening or False otherwise
-
lib
¶ Returns the library :class:~`sardana.sardanameta.SardanaLibrary` for this class.
Returns: a reference to the library where this class is located Return type: :class:~`sardana.sardanameta.SardanaLibrary`
-
log_level
= 20¶
-
manager
¶ reference to the
sardana.Manager
-
name
¶ object name
-
path
¶ Returns the absolute path for the library where this class is. On posix systems is something like: /abs/path
Returns: the absolute path for the library where this class is Return type: str
-
queue_event
(event_type, event_value, listeners=None)¶
-
remove_listener
(listener)¶ Removes an existing listener for this object.
Parameters: listener – the listener to be removed Returns: True is succeeded or False otherwise
-
root_inited
= True¶
-
serialize
(*args, **kwargs)¶ Returns a serializable object describing this object.
Returns: a serializable dict Return type: dict
-
serialized
(*args, **kwargs)¶
-
str
(*args, **kwargs)¶
-
unblock_events
()¶
-
klass
¶
-
sardanamanager
¶This module is part of the Python Sardana libray. It defines the base class for Sardana manager
Classes
sardanaattribute
¶This module is part of the Python Sardana libray. It defines the base classes for Sardana attributes
Classes
-
class
SardanaAttribute
(obj, name=None, initial_value=None, **kwargs)[source]¶ Class representing an atomic attribute like position of a motor or a counter value
-
has_value
()[source]¶ Determines if the attribute’s read value has been read at least once in the lifetime of the attribute.
Returns: True if the attribute has a read value stored or False otherwise Return type: bool
-
has_write_value
()[source]¶ Determines if the attribute’s write value has been read at least once in the lifetime of the attribute.
Returns: True if the attribute has a write value stored or False otherwise Return type: bool
-
get_obj
()[source]¶ Returns the object which owns this attribute
Returns: the object which owns this attribute Return type: obj
-
in_error
()[source]¶ Determines if this attribute is in error state.
Returns: True if the attribute is in error state or False otherwise Return type: bool
-
set_value
(value, exc_info=None, timestamp=None, propagate=1)[source]¶ Sets the current read value and propagates the event (if propagate > 0).
Parameters: - value (obj or SardanaValue) – the new read value for this attribute. If a SardanaValue is given, exc_info and timestamp are ignored (if given)
- exc_info (tuple<3> or None) – exception information as returned by
sys.exc_info()
[default: None, meaning no exception] - timestamp (float or None) – timestamp of attribute readout [default: None, meaning create a ‘now’ timestamp]
- propagate (int) – 0 for not propagating, 1 to propagate, 2 propagate with priority
-
get_value
()[source]¶ Returns the last read value for this attribute.
Returns: the last read value for this attribute Return type: obj Raises: Exception
if no read value has been set yet
-
get_value_obj
()[source]¶ Returns the last read value for this attribute.
Returns: the last read value for this attribute Return type: SardanaValue
Raises: Exception
if no read value has been set yet
-
set_write_value
(w_value, timestamp=None, propagate=1)[source]¶ Sets the current write value.
Parameters: - value (obj or SardanaValue) – the new write value for this attribute. If a SardanaValue is given, timestamp is ignored (if given)
- timestamp (float or None) – timestamp of attribute write [default: None, meaning create a ‘now’ timestamp]
- propagate (int) – 0 for not propagating, 1 to propagate, 2 propagate with priority
-
get_write_value
()[source]¶ Returns the last write value for this attribute.
Returns: the last write value for this attribute or None if value has not been written yet Return type: obj
-
get_write_value_obj
()[source]¶ Returns the last write value object for this attribute.
Returns: the last write value for this attribute or None if value has not been written yet Return type: SardanaValue
-
get_exc_info
()[source]¶ Returns the exception information (like
sys.exc_info()
) about last attribute readout or None if last read did not generate an exception.Returns: exception information or None Return type: tuple<3> or None
-
get_timestamp
()[source]¶ Returns the timestamp of the last readout or None if the attribute has never been read before
Returns: timestamp of the last readout or None Return type: float or None
-
get_write_timestamp
()[source]¶ Returns the timestamp of the last write or None if the attribute has never been written before
Returns: timestamp of the last write or None Return type: float or None
-
fire_write_event
(propagate=1)[source]¶ Fires an event to the listeners of the object which owns this attribute.
Parameters: propagate (int) – 0 for not propagating, 1 to propagate, 2 propagate with priority
-
fire_read_event
(propagate=1)[source]¶ Fires an event to the listeners of the object which owns this attribute.
Parameters: propagate (int) – 0 for not propagating, 1 to propagate, 2 propagate with priority
-
obj
¶ Returns the object which owns this attribute
Returns: the object which owns this attribute Return type: obj
-
value_obj
¶ Returns the last read value for this attribute.
Returns: the last read value for this attribute Return type: SardanaValue
Raises: Exception
if no read value has been set yet
-
write_value_obj
¶ Returns the last write value object for this attribute.
Returns: the last write value for this attribute or None if value has not been written yet Return type: SardanaValue
-
value
¶ Returns the last read value for this attribute.
Returns: the last read value for this attribute Return type: obj Raises: Exception
if no read value has been set yet
-
w_value
¶ Returns the last write value for this attribute.
Returns: the last write value for this attribute or None if value has not been written yet Return type: obj
-
timestamp
¶ the read timestamp
-
w_timestamp
¶ the write timestamp
-
error
¶ Determines if this attribute is in error state.
Returns: True if the attribute is in error state or False otherwise Return type: bool
-
exc_info
¶ Returns the exception information (like
sys.exc_info()
) about last attribute readout or None if last read did not generate an exception.Returns: exception information or None Return type: tuple<3> or None
-
add_listener
(listener)¶ Adds a new listener for this object.
Parameters: listener – a listener
-
fire_event
(event_type, event_value, listeners=None)¶
-
flush_queue
()¶
-
has_listeners
()¶ Returns True if anybody is listening to events from this object
Returns: True is at least one listener is listening or False otherwise
-
queue_event
(event_type, event_value, listeners=None)¶
-
remove_listener
(listener)¶ Removes an existing listener for this object.
Parameters: listener – the listener to be removed Returns: True is succeeded or False otherwise
-
-
class
SardanaSoftwareAttribute
(obj, name=None, initial_value=None, **kwargs)[source]¶ Class representing a software attribute. The difference between this and
SardanaAttribute
is that, because it is a pure software attribute, there is no difference ever between the read and write values.-
get_value
()¶ Returns the last read value for this attribute.
Returns: the last read value for this attribute Return type: obj Raises: Exception
if no read value has been set yet
-
set_value
(value, exc_info=None, timestamp=None, propagate=1)[source]¶ Sets the current read value and propagates the event (if propagate > 0).
Parameters: - value (obj) – the new read value for this attribute
- exc_info (tuple<3> or None) – exception information as returned by
sys.exc_info()
[default: None, meaning no exception] - timestamp (float or None) – timestamp of attribute readout [default: None, meaning create a ‘now’ timestamp]
- propagate (int) – 0 for not propagating, 1 to propagate, 2 propagate with priority
-
value
¶ Returns the last read value for this attribute.
Returns: the last read value for this attribute Return type: obj Raises: Exception
if no read value has been set yet
-
accepts
(propagate)¶
-
add_listener
(listener)¶ Adds a new listener for this object.
Parameters: listener – a listener
-
error
¶ Determines if this attribute is in error state.
Returns: True if the attribute is in error state or False otherwise Return type: bool
-
exc_info
¶ Returns the exception information (like
sys.exc_info()
) about last attribute readout or None if last read did not generate an exception.Returns: exception information or None Return type: tuple<3> or None
-
fire_event
(event_type, event_value, listeners=None)¶
-
fire_read_event
(propagate=1)¶ Fires an event to the listeners of the object which owns this attribute.
Parameters: propagate (int) – 0 for not propagating, 1 to propagate, 2 propagate with priority
-
fire_write_event
(propagate=1)¶ Fires an event to the listeners of the object which owns this attribute.
Parameters: propagate (int) – 0 for not propagating, 1 to propagate, 2 propagate with priority
-
flush_queue
()¶
-
get_exc_info
()¶ Returns the exception information (like
sys.exc_info()
) about last attribute readout or None if last read did not generate an exception.Returns: exception information or None Return type: tuple<3> or None
-
get_obj
()¶ Returns the object which owns this attribute
Returns: the object which owns this attribute Return type: obj
-
get_timestamp
()¶ Returns the timestamp of the last readout or None if the attribute has never been read before
Returns: timestamp of the last readout or None Return type: float or None
-
get_value_obj
()¶ Returns the last read value for this attribute.
Returns: the last read value for this attribute Return type: SardanaValue
Raises: Exception
if no read value has been set yet
-
get_write_timestamp
()¶ Returns the timestamp of the last write or None if the attribute has never been written before
Returns: timestamp of the last write or None Return type: float or None
-
get_write_value
()¶ Returns the last write value for this attribute.
Returns: the last write value for this attribute or None if value has not been written yet Return type: obj
-
get_write_value_obj
()¶ Returns the last write value object for this attribute.
Returns: the last write value for this attribute or None if value has not been written yet Return type: SardanaValue
-
has_listeners
()¶ Returns True if anybody is listening to events from this object
Returns: True is at least one listener is listening or False otherwise
-
has_value
()¶ Determines if the attribute’s read value has been read at least once in the lifetime of the attribute.
Returns: True if the attribute has a read value stored or False otherwise Return type: bool
-
has_write_value
()¶ Determines if the attribute’s write value has been read at least once in the lifetime of the attribute.
Returns: True if the attribute has a write value stored or False otherwise Return type: bool
-
in_error
()¶ Determines if this attribute is in error state.
Returns: True if the attribute is in error state or False otherwise Return type: bool
-
obj
¶ Returns the object which owns this attribute
Returns: the object which owns this attribute Return type: obj
-
queue_event
(event_type, event_value, listeners=None)¶
-
remove_listener
(listener)¶ Removes an existing listener for this object.
Parameters: listener – the listener to be removed Returns: True is succeeded or False otherwise
-
set_write_value
(w_value, timestamp=None, propagate=1)¶ Sets the current write value.
Parameters: - value (obj or SardanaValue) – the new write value for this attribute. If a SardanaValue is given, timestamp is ignored (if given)
- timestamp (float or None) – timestamp of attribute write [default: None, meaning create a ‘now’ timestamp]
- propagate (int) – 0 for not propagating, 1 to propagate, 2 propagate with priority
-
timestamp
¶ the read timestamp
-
value_obj
¶ Returns the last read value for this attribute.
Returns: the last read value for this attribute Return type: SardanaValue
Raises: Exception
if no read value has been set yet
-
w_timestamp
¶ the write timestamp
-
w_value
¶ Returns the last write value for this attribute.
Returns: the last write value for this attribute or None if value has not been written yet Return type: obj
-
write_value_obj
¶ Returns the last write value object for this attribute.
Returns: the last write value for this attribute or None if value has not been written yet Return type: SardanaValue
-
-
class
ScalarNumberAttribute
(*args, **kwargs)[source]¶ A
SardanaAttribute
specialized for numbers-
accepts
(propagate)¶
-
add_listener
(listener)¶ Adds a new listener for this object.
Parameters: listener – a listener
-
error
¶ Determines if this attribute is in error state.
Returns: True if the attribute is in error state or False otherwise Return type: bool
-
exc_info
¶ Returns the exception information (like
sys.exc_info()
) about last attribute readout or None if last read did not generate an exception.Returns: exception information or None Return type: tuple<3> or None
-
fire_event
(event_type, event_value, listeners=None)¶
-
fire_read_event
(propagate=1)¶ Fires an event to the listeners of the object which owns this attribute.
Parameters: propagate (int) – 0 for not propagating, 1 to propagate, 2 propagate with priority
-
fire_write_event
(propagate=1)¶ Fires an event to the listeners of the object which owns this attribute.
Parameters: propagate (int) – 0 for not propagating, 1 to propagate, 2 propagate with priority
-
flush_queue
()¶
-
get_exc_info
()¶ Returns the exception information (like
sys.exc_info()
) about last attribute readout or None if last read did not generate an exception.Returns: exception information or None Return type: tuple<3> or None
-
get_obj
()¶ Returns the object which owns this attribute
Returns: the object which owns this attribute Return type: obj
-
get_timestamp
()¶ Returns the timestamp of the last readout or None if the attribute has never been read before
Returns: timestamp of the last readout or None Return type: float or None
-
get_value
()¶ Returns the last read value for this attribute.
Returns: the last read value for this attribute Return type: obj Raises: Exception
if no read value has been set yet
-
get_value_obj
()¶ Returns the last read value for this attribute.
Returns: the last read value for this attribute Return type: SardanaValue
Raises: Exception
if no read value has been set yet
-
get_write_timestamp
()¶ Returns the timestamp of the last write or None if the attribute has never been written before
Returns: timestamp of the last write or None Return type: float or None
-
get_write_value
()¶ Returns the last write value for this attribute.
Returns: the last write value for this attribute or None if value has not been written yet Return type: obj
-
get_write_value_obj
()¶ Returns the last write value object for this attribute.
Returns: the last write value for this attribute or None if value has not been written yet Return type: SardanaValue
-
has_listeners
()¶ Returns True if anybody is listening to events from this object
Returns: True is at least one listener is listening or False otherwise
-
has_value
()¶ Determines if the attribute’s read value has been read at least once in the lifetime of the attribute.
Returns: True if the attribute has a read value stored or False otherwise Return type: bool
-
has_write_value
()¶ Determines if the attribute’s write value has been read at least once in the lifetime of the attribute.
Returns: True if the attribute has a write value stored or False otherwise Return type: bool
-
in_error
()¶ Determines if this attribute is in error state.
Returns: True if the attribute is in error state or False otherwise Return type: bool
-
obj
¶ Returns the object which owns this attribute
Returns: the object which owns this attribute Return type: obj
-
queue_event
(event_type, event_value, listeners=None)¶
-
remove_listener
(listener)¶ Removes an existing listener for this object.
Parameters: listener – the listener to be removed Returns: True is succeeded or False otherwise
-
set_value
(value, exc_info=None, timestamp=None, propagate=1)¶ Sets the current read value and propagates the event (if propagate > 0).
Parameters: - value (obj or SardanaValue) – the new read value for this attribute. If a SardanaValue is given, exc_info and timestamp are ignored (if given)
- exc_info (tuple<3> or None) – exception information as returned by
sys.exc_info()
[default: None, meaning no exception] - timestamp (float or None) – timestamp of attribute readout [default: None, meaning create a ‘now’ timestamp]
- propagate (int) – 0 for not propagating, 1 to propagate, 2 propagate with priority
-
set_write_value
(w_value, timestamp=None, propagate=1)¶ Sets the current write value.
Parameters: - value (obj or SardanaValue) – the new write value for this attribute. If a SardanaValue is given, timestamp is ignored (if given)
- timestamp (float or None) – timestamp of attribute write [default: None, meaning create a ‘now’ timestamp]
- propagate (int) – 0 for not propagating, 1 to propagate, 2 propagate with priority
-
timestamp
¶ the read timestamp
-
value
¶ Returns the last read value for this attribute.
Returns: the last read value for this attribute Return type: obj Raises: Exception
if no read value has been set yet
-
value_obj
¶ Returns the last read value for this attribute.
Returns: the last read value for this attribute Return type: SardanaValue
Raises: Exception
if no read value has been set yet
-
w_timestamp
¶ the write timestamp
-
w_value
¶ Returns the last write value for this attribute.
Returns: the last write value for this attribute or None if value has not been written yet Return type: obj
-
write_value_obj
¶ Returns the last write value object for this attribute.
Returns: the last write value for this attribute or None if value has not been written yet Return type: SardanaValue
-
-
class
SardanaAttributeConfiguration
[source]¶ Storage class for
SardanaAttribute
information (like ranges)-
NoRange
= (-inf, inf)¶
-
sardanavalue
¶This module is part of the Python Sardana libray. It defines the base classes for Sardana values
Classes
Sardana test API¶
-
@
macroTest
[source]¶ -
macroTest
(klass=None, helper_name=None, test_method_name=None, test_method_doc=None, **helper_kwargs)[source] This decorator is an specialization of :function::taurus.test.insertTest for macro testing. It inserts test methods from a helper method that may accept arguments.
macroTest provides a very economic API for creating new tests for a given macro based on a helper method.
macroTest accepts the following arguments:
- helper_name (str): the name of the helper method. macroTest will
insert a test method which calls the helper with any the helper_kwargs (see below).
- test_method_name (str): Optional. Name of the test method to be used.
If None given, one will be generated from the macro and helper names.
- test_method_doc (str): The docstring for the inserted test method
(this shows in the unit test output). If None given, a default one is generated which includes the input parameters and the helper name.
- **helper_kwargs: All remaining keyword arguments are passed to the
helper.
macroTest assumes that the decorated class has a macro_name class member.
This decorator can be considered a “base” decorator. It is often used to create other decorators in which the helper method is pre-set. Some of them are already provided in this module:
testRun()
is equivalent to macroTest with helper_name=’macro_runs’testStop()
is equivalent to macroTest with helper_name=’macro_stops’testFail()
is equivalent to macroTest with helper_name=’macro_fails’
The advantage of using the decorators compared to writing the test methods directly is that the helper method can get keyword arguments and therefore avoid duplication of code for very similar tests (think, e.g. on writing similar tests for various sets of macro input parameters):
Consider the following code written using the
RunMacroTestCase.macro_runs()
helper:1 2 3 4 5 6 7 8 9 10
class FooTest(RunMacroTestCase, unittest.TestCase) macro_name = twice def test_foo_runs_with_input_2(self): '''test that twice(2) runs''' self.macro_runs(macro_params=['2']) def test_foo_runs_with_input_minus_1(self): '''test that twice(2) runs''' self.macro_runs(macro_params=['-1'])
The equivalent code could be written as:
@macroTest(helper_name='macro_runs', macro_params=['2']) @macroTest(helper_name='macro_runs', macro_params=['-1']) class FooTest(RunMacroTestCase, unittest.TestCase): macro_name = 'twice'
Or, even better, using the specialized testRun decorator:
@testRun(macro_params=['2']) @testRun(macro_params=['-1']) class FooTest(RunMacroTestCase, unittest.TestCase): macro_name = 'twice'
See also
:function::taurus.test.insertTest
-
-
class
BaseMacroExecutor
[source]¶ Abstract MacroExecutor class. Inherit from it if you want to create your own macro executor.
-
log_levels
= ['debug', 'output', 'info', 'warning', 'critical', 'error']¶
-
run
(macro_name, macro_params=None, sync=True, timeout=inf)[source]¶ Execute macro.
Parameters: - macro_name – (string) name of macro to be executed
- macro_params – (list<string>) macro parameters (default is macro_params=None for macros without parameters or with the default values)
- sync – (bool) whether synchronous or asynchronous call (default is sync=True)
- timeout –
- (float) timeout (in s) that will be passed to the wait
- method, in case of synchronous execution
In asyncrhonous execution method
wait()
has to be explicitly called.
-
wait
(timeout=inf)[source]¶ Wait until macro is done. Use it in asynchronous executions.
Parameters: timeout – (float) waiting timeout (in s)
-
stop
(started_event_timeout=3.0)[source]¶ Stop macro execution. Execute macro in synchronous way before using this method.
Parameters: started_event_timeout – (float) waiting timeout for started event
-
registerLog
(log_level)[source]¶ Start registering log messages.
Parameters: log_level – (str) string indicating the log level
-
unregisterLog
(log_level)[source]¶ Stop registering log messages.
Parameters: log_level – (str) string indicating the log level
-
getLog
(log_level)[source]¶ Get log messages.
Parameters: log_level – (str) string indicating the log level Returns: (seq<str>) list of strings with log messages
-
-
class
MacroExecutorFactory
(*a, **kw)[source]¶ A scheme-agnostic factory for MacroExecutor instances
Example:
f = MacroExecutorFactory() f.getMacroExecutor('tango://my/door/name') #returns a TangoMacroExecutor
Note: For the moment, only TangoMacroExecutor is supported
-
getMacroExecutor
(door_name=None)[source]¶ Returns a macro executor instance (a subclass of
BaseMacroExecutor
) depending on the door being used.
-
-
class
BaseMacroTestCase
[source]¶ An abstract class for macro testing. BaseMacroTestCase will provide a macro_executor member which is an instance of BaseMacroExecutor and which can be used to run a macro.
To use it, simply inherit from BaseMacroTestCase and unittest.TestCase and provide the following class members:
macro_name (string) name of the macro to be tested (mandatory)
- door_name (string) name of the door where the macro will be executed.
This is optional. If not set, sardanacustomsettings.UNITTEST_DOOR_NAME is used
Then you may define test methods.
-
macro_name
= None¶
-
door_name
= 'door/demo1/1'¶
-
class
RunMacroTestCase
[source]¶ A base class for testing execution of arbitrary Sardana macros. See
BaseMacroTestCase
for requirements.- It provides the following helper methods:
-
setUp
()[source]¶ Preconditions: - Those from
BaseMacroTestCase
- the macro executor registers to all the log levels
-
macro_runs
(macro_params=None, wait_timeout=inf, data=0)[source]¶ A helper method to create tests that check if the macro can be successfully executed for the given input parameters. It may also optionally perform checks on the outputs from the execution.
Parameters: - macro_params – (seq<str>): parameters for running the macro. If passed, they must be given as a sequence of their string representations.
- wait_timeout – (float) maximum allowed time (in s) for the macro to finish. By default infinite timeout is used.
- data – (obj) Optional. If passed, the macro data after the execution is tested to be equal to this.
-
macro_fails
(macro_params=None, wait_timeout=inf, exception=None)[source]¶ Check that the macro fails to run for the given input parameters
Parameters: - macro_params – (seq<str>) input parameters for the macro
- wait_timeout – maximum allowed time for the macro to fail. By default infinite timeout is used.
- exception – (str or Exception) if given, an additional check of the type of the exception is done. (IMPORTANT: this is just a comparison of str representations of exception objects)
-
door_name
= 'door/demo1/1'¶
-
macro_name
= None¶
-
tearDown
()¶ The macro_executor instance must be removed
-
class
RunStopMacroTestCase
[source]¶ This is an extension of
RunMacroTestCase
to include helpers for testing the abort process of a macro. Useful for Runnable and Stopable macros.It provides the
macro_stops()
helper-
macro_stops
(macro_params=None, stop_delay=0.1, wait_timeout=inf)[source]¶ A helper method to create tests that check if the macro can be successfully stoped (a.k.a. aborted) after it has been launched.
Parameters: - macro_params – (seq<str>): parameters for running the macro. If passed, they must be given as a sequence of their string representations.
- stop_delay – (float) Time (in s) to wait between launching the macro and sending the stop command. default=0.1
- wait_timeout – (float) maximum allowed time (in s) for the macro to finish. By default infinite timeout is used.
-
assertFinished
(msg)¶ Asserts that macro has finished.
-
door_name
= 'door/demo1/1'¶
-
macro_fails
(macro_params=None, wait_timeout=inf, exception=None)¶ Check that the macro fails to run for the given input parameters
Parameters: - macro_params – (seq<str>) input parameters for the macro
- wait_timeout – maximum allowed time for the macro to fail. By default infinite timeout is used.
- exception – (str or Exception) if given, an additional check of the type of the exception is done. (IMPORTANT: this is just a comparison of str representations of exception objects)
-
macro_name
= None¶
-
macro_runs
(macro_params=None, wait_timeout=inf, data=0)¶ A helper method to create tests that check if the macro can be successfully executed for the given input parameters. It may also optionally perform checks on the outputs from the execution.
Parameters: - macro_params – (seq<str>): parameters for running the macro. If passed, they must be given as a sequence of their string representations.
- wait_timeout – (float) maximum allowed time (in s) for the macro to finish. By default infinite timeout is used.
- data – (obj) Optional. If passed, the macro data after the execution is tested to be equal to this.
-
setUp
()¶ Preconditions: - Those from
BaseMacroTestCase
- the macro executor registers to all the log levels
-
tearDown
()¶ The macro_executor instance must be removed
-
-
class
SarDemoEnv
(door_name=None)[source]¶ Class to get _SAR_DEMO environment variable with cross checking with the MacroServer (given by
UNITTEST_DOOR_NAME
)-
getElements
(elem_type='all')[source]¶ Return the name of sardemo element(s) of given elem type
Parameters: elem_type – (str) type of elemnts to return (all by default) Returns: (list<str>)
-
Sardana migration guide¶
This chapter describes how to migrate different sardana components between the different API versions.
How to migrate your macro code¶
This chapter describes the necessary steps to fully migrate your macros from API v0 ( sardana 0.x ) to API v1 ( sardana 1.x )
The following are the 2 necessary changes to make your macros work in sardana API v1:
from:
from macro import Macro, Type, Table, List
to:
from sardana.macroserver.macro import Macro, Type, Table, List
Parameter type
Type.Motor
should be changedType.Moveable
. In v0 the Motor meant any motor (including physical motor, pseudo motor). In v1, for consistency, Motor means only physical motor and Moveable means all moveable elements (including physical motor, pseudo motor).
This chapter is a summary of all new features in API v1.
- Macros can now be functions(see Writing macros).
How to migrate your controller code¶
This chapter describes the necessary steps to fully migrate your controller from API v0 ( sardana 0.x ) to API v1 ( sardana 1.x )
The following are the 2 necessary changes to make your controller work in sardana API v1:
from:
import pool from pool import <ControllerClass>/PoolUtil
to:
from sardana import pool from sardana.pool import PoolUtil from sardana.pool.controller import <ControllerClass>
change contructor from:
def __init__(self, inst, props): code
to:
def __init__(self, inst, props, *args, **kwargs): MotorController.__init__(self, inst, props, *args, **kwargs) code
(and don’t forget to call the super class constructor also with args and kwargs).
The following change is not mandatory but is necessary in order for your controller to be recognized by the pool to be a API v1 controller:
_log member changed from
logging.Logger
totaurus.core.util.Logger
. This means that you need to change code from:self._log.setLevel(logging.INFO)
to:
self._log.setLogLevel(logging.INFO)
or:
self._log.setLogLevel(taurus.Info)
since taurus.Info == logging.INFO.
The following changes are not necessary to make your controller work. The API v1 supports the API v0 on these matters.
- class members:
- from:
class_prop
to:ctrl_properties
- from:
ctrl_extra_attributes
to:axis_attributes
- new feature in API v1:
ctrl_attributes
data types:
StateOne()
return type: PreviouslyStateOne()
had to return a member ofPyTango.DevState
. Now it can instead return a member ofState
. This eliminates the need to importPyTango
.- In API v0 class member (like
ctrl_extra_attributes
) value for key type had to be a string (like ‘PyTango.DevString’ or ‘PyTango.DevDouble’). Now they can be a python type (like str or float). Please check Data Type definition for more information.
generic controller method names:
- from:
GetPar()
to:GetAxisPar()
- from:
SetPar()
to:SetAxisPar()
- from:
GetExtraAttributePar()
to:GetAxisExtraPar()
- from:
SetExtraAttributePar()
to:SetAxisExtraPar()
- new feature in API v1:
GetCtrlPar()
,SetCtrlPar()
- new feature in API v1:
AbortAll()
(has default implementation which callsAbortOne()
for each axis)
- from:
pseudo motor controller method names:
- from:
calc_pseudo()
to:CalcPseudo()
- from:
calc_physical()
to:CalcPhysical()
- from:
calc_all_pseudo()
to:CalcAllPseudo()
- from:
calc_all_physical()
to:CalcAllPhysical()
- new feature in API v1:
GetMotor()
- new feature in API v1:
GetPseudoMotor()
- from:
This chapter is a summary of all new features in API v1.
New controller features:
All Controllers now have a
ctrl_attributes
class member to define extra controller attributes (and new methods:GetCtrlPar()
,SetCtrlPar()
)For
ctrl_properties
,axis_attributes
andctrl_extra_attributes
:- new (more pythonic) syntax. Old syntax is still supported:
- can replace data type strings for python type (‘PyTango.DevDouble’ -> float)
- Default behavior. Example: before data access needed to be described explicitly. Now it is read-write by default.
- support for 2D
- new keys ‘fget’ and ‘fset’ override default method calls
no need to import
PyTango
(StateOne()
can return sardana.State.On instead of PyTango.DevState.ON)PseudoMotorController has new
GetMotor()
andGetPseudoMotor()
new
AbortAll()
(with default implementation which callsAbortOne()
for each axis)new
StopOne()
(with default implementation which callsAbortOne()
)new
StopAll()
(with default implementation which callsStoptOne()
for each axis)- new
GetAxisAttributes()
allows features like: - per axis customized dynamic attributes
- Basic interface (example: motor without velocity or acceleration)
- Discrete motor (declare position has an integer instead of a float). No need for IORegisters anymore
- new
- New
MotorController
constants:
- New
New acquisition features:
- Measurement group has a new Configuration attribute which contains the full description of the experiment in JSON format
New Tango API features:
- Controllers are now Tango devices
- Pool has a default PoolPath (points to <pool install dir>/poolcontrollers)
- Create* commands can receive JSON object or an old style list of parameters
- new CreateElement command (can replace CreateMotor, CreateExpChannel, etc)
- Pool Abort command: aborts all elements (non pseudo elements)
- Pool Stop command: stops all elements (non pseudo elements)
- Controller Abort command: aborts all controller elements
- Controller Stop command: stops all controller elements
- Controllers have a LogLevel attribute which allows remote python logging management
Others:
- Pool device is a python device :-)
- many command line parameters help logging, debugging
Examples¶
Macro examples¶
This chapter consists of a series of examples demonstrating how to declare macros which receive parameter(s).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | ##############################################################################
##
## This file is part of Sardana
##
## http://www.sardana-controls.org/
##
## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
##
## Sardana is free software: you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## Sardana is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with Sardana. If not, see <http://www.gnu.org/licenses/>.
##
##############################################################################
"""This module contains macros that demonstrate the usage of macro parameters"""
from sardana.macroserver.macro import *
__all__ = ["pt0", "pt1", "pt2", "pt3", "pt4", "pt5", "pt6", "pt7", "pt8",
"pt9"]
class pt0(Macro):
"""Macro without parameters. Pretty dull"""
param_def = []
def run(self):
pass
class pt1(Macro):
"""Macro with one float parameter: Each parameter is described in the
param_def sequence as being a sequence of for elements: name, type,
default value and description"""
param_def = [ [ 'value', Type.Float, None, 'some bloody float'] ]
def run(self, f):
pass
class pt2(Macro):
"""Macro with one Motor parameter: Each parameter is described in the
param_def sequence as being a sequence of for elements: name, type,
default value and description"""
param_def = [ [ 'motor', Type.Motor, None, 'some bloody motor'] ]
def run(self, m):
pass
class pt3(Macro):
"""Macro with a list of numbers as parameter: the type is a sequence of
parameter types which is repeated. In this case it is a repetition of a
float so only one parameter is defined.
By default the repetition as a semantics of 'at least one'"""
param_def = [
[ 'numb_list', [ [ 'pos', Type.Float, None, 'value'] ], None, 'List of values'],
]
def run(self, *args, **kwargs):
pass
class pt4(Macro):
"""Macro with a list of motors as parameter: the type is a sequence of
parameter types which is repeated. In this case it is a repetition of a
motor so only one parameter is defined.
By default the repetition as a semantics of 'at least one'"""
param_def = [
[ 'motor_list', [ [ 'motor', Type.Motor, None, 'motor name'] ], None, 'List of motors'],
]
def run(self, *args, **kwargs):
pass
class pt5(Macro):
"""Macro with a motor parameter followed by a list of numbers"""
param_def = [
[ 'motor', Type.Motor, None, 'Motor to move'],
[ 'numb_list', [ [ 'pos', Type.Float, None, 'value'] ], None, 'List of values'],
]
def run(self, *args, **kwargs):
pass
class pt6(Macro):
"""Macro with a motor parameter followed by a list of numbers. The list as
explicitly stated an optional last element which is a dictionary that defines the
min and max values for repetitions"""
param_def = [
[ 'motor', Type.Motor, None, 'Motor to move'],
[ 'numb_list', [ [ 'pos', Type.Float, None, 'value'], { 'min' : 1, 'max' : None } ], None, 'List of values'],
]
def run(self, *args, **kwargs):
pass
class pt7(Macro):
"""Macro with a list of pair Motor,Float"""
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
class pt8(Macro):
"""Macro with a list of pair Motor,Float. The min and max elements have been
explicitly stated"""
param_def = [
[ 'm_p_pair', [ [ 'motor', Type.Motor, None, 'Motor to move'],
[ 'pos', Type.Float, None, 'Position to move to'],
{ 'min' : 1, 'max' : 2 } ],
None, 'List of motor/position pairs']
]
def run(self, *args, **kwargs):
pass
class pt9(Macro):
"""Same as macro pt7 but witb old style ParamRepeat. If you are writing
a macro with variable number of parameters for the first time don't even
bother to look at this example since it is DEPRECATED."""
param_def = [
['m_p_pair',
ParamRepeat(['motor', Type.Motor, None, 'Motor to move'],
['pos', Type.Float, None, 'Position to move to'], min=1, max= 2),
None, 'List of motor/position pairs'],
]
def run(self, *args, **kwargs):
pass
class twice(Macro):
"""A macro that returns a float that is twice its input. It also sets its
data to be a dictionary with 'in','out' as keys and value,result
as values, respectively"""
# uncomment the following lines as necessary. Otherwise you may delete them
param_def = [ [ "value", Type.Float, 23, "value to be doubled" ] ]
result_def = [ [ "result", Type.Float, 23, "the double of the given value" ] ]
#hints = {}
#env = (,)
# uncomment the following lines if need prepare. Otherwise you may delete them
#def prepare(self):
# pass
def run(self, n):
ret = 2*n
self.setData({'in':n, 'out':ret})
return ret
|
This chapter consists of a series of examples demonstrating how to call macros from inside a macro
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | ##############################################################################
##
## This file is part of Sardana
##
## http://www.sardana-controls.org/
##
## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
##
## Sardana is free software: you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## Sardana is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with Sardana. If not, see <http://www.gnu.org/licenses/>.
##
##############################################################################
"""
A macro package to show examples on how to run a macro from inside another macro
"""
__all__ = ["call_wa", "call_wm", "subsubm", "subm", "mainmacro", "runsubs"]
__docformat__ = 'restructuredtext'
from sardana.macroserver.macro import Macro, Type, ParamRepeat
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# First example:
# A 'mainmacro' that executes a 'subm' that in turn executes a 'subsubm'.
# The 'subsubm' macro itself calls a short ascan macro
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~--~-~-
class call_wa(Macro):
def run(self):
self.macros.wa()
class call_wm(Macro):
param_def = [
['motor_list',
ParamRepeat(['motor', Type.Motor, None, 'Motor to move']),
None, 'List of motor to show'],
]
def run(self, *m):
self.macros.wm(*m)
class subsubm(Macro):
"""this macro just calls the 'subm' macro
This macro is part of the examples package. It was written for demonstration purposes"""
def run(self):
self.output("Starting %s" % self.getName())
m = self.macros
motors = self.getObjs('.*', type_class=Type.Motor)
m.ascan(motors[0], 0, 100, 10, 0.2)
self.output("Finished %s" % self.getName())
class subm(Macro):
"""this macro just calls the 'subsubm' macro
This macro is part of the examples package. It was written for demonstration purposes"""
def run(self):
self.output("Starting %s" % self.getName())
self.macros.subsubm()
self.output("Finished %s" % self.getName())
class mainmacro(Macro):
"""this macro just calls the 'subm' macro
This macro is part of the examples package. It was written for demonstration purposes"""
def run(self):
self.output("Starting %s" % self.getName())
self.macros.subm()
self.output("Finished %s" % self.getName())
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# Second example:
# a 'runsubs' macro that shows the different ways to call a macro from inside
# another macro
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~--~-~-
class runsubs(Macro):
""" A macro that calls a ascan macro using the motor given as first parameter.
This macro is part of the examples package. It was written for demonstration purposes
Call type will allow to choose to format in which the ascan macro is called
from this macro:
1 - m.ascan(motor.getName(), '0', '10', '4', '0.2')
2 - m.ascan(motor, 0, 10, 4, 0.2)
3 - self.execMacro('ascan', motor.getName(), '0', '10', '4', '0.2')
4 - self.execMacro(['ascan', motor, 0, 10, 4, 0.2])
5 - params = 'ascan', motor, 0, 10, 4, 0.2
self.execMacro(params)
6 - self.execMacro("ascan %s 0 10 4 0.2" % motor.getName())
7 - macro, prep = self.createMacro("ascan %s 0 10 4 0.2" % motor.getName())
macro.hooks = [ self.hook ]
self.runMacro(macro)
8 - macro, prep = self.createMacro('ascan', motor, 0, 10, 4, 0.2)
macro.hooks = [ self.hook ]
self.runMacro(macro)
9 - params = 'ascan', motor, 0, 10, 4, 0.2
macro, prep = self.createMacro(params)
macro.hooks = [ self.hook ]
self.runMacro(macro)
Options 7,8 and 9 use the lower level macro API in order to be able to
attach hooks to the ascan macro."""
param_def = [
['motor', Type.Motor, None, 'Motor to move'],
['call_type', Type.Integer, 2, 'type of run to execute internally'],
]
def hook(self):
self.info("executing hook in a step of a scan...")
def run(self, motor, call_type):
m = self.macros
self.output("Using type %d" % call_type)
if call_type == 1:
m.ascan(motor.getName(), '0', '10', '4', '0.2')
elif call_type == 2:
m.ascan(motor, 0, 10, 4, 0.2)
elif call_type == 3:
self.execMacro('ascan', motor.getName(), '0', '10', '4', '0.2')
elif call_type == 4:
self.execMacro('ascan', motor, 0, 10, 4, 0.2)
elif call_type == 5:
params = 'ascan', motor, 0, 10, 4, 0.2
self.execMacro(params)
elif call_type == 6:
self.execMacro("ascan %s 0 10 4 0.2" % motor.getName())
elif call_type == 7:
macro, prep = self.createMacro("ascan %s 0 10 4 0.2" % \
motor.getName())
macro.hooks = [ self.hook ]
self.runMacro(macro)
elif call_type == 8:
macro, prep = self.createMacro('ascan', motor, 0, 10, 4, 0.2)
macro.hooks = [ self.hook ]
self.runMacro(macro)
elif call_type == 9:
params = 'ascan', motor, 0, 10, 4, 0.2
macro, prep = self.createMacro(params)
macro.hooks = [ self.hook ]
self.runMacro(macro)
|
This chapter consists of a series of examples demonstrating how to plot graphics from inside a macro.
The complete set of pyplot
examples can be found
here
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | import math
from numpy import linspace
from scipy.integrate import quad
from scipy.special import j0
from sardana.macroserver.macro import macro, Type
def j0i(x):
"""Integral form of J_0(x)"""
def integrand(phi):
return math.cos(x * math.sin(phi))
return (1.0/math.pi) * quad(integrand, 0, math.pi)[0]
@macro()
def J0_plot(self):
"""Sample J0 at linspace(0, 20, 200)"""
x = linspace(0, 20, 200)
y = j0(x)
x1 = x[::10]
y1 = map(j0i, x1)
self.pyplot.plot(x, y, label=r'$J_0(x)$') #
self.pyplot.plot(x1, y1, 'ro', label=r'$J_0^{integ}(x)$')
self.pyplot.title(r'Verify $J_0(x)=\frac{1}{\pi}\int_0^{\pi}\cos(x \sin\phi)\,d\phi$')
self.pyplot.xlabel('$x$')
self.pyplot.legend()
from numpy import random
@macro()
def random_image(self):
"""Shows a random image 32x32"""
img = random.random((32, 32))
self.pyplot.matshow(img)
import numpy
@macro([["interactions", Type.Integer, None, ""],
["density", Type.Integer, None, ""]])
def mandelbrot(self, interactions, density):
x_min, x_max = -2, 1
y_min, y_max = -1.5, 1.5
x, y = numpy.meshgrid(numpy.linspace(x_min, x_max, density),
numpy.linspace(y_min, y_max, density))
c = x + 1j * y
z = c.copy()
fractal = numpy.zeros(z.shape, dtype=numpy.uint8) + 255
finteractions = float(interactions)
for n in range(interactions):
z *= z
z += c
mask = (fractal == 255) & (abs(z) > 10)
fractal[mask] = 254 * n / finteractions
self.pyplot.imshow(fractal)
|
This chapter consists of a series of examples demonstrating how to ask for user input inside macros.
A tutorial on macro input parameter can be found here.
The API documentation: input()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
from sardana.macroserver.macro import imacro, Type
@imacro()
def ask_number_of_points(self):
"""asks user for the number of points"""
nb_points = self.input("How many points?", data_type=Type.Integer)
@imacro()
def ask_for_moveable(self):
"""asks user for a motor"""
moveable = self.input("Which moveable?", data_type=Type.Moveable)
self.output("You selected %s which is at %f", moveable, moveable.getPosition())
@imacro()
def ask_for_car_brand(self):
"""asks user for a car brand"""
car_brands = "Mazda", "Citroen", "Renault"
car_brand = self.input("Which car brand?", data_type=car_brands)
self.output("You selected %s", car_brand)
@imacro()
def ask_for_multiple_car_brands(self):
"""asks user for several car brands"""
car_brands = "Mazda", "Citroen", "Renault", "Ferrari", "Porche", "Skoda"
car_brands = self.input("Which car brand(s)?", data_type=car_brands,
allow_multiple=True, title="Favorites")
self.output("You selected %s", ", ".join(car_brands))
@imacro()
def ask_peak(self):
"""asks user for peak current of points with a custom title"""
peak = self.input("What is the peak current?", data_type=Type.Float,
title="Peak selection")
self.output("You selected a peak of %f A", peak)
@imacro()
def ask_peak_v2(self):
"""asks user for peak current of points with a custom title,
default value, label and units"""
label, unit = "peak", "mA"
peak = self.input("What is the peak current?", data_type=Type.Float,
title="Peak selection", key=label, unit=unit,
default_value=123.4)
self.output("You selected a %s of %f %s", label, peak, unit)
@imacro()
def ask_peak_v3(self):
"""asks user for peak current of points with a custom title,
default value, label, units and ranges"""
label, unit = "peak", "mA"
peak = self.input("What is the peak current?", data_type=Type.Float,
title="Peak selection", key=label, unit=unit,
default_value=123.4, minimum=0.0, maximum=200.0)
self.output("You selected a %s of %f %s", label, peak, unit)
@imacro()
def ask_peak_v4(self):
"""asks user for peak current of points with a custom title,
default value, label, units, ranges and step size"""
label, unit = "peak", "mA"
peak = self.input("What is the peak current?", data_type=Type.Float,
title="Peak selection", key=label, unit=unit,
default_value=123.4, minimum=0.0, maximum=200.0,
step=5)
self.output("You selected a %s of %f %s", label, peak, unit)
@imacro()
def ask_peak_v5(self):
"""asks user for peak current of points with a custom title,
default value, label, units, ranges, step size and decimal places"""
label, unit = "peak", "mA"
peak = self.input("What is the peak current?", data_type=Type.Float,
title="Peak selection", key=label, unit=unit,
default_value=123.4, minimum=0.0, maximum=200.0,
step=5, decimals=2)
self.output("You selected a %s of %f %s", label, peak, unit)
|
Controller examples¶
This code let you create a basic template of a controller.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | #!/usr/bin/env python
import sys
"""
ControllerTemplate.py: Create a basic controller's template.
Its parameters are the file name plus .py,
the class inherited if it had (optional)
and "yes" if you want to use the obsolete convention.
The necessary "defs" are marked as #TODO
python ControllerTemplate.py ExampleClass.py InheritedClass NoCT
"""
__author__ = "Carlos Falcon - cfalcon@cells.es"
class ControllerTemplate():
def __init__(self,f, e=""):
self.filename = f
self.end = e
self.ind = 'ind'
# pass
def addHead(self):
f=open(self.filename,"w")
f.write('##############################################################################\n'+\
'##\n'+\
'## This file is part of Sardana\n'+\
'##\n'+\
'## http://www.sardana-controls.org/\n'+\
'##\n'+\
'## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain\n'+\
'##\n'+\
'## Sardana is free software: you can redistribute it and/or modify\n'+\
'## it under the terms of the GNU Lesser General Public License as published by\n'+\
'## the Free Software Foundation, either version 3 of the License, or\n'+\
'## (at your option) any later version.\n'+\
'##\n'+\
'## Sardana is distributed in the hope that it will be useful,\n'+\
'## but WITHOUT ANY WARRANTY; without even the implied warranty of\n'+\
'## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n'+\
'## GNU Lesser General Public License for more details.\n'+\
'##\n'+\
'## You should have received a copy of the GNU Lesser General Public License\n'+\
'## along with Sardana. If not, see <http://www.gnu.org/licenses/>.\n'+\
'##\\n'+\
'##############################################################################\n\n')\
def addIncludes(self,inherit,others=None):
f = open(self.filename,"a")
text = "from sardana import State\n"
if inherit!="":
text = text+"from sardana.pool.controller import "+inherit+"\n"
if inherit.find("Motor")>=0:
self.ind = 'axis'
if others is not None:
text = text+others
text = text+"#ADD others includes\n\n"
f.write(text)
#f.close()
def createBasicClass(self):
f = open(self.filename,"a")
text = "#TODO - Delete it if you don't need\n"
text = text +'class BasicClass():\n'+\
'\tpass\n\n'
f.write(text)
def createMainClass(self,inherit):
f = open(self.filename,"a")
text = "class "+self.filename[0:len(self.filename)-3]+"("+inherit+"):\n"+\
'\t"""Description""" #TODO\n'+\
'\tgender = "Simulation"\n'+\
'\tmodel = "Basic"\n'+\
'\torganization = "CELLS - ALBA"\n'+\
'\timage = "IMAGE.png"\n'+\
'\tlogo = "ALBA_logo.png"\n\n'+\
'\t#TODO - Delete it if you don\'t need\n'+\
'\tctrl_properties= { \'AAA\' : { \'Type\' : \'DevString\', \'Description\' : \'AAA\' } }\n'+\
'\taxis_attributes = { \'AAA\' : { \'type\' : str, \'Description\' : \'AAA\' }}\n\n'+\
'\tMaxDevice = 1024 #TODO Standar value\n\n'
fun = '# --------------------------------------------------------------------------\n'+\
'# Init()\n'+\
'# --------------------------------------------------------------------------\n'+\
'\tdef __init__(self, inst, props, *args, **kwargs):\n'
if inherit!="":
fun = fun + '\t\t' + inherit + '.__init__(self, inst, props, *args, **kwargs)\n'
fun = fun + '\t\t#TODO\n'
text = text + fun
fun = '# --------------------------------------------------------------------------\n'+\
'# AddDevice/DelDevice()\n'+\
'# --------------------------------------------------------------------------\n'+\
'\tdef AddDevice(self,'+self.ind+'):\n'
fun = fun + '\t\t#TODO\n'
fun = fun + '\tdef DeleteDevice(self, '+self.ind+'):\n'
fun = fun + '\t\t#TODO\n'
text = text + fun
fun = '# --------------------------------------------------------------------------\n'+\
'# State()\n'+\
'# --------------------------------------------------------------------------\n'+\
'\tdef PreStateOne'+self.end+'(self, '+self.ind+'):\n'+'\t\tpass\n'
fun = fun + '\tdef StateOne(self, '+self.ind+'):\n'
fun = fun + '\t\tstate = State.On\n'
fun = fun + '\t\tstatus = "Undefined"\n'
if inherit.find("Motor")>=0:
fun = fun + '\t\tswitchstate = 0"\n'
fun = fun + '\t\t#TODO\n'
fun = fun + '\t\treturn state, status, switchstate"\n'
else:
fun = fun + '\t\t#TODO\n'
fun = fun + '\t\treturn state, status\n'
fun = fun + '\tdef PreStateAll'+self.end+'(self):\n'
fun = fun + '\t\tpass\n'
fun = fun + '\tdef StateAll'+self.end+'(self):\n'
fun = fun + '\t\tpass\n'
text = text + fun
fun = '# --------------------------------------------------------------------------\n'+\
'# Read()\n'+\
'# --------------------------------------------------------------------------\n'+\
'\tdef PreReadOne'+self.end+'(self, '+self.ind+'):\n'+'\t\tpass\n'
fun = fun + '\tdef ReadOne(self, '+self.ind+'):\n'
fun = fun + '\t\t#TODO\n'
fun = fun + '\tdef PreReadAll'+self.end+'(self):\n'
fun = fun + '\t\tpass\n'
fun = fun + '\tdef ReadAll'+self.end+'(self):\n'
fun = fun + '\t\tpass\n'
text = text + fun
fun = '# --------------------------------------------------------------------------\n'+\
'# Start/Stop()\n'+\
'# --------------------------------------------------------------------------\n'+\
'\tdef PreStartOne'+self.end
if inherit.find("Motor")>=0:
fun = fun + '(self, '+self.ind+', pos):\n'
else:
fun = fun + '(self, '+self.ind+'):\n'
fun = fun + '\t\tpass\n'
fun = fun + '\tdef StartOne'+self.end+'(self, '+self.ind+', pos):\n'
fun = fun + '\t\t#TODO\n'
fun = fun + '\tdef AbortOne(self, '+self.ind+'):\n'
fun = fun + '\t\t#TODO\n'
fun = fun + '\tdef StopOne(self, '+self.ind+'):\n'
fun = fun + '\t\tself.AbortOne('+self.ind+')\n'
fun = fun + '\tdef PreStartAll'+self.end+'(self):\n'
fun = fun + '\t\tpass\n'
fun = fun + '\tdef StartAll'+self.end+'(self):\n'
fun = fun + '\t\tpass\n'
fun = fun + '\tdef AbortAll(self):\n'
fun = fun + '\t\tpass\n'
text = text + fun
fun = '# --------------------------------------------------------------------------\n'+\
'# SetAxisPar/GetAxisPar()\n'+\
'# --------------------------------------------------------------------------\n'+\
'\tdef SetAxisPar(self, '+self.ind+', name, value):\n'
fun = fun + '\t\t#TODO - Delete it if you don\'t need\n'
fun = fun + '\tdef GetAxisPar(self, '+self.ind+', name):\n'
fun = fun + '\t\t#TODO - Delete it if you don\'t need\n'
text = text + fun
fun = '# --------------------------------------------------------------------------\n'+\
'# SetAxisExtraPar/GetAxisExtraPar()\n'+\
'# --------------------------------------------------------------------------\n'+\
'\tdef SetAxisExtraPar(self, '+self.ind+', name, value):\n'
fun = fun + '\t\t#TODO - Delete it if you don\'t need\n'
fun = fun + '\tdef GetAxisExtraPar(self, '+self.ind+', name):\n'
fun = fun + '\t\t#TODO - Delete it if you don\'t need - \n'
text = text + fun
f.write(text)
def main():
#Add MACRO_PATH
filename = ""
end = ""
inherit = ""
if(len(sys.argv) > 1):
print "Creating " + sys.argv[1]
filename = sys.argv[1]
if(len(sys.argv) > 2):
inherit = sys.argv[2]
if(len(sys.argv) > 3):
end = "CT"
s= ControllerTemplate(filename,end)
s.addHead()
s.addIncludes(inherit)
s.createBasicClass()
s.createMainClass(inherit)
else:
print "Please introduce filename"
if __name__ == "__main__":
main()
|
Sardana development guidelines¶
Overview¶
This document describes sardana from the perspective of developers. Most importantly, it gives information for people who want to contribute code to the development of sardana. So if you want to help out, read on!
How to contribute to sardana¶
Sardana development is managed with the Sardana sourceforge project.
Apart from directly contributing code, you can contribute to sardana in many ways, such as reporting bugs or proposing new features. In all cases you will probably need a sourceforge account and you are strongly encouragedto subscribe to the sardana-devel and sardana-users mailing lists <https://sourceforge.net/p/sardana/mailman/>_.
The rest of this document will focus on how to contribute code.
Cloning and forking sardana from Git¶
You are welcome to clone the Sardana code from our main Git repository:
git clone git://git.code.sf.net/p/sardana/sardana.git sardana
Code contributions (bug patches, new features) are welcome, but the review process/workflow for accepting new code is yet to be discussed. For the moment, use the sardana-devel mailing list for proposing patches.
Note that you can also fork the git repository in sourceforge to get your own sourceforge-hosted clone of the sardana repository to which you will have full access. This will create a new git repository associated to your personal account in sourceforge, so that your changes can be easily shared and eventually merged into the official repository.
The old SVN code repository¶
After the release of Sardana 1.2 the Sardana code was migrated from its previous host in a SVN server to its current Git repository
The old SVN repository is still accessible for reference, but writing has been disabled and its contents are frozen as of 2013-07-31. For development, see the instructions above on cloning from Git
Documentation¶
All standalone documentation should be written in plain text (.rst
) files
using reStructuredText for markup and formatting. All such
documentation should be placed in directory docs/source
of the sardana
source tree. The documentation in this location will serve as the main source
for sardana documentation and all existing documentation should be converted
to this format.
Coding conventions¶
In general, we try to follow the standard Python style conventions as described in Style Guide for Python Code
Code must be python 2.6 compatible
Use 4 spaces for indentation
In the same file, different classes should be separated by 2 lines
use
lowercase
for module names. If possible prefix module names with the wordsardana
(likesardanautil.py
) to avoid import mistakes.use
CamelCase
for class namespython module first line should be:
#!/usr/bin/env python
python module should contain license information (see template below)
avoid poluting namespace by making private definitions private (
__
prefix) or/and implementing__all__
(see template below)whenever a python module can be executed from the command line, it should contain a
main
function and a call to it in aif __name__ == "__main__"
like statement (see template below)document all code using Sphinx extension to reStructuredText
The following code can serve as a template for writing new python modules to sardana:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #!/usr/bin/env python
# -*- coding: utf-8 -*-
##############################################################################
##
## This file is part of Sardana
##
## http://www.tango-controls.org/static/sardana/latest/doc/html/index.html
##
## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
##
## Sardana is free software: you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## Sardana is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with Sardana. If not, see <http://www.gnu.org/licenses/>.
##
##############################################################################
"""A :mod:`sardana` module written for template purposes only"""
__all__ = ["SardanaDemo"]
__docformat__ = "restructuredtext"
class SardanaDemo(object):
"""This class is written for template purposes only"""
def main():
print "SardanaDemo"s
if __name__ == "__main__":
main()
|
Glossary¶
...
- The default Python prompt of the interactive shell when entering code for an indented code block or within a pair of matching left and right delimiters (parentheses, square brackets or curly braces).
>>>
- The default Python prompt of the interactive shell. Often seen for code examples which can be executed interactively in the interpreter.
- API
- An application programming interface (API) is a particular set of rules and specifications that software programs can follow to communicate with each other. It serves as an interface between different software programs and facilitates their interaction, similar to the way the user interface facilitates interaction between humans and computers. An API can be created for applications, libraries, operating systems, etc., as a way of defining their “vocabularies” and resources request conventions (e.g. function-calling conventions). It may include specifications for routines, data structures, object classes, and protocols used to communicate between the consumer program and the implementer program of the API.
- argument
A value passed to a function or method, assigned to a named local variable in the function body. A function or method may have both positional arguments and keyword arguments in its definition. Positional and keyword arguments may be variable-length:
*
accepts or passes (if in the function definition or call) several positional arguments in a list, while**
does the same for keyword arguments in a dictionary.Any expression may be used within the argument list, and the evaluated value is passed to the local variable.
- attribute
A value associated with an object which is referenced by name using dotted expressions. For example, if an object o has an attribute a it would be referenced as o.a.
dictionary An associative array, where arbitrary keys are mapped to values. The keys can be any object with
__hash__()
and__eq__()
methods. Called a hash in Perl.- CCD
- A charge-coupled device (CCD) is a device for the movement of electrical charge, usually from within the device to an area where the charge can be manipulated, for example conversion into a digital value. This is achieved by “shifting” the signals between stages within the device one at a time. CCDs move charge between capacitive bins in the device, with the shift allowing for the transfer of charge between bins.
- class
- A template for creating user-defined objects. Class definitions normally contain method definitions which operate on instances of the class.
- CLI
- A command-line interface (CLI) is a mechanism for interacting with a computer operating system or software by typing commands to perform specific tasks. This text-only interface contrasts with the use of a mouse pointer with a graphical user interface (GUI) to click on options, or menus on a text user interface (TUI) to select options. This method of instructing a computer to perform a given task is referred to as “entering” a command: the system waits for the user to conclude the submitting of the text command by pressing the “Enter” key (a descendant of the “carriage return” key of a typewriter keyboard). A command-line interpreter then receives, parses, and executes the requested user command. The command-line interpreter may be run in a text terminal or in a terminal emulator window as a remote shell client such as PuTTY. Upon completion, the command usually returns output to the user in the form of text lines on the CLI. This output may be an answer if the command was a question, or otherwise a summary of the operation.
- client-server model
- The client-server model of computing is a distributed application structure that partitions tasks or workloads between the providers of a resource or service, called servers, and service requesters, called clients. Often clients and servers communicate over a computer network on separate hardware, but both client and server may reside in the same system. A server machine is a host that is running one or more server programs which share their resources with clients. A client does not share any of its resources, but requests a server’s content or service function. Clients therefore initiate communication sessions with servers which await incoming requests.
- closed loop
- A.k.a feedback loop, occurs when outputs of a system are routed back as inputs as part of a chain of cause-and-effect that forms a circuit or loop. In case of motion systems, closed loop positioning uses the position sensors e.g. encoders to measure the system’s output. The measured signal is looped back to the control unit as input and is used to correct the moveable’s position.
- daemon
- In Unix and other computer multitasking operating systems, a daemon is a computer program that runs in the background, rather than under the direct control of a user. They are usually initiated as background processes. Typically daemons have names that end with the letter “d”: for example, syslogd, the daemon that handles the system log, or sshd, which handles incoming SSH connections.
- dial
- See dial position
- dial position
- Position in controller units (See also user position).
- expression
- A piece of syntax which can be evaluated to some value. In other words,
an expression is an accumulation of expression elements like literals,
names, attribute access, operators or function calls which all return a
value. In contrast to many other languages, not all language constructs
are expressions. There are also statements which cannot be used
as expressions, such as
print()
orif
. Assignments are also statements, not expressions. - function
- A series of statements which returns some value to a caller. It can also be passed zero or more arguments which may be used in the execution of the body. See also argument and method.
- generator
- A function which returns an iterator. It looks like a normal function
except that it contains
yield
statements for producing a series a values usable in a for-loop or that can be retrieved one at a time with thenext()
function. Eachyield
temporarily suspends processing, remembering the location execution state (including local variables and pending try-statements). When the generator resumes, it picks-up where it left-off (in contrast to functions which start fresh on every invocation). - generator expression
An expression that returns an iterator. It looks like a normal expression followed by a
for
expression defining a loop variable, range, and an optionalif
expression. The combined expression generates values for an enclosing function:>>> sum(i*i for i in range(10)) # sum of squares 0, 1, 4, ... 81 285
- GUI
- A graphical user interface (GUI) is a type of user interface that allows users to interact with electronic devices with images rather than text commands. GUIs can be used in computers, hand-held devices such as MP3 players, portable media players or gaming devices, household appliances and office equipment. A GUI represents the information and actions available to a user through graphical icons and visual indicators such as secondary notation, as opposed to text-based interfaces (CLI), typed command labels or text navigation. The actions are usually performed through direct manipulation of the graphical elements.
- interactive
- Python has an interactive interpreter which means you can enter
statements and expressions at the interpreter prompt, immediately
execute them and see their results. Just launch
python
with no arguments (possibly by selecting it from your computer’s main menu). It is a very powerful way to test out new ideas or inspect modules and packages (rememberhelp(x)
). - interpreted
- Python is an interpreted language, as opposed to a compiled one, though the distinction can be blurry because of the presence of the bytecode compiler. This means that source files can be run directly without explicitly creating an executable which is then run. Interpreted languages typically have a shorter development/debug cycle than compiled ones, though their programs generally also run more slowly. See also interactive.
- iterable
- An object capable of returning its members one at a
time. Examples of iterables include all sequence types (such as
list
,str
, andtuple
) and some non-sequence types likedict
andfile
and objects of any classes you define with an__iter__()
or__getitem__()
method. Iterables can be used in afor
loop and in many other places where a sequence is needed (zip()
,map()
, ...). When an iterable object is passed as an argument to the built-in functioniter()
, it returns an iterator for the object. This iterator is good for one pass over the set of values. When using iterables, it is usually not necessary to calliter()
or deal with iterator objects yourself. Thefor
statement does that automatically for you, creating a temporary unnamed variable to hold the iterator for the duration of the loop. See also iterator, sequence, and generator. - iterator
An object representing a stream of data. Repeated calls to the iterator’s
next()
method return successive items in the stream. When no more data are available aStopIteration
exception is raised instead. At this point, the iterator object is exhausted and any further calls to itsnext()
method just raiseStopIteration
again. Iterators are required to have an__iter__()
method that returns the iterator object itself so every iterator is also iterable and may be used in most places where other iterables are accepted. One notable exception is code which attempts multiple iteration passes. A container object (such as alist
) produces a fresh new iterator each time you pass it to theiter()
function or use it in afor
loop. Attempting this with an iterator will just return the same exhausted iterator object used in the previous iteration pass, making it appear like an empty container.More information can be found in Iterator Types.
- key function
A key function or collation function is a callable that returns a value used for sorting or ordering. For example,
locale.strxfrm()
is used to produce a sort key that is aware of locale specific sort conventions.A number of tools in Python accept key functions to control how elements are ordered or grouped. They include
min()
,max()
,sorted()
,list.sort()
,heapq.nsmallest()
,heapq.nlargest()
, anditertools.groupby()
.There are several ways to create a key function. For example. the
str.lower()
method can serve as a key function for case insensitive sorts. Alternatively, an ad-hoc key function can be built from alambda
expression such aslambda r: (r[0], r[2])
. Also, theoperator
module provides three key function constructors:attrgetter()
,itemgetter()
, andmethodcaller()
. See the Sorting HOW TO for examples of how to create and use key functions.- keyword argument
- Arguments which are preceded with a
variable_name=
in the call. The variable name designates the local name in the function to which the value is assigned.**
is used to accept or pass a dictionary of keyword arguments. See argument. - lambda
- An anonymous inline function consisting of a single expression
which is evaluated when the function is called. The syntax to create
a lambda function is
lambda [arguments]: expression
- list
- A built-in Python sequence. Despite its name it is more akin to an array in other languages than to a linked list since access to elements are O(1).
- list comprehension
- A compact way to process all or part of the elements in a sequence and
return a list with the results.
result = ["0x%02x" % x for x in range(256) if x % 2 == 0]
generates a list of strings containing even hex numbers (0x..) in the range from 0 to 255. Theif
clause is optional. If omitted, all elements inrange(256)
are processed. - MCA
- Multichannel Analyzer (MCA) is a device for ...
- method
- A function which is defined inside a class body. If called as an attribute
of an instance of that class, the method will get the instance object as
its first argument (which is usually called
self
). See function and nested scope. - namespace
- The place where a variable is stored. Namespaces are implemented as
dictionaries. There are the local, global and built-in namespaces as well
as nested namespaces in objects (in methods). Namespaces support
modularity by preventing naming conflicts. For instance, the functions
__builtin__.open()
andos.open()
are distinguished by their namespaces. Namespaces also aid readability and maintainability by making it clear which module implements a function. For instance, writingrandom.seed()
oritertools.izip()
makes it clear that those functions are implemented by therandom
anditertools
modules, respectively. - nested scope
- The ability to refer to a variable in an enclosing definition. For instance, a function defined inside another function can refer to variables in the outer function. Note that nested scopes work only for reference and not for assignment which will always write to the innermost scope. In contrast, local variables both read and write in the innermost scope. Likewise, global variables read and write to the global namespace.
- new-style class
- Any class which inherits from
object
. This includes all built-in types likelist
anddict
. Only new-style classes can use Python’s newer, versatile features like__slots__
, descriptors, properties, and__getattribute__()
. - object
- Any data with state (attributes or value) and defined behavior (methods). Also the ultimate base class of any new-style class.
- OS
- An operating system (OS) is software, consisting of programs and data, that runs on computers, manages computer hardware resources, and provides common services for execution of various application software. Operating system is the most important type of system software in a computer system. Without an operating system, a user cannot run an application program on their computer, unless the application program is self booting.
- plug-in
- a plug-in (or plugin) is a set of software components that adds specific abilities to a larger software application. If supported, plug-ins enable customizing the functionality of an application. For example, plug-ins are commonly used in web browsers to play video, scan for viruses, and display new file types.
- plugin
- See plug-in.
- positional argument
- The arguments assigned to local names inside a function or method,
determined by the order in which they were given in the call.
*
is used to either accept multiple positional arguments (when in the definition), or pass several arguments as a list to a function. See argument. - Python 3000
- Nickname for the Python 3.x release line (coined long ago when the release of version 3 was something in the distant future.) This is also abbreviated “Py3k”.
- Pythonic
An idea or piece of code which closely follows the most common idioms of the Python language, rather than implementing code using concepts common to other languages. For example, a common idiom in Python is to loop over all elements of an iterable using a
for
statement. Many other languages don’t have this type of construct, so people unfamiliar with Python sometimes use a numerical counter instead:for i in range(len(food)): print food[i]
As opposed to the cleaner, Pythonic method:
for piece in food: print piece
- SCADA
- supervisory control and data acquisition (SCADA) generally refers to industrial control systems: computer systems that monitor and control industrial, infrastructure, or facility-based processes.
- SDS
- Sardana Device server (SDS) is the sardana tango device server daemon.
- sequence
- An iterable which supports efficient element access using integer
indices via the
__getitem__()
special method and defines alen()
method that returns the length of the sequence. Some built-in sequence types arelist
,str
,tuple
, andunicode
. Note thatdict
also supports__getitem__()
and__len__()
, but is considered a mapping rather than a sequence because the lookups use arbitrary immutable keys rather than integers. - slice
- An object usually containing a portion of a sequence. A slice is
created using the subscript notation,
[]
with colons between numbers when several are given, such as invariable_name[1:3:5]
. The bracket (subscript) notation usesslice
objects internally (or in older versions,__getslice__()
and__setslice__()
). - statement
- A statement is part of a suite (a “block” of code). A statement is either
an expression or a one of several constructs with a keyword, such
as
if
,while
orfor
. - stepper
- A stepper motor (or step motor) is a brushless DC electric motor that divides a full rotation into a number of equal steps. The motor’s position can then be commanded to move and hold at one of these steps without any feedback sensor (an open-loop controller), as long as the motor is carefully sized to the application.
- triple-quoted string
- A string which is bound by three instances of either a quotation mark (”) or an apostrophe (‘). While they don’t provide any functionality not available with single-quoted strings, they are useful for a number of reasons. They allow you to include unescaped single and double quotes within a string and they can span multiple lines without the use of the continuation character, making them especially useful when writing docstrings.
- type
- The type of a Python object determines what kind of object it is; every
object has a type. An object’s type is accessible as its
__class__
attribute or can be retrieved withtype(obj)
. - user
- See user position
- user position
Moveable position in user units (See also dial position). Dial and user units are related by the following expressions:
user = sign x dial + offset dial = controller_position / steps_per_unitwhere sign is -1 or 1. offset can be any number and steps_per_unit must be non zero.
Documentation to be done¶
Todo
document 0D channel API reference
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/api/api_0D.rst, line 9.)
Todo
document 1D channel API reference
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/api/api_1D.rst, line 9.)
Todo
document 2D channel API reference
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/api/api_2D.rst, line 9.)
Todo
document I/O register API reference
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/api/api_IOR.rst, line 9.)
Todo
document Counter/Timer API reference
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/api/api_countertimer.rst, line 9.)
Todo
document pseudo-counter API reference
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/api/api_pseudocounter.rst, line 9.)
Todo
document pseudo-motor API reference
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/api/api_pseudomotor.rst, line 9.)
Todo
Device Pool chapter is out of date. Need to update it and distribute chapters logically around the sardana documentation
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/api/tango_device_pool.rst, line 6.)
Todo
document this chapter
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/api/tango_macroserver.rst, line 6.)
Todo
document 0D controller howto
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/howto_controllers/howto_0dcontroller.rst, line 12.)
Todo
document 1D controller howto
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/howto_controllers/howto_1dcontroller.rst, line 12.)
Todo
document 2D controller howto
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/howto_controllers/howto_2dcontroller.rst, line 12.)
Todo
finish counter/timer controller howto
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/howto_controllers/howto_countertimercontroller.rst, line 78.)
Todo
document IORegister controller howto
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/howto_controllers/howto_ioregistercontroller.rst, line 12.)
Todo
document pseudo counter controller howto
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst, line 12.)
Todo
document pseudo motor controller howto
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/howto_controllers/howto_pseudomotorcontroller.rst, line 12.)
Todo
document 0D experiment channel overview
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/overview/overview_0D.rst, line 9.)
Todo
document 1D experiment channel overview
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/overview/overview_1D.rst, line 9.)
Todo
document 2D experiment channel overview
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/overview/overview_2D.rst, line 9.)
Todo
document I/O register overview
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/overview/overview_IOR.rst, line 9.)
Todo
document counter/timer overview
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/overview/overview_countertimer.rst, line 9.)
Todo
document pseudo counter overview
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/overview/overview_pseudocounter.rst, line 9.)
Todo
document pseudo motor overview
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/devel/overview/overview_pseudomotor.rst, line 9.)
Todo
The FAQ is work-in-progress. Many answers need polishing and mostly links need to be added
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/sardana/checkouts/1.6.2/doc/source/users/faq.rst, line 5.)
Last Update: | April 28, 2016 |
---|---|
Release: | 1.6.2 |