[sardana] 01/06: Imported Upstream version 2.0.0

Frédéric-Emmanuel Picca picca at moszumanska.debian.org
Fri Sep 30 08:55:49 UTC 2016


This is an automated email from the git hooks/post-receive script.

picca pushed a commit to branch master
in repository sardana.

commit d68196903b0de0f4099f554a3ccab49737d5fd88
Author: Picca Frédéric-Emmanuel <picca at debian.org>
Date:   Fri Apr 29 00:51:47 2016 +0200

    Imported Upstream version 2.0.0
---
 PKG-INFO                                           |    2 +-
 doc/logo.bmp                                       |  Bin 119070 -> 0 bytes
 doc/man/MacroServer.1                              |    4 +-
 doc/man/Pool.1                                     |    4 +-
 doc/man/Sardana.1                                  |    4 +-
 .../{macroexecutor.1 => diffractometeralignment.1} |   17 +-
 doc/man/{macroexecutor.1 => hklscan.1}             |   16 +-
 doc/man/macroexecutor.1                            |    4 +-
 doc/man/sequencer.1                                |    4 +-
 doc/man/spock.1                                    |    4 +-
 doc/man/{macroexecutor.1 => ubmatrix.1}            |   17 +-
 doc/source/_static/logo.png                        |  Bin 36269 -> 0 bytes
 doc/source/_templates/layout.html                  |   19 -
 doc/source/conf.py                                 |    7 +-
 .../devel/api/sardana/macroserver/macros.rst       |  188 ++-
 doc/source/devel/howto_macros/macros_general.rst   |   93 +-
 doc/source/devel/howto_recorders.rst               |   90 ++
 doc/source/devel/index.rst                         |    1 +
 doc/source/users/scan.rst                          |   59 +
 doc/source/users/standard_macro_catalog.rst        |   38 +
 .../__init__.py => scripts/diffractometeralignment |   18 +-
 .../scan/recorder/__init__.py => scripts/hklscan   |   18 +-
 .../scan/recorder/__init__.py => scripts/ubmatrix  |   18 +-
 setup.py                                           |   16 +-
 src/sardana/macroserver/macro.py                   |   90 +-
 src/sardana/macroserver/macros/demo.py             |  143 +-
 src/sardana/macroserver/macros/env.py              |    8 +-
 .../macroserver/macros/examples/parameters.py      |   62 +-
 src/sardana/macroserver/macros/examples/scans.py   |   12 +-
 .../macroserver/macros/examples/submacros.py       |    4 +-
 src/sardana/macroserver/macros/expert.py           |   52 +-
 src/sardana/macroserver/macros/hkl.py              | 1554 ++++++++++++++++++++
 src/sardana/macroserver/macros/lists.py            |    7 +-
 src/sardana/macroserver/macros/scan.py             |  128 +-
 src/sardana/macroserver/macros/standard.py         |  139 +-
 src/sardana/macroserver/macros/test/__init__.py    |    2 +-
 src/sardana/macroserver/macros/test/base.py        |   40 +-
 src/sardana/macroserver/macros/test/sardemoenv.py  |   54 +
 src/sardana/macroserver/macros/test/test_ct.py     |   13 +-
 src/sardana/macroserver/macros/test/test_env.py    |   79 +
 src/sardana/macroserver/macros/test/test_expert.py |  117 ++
 .../test/{__init__.py => test_ioregister.py}       |   18 +-
 src/sardana/macroserver/macros/test/test_list.py   |   49 +
 src/sardana/macroserver/macros/test/test_scan.py   |   21 +-
 .../macroserver/macros/test/test_standard.py       |   91 ++
 src/sardana/macroserver/macros/test/test_wm.py     |   22 +-
 src/sardana/macroserver/macroserver.py             |   37 +-
 src/sardana/macroserver/msexception.py             |    6 +-
 src/sardana/macroserver/msmacromanager.py          |  162 +-
 src/sardana/macroserver/msmetarecorder.py          |  100 ++
 src/sardana/macroserver/msparameter.py             |  211 +--
 src/sardana/macroserver/msrecordermanager.py       |  363 +++++
 .../{scan/recorder => recorders}/__init__.py       |    7 +-
 .../recorder => recorders/examples}/__init__.py    |    8 +-
 .../recorders/examples/dummy.py}                   |   59 +-
 src/sardana/macroserver/recorders/examples/xas.py  |  283 ++++
 .../{scan/recorder => recorders}/output.py         |    7 +-
 .../{scan/recorder => recorders}/sharedmemory.py   |   73 +-
 .../{scan/recorder => recorders}/storage.py        |  523 +------
 src/sardana/macroserver/scan/gscan.py              |  771 +++++-----
 src/sardana/macroserver/scan/recorder/__init__.py  |    1 -
 .../macroserver/scan/recorder/datarecorder.py      |   26 +-
 .../macroserver/scan/recorder/sharedmemory.py      |  290 +---
 src/sardana/macroserver/scan/recorder/storage.py   | 1051 +------------
 .../{scan/recorder => test}/__init__.py            |    9 -
 .../{scan/recorder => test/res}/__init__.py        |    9 -
 .../__init__.py => test/res/fakerecorders.py}      |   11 +-
 .../res/recorders/path1/fakerecorders.py}          |   13 +-
 .../res/recorders/path2/fakerecorders.py}          |   13 +-
 .../res/recorders/path3/fakerecorders.py}          |   13 +-
 .../res/recorders/pathexternal/specrecorder.py}    |   10 +-
 src/sardana/macroserver/test/test_msparameter.py   |  125 ++
 .../macroserver/test/test_msrecordermanager.py     |  171 +++
 src/sardana/pool/pool.py                           |    5 +
 src/sardana/pool/poolcontrollermanager.py          |   13 +-
 .../poolcontrollers/HklPseudoMotorController.py    | 1421 ++++++++++++++++++
 src/sardana/release.py                             |    2 +-
 src/sardana/sardanabase.py                         |   10 +-
 src/sardana/sardanacontainer.py                    |   12 +-
 src/sardana/sardanacustomsettings.py               |    7 +
 src/sardana/sardanadefs.py                         |    2 +
 src/sardana/sardanamodulemanager.py                |   27 +-
 src/sardana/spock/colors.py                        |   81 +-
 src/sardana/spock/config.py                        |   11 +-
 src/sardana/spock/ipython_01_00/genutils.py        |   38 +-
 src/sardana/spock/release.py                       |    2 +-
 src/sardana/tango/macroserver/Door.py              |   21 +-
 src/sardana/tango/macroserver/MacroServer.py       |   18 +-
 src/sardana/tango/pool/Pool.py                     |   44 +-
 src/sardana/taurus/core/tango/sardana/macro.py     |  118 +-
 .../taurus/core/tango/sardana/macroserver.py       |   33 +-
 src/sardana/taurus/core/tango/sardana/pool.py      |   67 +-
 .../core/tango/sardana/test}/__init__.py           |   17 +-
 .../taurus/core/tango/sardana/test/test_macro.py   |  116 ++
 .../qt/qtgui/extra_hkl}/__init__.py                |   19 +-
 src/sardana/taurus/qt/qtgui/extra_hkl/computeu.py  |   76 +
 .../qt/qtgui/extra_hkl/diffractometeralignment.py  |  395 +++++
 .../qt/qtgui/extra_hkl/displayscanangles.py}       |   65 +-
 src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py   |  397 +++++
 .../taurus/qt/qtgui/extra_hkl/reflectionseditor.py |  255 ++++
 .../qt/qtgui/extra_hkl/reflectionslist.py}         |   63 +-
 .../taurus/qt/qtgui/extra_hkl/selectsignal.py      |  114 ++
 src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py  |  487 ++++++
 .../taurus/qt/qtgui/extra_hkl/ui/computeu.ui       |   79 +
 .../qtgui/extra_hkl/ui/diffractometeralignment.ui  |  559 +++++++
 .../qt/qtgui/extra_hkl/ui/displayscanangles.ui     |   40 +
 .../taurus/qt/qtgui/extra_hkl/ui/hklscan.ui        |  624 ++++++++
 .../qt/qtgui/extra_hkl/ui/reflectionseditor.ui     |  117 ++
 .../qt/qtgui/extra_hkl/ui/reflectionslist.ui       |  118 ++
 .../taurus/qt/qtgui/extra_hkl/ui/selectsignal.ui   |  105 ++
 .../taurus/qt/qtgui/extra_hkl/ui/ubmatrix.ui       | 1003 +++++++++++++
 .../qt/qtgui/extra_macroexecutor/macrobutton.py    |    4 +
 .../qt/qtgui/extra_macroexecutor/macroexecutor.py  |   23 +-
 .../macroparameterseditor/delegate.py              |    2 +-
 .../macroparameterseditor/model.py                 |    2 +-
 .../extra_macroexecutor/sequenceeditor/delegate.py |    4 +-
 .../extra_macroexecutor/sequenceeditor/model.py    |    2 +-
 .../sequenceeditor/sequenceeditor.py               |    3 +-
 src/sardana/taurus/qt/qtgui/extra_pool/motor.py    |  851 -----------
 src/sardana/test/testsuite.py                      |    7 +-
 src/sardana/tools/config/sardana.py                |   16 +-
 121 files changed, 11237 insertions(+), 3956 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index b6095d4..32dacfe 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: sardana
-Version: 1.6.1
+Version: 2.0.0
 Summary: instrument control and data acquisition system
 Home-page: http://www.sardana-controls.org
 Author: Zbigniew Reszela
diff --git a/doc/logo.bmp b/doc/logo.bmp
deleted file mode 100644
index 8ab0241..0000000
Binary files a/doc/logo.bmp and /dev/null differ
diff --git a/doc/man/MacroServer.1 b/doc/man/MacroServer.1
index c9ca7e2..1558828 100644
--- a/doc/man/MacroServer.1
+++ b/doc/man/MacroServer.1
@@ -1,7 +1,7 @@
 .\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.43.3.
-.TH MACROSERVER "1" "July 2015" "MacroServer 1.6.1" "User Commands"
+.TH MACROSERVER "1" "April 2016" "MacroServer 2.0.0" "User Commands"
 .SH NAME
-MacroServer \- manual page for MacroServer 1.6.1
+MacroServer \- manual page for MacroServer 2.0.0
 .SH SYNOPSIS
 .B usage:
 \fIMacroServer instance_name \fR[\fIoptions\fR]
diff --git a/doc/man/Pool.1 b/doc/man/Pool.1
index b300983..352b116 100644
--- a/doc/man/Pool.1
+++ b/doc/man/Pool.1
@@ -1,7 +1,7 @@
 .\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.43.3.
-.TH POOL "1" "July 2015" "Pool 1.6.1" "User Commands"
+.TH POOL "1" "April 2016" "Pool 2.0.0" "User Commands"
 .SH NAME
-Pool \- manual page for Pool 1.6.1
+Pool \- manual page for Pool 2.0.0
 .SH SYNOPSIS
 .B usage:
 \fIPool instance_name \fR[\fIoptions\fR]
diff --git a/doc/man/Sardana.1 b/doc/man/Sardana.1
index 310c62f..d6fae7f 100644
--- a/doc/man/Sardana.1
+++ b/doc/man/Sardana.1
@@ -1,7 +1,7 @@
 .\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.43.3.
-.TH SARDANA "1" "July 2015" "Sardana 1.6.1" "User Commands"
+.TH SARDANA "1" "April 2016" "Sardana 2.0.0" "User Commands"
 .SH NAME
-Sardana \- manual page for Sardana 1.6.1
+Sardana \- manual page for Sardana 2.0.0
 .SH SYNOPSIS
 .B usage:
 \fISardana instance_name \fR[\fIoptions\fR]
diff --git a/doc/man/macroexecutor.1 b/doc/man/diffractometeralignment.1
similarity index 71%
copy from doc/man/macroexecutor.1
copy to doc/man/diffractometeralignment.1
index 8ea2050..9110451 100644
--- a/doc/man/macroexecutor.1
+++ b/doc/man/diffractometeralignment.1
@@ -1,17 +1,20 @@
 .\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.43.3.
-.TH MACROEXECUTOR "1" "July 2015" "macroexecutor 3.6.0" "User Commands"
+.TH DIFFRACTOMETERALIGNMENT "1" "April 2016" "diffractometeralignment 2.0.0" "User Commands"
 .SH NAME
-macroexecutor \- manual page for macroexecutor 3.6.0
+diffractometeralignment \- manual page for diffractometeralignment 2.0.0
 .SH SYNOPSIS
-.B macroexecutor
-[\fIoptions\fR]
+.B diffractometeralignment
+\fI<model> \fR[\fIdoor_name\fR]
+.SH DESCRIPTION
+a taurus application for diffractometer alignment: h, k, l movements and
+scans, go to maximum, ...
 .SH OPTIONS
 .TP
-\fB\-\-version\fR
-show program's version number and exit
-.TP
 \fB\-h\fR, \fB\-\-help\fR
 show this help message and exit
+.TP
+\fB\-\-version\fR
+show program's version number and exit
 .IP
 Taurus Options:
 .IP
diff --git a/doc/man/macroexecutor.1 b/doc/man/hklscan.1
similarity index 80%
copy from doc/man/macroexecutor.1
copy to doc/man/hklscan.1
index 8ea2050..796e032 100644
--- a/doc/man/macroexecutor.1
+++ b/doc/man/hklscan.1
@@ -1,17 +1,19 @@
 .\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.43.3.
-.TH MACROEXECUTOR "1" "July 2015" "macroexecutor 3.6.0" "User Commands"
+.TH HKLSCAN "1" "April 2016" "hklscan 2.0.0" "User Commands"
 .SH NAME
-macroexecutor \- manual page for macroexecutor 3.6.0
+hklscan \- manual page for hklscan 2.0.0
 .SH SYNOPSIS
-.B macroexecutor
-[\fIoptions\fR]
+.B hklscan
+\fI<model> \fR[\fIdoor_name\fR]
+.SH DESCRIPTION
+a taurus application for performing hkl scans
 .SH OPTIONS
 .TP
-\fB\-\-version\fR
-show program's version number and exit
-.TP
 \fB\-h\fR, \fB\-\-help\fR
 show this help message and exit
+.TP
+\fB\-\-version\fR
+show program's version number and exit
 .IP
 Taurus Options:
 .IP
diff --git a/doc/man/macroexecutor.1 b/doc/man/macroexecutor.1
index 8ea2050..039a949 100644
--- a/doc/man/macroexecutor.1
+++ b/doc/man/macroexecutor.1
@@ -1,7 +1,7 @@
 .\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.43.3.
-.TH MACROEXECUTOR "1" "July 2015" "macroexecutor 3.6.0" "User Commands"
+.TH MACROEXECUTOR "1" "April 2016" "macroexecutor 2.0.0" "User Commands"
 .SH NAME
-macroexecutor \- manual page for macroexecutor 3.6.0
+macroexecutor \- manual page for macroexecutor 2.0.0
 .SH SYNOPSIS
 .B macroexecutor
 [\fIoptions\fR]
diff --git a/doc/man/sequencer.1 b/doc/man/sequencer.1
index b7bba04..19dcc3e 100644
--- a/doc/man/sequencer.1
+++ b/doc/man/sequencer.1
@@ -1,7 +1,7 @@
 .\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.43.3.
-.TH SEQUENCER "1" "July 2015" "sequencer 3.6.0" "User Commands"
+.TH SEQUENCER "1" "April 2016" "sequencer 2.0.0" "User Commands"
 .SH NAME
-sequencer \- manual page for sequencer 3.6.0
+sequencer \- manual page for sequencer 2.0.0
 .SH SYNOPSIS
 .B sequencer
 [\fIoptions\fR]
diff --git a/doc/man/spock.1 b/doc/man/spock.1
index cbfb90a..8e67216 100644
--- a/doc/man/spock.1
+++ b/doc/man/spock.1
@@ -1,7 +1,7 @@
 .\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.43.3.
-.TH SPOCK "1" "July 2015" "spock 1.6.1" "User Commands"
+.TH SPOCK "1" "April 2016" "spock 2.0.0" "User Commands"
 .SH NAME
-spock \- manual page for spock 1.6.1
+spock \- manual page for spock 2.0.0
 .SH DESCRIPTION
 =========
 .IP
diff --git a/doc/man/macroexecutor.1 b/doc/man/ubmatrix.1
similarity index 77%
copy from doc/man/macroexecutor.1
copy to doc/man/ubmatrix.1
index 8ea2050..40c78df 100644
--- a/doc/man/macroexecutor.1
+++ b/doc/man/ubmatrix.1
@@ -1,17 +1,20 @@
 .\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.43.3.
-.TH MACROEXECUTOR "1" "July 2015" "macroexecutor 3.6.0" "User Commands"
+.TH UBMATRIX "1" "April 2016" "ubmatrix 2.0.0" "User Commands"
 .SH NAME
-macroexecutor \- manual page for macroexecutor 3.6.0
+ubmatrix \- manual page for ubmatrix 2.0.0
 .SH SYNOPSIS
-.B macroexecutor
-[\fIoptions\fR]
+.B ubmatrix
+\fI<model>\fR
+.SH DESCRIPTION
+a taurus application for setting diffractometer parameters: ubmatrix, lattice,
+reflections, ...
 .SH OPTIONS
 .TP
-\fB\-\-version\fR
-show program's version number and exit
-.TP
 \fB\-h\fR, \fB\-\-help\fR
 show this help message and exit
+.TP
+\fB\-\-version\fR
+show program's version number and exit
 .IP
 Taurus Options:
 .IP
diff --git a/doc/source/_static/logo.png b/doc/source/_static/logo.png
deleted file mode 100644
index c046c66..0000000
Binary files a/doc/source/_static/logo.png and /dev/null differ
diff --git a/doc/source/_templates/layout.html b/doc/source/_templates/layout.html
deleted file mode 100644
index 0a678b1..0000000
--- a/doc/source/_templates/layout.html
+++ /dev/null
@@ -1,19 +0,0 @@
-{% extends "sphinxdoc/layout.html" %}
-
-{% block rootrellink %}
-    <li><a href="http://sardana-controls.org">home</a>| </li>
-    <li><a href="http://sourceforge.net/projects/sardana">project</a>| </li>
-    <li><a href="https://pypi.python.org/pypi/sardana">download</a>| </li>
-    <li><a href="http://sardana.readthedocs.org">documentation </a> »</li>
-
-
-{% endblock %}
-
-{% block relbar1 %}
-{{ super() }}
-{% endblock %}
-
-
-{% block sidebar2 %}
-{{ super() }}
-{% endblock %}
diff --git a/doc/source/conf.py b/doc/source/conf.py
index eb49692..2c79e3d 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -102,7 +102,7 @@ except:
     pass
     
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+#templates_path = ['_templates']
 
 # The suffix of source filenames.
 source_suffix = '.rst'
@@ -174,8 +174,9 @@ pygments_style = 'spock_console_highlighting.SpockStyle'
 
 # The theme to use for HTML and HTML Help pages.  Major themes that come with
 # Sphinx are currently 'default' and 'sphinxdoc'.
-html_theme = 'default'
+#html_theme = 'default'
 #html_theme = 'sphinxdoc'
+html_theme = 'sphinx_rtd_theme'
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
@@ -195,7 +196,7 @@ html_theme_path = []
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
-html_logo = os.path.join("_static", "logo.png")
+html_logo = os.path.join(os.pardir,os.pardir,"logo.png")
 
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
diff --git a/doc/source/devel/api/sardana/macroserver/macros.rst b/doc/source/devel/api/sardana/macroserver/macros.rst
index 66ddd02..97470c5 100644
--- a/doc/source/devel/api/sardana/macroserver/macros.rst
+++ b/doc/source/devel/api/sardana/macroserver/macros.rst
@@ -58,6 +58,27 @@
     four-motor continuous scan
     
 
+.. class:: hkl.addreflection
+
+    Add reflection at the botton of reflections list.
+
+
+.. 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:: hkl.affine
+
+    Affine current crystal.
+    Fine tunning of lattice parameters and UB matrix based on 
+    current crystal reflections. Reflections with affinement 
+    set to 0 are not used. A new crystal with the post fix 
+    (affine) is created and set as current crystal.
+
+
 .. class:: scan.amultiscan
 
     Multiple motor scan.
@@ -94,7 +115,32 @@
     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:: hkl.br
+
+    Move the diffractometer to the reciprocal space 
+    coordinates given by H, K and L. If a fourth parameter is given, the combination
+    of angles to be set is the correspondig to the given index. The index of the
+    angles combinations are then changed.
+
+   
+.. class:: hkl.ca
+
+    Calculate motor positions for given H K L according to the current
+    operation mode (trajectory 0).
+
+
+.. class:: hkl.caa
+
+    Calculate motor positions for given H K L according to the current
+    operation mode (all trajectories).
+
+
+.. class:: hkl.ci
+
+    Calculate hkl for given angle values.
+
+
 .. class:: demo.clear_sar_demo
 
     Undoes changes done with sar_demo
@@ -104,6 +150,10 @@
     Puts the contents of the given data in a file inside the pool
     
 
+.. class:: hkl.computeub
+
+   Compute UB matrix with reflections 0 and 1.
+
 .. class:: standard.ct
 
     Count for the specified time on the active measurement group
@@ -247,6 +297,10 @@
     Returns the contents of the given library file
     
 
+.. class:: hkl.freeze
+
+   Set psi value for psi constant modes.
+
 .. class:: scan.fscan
 
     N-dimensional scan along user defined paths.
@@ -277,6 +331,36 @@
     Reads and outputs the data from the communication channel
     
 
+.. class:: hkl.getmode
+
+    Get operation mode.
+
+
+.. class:: hkl.hklscan
+
+    Scan h k l axes. 
+
+
+.. class:: hkl.hscan
+
+    Scan h axis.
+
+
+.. class:: hkl.kscan
+
+    Scan k axis.
+
+
+.. class:: hkl.latticecal
+
+    Calibrate lattice parameters a, b or c to current 2theta value.
+  
+
+.. class:: hkl.loadcrystal
+
+    Load crystal information from file
+
+
 .. class:: env.load_env
 
     Read environment variables from config_env.xml file
@@ -299,7 +383,12 @@
 
 .. class:: lists.lsa
 
-    Lists all existing objects
+    Lists all existing objects 
+
+
+.. class:: hkl.lscan
+
+    Scan l axis. 
     
 
 .. class:: lists.lscom
@@ -419,7 +508,31 @@
 .. class:: standard.mvr
 
     Move motor(s) relative to the current position(s)
-    
+
+.. class:: hkl.newcrystal
+
+    Create a new crystal (if it does not exist) and select it.
+
+
+.. class:: hkl.or0
+
+    Set primary orientation reflection.
+
+
+.. class:: hkl.or1
+
+    Set secondary orientation reflection.
+
+
+.. class:: hkl.orswap
+
+    Swap values for primary and secondary vectors.
+
+
+.. class:: hkl.pa
+
+    Prints information about the active diffractometer.
+
 
 .. class:: expert.prdef
 
@@ -445,13 +558,6 @@
 
     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.
@@ -500,6 +606,11 @@
 .. class:: expert.sar_info
 
     Prints details about the given sardana object
+
+
+.. class:: hkl.savecrystal
+
+    Save crystal information to file.
     
 
 .. class:: scan.scanhist
@@ -546,6 +657,37 @@
     Sets the USER position of the motor to the specified value (by changing OFFSET and keeping DIAL)
     
 
+.. class:: hkl.setaz
+
+    Set hkl values of the psi reference vector.
+
+
+.. class:: hkl.setlat
+
+    Set the crystal lattice parameters a, b, c, alpha, beta and gamma
+    for the currently active diffraction pseudo motor controller.
+
+
+.. class:: hkl.setmode
+
+    Set operation mode.
+
+
+.. class:: hkl.setor0
+
+    Set primary orientation reflection choosing hkl and angle values.
+
+
+.. class:: hkl.setor1
+
+    Set secondary orientation reflection choosing hkl and angle values.
+
+
+.. class:: hkl.setorn
+
+    Set orientation reflection indicated by the index.
+
+
 .. class:: standard.settimer
 
     Defines the timer channel for the active measurement group
@@ -554,8 +696,18 @@
 .. class:: env.setvo
 
     Sets the given view option to the given value
-    
 
+
+.. class:: hkl.th2th
+
+    Relative scan around current position in del and th with d_th=2*d_delta.
+
+
+.. class:: hkl.ubr
+
+    Move the diffractometer to the reciprocal space coordinates given by H, K and L und update.
+
+    
 .. class:: standard.uct
 
     Count on the active measurement group and update
@@ -586,6 +738,10 @@
     Move motor(s) relative to the current position(s) and update
     
 
+.. class:: standard.tw
+
+   Tweak motor by variable delta
+
 .. class:: env.usenv
 
     Unsets the given environment variable
@@ -601,6 +757,16 @@
     Show all motor positions
     
 
+.. class:: hkl.wh
+
+    Show principal axes and reciprocal space positions.
+
+    Prints the current reciprocal space coordinates (H K L) and the user 
+    positions of the principal motors. Depending on the diffractometer geometry, 
+    other parameters such as the angles of incidence and reflection (ALPHA and 
+    BETA) and the incident wavelength (LAMBDA) may be displayed.
+    
+
 .. class:: standard.wm
 
     Show the position of the specified motors.
diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst
index 0a32ddf..8f6144d 100644
--- a/doc/source/devel/howto_macros/macros_general.rst
+++ b/doc/source/devel/howto_macros/macros_general.rst
@@ -319,6 +319,58 @@ 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 (:obj:`~sardana.sardanadefs.Interface`)
 
+.. _sardana-macro-repeat-parameters:
+
+Repeat parameters
+~~~~~~~~~~~~~~~~~
+
+A special parameter type is the repeat parameter (a.k.a. *ParamRepeat*,
+originating from the ``ParamRepeat`` class which usage is deprecated).
+The repeat parameter type is a list of parameter members. It is possible to
+pass from zero to multiple repetitions of the repeat parameter items at the
+execution time.
+
+The repeat parameter definition allows to:
+
+    - restrict the minimum and/or maximum number of repetitions
+    - nest repeat parameters inside of another repeat parameters [#f3]_
+    - define multiple repeat parameters in the same macro [#f3]_
+
+Repeat parameter values are passed to the macro function in the form of a list.
+If the repeat parameter definition contains just one member it is a plain list
+of items.
+
+::
+
+    @macro([["moveables", [
+                 "moveable", Type.Moveable, None, "moveable to get position"
+                 ],
+                 None, "list of moveables to get positions"]])
+    def where_moveables(self, moveables):
+        """This macro prints the current moveables positions"""
+        for moveable in moveables:
+            self.output("%s is now at %s", moveable.getName(), moveable.getPosition())
+
+But if the repeat parameter definition contains more than one member
+each item is an internal list of the members.
+
+::
+
+    @macro([["m_p_pairs", [
+                 ["moveable", Type.Moveable, None, "moveable to be moved"],
+                 ["position", Type.Float, None, "absolute position"]
+                 ],
+                 None, "list of moveables and positions to be moved to"]])
+    def move_multiple(self, m_p_pairs):
+        """This macro moves moveables to the specified positions"""
+        for moveable, position in zip(*m_p_pairs):
+            moveable.move(position)
+            self.output("%s is now at %s", moveable.getName(), moveable.getPosition())
+
+
+A set of macro parameter examples can be found
+:ref:`here <sardana-devel-macro-parameter-examples>`.
+
 .. _sardana-macro-context:
 
 Macro context
@@ -367,7 +419,7 @@ 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)
+        self.wm([moveable])
 
 ... and the new version of *move* ::
 
@@ -375,9 +427,16 @@ Here is the new version of *where_moveable* ::
              ["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.mv([[moveable, position]])
         self.output("%s is now at %s", moveable.getName(), moveable.getPosition())
 
+.. note::
+    Both :class:`~sardana.macroserver.macros.standard.wm` and
+    :class:`~sardana.macroserver.macros.standard.mv`
+    use :ref:`repeat parameters <sardana-macro-repeat-parameters>`.
+    From Sardana 2.0 the repeat parameter values must be passed as lists of
+    items. An item of a repeat parameter containing more than one member is a list.
+
 .. _sardana-macro-environment:
 
 Accessing environment
@@ -530,21 +589,34 @@ parameters with different *flavors*:
     * parameters as strings::
 
         self.execMacro('ascan', motor.getName(), '0', '100', '10', '0.2')
-        
-    * parameters as space separated string::
-        
+        self.execMacro('mv', [[motor.getName(), '0']])
+
+    * parameters as space separated string (this is not compatible with multiple
+      or nested repeat parameters, furthermore the repeat parameter must be the last one)::
+
         self.execMacro('ascan %s 0 100 10 0.2' % motor.getName())
+        self.execMacro('mv %s 0' % motor.getName())
 
     * parameters as concrete types::
-    
+
         self.execMacro(['ascan', motor, 0, 100, 10, 0.2])
-        
+        self.execMacro(['mv', [[motor, 0]]])
+
+.. note::
+    Macro :class:`~sardana.macroserver.macros.standard.mv`
+    use :ref:`repeat parameters <sardana-macro-repeat-parameters>`.
+    From Sardana 2.0 the repeat parameter values must be passed as lists of
+    items. An item of a repeat parameter containing more than one member is a list.
+
+Accessing macro data
+~~~~~~~~~~~~~~~~~~~~
+
 Sometimes it is desirable to access data generated by the macro we just called.
 For these cases, the Macro :term:`API` provides a pair of low level methods
 :meth:`~Macro.createMacro` and :meth:`~Macro.runMacro` together with
 :meth:`~Macro.data`.
 
-Let's say that you need access to the data generated by an ascan. First you call
+Let's say that you need access to the data generated by a scan. First you call
 :meth:`~Macro.createMacro` with the same parameter you would give to
 :meth:`~Macro.execMacro`. This will return a tuple composed from a macro object 
 and the result of the :meth:`~Macro.prepare` method. Afterward you call :meth:`~Macro.runMacro` giving 
@@ -557,7 +629,7 @@ using :meth:`~Macro.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
+        """This runs the 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')
@@ -1168,6 +1240,9 @@ value but also the ranges so it is safe to change them if necessary.
 .. [#f2] To check which version of Python_ you are using type on the command
          line ``python -c "import sys; sys.stdout.write(sys.version)"``
 
+.. [#f3] Currently only GUI macro clients (*macroexecutor* or *sequencer*)
+       are compatible with this kind of repeat parameters usage.
+
 .. |input_integer| image:: ../../_static/macro_input_integer.png
     :align: middle
 
diff --git a/doc/source/devel/howto_recorders.rst b/doc/source/devel/howto_recorders.rst
new file mode 100644
index 0000000..08397c9
--- /dev/null
+++ b/doc/source/devel/howto_recorders.rst
@@ -0,0 +1,90 @@
+.. _sardana-writing-recorders:
+
+=================
+Writing recorders
+=================
+
+Overview
+---------
+
+Sardana macros may produce data and users are usually interested in storing
+or visualizing it. Sardana delegates this work to the recorders.
+A good example of the recorder usage are the scan macros developed with the
+:ref:`sardana-macros-scanframework`. Recorders are in charge of writing data to
+its destinations, for example a file, the Spock output or to plot it on a graph.
+
+What is a recorder?
+-------------------
+
+Recorder class is a Sardana element managed by the MacroServer. It is
+identified by its name, and is located in a recorder library - another Sardana
+element which is also identified by its name. Recorders are developed as
+Python classes, and recorder libraries are just Python modules aggregating these
+classes.
+
+Type of recorders
+-----------------
+
+Sardana defines some standard recorders e.g. the Spock output recorder or the 
+SPEC file recorder. From the other hand users may define their custom recorders.
+Sardana provides the following standard recorders (grouped by types):
+
+* file [*]
+    * FIO_FileRecorder
+    * NXscan_FileRecorder
+    * SPEC_FileRecorder
+
+* shared memory [*]
+    * SPSRecorder
+    * ShmRecorder
+
+* output
+    * JsonRecorder [*]
+    * OutputRecorder
+
+[*] Scan Framework provides mechanisms to enable and select this recorders using
+the environment variables.
+
+Writing a custom recorder
+-------------------------
+
+.. todo:: document how to write custom recorders
+
+Configuration
+-------------
+
+Custom recorders may be added to the Sardana system by placing the recorder
+library module in a directory which is specified by the MacroServer
+*RecorderPath* property. RecorderPath property may contain an ordered, 
+colon-separated list of directories.
+In case of overriding recorders by name or by file extension (in case of the
+file recorders), recorders located in the first paths are of higher priority
+than the ones from the last paths.
+
+Three types of overriding may occur:
+
+**By recorder library name**
+   If Python modules with the same name are located in different directories, 
+   the library located in the the higher priority directory will be loaded.
+
+**By recorder name**
+   If two recorder classes with the same name appear in two different modules,
+   only the recorder from the library located in the higher
+   priority module will be loaded. If both modules are located in the same
+   directory, the behavior is undetermined.
+
+**By file extension**
+   If two different recorders supporting the same file extension appear in two 
+   different modules, the one from the higher priority path will be used
+   when selection is based on the extension (but both will be available for the
+   selection by name). If both of these recorders' modules are located in the
+   same directory, the system will assign a list of recorders to a given
+   extension. Then the application is in charge of deciding which one to use.
+
+As previously mentioned recorders are selectable by either the recorder name or
+the extension. During the MacroServer startup the extension to recorder map is
+generated while loading the recorder libraries. This dynamically created map
+may be overridden by the custom map defined in the *sardanacustomsettings*
+module (SCAN_RECORDER_MAP variable with a dictionary where key is the scan file
+extension e.g. ".h5" and value is the recorder name e.g. "MyCustomRecorder",
+where both keys and values are of type string).
diff --git a/doc/source/devel/index.rst b/doc/source/devel/index.rst
index 107d707..7b1aee9 100644
--- a/doc/source/devel/index.rst
+++ b/doc/source/devel/index.rst
@@ -10,6 +10,7 @@ Developer's Guide
     Overview <overview/index>
     howto_macros/index
     howto_controllers/index
+    Writing recorders <howto_recorders>
     howto_test/index
     API <api/api_sardana>
     Migration guide <guide_migration>
diff --git a/doc/source/users/scan.rst b/doc/source/users/scan.rst
index 3996ac0..0a18de7 100644
--- a/doc/source/users/scan.rst
+++ b/doc/source/users/scan.rst
@@ -149,6 +149,65 @@ Some examples of continuous scan macros are:
 :class:`d2scanc`, ...
 :class:`meshc`. 
 
+Configuration
+-------------
+
+Scans are highly configurable using the environment variables
+(on how to use environment variables see environment related macros in
+:ref:`sardana-standard-macro-catalog`).
+
+Following variables are supported:
+
+**ScanDir**
+    Its value is of string type and indicates an absolute path to the directory
+    where scan data will be stored.
+
+**Scan File**
+    Its value may be either of type string or of list of strings. In the second
+    case data will be duplicated in mulitple files (different file formats may
+    be used). Recorder class is implicitly selected based on the file extension.
+    For example "myexperiment.spec" will by default store data in SPEC
+    compatible format (see more about the extension to recorder map in
+    :ref:`sardana-writing-recorders`).
+
+
+**ScanRecorder**
+    Its value may be either of type string or of list of strings. If
+    ScanRecorder variable is defined, it explicitly indicates which recorder
+    class should be used and for which file defined by ScanFile (based on the 
+    order).
+
+    Example 1:
+	
+	::
+
+		ScanFile = myexperiment.spec
+		ScanRecorder = FIO_FileRecorder
+
+        FIO_FileRecorder will write myexperiment.spec file.
+
+    Example 2:
+
+	::
+	
+		ScanFile = myexperiment.spec, myexperiment.h5
+		ScanRecorder = FIO_FileRecorder
+
+        FIO_FileRecorder will write myexperiment.spec file and
+        NXscan_FileRecorder will write the myexpriment.h5. The selection of the
+        second recorder is based on the extension.
+
+**JsonRecorder**
+    Its value is of boolean type and it indicates whether JSON encoded scan
+    records will be emitted by the Door. Online scan plot uses this feature.
+
+**SharedMemory**
+    Its value is of string type and it indicates which shared memory recorder should
+    be used during the scan e.g. "sps" will use SPSRecorder (sps Python module
+    must be installed on the PC where the MacroServer runs).
+
+
+
 .. seealso:: For more information about the implementation details of the scan
              macros in Sardana, see 
              :ref:`scan framework <sardana-macros-scanframework>`
diff --git a/doc/source/users/standard_macro_catalog.rst b/doc/source/users/standard_macro_catalog.rst
index 33aac15..44db884 100644
--- a/doc/source/users/standard_macro_catalog.rst
+++ b/doc/source/users/standard_macro_catalog.rst
@@ -22,6 +22,7 @@ motion related macros
     * :class:`~sardana.macroserver.macros.standard.umv`
     * :class:`~sardana.macroserver.macros.standard.mvr`
     * :class:`~sardana.macroserver.macros.standard.umvr`
+    * :class:`~sardana.macroserver.macros.standard.tw`
     * :class:`~sardana.macroserver.macros.lists.lsm`
     * :class:`~sardana.macroserver.macros.lists.lspm`
 
@@ -42,6 +43,43 @@ counting macros
     * :class:`~sardana.macroserver.macros.lists.ls2d`
     * :class:`~sardana.macroserver.macros.lists.lspc`
 
+diffractometer related macros
+-----------------------------
+
+.. hlist::
+    :columns: 5
+
+    * :class:`~sardana.macroserver.macros.hkl.addreflection`
+    * :class:`~sardana.macroserver.macros.hkl.affine`
+    * :class:`~sardana.macroserver.macros.hkl.br`
+    * :class:`~sardana.macroserver.macros.hkl.ca`
+    * :class:`~sardana.macroserver.macros.hkl.caa`
+    * :class:`~sardana.macroserver.macros.hkl.ci`
+    * :class:`~sardana.macroserver.macros.hkl.computeub`
+    * :class:`~sardana.macroserver.macros.hkl.freeze`
+    * :class:`~sardana.macroserver.macros.hkl.getmode`
+    * :class:`~sardana.macroserver.macros.hkl.hklscan`
+    * :class:`~sardana.macroserver.macros.hkl.hscan`
+    * :class:`~sardana.macroserver.macros.hkl.kscan`
+    * :class:`~sardana.macroserver.macros.hkl.latticecal`
+    * :class:`~sardana.macroserver.macros.hkl.loadcrystal`
+    * :class:`~sardana.macroserver.macros.hkl.lscan`
+    * :class:`~sardana.macroserver.macros.hkl.newcrystal`
+    * :class:`~sardana.macroserver.macros.hkl.or0`
+    * :class:`~sardana.macroserver.macros.hkl.or1`
+    * :class:`~sardana.macroserver.macros.hkl.orswap`
+    * :class:`~sardana.macroserver.macros.hkl.pa`
+    * :class:`~sardana.macroserver.macros.hkl.savecrystal`
+    * :class:`~sardana.macroserver.macros.hkl.setaz`
+    * :class:`~sardana.macroserver.macros.hkl.setlat`
+    * :class:`~sardana.macroserver.macros.hkl.setmode`
+    * :class:`~sardana.macroserver.macros.hkl.setor0`
+    * :class:`~sardana.macroserver.macros.hkl.setor1`
+    * :class:`~sardana.macroserver.macros.hkl.setorn`
+    * :class:`~sardana.macroserver.macros.hkl.th2th`
+    * :class:`~sardana.macroserver.macros.hkl.ubr`
+    * :class:`~sardana.macroserver.macros.hkl.wh`
+
 environment related macros
 --------------------------
 
diff --git a/src/sardana/macroserver/scan/recorder/__init__.py b/scripts/diffractometeralignment
old mode 100644
new mode 100755
similarity index 79%
copy from src/sardana/macroserver/scan/recorder/__init__.py
copy to scripts/diffractometeralignment
index 8f71c3f..3d154fa
--- a/src/sardana/macroserver/scan/recorder/__init__.py
+++ b/scripts/diffractometeralignment
@@ -4,30 +4,24 @@
 ##
 ## This file is part of Sardana
 ##
-## http://www.sardana-controls.org/
+## 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/>.
 ##
 ##############################################################################
 
-"""This is the macro server scan recorder module"""
-
-__docformat__ = 'restructuredtext'
-
-from .datarecorder import *
-from .output import *
-from .sharedmemory import *
-from .storage import *
\ No newline at end of file
+from sardana.taurus.qt.qtgui.extra_hkl.diffractometeralignment import main
+main()
diff --git a/src/sardana/macroserver/scan/recorder/__init__.py b/scripts/hklscan
old mode 100644
new mode 100755
similarity index 79%
copy from src/sardana/macroserver/scan/recorder/__init__.py
copy to scripts/hklscan
index 8f71c3f..c816c3a
--- a/src/sardana/macroserver/scan/recorder/__init__.py
+++ b/scripts/hklscan
@@ -4,30 +4,24 @@
 ##
 ## This file is part of Sardana
 ##
-## http://www.sardana-controls.org/
+## 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/>.
 ##
 ##############################################################################
 
-"""This is the macro server scan recorder module"""
-
-__docformat__ = 'restructuredtext'
-
-from .datarecorder import *
-from .output import *
-from .sharedmemory import *
-from .storage import *
\ No newline at end of file
+from sardana.taurus.qt.qtgui.extra_hkl.hklscan import main
+main()
diff --git a/src/sardana/macroserver/scan/recorder/__init__.py b/scripts/ubmatrix
old mode 100644
new mode 100755
similarity index 79%
copy from src/sardana/macroserver/scan/recorder/__init__.py
copy to scripts/ubmatrix
index 8f71c3f..86b5b73
--- a/src/sardana/macroserver/scan/recorder/__init__.py
+++ b/scripts/ubmatrix
@@ -4,30 +4,24 @@
 ##
 ## This file is part of Sardana
 ##
-## http://www.sardana-controls.org/
+## 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/>.
 ##
 ##############################################################################
 
-"""This is the macro server scan recorder module"""
-
-__docformat__ = 'restructuredtext'
-
-from .datarecorder import *
-from .output import *
-from .sharedmemory import *
-from .storage import *
\ No newline at end of file
+from sardana.taurus.qt.qtgui.extra_hkl.ubmatrix import main
+main()
diff --git a/setup.py b/setup.py
index 3225d90..d94528b 100644
--- a/setup.py
+++ b/setup.py
@@ -317,6 +317,11 @@ def main():
         'sardana.macroserver.macros.examples',
         'sardana.macroserver.scan',
         'sardana.macroserver.scan.recorder',
+        'sardana.macroserver.recorders',
+        'sardana.macroserver.recorders.examples',
+
+        'sardana.macroserver.test',
+        'sardana.macroserver.test.res',
 
         'sardana.tango',
         'sardana.tango.core',
@@ -335,6 +340,7 @@ def main():
         'sardana.taurus.core',
         'sardana.taurus.core.tango',
         'sardana.taurus.core.tango.sardana',
+        'sardana.taurus.core.tango.sardana.test',
         'sardana.taurus.qt',
         'sardana.taurus.qt.qtcore',
         'sardana.taurus.qt.qtcore.tango',
@@ -348,11 +354,14 @@ def main():
         'sardana.taurus.qt.qtgui.extra_sardana',
         'sardana.taurus.qt.qtgui.extra_sardana.ui',
         'sardana.taurus.qt.qtgui.extra_pool',
+        'sardana.taurus.qt.qtgui.extra_hkl',
     ]
     
     package_data = {'sardana.taurus.qt.qtgui.extra_macroexecutor': ['ui/*.ui'],
                     'sardana.taurus.qt.qtgui.extra_pool': ['ui/*.ui'],
-                    'sardana.taurus.qt.qtgui.extra_sardana': ['ui/*.ui']
+                    'sardana.taurus.qt.qtgui.extra_sardana': ['ui/*.ui'],
+                    'sardana.taurus.qt.qtgui.extra_hkl': ['ui/*.ui'],
+                    'sardana.macroserver.test.res': ['recorders/path*/*'],
                     }
     
     provides = [
@@ -379,7 +388,10 @@ def main():
         "scripts/spectoascii",
         "scripts/spock",
         "scripts/macroexecutor",
-        "scripts/sequencer"
+        "scripts/sequencer",
+        "scripts/ubmatrix",
+        "scripts/diffractometeralignment",
+        "scripts/hklscan"
     ]
 
     classifiers = [
diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py
index 9353e9d..17e800c 100644
--- a/src/sardana/macroserver/macro.py
+++ b/src/sardana/macroserver/macro.py
@@ -373,7 +373,10 @@ class Macro(Logger):
     Ready = State.On
 
     #: internal variable
-    Abort = State.Alarm
+    Abort = State.On
+
+    #: internal variable
+    Exception = State.Alarm
 
     #: Constant used to specify all elements in a parameter
     All = ParamType.All
@@ -1046,19 +1049,28 @@ class Macro(Logger):
         Several different parameter formats are supported::
 
             # several parameters:
-            self.createMacro('ascan', 'th', '0', '100', '10', '1.0')
-            self.createMacro('ascan', 'th', 0, 100, 10, 1.0)
+            self.execMacro('ascan', 'th', '0', '100', '10', '1.0')
+            self.execMacro('mv', [[motor.getName(), '0']])
+            self.execMacro('ascan', 'th', 0, 100, 10, 1.0)
+            self.execMacro('mv', [[motor.getName(), 0]])
             th = self.getObj('th')
-            self.createMacro('ascan', th, 0, 100, 10, 1.0)
+            self.execMacro('ascan', th, 0, 100, 10, 1.0)
+            self.execMacro('mv', [th, 0]])
 
             # a sequence of parameters:
-            self.createMacro(['ascan', 'th', '0', '100', '10', '1.0')
-            self.createMacro(('ascan', 'th', 0, 100, 10, 1.0))
+            self.execMacro(['ascan', 'th', '0', '100', '10', '1.0')
+            self.execMacro(['mv', [[motor.getName(), '0']]])
+            self.execMacro(('ascan', 'th', 0, 100, 10, 1.0))
+            self.execMacro(['mv', [[motor.getName(), 0]]])
             th = self.getObj('th')
-            self.createMacro(['ascan', th, 0, 100, 10, 1.0])
+            self.execMacro(['ascan', th, 0, 100, 10, 1.0])
+            self.execMacro(['mv', [[th, 0]]])
 
-            # a space separated string of parameters:
-            self.createMacro('ascan th 0 100 10 1.0')
+            # a space separated string of parameters (this is not compatible
+            # with multiple or nested repeat parameters, furthermore the repeat
+            # parameter must be the last one):
+            self.execMacro('ascan th 0 100 10 1.0')
+            self.execMacro('mv %s 0' % motor.getName())
 
         :param pars: the command parameters as explained above
         :return:
@@ -1094,19 +1106,28 @@ class Macro(Logger):
         Several different parameter formats are supported::
 
             # several parameters:
-            executor.prepareMacro('ascan', 'th', '0', '100', '10', '1.0')
-            executor.prepareMacro('ascan', 'th', 0, 100, 10, 1.0)
+            self.execMacro('ascan', 'th', '0', '100', '10', '1.0')
+            self.execMacro('mv', [[motor.getName(), '0']])
+            self.execMacro('ascan', 'th', 0, 100, 10, 1.0)
+            self.execMacro('mv', [[motor.getName(), 0]])
             th = self.getObj('th')
-            executor.prepareMacro('ascan', th, 0, 100, 10, 1.0)
+            self.execMacro('ascan', th, 0, 100, 10, 1.0)
+            self.execMacro('mv', [th, 0]])
 
             # a sequence of parameters:
-            executor.prepareMacro(['ascan', 'th', '0', '100', '10', '1.0')
-            executor.prepareMacro(('ascan', 'th', 0, 100, 10, 1.0))
+            self.execMacro(['ascan', 'th', '0', '100', '10', '1.0')
+            self.execMacro(['mv', [[motor.getName(), '0']]])
+            self.execMacro(('ascan', 'th', 0, 100, 10, 1.0))
+            self.execMacro(['mv', [[motor.getName(), 0]]])
             th = self.getObj('th')
-            executor.prepareMacro(['ascan', th, 0, 100, 10, 1.0])
+            self.execMacro(['ascan', th, 0, 100, 10, 1.0])
+            self.execMacro(['mv', [[th, 0]]])
 
-            # a space separated string of parameters:
-            executor._prepareMacro('ascan th 0 100 10 1.0')
+            # a space separated string of parameters (this is not compatible
+            # with multiple or nested repeat parameters, furthermore the repeat
+            # parameter must be the last one):
+            self.execMacro('ascan th 0 100 10 1.0')
+            self.execMacro('mv %s 0' % motor.getName())
 
         :param args: the command parameters as explained above
         :param kwargs: keyword optional parameters for prepare
@@ -1168,33 +1189,46 @@ class Macro(Logger):
 
             # several parameters:
             self.execMacro('ascan', 'th', '0', '100', '10', '1.0')
+            self.execMacro('mv', [[motor.getName(), '0']])
             self.execMacro('ascan', 'th', 0, 100, 10, 1.0)
+            self.execMacro('mv', [[motor.getName(), 0]])
             th = self.getObj('th')
             self.execMacro('ascan', th, 0, 100, 10, 1.0)
+            self.execMacro('mv', [th, 0]])
 
             # a sequence of parameters:
             self.execMacro(['ascan', 'th', '0', '100', '10', '1.0')
+            self.execMacro(['mv', [[motor.getName(), '0']]])
             self.execMacro(('ascan', 'th', 0, 100, 10, 1.0))
+            self.execMacro(['mv', [[motor.getName(), 0]]])
             th = self.getObj('th')
             self.execMacro(['ascan', th, 0, 100, 10, 1.0])
+            self.execMacro(['mv', [[th, 0]]])
 
-            # a space separated string of parameters:
+            # a space separated string of parameters (this is not compatible
+            # with multiple or nested repeat parameters, furthermore the repeat
+            # parameter must be the last one):
             self.execMacro('ascan th 0 100 10 1.0')
+            self.execMacro('mv %s 0' % motor.getName())
 
         :param pars: the command parameters as explained above
 
         :return: a macro object"""
-        par0 = args[0]
+        # obtaining macro name
+        macro_name = None
+        arg0 = args[0]
         if len(args) == 1:
-            if type(par0) in types.StringTypes :
-                args = par0.split()
-                
-            elif operator.isSequenceType(par0):
-                args = par0
-        args = map(str, args)
-
-        self.debug("Executing macro: %s" % args[0])
-        macro_obj, prepare_result = self.prepareMacro(*args, **kwargs)
+            if type(arg0) in types.StringTypes :
+                # dealing with sth like args = ('ascan th 0 100 10 1.0',)
+                macro_name = arg0.split()[0]
+            elif operator.isSequenceType(arg0):
+                # dealing with sth like args = (['ascan', 'th', '0', '100', '10', '1.0'],)
+                macro_name = arg0[0]
+        else:
+            # dealing with sth like args = ('ascan', 'th', '0', '100', '10', '1.0')
+            macro_name = args[0]
+        self.debug("Executing macro: %s" % macro_name)
+        macro_obj, _ = self.prepareMacro(*args, **kwargs)
         self.runMacro(macro_obj)
         return macro_obj
 
diff --git a/src/sardana/macroserver/macros/demo.py b/src/sardana/macroserver/macros/demo.py
index 038c1ce..50536a2 100644
--- a/src/sardana/macroserver/macros/demo.py
+++ b/src/sardana/macroserver/macros/demo.py
@@ -25,14 +25,17 @@
 
 from __future__ import print_function
 
-__all__ = ["sar_demo"]
+__all__ = ["sar_demo", "sar_demo_hkl", "clear_sar_demo_hkl"]
 
 import PyTango
 
 from sardana.macroserver.macro import macro, Type
+from sardana.macroserver.msexception import UnknownEnv
 
 _ENV = "_SAR_DEMO"
 
+_ENV_HKL = "_SAR_DEMO_HKL"
+
 def get_free_names(db, prefix, nb, start_at=1):
     ret = []
     i = start_at
@@ -58,14 +61,23 @@ def clear_sar_demo(self):
     except:
         self.error("No demo has been prepared yet on this sardana!")
         return
-    
+
+    try:
+        _ActiveMntGrp = self.getEnv("ActiveMntGrp")
+    except UnknownEnv:
+        _ActiveMntGrp = None
+
     self.print("Removing measurement groups...")
     for mg in SAR_DEMO.get("measurement_groups", ()):
         self.udefmeas(mg)
+        if mg == _ActiveMntGrp:
+            self.print("Unsetting ActiveMntGrp (was: %s)" % _ActiveMntGrp)
+            self.unsetEnv("ActiveMntGrp")
     
     self.print("Removing elements...")
-    for elem in SAR_DEMO.get("elements", ()):
-        self.udefelem(elem)
+    elements =  SAR_DEMO.get("elements", ())
+    if len(elements) > 0:
+        self.udefelem(elements)
     
     self.print("Removing controllers...")
     for ctrl in SAR_DEMO.get("controllers", ()):
@@ -94,14 +106,15 @@ def sar_demo(self):
     oned_ctrl_name = get_free_names(db, "onedctrl", 1)[0]
     twod_ctrl_name = get_free_names(db, "twodctrl", 1)[0]
     pm_ctrl_name = get_free_names(db, "slitctrl", 1)[0]
-    
+    ior_ctrl_name = get_free_names(db, "iorctrl", 1)[0]
+
     motor_names = get_free_names(db, "mot", 4)
     ct_names = get_free_names(db, "ct", 4)
     zerod_names = get_free_names(db, "zerod", 4)
     oned_names = get_free_names(db, "oned", 1)
     twod_names = get_free_names(db, "twod", 1)
     gap, offset = get_free_names(db, "gap", 1) + get_free_names(db, "offset", 1)
-    
+    ior_names = get_free_names(db, "ior", 2)
     mg_name = get_free_names(db, "mntgrp", 1)[0]
     
     pools = self.getPools()
@@ -144,16 +157,28 @@ def sar_demo(self):
     
     self.print("Creating Slit", pm_ctrl_name, "with", gap, ",", offset, "...")
     sl2t, sl2b = motor_names[:2]
-    self.defctrl("Slit", pm_ctrl_name, "sl2t="+sl2t, "sl2b="+sl2b,
-                 "Gap="+gap, "Offset="+offset)
-   
+    self.defctrl("Slit", pm_ctrl_name, ["sl2t="+sl2t, "sl2b="+sl2b,
+                 "Gap="+gap, "Offset="+offset])
+
+    self.print("Creating IORegister controller", ior_ctrl_name, "...")
+    self.defctrl("DummyIORController", ior_ctrl_name)
+    for axis, ior_name in enumerate(ior_names, 1):
+        self.print("Creating IORegister", ior_name, "...")
+        self.defelem(ior_name, ior_ctrl_name, axis)
+
     self.print("Creating measurement group", mg_name, "...")
-    self.defmeas(mg_name, *ct_names)
-    
+    self.defmeas(mg_name, ct_names)
+
+    try:
+        self.getEnv("ActiveMntGrp")
+    except UnknownEnv:
+        self.print("Setting %s as ActiveMntGrp" % mg_name)
+        self.setEnv("ActiveMntGrp", mg_name)
+
     controllers = pm_ctrl_name, mot_ctrl_name, ct_ctrl_name, \
-            zerod_ctrl_name, oned_ctrl_name, twod_ctrl_name
+            zerod_ctrl_name, oned_ctrl_name, twod_ctrl_name, ior_ctrl_name
     elements = [gap, offset] + motor_names + ct_names + \
-            zerod_names + oned_names + twod_names
+            zerod_names + oned_names + twod_names + ior_names
     d = dict(controllers=controllers, elements=elements,
              measurement_groups=[mg_name])
     
@@ -169,3 +194,95 @@ def mym2(self, pm):
     self.output(type(pm))
     self.output(type(elements[0]))
  
+
+ at macro()
+def clear_sar_demo_hkl(self):
+    """Undoes changes done with sar_demo"""
+    try:
+        SAR_DEMO_HKL = self.getEnv(_ENV_HKL)
+    except:
+        self.error("No hkl demo has been prepared yet on this sardana!")
+        return
+
+    self.print("Removing hkl demo elements...")
+    for elem in SAR_DEMO_HKL.get("elements", ()):
+        self.udefelem(elem)
+
+    self.print("Removing hkl demo controllers...")
+    for ctrl in SAR_DEMO_HKL.get("controllers", ()):
+        self.udefctrl(ctrl)
+
+    self.unsetEnv(_ENV_HKL)
+
+    self.clear_sar_demo()
+
+    self.print("DONE!")
+
+
+ at macro()
+def sar_demo_hkl(self):
+    """Sets up a demo environment. It creates many elements for testing"""
+
+    self.sar_demo()
+
+    try:
+        SAR_DEMO_HKL = self.getEnv(_ENV_HKL)
+        self.error("An hkl demo has already been prepared on this sardana")
+        return
+    except:
+        pass
+
+    db = PyTango.Database()
+
+    motor_ctrl_name = get_free_names(db, "motctrl", 1)[0]
+    hkl_ctrl_name = get_free_names(db, "hklctrl", 1)[0]
+
+    motor_names = []
+    for motor in ["mu", "omega", "chi", "phi", "gamma", "delta"]:
+        motor_names += get_free_names(db, motor, 1)
+
+    pseudo_names = []
+    for pseudo in ["h", "k", "l",
+                   "psi",
+                   "q", "alpha",
+                   "qper", "qpar"]:
+        pseudo_names += get_free_names(db, pseudo, 1)
+
+    pools = self.getPools()
+    if not len(pools):
+        self.error('This is not a valid sardana demonstration system.\n'
+                   'Sardana demonstration systems must be connect to at least '
+                   'one Pool')
+        return
+    pool = pools[0]
+
+    self.print("Creating motor controller", motor_ctrl_name, "...")
+    self.defctrl("DummyMotorController", motor_ctrl_name)
+    for axis, motor_name in enumerate(motor_names, 1):
+        self.print("Creating motor", motor_name, "...")
+        self.defelem(motor_name, motor_ctrl_name, axis)
+
+    self.print("Creating hkl controller", hkl_ctrl_name, "...")
+    self.defctrl("DiffracE6C", hkl_ctrl_name,
+                 "mu=" + motor_names[0],  # motor role
+                 "omega=" + motor_names[1],
+                 "chi=" + motor_names[2],
+                 "phi=" + motor_names[3],
+                 "gamma=" + motor_names[4],
+                 "delta=" + motor_names[5],
+                 "h=" + pseudo_names[0],  # pseudo role
+                 "k=" + pseudo_names[1],
+                 "l=" + pseudo_names[2],
+                 "psi=" + pseudo_names[3],
+                 "q=" + pseudo_names[4],
+                 "alpha=" + pseudo_names[5],
+                 "qper=" + pseudo_names[6],
+                 "qpar=" + pseudo_names[7],
+                 "diffractometertype", "E6C")
+
+    controllers = motor_ctrl_name, hkl_ctrl_name
+    elements = pseudo_names + motor_names
+    d = dict(controllers=controllers, elements=elements)
+    self.setEnv(_ENV_HKL, d)
+
+    self.print("DONE!")
diff --git a/src/sardana/macroserver/macros/env.py b/src/sardana/macroserver/macros/env.py
index 9ba1dfe..8d7abb5 100644
--- a/src/sardana/macroserver/macros/env.py
+++ b/src/sardana/macroserver/macros/env.py
@@ -105,10 +105,10 @@ class lsenv(Macro):
          None, 'List of macros to show environment'],
     ]
     
-    def prepare(self, *macro_list, **opts):
+    def prepare(self, macro_list, **opts):
         self.table_opts = opts
         
-    def run(self, *macro_list):
+    def run(self, macro_list):
         # list the environment for the current door
         if len(macro_list) == 0:
             # list All the environment for the current door
@@ -155,7 +155,7 @@ class senv(Macro):
                   None, 'value(s). one item will eval to a single element. More than one item will eval to a tuple of elements'],
                 ]
 
-    def run(self, env, *value):
+    def run(self, env, value):
         if len(value) == 1: 
             value = value[0]
         else:
@@ -172,7 +172,7 @@ class usenv(Macro):
          None, 'List of environment items to be removed'],
     ]    
     
-    def run(self, *env):
+    def run(self, env):
         self.unsetEnv(env)
         self.output("Success!")
         
diff --git a/src/sardana/macroserver/macros/examples/parameters.py b/src/sardana/macroserver/macros/examples/parameters.py
index bfd46ae..b164a1c 100644
--- a/src/sardana/macroserver/macros/examples/parameters.py
+++ b/src/sardana/macroserver/macros/examples/parameters.py
@@ -38,7 +38,7 @@ class pt0(Macro):
     
 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, 
+    param_def sequence as being a sequence of four elements: name, type, 
     default value and description"""
     
     param_def = [ [ 'value', Type.Float, None, 'some bloody float'] ]
@@ -48,7 +48,7 @@ class pt1(Macro):
 
 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, 
+    param_def sequence as being a sequence of four elements: name, type, 
     default value and description"""
 
     param_def = [ [ 'motor', Type.Motor, None, 'some bloody motor'] ]
@@ -133,7 +133,7 @@ class pt8(Macro):
         pass
 
 class pt9(Macro):
-    """Same as macro pt7 but witb old style ParamRepeat. If you are writing
+    """Same as macro pt7 but with 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."""
 
@@ -146,7 +146,61 @@ class pt9(Macro):
     
     def run(self, *args, **kwargs):
         pass
-    
+
+class pt10(Macro):
+    """Macro with list of numbers followed by a motor parameter. The repeat
+    parameter may be defined as first one."""
+
+    param_def = [
+       ['numb_list', [['pos', Type.Float, None, 'value']], None, 'List of values'],
+       ['motor', Type.Motor, None, 'Motor to move']
+    ]
+
+    def run(self, *args, **kwargs):
+        pass
+
+
+class pt11(Macro):
+    """Macro with list of numbers followed by a motor parameter. The repeat
+    parameter may be defined as first one."""
+
+    param_def = [
+       ['counter', Type.ExpChannel, None, 'Counter to count'],
+       ['numb_list', [['pos', Type.Float, None, 'value']], None, 'List of values'],
+       ['motor', Type.Motor, None, 'Motor to move']
+    ]
+
+    def run(self, *args, **kwargs):
+        pass
+
+
+class pt12(Macro):
+    """Macro with list of motors followed by list of numbers. Two repeat
+    parameters may defined."""
+
+    param_def = [
+       ['numb_list', [['pos', Type.Float, None, 'value']], None, 'List of values'],
+       ['motor_list', [['motor', Type.Motor, None, 'Motor to move']], None, 'List of motors']
+    ]
+
+    def run(self, *args, **kwargs):
+        pass
+
+
+class pt13(Macro):
+    """Macro with list of motors groups, where each motor group is a list of 
+    motors. Repeat parameters may be defined as nested."""
+
+    param_def = [
+       ['motor_group_list',
+        [['motor_list', [['motor', Type.Motor, None, 'Motor to move']], None, 'List of motors']],
+        None, 'Motor groups']
+    ]
+
+    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 
diff --git a/src/sardana/macroserver/macros/examples/scans.py b/src/sardana/macroserver/macros/examples/scans.py
index c83acf2..649e51e 100644
--- a/src/sardana/macroserver/macros/examples/scans.py
+++ b/src/sardana/macroserver/macros/examples/scans.py
@@ -273,9 +273,6 @@ class regscan(Macro):
     """regscan.
     Do an absolute scan of the specified motor with different number of intervals for each region.
     It uses the gscan framework.
-
-    NOTE: Due to a ParamRepeat limitation, integration time has to be
-    specified before the regions.
     """
 
     hints = {'scan' : 'regscan'}
@@ -291,7 +288,7 @@ class regscan(Macro):
          None, 'List of tuples: (next_pos, region_nr_intervals']
     ]
 
-    def prepare(self, motor, integ_time, start_pos, *regions, **opts):
+    def prepare(self, motor, integ_time, start_pos, regions, **opts):
         self.name='regscan'
         self.integ_time = integ_time
         self.start_pos = start_pos
@@ -333,9 +330,6 @@ class reg2scan(Macro):
     """reg2scan.
     Do an absolute scan of the specified motors with different number of intervals for each region.
     It uses the gscan framework. All the motors will be drived to the same position in each step
-
-    NOTE: Due to a ParamRepeat limitation, integration time has to be
-    specified before the regions.
     """
 
     hints = {'scan' : 'reg2scan'}
@@ -352,7 +346,7 @@ class reg2scan(Macro):
          None, 'List of tuples: (next_pos, region_nr_intervals']
     ]
 
-    def prepare(self, motor1, motor2, integ_time, start_pos, *regions, **opts):
+    def prepare(self, motor1, motor2, integ_time, start_pos, regions, **opts):
         self.name='reg2scan'
         self.integ_time = integ_time
         self.start_pos = start_pos
@@ -414,7 +408,7 @@ class reg3scan(Macro):
          None, 'List of tuples: (next_pos, region_nr_intervals']
     ]
 
-    def prepare(self, motor1, motor2, motor3, integ_time, start_pos, *regions, **opts):
+    def prepare(self, motor1, motor2, motor3, integ_time, start_pos, regions, **opts):
         self.name='reg3scan'
         self.integ_time = integ_time
         self.start_pos = start_pos
diff --git a/src/sardana/macroserver/macros/examples/submacros.py b/src/sardana/macroserver/macros/examples/submacros.py
index cb47894..90445e1 100644
--- a/src/sardana/macroserver/macros/examples/submacros.py
+++ b/src/sardana/macroserver/macros/examples/submacros.py
@@ -50,8 +50,8 @@ class call_wm(Macro):
          None, 'List of motor to show'],
     ]
 
-    def run(self, *m):
-        self.macros.wm(*m)
+    def run(self, m):
+        self.macros.wm(m)
         
 class subsubm(Macro):
     """this macro just calls the 'subm' macro
diff --git a/src/sardana/macroserver/macros/expert.py b/src/sardana/macroserver/macros/expert.py
index 3dd666f..15133c6 100644
--- a/src/sardana/macroserver/macros/expert.py
+++ b/src/sardana/macroserver/macros/expert.py
@@ -73,14 +73,14 @@ class defmeas(Macro):
             None, 'List of measurement channels'],
     ]
 
-    def prepare(self, name, *channel_list, **opts):
+    def prepare(self, name, channel_list, **opts):
 
         mntgrp_list = self.findObjs(name, type_class=Type.MeasurementGroup)
 
         if len(mntgrp_list) != 0:
             raise Exception('A measurement group with that name already exists')
 
-    def run(self, name, *channel_list):
+    def run(self, name, channel_list):
         channel0 = self.getObj(channel_list[0])
         pool = channel0.getPoolObj()
         mg = pool.createMeasurementGroup(name, channel_list)
@@ -112,6 +112,23 @@ class defelem(Macro):
         self.print("Created %s" % str(elem))
 
 
+class renameelem(Macro):
+    """Renames any type of Pool elements apart of Pools."""
+
+    param_def = [ ['element', Type.PoolElement, None, 'element to be renamed'],
+                  ['new_name', Type.String, None, 'new name']]
+
+    def prepare(self, elem, new_name):
+        if elem.getType() == "Pool":
+            raise WrongParam('Pool elements can not be renamed')
+
+    def run(self, elem, new_name):
+        pool = elem.getPoolObj()
+        old_name = elem.getName()
+        pool.renameElement(old_name, new_name)
+        self.print("Renamed %s to %s" % (old_name, new_name))
+
+
 class udefelem(Macro):
     """Deletes an existing element(s)"""
 
@@ -121,12 +138,13 @@ class udefelem(Macro):
         None, 'List of element(s) name'],
     ]
 
-    def run(self, *elements):
+    def run(self, elements):
         for element in elements:
             pool = element.getPoolObj()
             pool.deleteElement(element.getName())
 
 
+
 class defctrl(Macro):
     """Creates a new controller
     'role_prop' is a sequence of roles and/or properties.
@@ -152,7 +170,7 @@ class defctrl(Macro):
                    'a role or property item'],min=0),
                    None, 'roles and/or properties'] ]
 
-    def run(self, ctrl_class, name, *props):
+    def run(self, ctrl_class, name, props):
         pool = ctrl_class.getPoolObj()
         elem = pool.createController(ctrl_class.name, name, *props)
         self.print("Created %s" % str(elem))
@@ -182,7 +200,7 @@ class send2ctrl(Macro):
                   ParamRepeat(['string item', Type.String, None, 'a string item'],),
                   None, 'data to be sent']]
 
-    def run(self, controller, *data):
+    def run(self, controller, data):
         name = controller.getName()
         pool = controller.getPoolObj()
         str_data = " ".join(data)
@@ -275,9 +293,9 @@ class prdef(Macro):
     ]
 
     def run(self,macro_data):
-       code_lines, first_line = macro_data.code
-       for code_line in code_lines:
-           self.output(code_line.strip('\n'))
+        code_lines, _ = macro_data.code
+        for code_line in code_lines:
+            self.output(code_line.strip('\n'))
 
 
 class rellib(Macro):
@@ -333,14 +351,16 @@ class relmaclib(Macro):
         name = macro_library.name
         new_macro_library = self.reloadMacroLibrary(name)
         if new_macro_library.has_errors():
+            self.warning("%s could not be (re)loaded", name)
             exc_info = new_macro_library.get_error()
-            #msg = "".join(traceback.format_exception(*exc_info))
             msg = "".join(traceback.format_exception_only(*exc_info[:2]))
             self.error(msg)
+            self.warning("The old %s macro library is still available.", name)
         else:
             macros = new_macro_library.get_macros()
             self.output("%s successfully (re)loaded (found %d macros)", name, len(macros))
 
+
 class addmaclib(Macro):
     """Loads a new macro library.
 
@@ -365,6 +385,7 @@ class addmaclib(Macro):
         old_macros = self.getMacroNames()
         new_macro_library = self.reloadMacroLibrary(macro_library_name)
         if new_macro_library.has_errors():
+            self.warning("%s could not be added", macro_library_name)
             exc_info = new_macro_library.get_error()
             msg = "".join(traceback.format_exception_only(*exc_info[:2]))
             self.error(msg)
@@ -393,15 +414,20 @@ class relmac(Macro):
     def run(self, macro_code):
         name = macro_code.name
         macro_library_name = macro_code.lib.name
-        self.reloadMacro(name)
-        macro_library = self.getMacroLibrary(macro_library_name)
+        macro_library = self.reloadMacro(name)
         if macro_library.has_errors():
+            self.warning("%s could not be (re)loaded", name)
             exc_info = macro_library.get_error()
-            #msg = "".join(traceback.format_exception(*exc_info))
             msg = "".join(traceback.format_exception_only(*exc_info[:2]))
             self.error(msg)
+            self.warning("The old %s macro is still available.", name)
         else:
-            self.output("%s successfully (re)loaded", name)
+            maclibname = macro_library_name
+            self.output("%s macro successfully (re)loaded", name)
+            macros_in_lib = macro_library.get_macros()
+            self.output("\nAll macros from macro library %s have " + \
+                        "been reloaded:", maclibname)
+            self.output([macro.name for macro in macros_in_lib])
 
 
 class sar_info(Macro):
diff --git a/src/sardana/macroserver/macros/hkl.py b/src/sardana/macroserver/macros/hkl.py
new file mode 100644
index 0000000..afdb968
--- /dev/null
+++ b/src/sardana/macroserver/macros/hkl.py
@@ -0,0 +1,1554 @@
+
+"""
+    Macro library containning diffractometer related macros for the macros 
+    server Tango device server as part of the Sardana project.
+    
+"""
+
+# TODO: use taurus instead of PyTango API e.g. read_attribute,
+# write_attribute. This module is full of PyTango centric calls.
+
+# TODO: use explicit getters to obtain Sardana elements
+# (controller - getController, pseudomotor - getPseudoMotor, ...) instead of
+# using getDevice. However this getter seems to accept only the elements names
+# and not the full names.
+
+__all__ = ["addreflexion", "affine", "br", "ca", "caa", "ci", "computeub",
+           "freeze", "getmode", "hklscan", "hscan", "kscan", "latticecal",
+           "loadcrystal", "lscan", "newcrystal", "or0", "or1", "orswap",
+           "pa", "savecrystal", "setaz", "setlat", "setmode", "setor0",
+           "setor1", "setorn", "th2th", "ubr", "wh"]
+
+import time
+import math
+import os
+import re
+import numpy as np
+
+from sardana.macroserver.macro import *
+from sardana.macroserver.macros.scan import aNscan
+
+from taurus.core.util.log import Logger
+
+logger = Logger.getLogger("MacroManager")
+
+logger.info("Diffractometer macros are at early stage. They can slightly change. Macro luppsi is not tested.")
+
+
+class _diffrac:
+    """Internal class used as a base class for the diffractometer macros"""
+
+    env = ('DiffracDevice',)
+
+    def prepare(self):
+
+        dev_name = self.getEnv('DiffracDevice')
+        self.diffrac = self.getDevice(dev_name)
+
+        try:
+            dev_name = self.getEnv('Psi')
+            self.psidevice = self.getDevice(dev_name)
+        except:
+            pass
+
+        motorlist = self.diffrac.motorlist
+
+        pseudo_motor_names = []
+        for motor in self.diffrac.hklpseudomotorlist:
+            pseudo_motor_names.append(motor.split(' ')[0])
+
+        self.h_device = self.getDevice(pseudo_motor_names[0])
+        self.k_device = self.getDevice(pseudo_motor_names[1])
+        self.l_device = self.getDevice(pseudo_motor_names[2])
+
+        motor_list = self.diffrac.motorlist
+
+        self.nb_motors = len(motor_list)
+
+        try:
+            self.angle_names = self.diffrac.motorroles
+        except:  # Only for compatibility
+            self.angle_names = []
+            if self.nb_motors == 4:
+                self.angle_names.append("omega")
+                self.angle_names.append("chi")
+                self.angle_names.append("phi")
+                self.angle_names.append("theta")
+            elif self.nb_motors == 6:
+                self.angle_names.append("mu")
+                self.angle_names.append("omega")
+                self.angle_names.append("chi")
+                self.angle_names.append("phi")
+                self.angle_names.append("gamma")
+                self.angle_names.append("delta")
+
+        if self.nb_motors == 4:
+            self.labelmotor = {'Omega': "omega",
+                               'Chi': "chi", 'Phi': "phi", 'Theta': "theta"}
+        elif self.nb_motors == 6:
+            self.labelmotor = {'Mu': "mu", 'Theta': "omega", 'Chi': "chi",
+                               'Phi': "phi", 'Gamma': "gamma", 'Delta': "delta"}
+
+        prop = self.diffrac.get_property(['DiffractometerType'])
+        for v in prop['DiffractometerType']:
+            self.type = v
+
+        self.angle_device_names = {}
+        i = 0
+        for motor in motor_list:
+            self.angle_device_names[self.angle_names[i]] = motor.split(' ')[0]
+            i = i + 1
+
+    # TODO: it should not be necessary to implement on_stop methods in the
+    # macros in order to stop the moveables. Macro API should provide this kind
+    # of emergency stop (if the moveables are correctly reserved with the
+    # getMotion method) in case of aborting a macro.
+    def on_stop(self):
+
+        for angle in self.angle_names:
+            angle_dev = self.getDevice(self.angle_device_names[angle])
+            angle_dev.Stop()
+
+    def check_collinearity(self, h0, k0, l0, h1, k1, l1):
+
+        print h0
+        cpx = k0 * l1 - l0 * k1
+        cpy = l0 * h1 - h0 * l1
+        cpz = h0 * k1 - k0 * h1
+        cp_square = math.sqrt(cpx * cpx + cpy * cpy + cpz * cpz)
+
+        collinearity = False
+
+        if cp_square < 0.01:
+            collinearity = True
+
+        return collinearity
+
+    def get_hkl_ref0(self):
+
+        reflections = []
+        try:
+            reflections = self.diffrac.reflectionlist
+        except:
+            pass
+
+        hkl = []
+        if reflections != None:
+            for i in range(1, 4):
+                hkl.append(reflections[0][i])
+
+        return hkl
+
+    def get_hkl_ref1(self):
+
+        reflections = []
+        try:
+            reflections = self.diffrac.reflectionlist
+        except:
+            pass
+
+        hkl = []
+        if reflections != None:
+            if len(reflections) > 1:
+                for i in range(1, 4):
+                    hkl.append(reflections[1][i])
+
+        return hkl
+
+    def fl(self, ch,
+           regx=re.compile(
+            '(?<![\d.])'
+            '(?![1-9]\d*(?![\d.])|\d*\.\d*\.)'
+            '0*(?!(?<=0)\.)'
+            '([\d.]+?)'
+            '\.?0*'
+            '(?![\d.])'
+           ),
+           repl=lambda mat: mat.group(mat.lastindex)
+           if mat.lastindex != 3
+           else '0' + mat.group(3)):
+        mat = regx.search(ch)
+        if mat:
+            return regx.sub(repl, ch)
+
+# TODO: Revrite this macro in order to use events instead of polling to obtain 
+# the position updates. See umv macro as an example
+# TODO: H, K, L parameters should be of type float and not string
+class br(Macro, _diffrac):
+    """Move the diffractometer to the reciprocal space coordinates given by 
+    H, K and L. 
+    If a fourth parameter is given, the combination of angles to be set is 
+    the correspondig to the given index. The index of the
+    angles combinations are then changed."""
+
+    param_def = [
+        ['H', Type.String, None, "H value"],
+        ['K', Type.String, None, "K value"],
+        ['L', Type.String, None, "L value"],
+        ['AnglesIndex', Type.Integer, -1, "Angles index"],
+        ['FlagNotBlocking', Type.Integer,  0,
+         "If 1 not block. Return without finish movement"]
+    ]
+
+    def prepare(self, H, K, L, AnglesIndex, FlagNotBlocking):
+        _diffrac.prepare(self)
+
+    def run(self, H, K, L, AnglesIndex, FlagNotBlocking):
+
+        if AnglesIndex != -1:
+            sel_tr = AnglesIndex
+        else:
+            sel_tr = self.diffrac.selectedtrajectory
+
+        hkl_labels = ["H", "K", "L"]
+
+        if H in hkl_labels or K in hkl_labels or L in hkl_labels:
+            try:
+                q_vector = self.getEnv('Q')
+                q_dict = {}
+                q_dict["H"] = q_vector[0]
+                q_dict["K"] = q_vector[1]
+                q_dict["L"] = q_vector[2]
+
+                if H in hkl_labels:
+                    H = float(q_dict[H])
+                if K in hkl_labels:
+                    K = float(q_dict[K])
+                if L in hkl_labels:
+                    L = float(q_dict[L])
+            except:
+                self.error("Environment Q not defined. Run wh to define it")
+                return
+
+        hkl_values = [float(H), float(K), float(L)]
+
+        self.diffrac.write_attribute("computetrajectoriessim", hkl_values)
+
+        angles_list = self.diffrac.trajectorylist[sel_tr]
+
+        i = 0
+        for angle in self.angle_names:
+            angle_dev = self.getDevice(self.angle_device_names[angle])
+            angle_dev.write_attribute("Position", angles_list[i])
+            i = i + 1
+            self.checkPoint()
+
+        self.checkPoint()
+
+        if FlagNotBlocking == 0:
+            self.execMacro('_blockprintmove', 0)
+
+        self.setEnv('Q', [hkl_values[0], hkl_values[1],
+                          hkl_values[2], self.diffrac.WaveLength])
+
+# TODO: hh, kk, ll parameters should be of type float and not string
+class ubr(Macro, _diffrac):
+    """Move the diffractometer to the reciprocal space coordinates given by 
+    H, K and L und update.
+    """
+
+    param_def = [
+        ["hh", Type.String, "Not set", "H position"],
+        ["kk", Type.String, "Not set", "K position"],
+        ["ll", Type.String, "Not set", "L position"],
+        ['AnglesIndex', Type.Integer, -1, "Angles index"]
+    ]
+
+    def prepare(self, hh, kk, ll, AnglesIndex):
+        _diffrac.prepare(self)
+
+    def run(self, hh, kk, ll, AnglesIndex):
+
+        hkl_labels = ["H", "K", "L"]
+
+        if hh in hkl_labels or kk in hkl_labels or ll in hkl_labels:  # Needs to be checked also here
+            try:
+                q_vector = self.getEnv('Q')
+            except:
+                self.error("Environment Q not defined. Run wh to define it")
+                return
+
+        if ll != "Not set":
+            br, pars = self.createMacro("br", hh, kk, ll, AnglesIndex, 1)
+            self.runMacro(br)
+            self.execMacro('_blockprintmove', 1)
+        else:
+            self.output("usage:  ubr H K L [Trajectory]")
+
+
+class _ca(Macro, _diffrac):
+    """Calculate motor positions for given H K L according to the current
+    operation mode, for all trajectories or for the first one"""
+
+    param_def = [
+        ['H', Type.Float, None, "H value for the azimutal vector"],
+        ['K', Type.Float, None, "K value for the azimutal vector"],
+        ['L', Type.Float, None, "L value for the azimutal vector"],
+        ['Trajectory', Type.Float, -1, "If -1, all trajectories"],
+    ]
+
+    def prepare(self, H, K, L, Trajectory):
+        _diffrac.prepare(self)
+
+    def run(self, H, K, L, Trajectory):
+
+        hkl_values = [H, K, L]
+
+        self.diffrac.write_attribute("computetrajectoriessim", hkl_values)
+
+        if Trajectory == -1:
+            start_range = 0
+            end_range = len(self.diffrac.trajectorylist)
+        else:
+            start_range = Trajectory
+            end_range = Trajectory + 1
+            
+        for i in range(int(start_range), int(end_range)):
+            angles_list = self.diffrac.trajectorylist[i]
+            self.output("")
+            self.output("Trajectory %2d " % i)
+
+            self.output("H K L =  %9.5f %9.5f %9.5f " %
+                        (self.h_device.position, self.k_device.position,
+                         self.l_device.position))
+
+            try:
+                self.output("Azimuth (Psi) = %7.5f" %
+                            (self.psidevice.Position))
+            except:
+                self.warning(
+                    "Not able to read psi. Check if environment Psi is defined")
+                
+            self.output("Wavelength =  %7.5f" % (self.diffrac.WaveLength))
+            self.output("")
+
+            str_pos = {}
+            j = 0
+            for name in self.angle_names:
+                str_pos[name] = "%7.5f" % angles_list[j]
+                j = j + 1
+
+            self.output("%10s %11s %12s %11s %10s %11s" %
+                        ("Delta", "Theta", "Chi", "Phi", "Mu", "Gamma"))
+            self.output("%10s %11s %12s %11s %10s %11s" %
+                        (str_pos[self.labelmotor["Delta"]], str_pos[self.labelmotor["Theta"]], str_pos[self.labelmotor["Chi"]], str_pos[self.labelmotor["Phi"]], str_pos[self.labelmotor["Mu"]], str_pos[self.labelmotor["Gamma"]]))
+
+            
+class ca(Macro, _diffrac):
+    """Calculate motor positions for given H K L according to the current
+    operation mode (trajectory 0)."""
+
+    param_def = [
+        ['H', Type.Float, None, "H value for the azimutal vector"],
+        ['K', Type.Float, None, "K value for the azimutal vector"],
+        ['L', Type.Float, None, "L value for the azimutal vector"],
+    ]
+
+    def prepare(self, H, K, L):
+        _diffrac.prepare(self)
+
+    def run(self, H, K, L):
+
+        hkl_values = [H, K, L]
+
+        self.execMacro("_ca", H, K, L, 0)
+
+
+class caa(Macro, _diffrac):
+    """Calculate motor positions for given H K L according to the current
+    operation mode (all trajectories)"""
+
+    param_def = [
+        ['H', Type.Float, None, "H value for the azimutal vector"],
+        ['K', Type.Float, None, "K value for the azimutal vector"],
+        ['L', Type.Float, None, "L value for the azimutal vector"],
+    ]
+
+    def prepare(self, H, K, L):
+        _diffrac.prepare(self)
+
+    def run(self, H, K, L):
+
+        hkl_values = [H, K, L]
+
+        self.execMacro("_ca", H, K, L)
+
+
+class ci(Macro, _diffrac):
+    """ Calculate hkl for given angle values """
+
+    param_def = [
+        ['mu', Type.Float, None, "Mu value"],
+        ['theta', Type.Float, None, "Theta value"],
+        ['chi', Type.Float, None, "Chi value"],
+        ['phi', Type.Float, None, "Phi value"],
+        ['gamma', Type.Float, -999, "Gamma value"],
+        ['delta', Type.Float, -999, "Delta value"],
+    ]
+
+    def prepare(self, mu, theta, chi, phi, gamma, delta):
+        _diffrac.prepare(self)
+
+    def run(self, mu, theta, chi, phi, gamma, delta):
+
+        if delta == -999 and self.nb_motors == 6:
+            self.error("Six angle values are need as argument")
+        else:
+
+            angles = [mu, theta, chi, phi, gamma, delta]
+
+            self.diffrac.write_attribute("computehkl", angles)
+
+            hkl_values = self.diffrac.computehkl
+
+            self.output("h %f k %f l %f" %
+                        (hkl_values[0], hkl_values[1], hkl_values[2]))
+
+
+class pa(Macro, _diffrac):
+    """Prints information about the active diffractometer."""
+
+    suffix = ("st", "nd", "rd", "th")
+
+    def prepare(self):
+        _diffrac.prepare(self)
+
+    def run(self):
+        
+        str_type = "Eulerian 6C"
+        if self.type == 'E4CV':
+            str_type = "Eulerian 4C Vertical"
+        elif self.type == 'E4CH':
+            str_type = "Eulerian 4C Horizontal"
+        elif self.type == 'K6C':
+            str_type = "Kappa 6C"
+        elif self.type == 'K4CV':
+            str_type = "Kappa 4C Vertical"
+
+        self.output("%s Geometry (%s), %s" %
+                    (str_type, self.type, self.diffrac.enginemode))
+        #self.output("Sector %s" % "[ToDo]")
+        self.output("")
+
+        reflections = self.diffrac.reflectionlist
+
+        nb_ref = 0
+        if reflections != None:
+            for ref in reflections:
+                if nb_ref < len(self.suffix):
+                    sf = self.suffix[nb_ref]
+                else:
+                    sf = self.suffix[3]
+                self.output("  %d%s Reflection (index %d): " %
+                            (nb_ref + 1, sf, ref[0]))
+                #self.output("    Affinement, Relevance : %d %d" % (ref[4], ref[5]))
+                if len(ref) > 10:
+                    self.output("    %s %s %s %s %s %s: %s %s %s %s %s %s" % (self.angle_names[5], self.angle_names[1], self.angle_names[2], self.angle_names[3], self.angle_names[4], self.angle_names[0], _diffrac.fl(
+                        self, str(ref[11])), _diffrac.fl(self, str(ref[7])), _diffrac.fl(self, str(ref[8])), _diffrac.fl(self, str(ref[9])), _diffrac.fl(self, str(ref[10])), _diffrac.fl(self, str(ref[6]))))
+                else:
+                    self.output("    %s %s %s %s: %s %s %s %s" % (self.angle_names[0], self.angle_names[1], self.angle_names[2], self.angle_names[
+                                3], _diffrac.fl(self, str(ref[6])), _diffrac.fl(self, str(ref[7])), _diffrac.fl(self, str(ref[8])), _diffrac.fl(self, str(ref[9]))))
+                nb_ref = nb_ref + 1
+                self.output(" %33s  %s %s %s" % ("H K L =", _diffrac.fl(self, str(
+                    ref[1])), _diffrac.fl(self, str(ref[2])), _diffrac.fl(self, str(ref[3]))))
+                self.output("")
+
+
+#        self.output("")
+        self.output("  Lattice Constants (lengths / angles):")
+        self.output("%32s = %s %s %s / %s %s %s" % ("real space", self.diffrac.a,
+                                                    self.diffrac.b, self.diffrac.c, _diffrac.fl(
+                                                        self, str(self.diffrac.alpha)),
+                                                    _diffrac.fl(self, str(self.diffrac.beta)), _diffrac.fl(self, str(self.diffrac.gamma))))
+
+        self.output("")
+        self.output("  Azimuthal reference:")
+        self.output("%34s %s %s %s " %
+                    ("H K L =", _diffrac.fl(self, str(self.diffrac.psirefh)), _diffrac.fl(self, str(self.diffrac.psirefk)), _diffrac.fl(self, str(self.diffrac.psirefl))))
+
+        self.output("")
+        self.output("  Lambda = %s" % (self.diffrac.WaveLength))
+
+        lst = self.diffrac.ubmatrix
+        self.output("  UB-Matrix")
+        self.output("  %15g %15g %15g" % (lst[0][0], lst[0][1], lst[0][2]))
+        self.output("  %15g %15g %15g" % (lst[1][0], lst[1][1], lst[1][2]))
+        self.output("  %15g %15g %15g" % (lst[2][0], lst[2][1], lst[2][2]))
+
+
+class wh(Macro, _diffrac):
+    """Show principal axes and reciprocal space positions.
+
+    Prints the current reciprocal space coordinates (H K L) and the user 
+    positions of the principal motors. Depending on the diffractometer geometry, 
+    other parameters such as the angles of incidence and reflection (ALPHA and 
+    BETA) and the incident wavelength (LAMBDA) may be displayed."""
+
+    def prepare(self):
+        _diffrac.prepare(self)
+
+    def run(self):
+
+        self.output("")
+        self.output("Engine: %s" % self.diffrac.engine)
+        self.output("")
+        self.output("Mode: %s" % self.diffrac.enginemode)
+
+        self.output("")
+        self.output("%s %s %3s %9.5f %9.5f %9.5f " %
+                    ("H", "K", "L = ", self.h_device.position, self.k_device.position, self.l_device.position))
+
+        if self.diffrac.psirefh == -999:
+            self.output("")
+        else:
+            self.output("%8s %9.5f %9.5f %9.5f " %
+                        ("Ref   = ", self.diffrac.psirefh, self.diffrac.psirefk, self.diffrac.psirefl))
+
+            psirefh_in = self.diffrac.psirefh
+            psirefk_in = self.diffrac.psirefk
+            psirefl_in = self.diffrac.psirefl
+            engine_restore = self.diffrac.engine
+            mode_restore = self.diffrac.enginemode
+
+            self.diffrac.write_attribute("engine", "psi")
+
+            psirefh_psi = self.diffrac.psirefh
+            psirefk_psi = self.diffrac.psirefk
+            psirefl_psi = self.diffrac.psirefl
+
+            self.diffrac.write_attribute("engine", engine_restore)
+            self.diffrac.write_attribute("enginemode", mode_restore)
+
+            if psirefh_in != psirefh_psi or psirefk_in != psirefk_psi or psirefl_in != psirefl_psi:
+                self.warning(
+                    "Psiref vector missmatch. Calculated value corresponds to:")
+                self.warning("%8s %9.5f %9.5f %9.5f " %
+                             ("Ref   = ", psirefh_psi, psirefk_psi, psirefl_psi))
+                self.warning("Use setaz for setting it consistently")
+
+        try:
+            self.output("%s %7.5f" % (
+                "Azimuth (Psi - calculated) = ", self.psidevice.Position))
+        except:
+            self.warning(
+                "Not able to read psi. Check if environment Psi is defined")
+
+        parameter_names = self.diffrac.modeparametersnames
+
+        if parameter_names != None:
+            i = 0
+            for par in parameter_names:
+                if par == "psi":
+                    parameter_values = self.diffrac.modeparametersvalues
+                    self.info("%s %7.5f" %
+                              ("Azimuth (Psi - set) = ", parameter_values[i]))
+                i = i + 1
+
+        self.output("%s %7.5f" % ("Wavelength = ", self.diffrac.WaveLength))
+        self.output("")
+
+        str_pos1 = "%7.5f" % self.getDevice(
+            self.angle_device_names[self.labelmotor["Delta"]]).Position
+        str_pos2 = "%7.5f" % self.getDevice(
+            self.angle_device_names[self.labelmotor["Theta"]]).Position
+        str_pos3 = "%7.5f" % self.getDevice(
+            self.angle_device_names[self.labelmotor["Chi"]]).Position
+        str_pos4 = "%7.5f" % self.getDevice(
+            self.angle_device_names[self.labelmotor["Phi"]]).Position
+        str_pos5 = "%7.5f" % self.getDevice(
+            self.angle_device_names[self.labelmotor["Mu"]]).Position
+        str_pos6 = "%7.5f" % self.getDevice(
+            self.angle_device_names[self.labelmotor["Gamma"]]).Position
+
+        self.output("%10s %11s %12s %11s %10s %11s" %
+                    ("Delta", "Theta", "Chi", "Phi", "Mu", "Gamma"))
+        self.output("%10s %11s %12s %11s %10s %11s" %
+                    (str_pos1, str_pos2, str_pos3, str_pos4, str_pos5, str_pos6))
+
+        self.setEnv('Q', [self.h_device.position, self.k_device.position,
+                          self.l_device.position, self.diffrac.WaveLength])
+
+
+class freeze(Macro, _diffrac):
+    """ Set psi value for psi constant modes """
+
+    param_def = [
+        ['parameter', Type.String, None, "Parameter to freeze"],
+        ['value',     Type.Float,  None, "Value to be frozen"]
+    ]
+
+    def prepare(self, parameter, value):
+        _diffrac.prepare(self)
+
+    def run(self, parameter, value):
+
+        if parameter == "psi":
+            engine_restore = self.diffrac.engine
+            mode_restore = self.diffrac.enginemode
+
+            if mode_restore != "psi_constant_vertical" and mode_restore != "psi_constant_horizontal":
+                self.warning(
+                    "Psi frozen to set value. But current mode is not set to psi_constant_vertical or psi_constant_horizontal ")
+
+            self.diffrac.write_attribute("engine", "hkl")
+            self.diffrac.write_attribute("enginemode", "psi_constant_vertical")
+            parameter_values = self.diffrac.modeparametersvalues
+            parameter_values[3] = value
+            self.diffrac.write_attribute(
+                "modeparametersvalues", parameter_values)
+            self.diffrac.write_attribute(
+                "enginemode", "psi_constant_horizontal")
+            parameter_values = self.diffrac.modeparametersvalues
+            parameter_values[3] = value
+            self.diffrac.write_attribute(
+                "modeparametersvalues", parameter_values)
+
+            self.diffrac.write_attribute("engine", engine_restore)
+            self.diffrac.write_attribute("enginemode", mode_restore)
+
+        else:
+            self.error("Only implemented for parameter psi. Nothing done")
+
+
+class setmode(iMacro, _diffrac):
+    """Set operation mode."""
+
+    param_def = [
+        ['new_mode', Type.Integer, -1, "Mode to be set"]
+    ]
+
+
+    def prepare(self, new_mode):
+        _diffrac.prepare(self)
+
+    def run(self, new_mode):
+
+        modes = self.diffrac.enginemodelist
+
+        if new_mode == -1:
+            self.output("Available modes:")
+            imode = 1
+            old_mode = self.diffrac.read_attribute("enginemode").value
+            for mode in modes:
+                if mode == old_mode:
+                    def_mode = imode
+                self.output(" %d -> %s " % (imode, mode))
+                imode = imode + 1
+            old_mode = self.diffrac.read_attribute("enginemode").value
+            self.output("")
+            a = self.input("Your choice?", default_value=def_mode)
+            imode = 1
+            for mode in modes:
+                if imode == int(a):
+                    def_mode = imode
+                imode = imode + 1
+
+            self.diffrac.write_attribute("enginemode", modes[def_mode - 1])
+            self.output("")
+            self.output("Now using %s mode" % modes[def_mode - 1])
+            return
+
+        if new_mode > len(modes):
+            self.output(
+                "Wrong index mode -> only from 1 to %d allowed:" % len(modes))
+            imode = 1
+            for mode in modes:
+                self.output(" %d -> %s " % (imode, mode))
+                imode = imode + 1
+            return
+        else:
+            self.diffrac.write_attribute("enginemode", modes[new_mode - 1])
+            self.output("Now using %s mode" % modes[new_mode - 1])
+
+            self.execMacro('savecrystal')
+
+
+class getmode(Macro, _diffrac):
+    """Get operation mode."""
+
+    def prepare(self):
+        _diffrac.prepare(self)
+
+    def run(self):
+
+        self.output(self.diffrac.enginemode)
+
+
+class setlat(iMacro, _diffrac):
+    """Set the crystal lattice parameters a, b, c, alpha, beta and gamma
+       for the currently active diffraction pseudo motor controller."""
+
+    param_def = [
+        ['a', Type.Float, -999, "Lattice 'a' parameter"],
+        ['b', Type.Float, -999, "Lattice 'b' parameter"],
+        ['c', Type.Float, -999, "Lattice 'c' parameter"],
+        ['alpha', Type.Float, -999, "Lattice 'alpha' parameter"],
+        ['beta',  Type.Float, -999, "Lattice 'beta' parameter"],
+        ['gamma', Type.Float, -999, "Lattice 'gamma' parameter"]
+    ]
+
+
+    def prepare(self, a, b, c, alpha, beta, gamma):
+        _diffrac.prepare(self)
+
+    def run(self, a, b, c, alpha, beta, gamma):
+
+        if gamma == -999:
+            a = self.diffrac.a
+            b = self.diffrac.b
+            c = self.diffrac.c
+            alpha = self.diffrac.alpha
+            beta = self.diffrac.beta
+            gamma = self.diffrac.gamma
+            self.output("")
+            self.output("Enter real space lattice parameters:")
+            a = self.input(" Lattice a?", default_value=a,
+                           data_type=Type.String)
+            b = self.input(" Lattice b?", default_value=b,
+                           data_type=Type.String)
+            c = self.input(" Lattice c?", default_value=c,
+                           data_type=Type.String)
+            alpha = self.input(" Lattice alpha?",
+                               default_value=alpha, data_type=Type.String)
+            beta = self.input(" Lattice beta?",
+                              default_value=beta, data_type=Type.String)
+            gamma = self.input(" Lattice gamma?",
+                               default_value=gamma, data_type=Type.String)
+            self.output("")
+            self.diffrac.write_attribute("a", float(a))
+            self.diffrac.write_attribute("b", float(b))
+            self.diffrac.write_attribute("c", float(c))
+            self.diffrac.write_attribute("alpha", float(alpha))
+            self.diffrac.write_attribute("beta", float(beta))
+            self.diffrac.write_attribute("gamma", float(gamma))
+        else:
+            self.diffrac.write_attribute("a", a)
+            self.diffrac.write_attribute("b", b)
+            self.diffrac.write_attribute("c", c)
+            self.diffrac.write_attribute("alpha", alpha)
+            self.diffrac.write_attribute("beta", beta)
+            self.diffrac.write_attribute("gamma", gamma)
+
+        self.execMacro('computeub')
+
+
+class or0(Macro, _diffrac):
+    """Set primary orientation reflection."""
+
+    param_def = [
+        ['H', Type.Float, None, "H value"],
+        ['K', Type.Float, None, "K value"],
+        ['L', Type.Float, None, "L value"],
+    ]
+
+    def prepare(self, H, K, L):
+        _diffrac.prepare(self)
+
+    def run(self, H, K, L):
+
+        # Check collinearity
+
+        hkl_ref1 = _diffrac.get_hkl_ref1(self)
+        if len(hkl_ref1) > 1:
+            check = _diffrac.check_collinearity(
+                self, H, K, L, hkl_ref1[0], hkl_ref1[1], hkl_ref1[2])
+            if check:
+                self.warning(
+                    "Can not orient: or0 %9.5f %9.5f %9.5f are parallel to or1" % (H, K, L))
+                return
+
+        values = [0, H, K, L]
+        self.diffrac.write_attribute("SubstituteReflection", values)
+
+        self.execMacro('computeub')
+
+
+class or1(Macro, _diffrac):
+    """Set secondary orientation reflection."""
+
+    param_def = [
+        ['H', Type.Float, None, "H value"],
+        ['K', Type.Float, None, "K value"],
+        ['L', Type.Float, None, "L value"],
+    ]
+
+    def prepare(self, H, K, L):
+        _diffrac.prepare(self)
+
+    def run(self, H, K, L):
+
+        # Check collinearity
+
+        hkl_ref0 = _diffrac.get_hkl_ref0(self)
+        if len(hkl_ref0) > 1:
+            check = _diffrac.check_collinearity(
+                self, hkl_ref0[0], hkl_ref0[1], hkl_ref0[2], H, K, L)
+            if check:
+                self.warning(
+                    "Can not orient: or0 is parallel to or1 %9.5f %9.5f %9.5f" % (H, K, L))
+                return
+
+        values = [1, H, K, L]
+        self.diffrac.write_attribute("SubstituteReflection", values)
+
+        self.execMacro('computeub')
+
+
+class setor0(Macro, _diffrac):
+    """Set primary orientation reflection choosing hkl and angle values"""
+
+    param_def = [
+        ['H', Type.Float, -999, "H value"],
+        ['K', Type.Float, -999, "K value"],
+        ['L', Type.Float, -999, "L value"],
+        ['mu', Type.Float, -999, "Mu value"],
+        ['theta', Type.Float, -999, "Theta value"],
+        ['chi', Type.Float, -999, "Chi value"],
+        ['phi', Type.Float, -999, "Phi value"],
+        ['gamma', Type.Float, -999, "Gamma value"],
+        ['delta', Type.Float, -999, "Delta value"],
+    ]
+
+    def prepare(self, H, K, L, mu, theta, chi, phi, gamma, delta):
+        _diffrac.prepare(self)
+
+    def run(self, H, K, L, mu, theta, chi, phi, gamma, delta):
+
+        setorn, pars = self.createMacro(
+            "setorn", 0, H, K, L, mu, theta, chi, phi, gamma, delta)
+
+        self.runMacro(setorn)
+
+
+class setor1(Macro, _diffrac):
+    """Set secondary orientation reflection choosing hkl and angle values"""
+
+    param_def = [
+        ['H', Type.Float, -999, "H value"],
+        ['K', Type.Float, -999, "K value"],
+        ['L', Type.Float, -999, "L value"],
+        ['mu', Type.Float, -999, "Mu value"],
+        ['theta', Type.Float, -999, "Theta value"],
+        ['chi', Type.Float, -999, "Chi value"],
+        ['phi', Type.Float, -999, "Phi value"],
+        ['gamma', Type.Float, -999, "Gamma value"],
+        ['delta', Type.Float, -999, "Delta value"],
+    ]
+
+    def prepare(self, H, K, L, mu, theta, chi, phi, gamma, delta):
+        _diffrac.prepare(self)
+
+    def run(self, H, K, L, mu, theta, chi, phi, gamma, delta):
+
+        setorn, pars = self.createMacro(
+            "setorn", 1, H, K, L, mu, theta, chi, phi, gamma, delta)
+
+        self.runMacro(setorn)
+
+
+class setorn(iMacro, _diffrac):
+    """Set orientation reflection indicated by the index."""
+
+    param_def = [
+        ['ref_id', Type.Integer, None, "reflection index (starting at 0)"],
+        ['H', Type.Float, -999, "H value"],
+        ['K', Type.Float, -999, "K value"],
+        ['L', Type.Float, -999, "L value"],
+        ['mu', Type.Float, -999, "Mu value"],
+        ['theta', Type.Float, -999, "Theta value"],
+        ['chi', Type.Float, -999, "Chi value"],
+        ['phi', Type.Float, -999, "Phi value"],
+        ['gamma', Type.Float, -999, "Gamma value"],
+        ['delta', Type.Float, -999, "Delta value"],
+    ]
+
+    def prepare(self, ref_id, H, K, L, mu, theta, chi, phi, gamma, delta):
+        _diffrac.prepare(self)
+
+    def run(self, ref_id, H, K, L, mu, theta, chi, phi, gamma, delta):
+
+        if delta == -999:
+            reflections = []
+            try:
+                reflections = self.diffrac.reflectionlist
+            except:
+                pass
+            tmp_ref = {}
+            hkl_names = ["h", "k", "l"]
+            if reflections != None:
+                if len(reflections) > ref_id:
+                    for i in range(1, 4):
+                        tmp_ref[hkl_names[i - 1]] = reflections[ref_id][i]
+                    for i in range(6, 12):
+                        tmp_ref[self.angle_names[i - 6]
+                                ] = reflections[ref_id][i]
+                else:
+                    for i in range(0, 3):
+                        tmp_ref[hkl_names[i]] = 0
+                    for i in range(0, 6):
+                        tmp_ref[self.angle_names[i]] = 0
+            else:
+                for i in range(0, 3):
+                    tmp_ref[hkl_names[i]] = 0
+                for i in range(0, 6):
+                    tmp_ref[self.angle_names[i]] = 0
+
+            self.output("")
+            if ref_id == 0:
+                ref_txt = "primary-reflection"
+            elif ref_id == 1:
+                ref_txt = "secondary-reflection"
+            else:
+                ref_txt = "reflection " + str(ref_id)
+
+            self.output("Enter %s angles" % ref_txt)
+            delta = float(self.input(" Delta?", default_value=tmp_ref[
+                          "delta"], data_type=Type.String))
+            theta = float(self.input(" Theta? ", default_value=tmp_ref[
+                          "omega"], data_type=Type.String))
+            chi = float(self.input(" Chi?", default_value=tmp_ref[
+                        "chi"], data_type=Type.String))
+            phi = float(self.input(" Phi?", default_value=tmp_ref[
+                        "phi"], data_type=Type.String))
+            gamma = float(self.input(" Gamma?", default_value=tmp_ref[
+                          "gamma"], data_type=Type.String))
+            mu = float(self.input(" Mu?", default_value=tmp_ref[
+                       "mu"], data_type=Type.String))
+
+            self.output("")
+            self.output("Enter %s HKL coordinates" % ref_txt)
+            H = float(self.input(" H?", default_value=tmp_ref[
+                      "h"], data_type=Type.String))
+            K = float(self.input(" K?", default_value=tmp_ref[
+                      "k"], data_type=Type.String))
+            L = float(self.input(" L?", default_value=tmp_ref[
+                      "l"], data_type=Type.String))
+            self.output("")
+
+        # Check collinearity
+
+        if ref_id == 0:
+            hkl_ref = _diffrac.get_hkl_ref1(self)
+        if ref_id == 1:
+            hkl_ref = _diffrac.get_hkl_ref0(self)
+        if ref_id < 2:
+            if len(hkl_ref) > 1:
+                check = _diffrac.check_collinearity(
+                    self, hkl_ref[0], hkl_ref[1], hkl_ref[2], H, K, L)
+                if check:
+                    self.warning(
+                        "Can not orient: ref0 is parallel to ref1 %9.5f %9.5f %9.5f" % (H, K, L))
+                    return
+
+        # Set reflection
+
+        values = [ref_id, H, K, L]
+        self.diffrac.write_attribute("SubstituteReflection", values)
+
+        # Adjust angles
+
+        self.angle_values = {"mu": mu, "omega": theta,
+                             "chi": chi, "phi": phi, "gamma": gamma, "delta": delta}
+
+        values = []
+        values.append(ref_id)
+
+        for angle_name in self.angle_names:
+            values.append(self.angle_values[angle_name])
+
+        self.diffrac.write_attribute("AdjustAnglesToReflection", values)
+
+        # Recompute u
+
+        self.execMacro('computeub')
+
+
+class setaz(iMacro, _diffrac):
+    """ Set hkl values of the psi reference vector"""
+
+    param_def = [
+        ['PsiH', Type.Float, -999, "H value of psi reference vector"],
+        ['PsiK', Type.Float, -999, "K value of psi reference vector"],
+        ['PsiL', Type.Float, -999, "L value of psi reference vector"],
+    ]
+
+    def prepare(self, PsiH, PsiK, PsiL):
+        _diffrac.prepare(self)
+
+    def run(self, PsiH, PsiK, PsiL):
+        engine_restore = self.diffrac.engine
+        mode_restore = self.diffrac.enginemode
+
+        if PsiL == -999:
+            self.diffrac.write_attribute("engine", "hkl")
+            self.diffrac.write_attribute("enginemode", "psi_constant_vertical")
+            azh = self.diffrac.read_attribute("psirefh").value
+            azk = self.diffrac.read_attribute("psirefk").value
+            azl = self.diffrac.read_attribute("psirefl").value
+            self.output("")
+            self.output("Enter azimuthal reference H K L:")
+            a1 = self.input(" Azimuthal H?", default_value=azh,
+                            data_type=Type.String)
+            a2 = self.input(" Azimuthal K?", default_value=azk,
+                            data_type=Type.String)
+            a3 = self.input(" Azimuthal L?", default_value=azl,
+                            data_type=Type.String)
+            PsiH = float(a1)
+            PsiK = float(a2)
+            PsiL = float(a3)
+
+        self.diffrac.write_attribute("engine", "hkl")
+        self.diffrac.write_attribute("enginemode", "psi_constant_vertical")
+        self.diffrac.write_attribute("psirefh", PsiH)
+        self.diffrac.write_attribute("psirefk", PsiK)
+        self.diffrac.write_attribute("psirefl", PsiL)
+        self.diffrac.write_attribute("enginemode", "psi_constant_horizontal")
+        self.diffrac.write_attribute("psirefh", PsiH)
+        self.diffrac.write_attribute("psirefk", PsiK)
+        self.diffrac.write_attribute("psirefl", PsiL)
+        self.diffrac.write_attribute("engine", "psi")
+        self.diffrac.write_attribute("psirefh", PsiH)
+        self.diffrac.write_attribute("psirefk", PsiK)
+        self.diffrac.write_attribute("psirefl", PsiL)
+
+        self.diffrac.write_attribute("engine", engine_restore)
+        self.diffrac.write_attribute("enginemode", mode_restore)
+        self.execMacro('savecrystal')
+
+
+class computeub(Macro, _diffrac):
+    """ Compute UB matrix with reflections 0 and 1 """
+
+    def prepare(self):
+        _diffrac.prepare(self)
+
+    def run(self):
+
+        reflections = self.diffrac.reflectionlist
+        if reflections != None:
+            if len(reflections) > 1:
+                self.output("Computing UB with reflections 0 and 1")
+                values = [0,1]
+                self.diffrac.write_attribute("ComputeUB", values)
+                self.execMacro('savecrystal')
+            else:
+                self.warning("UB can not be computed. Only one reflection")
+        else:
+            self.warning("UB can not be computed. No reflection")
+
+
+class addreflection(Macro, _diffrac):
+    """ Add reflection at the botton of reflections list """
+
+    param_def = [
+        ['H', Type.Float, None, "H value"],
+        ['K', Type.Float, None, "K value"],
+        ['L', Type.Float, None, "L value"],
+        ['affinement', Type.Float, -999., "Affinement"]
+    ]
+
+    def prepare(self, H, K, L, affinement):
+        _diffrac.prepare(self)
+
+    def run(self, H, K, L, affinement):
+
+        values = [H, K, L]
+        if affinement != -999.:
+            values.append(affinement)
+
+        self.diffrac.write_attribute("AddReflection", values)
+
+
+class affine(Macro, _diffrac):
+    """Affine current crystal.
+    Fine tunning of lattice parameters and UB matrix based on 
+    current crystal reflections. Reflections with affinement 
+    set to 0 are not used. A new crystal with the post fix 
+    (affine) is created and set as current crystal"""
+
+    def prepare(self):
+        _diffrac.prepare(self)
+
+    def run(self):
+
+        self.diffrac.write_attribute("AffineCrystal", 0)
+
+
+class orswap(Macro, _diffrac):
+    """Swap values for primary and secondary vectors."""
+
+    def prepare(self):
+        _diffrac.prepare(self)
+
+    def run(self):
+
+        self.diffrac.write_attribute("SwapReflections01", 0)
+        self.output("Orientation vectors swapped.")
+        self.execMacro('computeub')
+
+
+class newcrystal(iMacro, _diffrac):
+    """ Create a new crystal (if it does not exist) and select it. """
+
+    param_def = [
+        ['crystal_name',  Type.String, "", 'Name of the crystal to add and select']
+    ]
+
+
+    def prepare(self, crystal_name):
+        _diffrac.prepare(self)
+
+    def run(self, crystal_name):
+
+        crystal_list = self.diffrac.crystallist
+
+        to_add = 1
+        i = 1
+        if crystal_name == "":
+            crystal_name = self.diffrac.crystal
+            self.output("Available crystals:")
+            for crystal in crystal_list:
+                self.output("(%s) %s" % (i, crystal))
+                if crystal_name == crystal:
+                    iselname = crystal
+                i = i + 1
+            a = self.input("New crystal?", default_value=iselname,
+                           data_type=Type.String)
+            try:
+                a1 = int(a)
+                i = 1
+                for crystal in crystal_list:
+                    if a1 == i:
+                        a = crystal
+                    i = i + 1
+                if a1 > i - 1:
+                    a = iselname
+            except:
+                pass
+
+            if a != iselname:
+                crystal_name = a
+            else:
+                crystal_name = iselname
+
+        for crystal in crystal_list:
+            if crystal_name == crystal:
+                to_add = 0
+
+        if to_add:
+            self.diffrac.write_attribute("addcrystal", crystal_name)
+
+        self.diffrac.write_attribute("crystal", crystal_name)
+
+        self.output("")
+        self.output("Crystal selected: %s " % crystal_name)
+
+        if to_add:
+            a = self.input(" Lattice a?", default_value=5.43,
+                           data_type=Type.String)
+            b = self.input(" Lattice b?", default_value=5.43,
+                           data_type=Type.String)
+            c = self.input(" Lattice c?", default_value=5.43,
+                           data_type=Type.String)
+            alpha = self.input(" Lattice alpha?",
+                               default_value=90, data_type=Type.String)
+            beta = self.input(" Lattice beta?",
+                              default_value=90, data_type=Type.String)
+            gamma = self.input(" Lattice gamma?",
+                               default_value=90, data_type=Type.String)
+            self.output("")
+            self.diffrac.write_attribute("a", float(a))
+            self.diffrac.write_attribute("b", float(b))
+            self.diffrac.write_attribute("c", float(c))
+            self.diffrac.write_attribute("alpha", float(alpha))
+            self.diffrac.write_attribute("beta", float(beta))
+            self.diffrac.write_attribute("gamma", float(gamma))
+
+
+class hscan(aNscan, Macro, _diffrac):
+    "Scan h axis"
+
+    param_def = [
+        ['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, start_pos, final_pos, nr_interv, integ_time):
+        _diffrac.prepare(self)
+        aNscan._prepare(self, [self.h_device], [start_pos], [final_pos], nr_interv, integ_time)
+
+
+class kscan(aNscan, Macro, _diffrac):
+    "Scan k axis"
+
+    param_def = [
+        ['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, start_pos, final_pos, nr_interv, integ_time):
+        _diffrac.prepare(self)
+        aNscan._prepare(self, [self.k_device], [start_pos], [final_pos], nr_interv, integ_time)
+
+
+class lscan(aNscan, Macro, _diffrac):
+    "Scan l axis"
+
+    param_def = [
+        ['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, start_pos, final_pos, nr_interv, integ_time):
+        _diffrac.prepare(self)
+        aNscan._prepare(self, [self.l_device], [start_pos], [final_pos], nr_interv, integ_time)
+
+
+class hklscan(aNscan, Macro, _diffrac):
+    "Scan h k l axes"
+
+    param_def = [
+        ['h_start_pos',  Type.Float,   None, 'Scan h start position'],
+        ['h_final_pos',  Type.Float,   None, 'Scan h final position'],
+        ['k_start_pos',  Type.Float,   None, 'Scan k start position'],
+        ['k_final_pos',  Type.Float,   None, 'Scan k final position'],
+        ['l_start_pos',  Type.Float,   None, 'Scan l start position'],
+        ['l_final_pos',  Type.Float,   None, 'Scan l final position'],
+        ['nr_interv',  Type.Integer, None, 'Number of scan intervals'],
+        ['integ_time', Type.Float,   None, 'Integration time'],
+    ]
+
+    def prepare(self, h_start_pos, h_final_pos, k_start_pos, k_final_pos, l_start_pos, l_final_pos, nr_interv, integ_time):
+        _diffrac.prepare(self)
+        aNscan._prepare(self, [self.h_device, self.k_device, self.l_device], [h_start_pos, k_start_pos, l_start_pos], [h_final_pos, k_final_pos, l_final_pos], nr_interv, integ_time)
+        
+
+
+class th2th(Macro):
+    """th2th - scan:
+
+    Relative scan around current position in del and th with d_th=2*d_delta    
+    """
+
+    param_def = [
+        ['rel_start_pos',  Type.Float,   -999, 'Scan start position'],
+        ['rel_final_pos',  Type.Float,   -999, 'Scan final position'],
+        ['nr_interv',  Type.Integer, -999, 'Number of scan intervals'],
+        ['integ_time', Type.Float,   -999, 'Integration time']
+    ]
+
+    def run(self, rel_start_pos, rel_final_pos, nr_interv, integ_time):
+
+        if ((integ_time != -999)):
+            motor_del = self.getObj("del")
+            motor_th = self.getObj("th")
+            pos_del = motor_del.getPosition()
+            pos_th = motor_th.getPosition()
+            scan = self.d2scan(motor_del, rel_start_pos, rel_final_pos,
+                               motor_th, rel_start_pos / 2, rel_final_pos / 2,
+                               nr_interv, integ_time)
+        else:
+            self.output(
+                "Usage:   th2th tth_start_rel tth_stop_rel intervals time")
+
+
+count_scan = 1
+
+
+class HookPars:
+    pass
+
+
+def hook_pre_move(self, hook_pars):
+    global count_scan
+
+    self.execMacro('freeze', 'psi', hook_pars.psi_save + +
+                   hook_pars.angle_start + (count_scan - 1) * hook_pars.angle_interv)
+    self.execMacro('ubr', hook_pars.h, hook_pars.k, hook_pars.l)
+
+    count_scan = count_scan + 1
+
+
+class luppsi(Macro, _diffrac):
+    """psi scan:
+
+    Relative scan psi angle
+
+    [TODO] Still not tested    
+    """
+
+    param_def = [
+        ['rel_start_angle',  Type.Float,   -999, 'Relative start scan angle'],
+        ['rel_final_angle',  Type.Float,   -999, 'Relative final scan angle'],
+        ['nr_interv',  Type.Integer, -999, 'Number of scan intervals'],
+        ['integ_time', Type.Float,   -999, 'Integration time']
+    ]
+
+    def prepare(self, H, K, L, AnglesIndex):
+        _diffrac.prepare(self)
+
+    def run(self, rel_start_angle, rel_final_angle, nr_interv, integ_time):
+
+        global count_scan
+        count_scan = 1
+
+        if ((integ_time != -999)):
+            self.diffrac.write_attribute("engine", "hkl")
+            self.diffrac.write_attribute("enginemode", "psi_constant_vertical")
+            h = self.h_device.position
+            k = self.k_device.position
+            l = self.l_device.position
+
+            psi_positions = []
+
+            try:
+                psi_save = self.psidevice.Position
+            except:
+                self.error(
+                    "Not able to read psi. Check if environment Psi is defined")
+                return
+
+            angle_interv = abs(rel_final_angle - rel_start_angle) / nr_interv
+
+            # Construct scan macro
+
+            self.output(self.psidevice.alias())
+            psi_motor = self.getMotor(self.psidevice.alias())
+            self.output(psi_motor)
+
+            macro, pars = self.createMacro('dscan %s %f %f %d %f ' %
+                                           (self.psidevice.alias(), rel_start_angle, rel_final_angle, nr_interv, integ_time))
+
+            # Parameters for scan hook function
+
+            hook_pars = HookPars()
+            hook_pars.psi_save = psi_save
+            hook_pars.angle_interv = angle_interv
+            hook_pars.angle_start = rel_start_angle
+            hook_pars.h = h
+            hook_pars.k = k
+            hook_pars.l = l
+            f = lambda: hook_pre_move(self, hook_pars)
+            macro.hooks = [
+                (f, ["pre-move"]),
+            ]
+
+            # Start the scan
+
+            self.runMacro(macro)
+
+            # Return to start position
+
+            self.info("Return to start position " + str(psi_save))
+            self.execMacro('freeze', 'psi', psi_save)
+            self.execMacro('ubr', h, k, l)
+            self.psidevice.write_attribute("Position", psi_save)
+
+        else:
+            self.output(
+                "Usage:  luppsi rel_startangle  rel_stopangle n_intervals time")
+
+
+class savecrystal(Macro, _diffrac):
+    """
+         Save crystal information to file
+    """
+
+    def prepare(self):
+        _diffrac.prepare(self)
+
+    def run(self):
+
+        self.info("Be aware: changes in crystal file format are still possible.")
+        self.diffrac.write_attribute("SaveCrystal", 1)
+
+
+class loadcrystal(iMacro, _diffrac):
+    """
+         Load crystal information from file
+    """
+
+    def prepare(self):
+        _diffrac.prepare(self)
+
+    def run(self):
+        self.info("Be aware: changes in crystal file format are still possible.")
+        active_dir = ""
+        try:
+            files = os.listdir(os.path.expanduser('~') + '/crystals/')
+            active_dir = os.path.expanduser('~') + '/crystals/'
+        except:
+            self.output(
+                "Directory for loading files %s/crystals does not exist" % os.path.expanduser('~'))
+            newdir = self.input("Type new directory")
+            try:
+                files = os.listdir(newdir)
+                active_dir = newdir
+            except:
+                self.output("New directory %s not found" % newdir)
+                return
+
+        res = filter(lambda x: x.endswith('.txt'), files)
+        if len(res) == 0:
+            self.output("No crystals available in set directory. Nothing done")
+            return
+        
+        i = 1
+        for filename in res:
+            filename = filename.split('.')[0]
+            self.output("(%s) %s" % (i, filename))
+            i = i + 1
+        a0 = self.input("Your choice? ")
+        try:
+            a1 = int(a0)
+            i = 1
+            for filename in res:
+                if i == int(a0) and i < len(res) + 1:
+                    file = filename
+                i = i + 1
+            if a1 < len(res) + 1:
+                self.output("")
+                self.output("File to load %s" % active_dir + file)
+            else:
+                self.output("Input out of range!")
+
+            self.diffrac.write_attribute("loadcrystal", active_dir + file)
+            self.diffrac.read_attribute("loadcrystal")
+
+        except:
+            if a0 != "":
+                self.output("Wrong input!")
+            else:
+                self.output("An input file has to be given. Nothing done")
+
+
+class latticecal(iMacro, _diffrac):
+    """
+        Calibrate lattice parameters a, b or c to current 2theta value
+    """
+
+    param_def = [
+        ["parameter", Type.String, "", "Parameter"],
+    ]
+
+    def prepare(self, parameter):
+        _diffrac.prepare(self)
+
+    def run(self, parameter):
+        if parameter != "":
+            if parameter == "a" or parameter == "b" or parameter == "c":
+                if parameter == "a":
+                    a0 = self.diffrac.a
+                    self.output("Old lattice parameter %s = %s" %
+                                (parameter, a0))
+                    h0 = self.h_device.position
+                    h1 = round(h0)
+                    a1 = h1 / h0 * a0
+                    self.output("New lattice parameter %s = %s" %
+                                (parameter, a1))
+                    self.diffrac.write_attribute("a", a1)
+                if parameter == "b":
+                    a0 = self.diffrac.b
+                    self.output("Old lattice parameter %s = %s" %
+                                (parameter, a0))
+                    h0 = self.k_device.position
+                    h1 = round(h0)
+                    a1 = h1 / h0 * a0
+                    self.output("New lattice parameter %s = %s" %
+                                (parameter, a1))
+                    self.diffrac.write_attribute("b", a1)
+                if parameter == "c":
+                    a0 = self.diffrac.c
+                    self.output("Old lattice parameter %s = %s" %
+                                (parameter, a0))
+                    h0 = self.l_device.position
+                    h1 = round(h0)
+                    a1 = h1 / h0 * a0
+                    self.output("New lattice parameter %s = %s" %
+                                (parameter, a1))
+                    self.diffrac.write_attribute("c", a1)
+
+                self.execMacro('computeub')
+
+            else:
+                self.output("Lattice parameter a, b or c")
+
+        else:
+            self.output(
+                "Calibration of lattice parameters a, b or c to current 2theta value")
+            self.output("usage:  latticecal parameter")
+
+
+class _blockprintmove(Macro, _diffrac):
+    """This macro is internal and reserved to the hkl infrastucture
+    """
+    param_def = [
+        ['flagprint', Type.Integer, 0, '1 for printing']
+    ]
+
+    def prepare(self, flagprint):
+        _diffrac.prepare(self)
+
+    def run(self, flagprint):
+        
+        moving = 1
+        tmp_dev = {}
+        for angle in self.angle_names:
+            tmp_dev[angle] = self.getDevice(self.angle_device_names[angle])
+        while(moving):
+            moving = 0
+            for angle in self.angle_names:
+                if tmp_dev[angle].state() == 6:
+                    moving = 1
+            if flagprint == 1:
+                self.outputBlock(" %7.5f  %7.5f  %7.5f" % (
+                    self.h_device.position, self.k_device.position, self.l_device.position))
+                self.flushOutput()
+            self.checkPoint()
+            time.sleep(1.0)
+        if flagprint == 1:
+            self.outputBlock(" %7.5f  %7.5f  %7.5f" % (
+                self.h_device.position, self.k_device.position, self.l_device.position))
+            self.flushOutput()
+
+
+class _diff_scan(Macro):
+    """Perfoms an scan keeping the data for further analysis/moves.
+    This macro is internal and reserved to the hkl infrastucture.
+    """
+    param_def = [
+       ['motor',      Type.Motor,   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'],
+       ['channel',    Type.ExpChannel,   None, 'Channel to analize']
+    ]
+
+    def run(self, motor, start_pos, final_pos, nr_interv, integ_time, channel):
+
+        ascan, pars= self.createMacro("ascan",motor, start_pos, final_pos, nr_interv, integ_time)
+        self.runMacro(ascan)
+
+        channel_fullname = channel.getFullName()
+        motor_name = motor.getName()
+
+        arr_data = []
+        arr_motpos = []
+        for record in ascan.data.records:
+            record_data = record.data
+            arr_data.append(record_data[channel_fullname])
+            arr_motpos.append(record_data[motor_name])
+
+        # Find motor position corresponding to the maximum of channel values
+        idx_max = np.argmax(arr_data)
+        pos_max = arr_motpos[idx_max]
+
+        self.output("Position to move")
+        self.output(pos_max)
diff --git a/src/sardana/macroserver/macros/lists.py b/src/sardana/macroserver/macros/lists.py
index cb05700..3942fab 100644
--- a/src/sardana/macroserver/macros/lists.py
+++ b/src/sardana/macroserver/macros/lists.py
@@ -46,7 +46,7 @@ Left, Right, HCenter = Alignment.Left, Alignment.Right, Alignment.HCenter
 class _ls(Macro):
     param_def = [
         ['filter',
-         ParamRepeat(['filter', Type.String, '.*', 'a regular expression filter'], min=0, max=1),
+         ParamRepeat(['filter', Type.String, '.*', 'a regular expression filter'], min=1),
          '.*', 'a regular expression filter'],
     ]
 
@@ -79,7 +79,10 @@ class lsdef(_ls):
     align =  Right,    Right,                Left
 
     def run(self, filter):
-        
+        # TODO: passing a list causes TypeError: unhashable type: 'list'
+        # uncomment it when bug-473 is fixed:
+        # https://sourceforge.net/p/sardana/tickets/473/
+        filter = filter[0]
         cols = self.get_column_names()
         out = List(cols, text_alignment=self.align,
                    max_col_width=self.width)
diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py
index 021d18d..e4e7bce 100644
--- a/src/sardana/macroserver/macros/scan.py
+++ b/src/sardana/macroserver/macros/scan.py
@@ -53,6 +53,7 @@ import numpy
 from taurus.console import Alignment
 from taurus.console.list import List
 from taurus.console.table import Table
+from taurus.core.util import SafeEvaluator
 
 from sardana.macroserver.msexception import UnknownEnv
 from sardana.macroserver.macro import *
@@ -721,8 +722,7 @@ class dmesh(mesh):
         self.info("Returning to start positions...")
         self._motion.move(self.originalPositions)
 
-
-class fscan(Macro,Hookable):
+class fscan(Macro, Hookable):
     '''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
@@ -737,75 +737,84 @@ class fscan(Macro,Hookable):
     (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.   
+    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
+
+    EXAMPLE: fscan x=[1,3,5,7,9],y=arange(5) 0.1 motor1 x**2 motor2 sqrt(y*x+3)
+             fscan x=[1,3,5,7,9],y=arange(5) [0.1,0.2,0.3,0.4,0.5] motor1 x**2 motor2 sqrt(y*x+3)
     '''
-    
-    hints = { 'scan' : 'fscan', 'allowsHooks': ('pre-scan', 'pre-move', 'post-move', 'pre-acq', 'post-acq', 'post-step', 'post-scan') }
+    # ['integ_time', Type.String,   None, 'Integration time']
+    hints = {'scan': 'fscan',
+             'allowsHooks': ('pre-scan', 'pre-move', 'post-move', 'pre-acq', 'post-acq', 'post-step', 'post-scan')}
     env = ('ActiveMntGrp',)
-    
+
     param_def = [
-       ['indepvars',  Type.String, None, 'Independent Variables'],
-       ['motor_funcs',
-        ParamRepeat(['motor', Type.Moveable, None, 'motor'],
-                    ['func', Type.String, None, 'curve defining path']),
-        None, 'List of motor and path curves'],
-       ['integ_time', Type.String,   None, 'Integration time']
+        ['indepvars', Type.String, None, 'Independent Variables'],
+        ['integ_time', Type.String, None, 'Integration time'],
+        ['motor_funcs',
+         ParamRepeat(['motor', Type.Moveable, None, 'motor'],
+                     ['func', Type.String, None, 'curve defining path']),
+         None, 'List of motor and path curves']
     ]
-    
+
     def prepare(self, *args, **opts):
-        if args[0].lower() in ["!", "*", "none", None]: indepvars={}
-        else: indepvars=SafeEvaluator({'dict':dict}).eval('dict(%s)'%args[0]) #create a dict containing the indepvars
-        self.motors=args[1:-1:2] #get motors
-        sev=SafeEvaluator(indepvars) #create a safe evaluator whitelisting the indepvars
-        self.funcstrings=args[2:-1:2]
-        self.paths = map(sev.eval,self.funcstrings) #evaluate the functions
-        self.integ_time=numpy.array(sev.eval(args[-1]), dtype='d')
+        if args[0].lower() in ["!", "*", "none", None]:
+            indepvars = {}
+        else:
+            indepvars = SafeEvaluator({'dict': dict}).eval(
+                'dict(%s)' % args[0])  # create a dict containing the indepvars
+
+        self.motors = [item[0] for item in args[2:]]
+        self.funcstrings = [item[1] for item in args[2:]]
+
+        globals_lst = [dict(zip(indepvars, values)) for values in zip(*indepvars.values())]
+        self.paths = [[SafeEvaluator(globals).eval(func) for globals in globals_lst] for func in self.funcstrings]
+
+        self.integ_time = numpy.array(eval(args[1]), dtype='d')
+
         self.opts = opts
-        if len(self.motors)==len(self.paths)>0:
-            self.N=len(self.motors)
+        if len(self.motors) == len(self.paths) > 0:
+            self.N = len(self.motors)
         else:
             raise ValueError('Moveable and func lists must be non-empty and same length')
-        npoints=len(self.paths[0])
+        npoints = len(self.paths[0])
         try:
-            #if everything is OK, the following lines should return a 2D array
+            # if everything is OK, the following lines should return a 2D array
             # n which each motor path is a row.
-            #Typical failure is due to shape mismatch due to inconsistent input
-            self.paths=numpy.array(self.paths,dtype='d')
+            # Typical failure is due to shape mismatch due to inconsistent input
+            self.paths = numpy.array(self.paths, dtype='d')
             self.paths.reshape((self.N, npoints))
-        except: #shape mismatch?
-            #try to give a meaningful description of the error
-            for p,fs in zip(self.paths,self.funcstrings):
-                if len(p)!=npoints:
+        except:  # shape mismatch?
+            # try to give a meaningful description of the error
+            for p, fs in zip(self.paths, self.funcstrings):
+                if len(p) != npoints:
                     raise ValueError(
                         '"%s" and "%s" yield different number of points (%i vs %i)'
-                        %(self.funcstrings[0],fs,npoints,len(p)))
-            raise #the problem wasn't a shape mismatch
-        self.nr_points=npoints
-        
-        if self.integ_time.size==1:
-            self.integ_time=self.integ_time*numpy.ones(self.nr_points) #extend integ_time
-        elif self.integ_time.size!=self.nr_points:
-            raise ValueError('time_integ must either be a scalar or length=npoints (%i)'%self.nr_points)
-                
-        self.name=opts.get('name','fscan')
-                
-        generator=self._generator
-        moveables=self.motors
-        env=opts.get('env',{})
-        constrains=[getCallable(cns) for cns in opts.get('constrains',[UNCONSTRAINED])]
-        
-        #Hooks are not always set at this point. We will call getHooks later on in the scan_loop
-        #self.pre_scan_hooks = self.getHooks('pre-scan')
-        #self.post_scan_hooks = self.getHooks('post-scan'
-        
-        self._gScan=SScan(self, generator, moveables, env, constrains)
-        
+                        % (self.funcstrings[0], fs, npoints, len(p)))
+            raise  # the problem wasn't a shape mismatch
+        self.nr_points = npoints
+
+        if self.integ_time.size == 1:
+            self.integ_time = self.integ_time * numpy.ones(self.nr_points)  # extend integ_time
+        elif self.integ_time.size != self.nr_points:
+            raise ValueError('time_integ must either be a scalar or length=npoints (%i)' % self.nr_points)
+
+        self.name = opts.get('name', 'fscan')
+
+        generator = self._generator
+        moveables = self.motors
+        env = opts.get('env', {})
+        constrains = [getCallable(cns) for cns in opts.get('constrains', [UNCONSTRAINED])]
+
+        # Hooks are not always set at this point. We will call getHooks later on in the scan_loop
+        # self.pre_scan_hooks = self.getHooks('pre-scan')
+        # self.post_scan_hooks = self.getHooks('post-scan'
+
+        self._gScan = SScan(self, generator, moveables, env, constrains)
+
     def _generator(self):
         step = {}
         step["pre-move-hooks"] = self.getHooks('pre-move')
@@ -813,15 +822,15 @@ class fscan(Macro,Hookable):
         step["pre-acq-hooks"] = self.getHooks('pre-acq')
         step["post-acq-hooks"] = self.getHooks('post-acq') + self.getHooks('_NOHINTS_')
         step["post-step-hooks"] = self.getHooks('post-step')
-        
+
         step["check_func"] = []
         for i in xrange(self.nr_points):
-            step["positions"] = self.paths[:,i]
+            step["positions"] = self.paths[:, i]
             step["integ_time"] = self.integ_time[i]
-            step["point_id"]= i
+            step["point_id"] = i
             yield step
-    
-    def run(self,*args):
+
+    def run(self, *args):
         for step in self._gScan.step_scan():
             yield step
 
@@ -829,7 +838,6 @@ class fscan(Macro,Hookable):
     def data(self):
         return self._gScan.data
 
-
 class ascanh(aNscan, Macro): 
     """Do an absolute scan of the specified motor.
     ascan scans one motor, as specified by motor. The motor starts at the
diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py
index 082e7e5..c8c72d4 100644
--- a/src/sardana/macroserver/macros/standard.py
+++ b/src/sardana/macroserver/macros/standard.py
@@ -24,7 +24,7 @@
 """This is the standard macro module"""
 
 __all__ = ["ct", "mstate", "mv", "mvr", "pwa", "pwm", "set_lim", "set_lm",
-           "set_pos", "settimer", "uct", "umv", "umvr", "wa", "wm"] 
+           "set_pos", "settimer", "uct", "umv", "umvr", "wa", "wm", "tw"] 
 
 __docformat__ = 'restructuredtext'
 
@@ -33,7 +33,10 @@ from taurus.console.table import Table
 
 import PyTango
 from PyTango import DevState
-from sardana.macroserver.macro import Macro, macro, Type, ParamRepeat, ViewOption
+from sardana.macroserver.macro import Macro, macro, Type, ParamRepeat, ViewOption, iMacro
+from sardana.macroserver.msexception import StopException
+
+import numpy as np
 
 ################################################################################
 #
@@ -50,7 +53,7 @@ class _wm(Macro):
          None, 'List of motor to show'],
     ]
 
-    def run(self, *motor_list):
+    def run(self, motor_list):
         show_dial = self.getViewOption(ViewOption.ShowDial)
         show_ctrlaxis = self.getViewOption(ViewOption.ShowCtrlAxis)
         pos_format = self.getViewOption(ViewOption.PosFormat)
@@ -93,9 +96,9 @@ class _wm(Macro):
                 except PyTango.AsynReplyNotArrived, e:
                     continue
                 except PyTango.DevFailed:
-                    data[name].append('NaN')
+                    data[name].append(float('NaN'))
                     if show_dial:
-                        data[name].append('NaN')
+                        data[name].append(float('NaN'))
                     req2delete.append(name)
                     self.debug('Error when reading %s position(s)' % name)
                     self.debug('Details:', exc_info=1)
@@ -140,10 +143,10 @@ class _wum(Macro):
          None, 'List of motor to show'],
     ]
 
-    def prepare(self, *motor_list, **opts):
+    def prepare(self, motor_list, **opts):
         self.table_opts = {}
     
-    def run(self, *motor_list):
+    def run(self, motor_list):
         show_dial = self.getViewOption(ViewOption.ShowDial)
         motor_width = 9
         motor_names = []
@@ -183,22 +186,22 @@ class wu(Macro):
         self.output('Current positions (user) on %s'%datetime.datetime.now().isoformat(' '))
         self.output('')
         
-        self.execMacro('_wum',*self.all_motors, **self.table_opts)
+        self.execMacro('_wum',self.all_motors, **self.table_opts)
 
 class wa(Macro):
     """Show all motor positions"""
 
     param_def = [
         ['filter',
-         ParamRepeat(['filter', Type.String, '.*', 'a regular expression filter'], min=0, max=1),
+         ParamRepeat(['filter', Type.String, '.*', 'a regular expression filter'], min=1),
          '.*', 'a regular expression filter'],
     ]
 
-    def prepare(self, *filter, **opts):
+    def prepare(self, filter, **opts):
         self.all_motors = self.findObjs(filter, type_class=Type.Moveable)
         self.table_opts = {}
     
-    def run(self, *filter):
+    def run(self, filter):
         nr_motors = len(self.all_motors)
         if nr_motors == 0:
             self.output('No motor defined')
@@ -210,8 +213,7 @@ class wa(Macro):
         else:
             self.output('Current positions (user) on %s'%datetime.datetime.now().isoformat(' '))
         self.output('')
-        
-        self.execMacro('_wm',*self.all_motors, **self.table_opts)
+        self.execMacro('_wm', self.all_motors, **self.table_opts)
 
 
 class pwa(Macro):
@@ -219,12 +221,12 @@ class pwa(Macro):
 
     param_def = [
         ['filter',
-         ParamRepeat(['filter', Type.String, '.*', 'a regular expression filter'], min=0, max=1),
+         ParamRepeat(['filter', Type.String, '.*', 'a regular expression filter'], min=1),
          '.*', 'a regular expression filter'],
     ]
 
-    def run(self, *filter):
-        self.execMacro('wa', *filter, **Table.PrettyOpts)
+    def run(self, filter):
+        self.execMacro('wa', filter, **Table.PrettyOpts)
 
 class set_lim(Macro):
     """Sets the software limits on the specified motor hello"""
@@ -294,10 +296,10 @@ class wm(Macro):
          None, 'List of motor to show'],
     ]
 
-    def prepare(self, *motor_list, **opts):
+    def prepare(self, motor_list, **opts):
         self.table_opts = {}
         
-    def run(self, *motor_list):
+    def run(self, motor_list):
         motor_width = 10
         motor_names = []
         motor_pos   = []
@@ -384,10 +386,10 @@ class wum(Macro):
          None, 'List of motor to show'],
     ]
 
-    def prepare(self, *motor_list, **opts):
+    def prepare(self, motor_list, **opts):
         self.table_opts = {}
         
-    def run(self, *motor_list):
+    def run(self, motor_list):
         motor_width = 10
         motor_names = []
         motor_pos   = []
@@ -418,8 +420,8 @@ class pwm(Macro):
          None, 'List of motor to show'],
     ]
 
-    def run(self, *motor_list):
-        self.execMacro('wm', *motor_list, **Table.PrettyOpts)
+    def run(self, motor_list):
+        self.execMacro('wm', motor_list, **Table.PrettyOpts)
 
 class mv(Macro):
     """Move motor(s) to the specified position(s)"""
@@ -431,12 +433,12 @@ class mv(Macro):
         None, 'List of motor/position pairs'],
     ]
 
-    def run(self, *motor_pos_list):
+    def run(self, motor_pos_list):
         motors, positions = [], []
-        for motor, pos in motor_pos_list:
-            motors.append(motor)
-            positions.append(pos)
-            self.debug("Starting %s movement to %s", motor.getName(), pos)
+        for m, p in motor_pos_list:
+            motors.append(m)
+            positions.append(p)
+            self.debug("Starting %s movement to %s", m.getName(), p)
         motion = self.getMotion(motors)
         state, pos = motion.move(positions)
         if state != DevState.ON:
@@ -459,7 +461,7 @@ class umv(Macro):
 
     param_def = mv.param_def
 
-    def prepare(self, *motor_pos_list, **opts):
+    def prepare(self, motor_pos_list, **opts):
         self.all_names = []
         self.all_pos = []
         self.print_pos = False
@@ -468,15 +470,9 @@ class umv(Macro):
             pos, posObj = motor.getPosition(force=True), motor.getPositionObj()
             self.all_pos.append([pos])
             posObj.subscribeEvent(self.positionChanged, motor)
-        
-    def run(self, *motor_pos_list):
-        self.print_pos = True
-        pars = []
-        for motor, pos in motor_pos_list:
-            pars.append(motor)
-            pars.append(pos)
 
-        self.execMacro('mv', *pars)
+    def run(self, motor_pos_list):
+        self.execMacro('mv', motor_pos_list)
         self.finish()
  
     def finish(self):
@@ -487,7 +483,7 @@ class umv(Macro):
         self.finish()
         
     def _clean(self):
-        for motor, pos in self.getParameters():
+        for motor, pos in self.getParameters()[0]:
             posObj = motor.getPositionObj()
             try:
                 posObj.unsubscribeEvent(self.positionChanged, motor)
@@ -518,7 +514,7 @@ class mvr(Macro):
         None, 'List of motor/displacement pairs'],
     ]
     
-    def run(self, *motor_disp_list):
+    def run(self, motor_disp_list):
         motor_pos_list = []
         for motor, disp in motor_disp_list:
             pos = motor.getPosition(force=True) 
@@ -527,15 +523,15 @@ class mvr(Macro):
                 return
             else:
                 pos += disp
-            motor_pos_list.extend([motor, pos])
-        self.execMacro('mv', *motor_pos_list)
+            motor_pos_list.append([motor, pos])
+        self.execMacro('mv', motor_pos_list)
 
 class umvr(Macro):
     """Move motor(s) relative to the current position(s) and update"""
 
     param_def = mvr.param_def
 
-    def run(self, *motor_disp_list):
+    def run(self, motor_disp_list):
         motor_pos_list = []
         for motor, disp in motor_disp_list:
             pos = motor.getPosition(force=True) 
@@ -544,8 +540,63 @@ class umvr(Macro):
                 return
             else:
                 pos += disp
-            motor_pos_list.extend([motor, pos])
-        self.execMacro('umv', *motor_pos_list)
+            motor_pos_list.append([motor, pos])
+        self.execMacro('umv', motor_pos_list)
+
+# TODO: implement tw macro with param repeats in order to be able to pass
+# multiple motors and multiple deltas. Also allow to pass the integration time
+# in order to execute the measurement group acquisition after each move and 
+# print the results. Basically follow the SPEC's API: https://certif.com/spec_help/tw.html
+class tw(iMacro):
+    """Tweak motor by variable delta"""
+
+    param_def = [
+        ['motor', Type.Moveable, "test", 'Motor to move'],
+        ['delta',   Type.Float, None, 'Amount to tweak']
+    ]
+
+    def run(self, motor, delta):
+        self.output(
+            "Indicate direction with + (or p) or - (or n) or enter")
+        self.output(
+            "new step size. Type something else (or ctrl-C) to quit.")
+        self.output("")
+        if np.sign(delta) == -1:
+            a = "-"
+        if np.sign(delta) == 1:
+            a = "+"
+        while a in ('+', '-', 'p', 'n'):
+            pos = motor.position
+            a = self.input("%s = %s, which way? " % (
+                motor, pos), default_value=a, data_type=Type.String)
+            try:
+                # check if the input is a new delta
+                delta = float(a)
+                # obtain the sign of the new delta
+                if np.sign(delta) == -1:
+                    a = "-"
+                else:
+                    a = "+"
+            except:
+                # convert to the common sign
+                if a == "p":
+                    a = "+"
+                # convert to the common sign
+                elif a == "n":
+                    a = "-"
+                # the sign is already correct, just continue
+                elif a  in ("+", "-"):
+                    pass
+                else:
+                    msg = "Typing '%s' caused 'tw' macro to stop." % a
+                    self.info(msg)
+                    raise StopException()
+                # invert the delta if necessary
+                if (a == "+" and np.sign(delta) < 0) or \
+                   (a == "-" and np.sign(delta) > 0):
+                    delta = -delta
+            pos += delta
+            self.mv(motor, pos)
 
 
 ################################################################################
@@ -676,7 +727,7 @@ class settimer(Macro):
             self.output("%s is not a valid channel in the active measurement group" % timer)
 
 @macro([['message', ParamRepeat(['message_item', Type.String, None, 'message item to be reported']), None, 'message to be reported']])
-def report(self, *message):
+def report(self, message):
     """Logs a new record into the message report system (if active)"""
     self.report(' '.join(message))
 
diff --git a/src/sardana/macroserver/macros/test/__init__.py b/src/sardana/macroserver/macros/test/__init__.py
index 701d235..fc0cbde 100644
--- a/src/sardana/macroserver/macros/test/__init__.py
+++ b/src/sardana/macroserver/macros/test/__init__.py
@@ -26,4 +26,4 @@
 from macroexecutor import BaseMacroExecutor, MacroExecutorFactory
 from base import (macroTest, BaseMacroTestCase, RunMacroTestCase,
                   RunStopMacroTestCase, testRun, testFail, testStop)
-from sardemoenv import SarDemoEnv
+from sardemoenv import *
diff --git a/src/sardana/macroserver/macros/test/base.py b/src/sardana/macroserver/macros/test/base.py
index 46d4a7e..57c7359 100644
--- a/src/sardana/macroserver/macros/test/base.py
+++ b/src/sardana/macroserver/macros/test/base.py
@@ -65,8 +65,7 @@ def macroTest(klass=None, helper_name=None, test_method_name=None,
         - \*\*helper_kwargs: All remaining keyword arguments are passed to the
                              helper.
 
-    `macroTest` assumes that the decorated class has a `macro_name` 
-    class member.
+    `macroTest` can work with the `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
@@ -122,8 +121,8 @@ def macroTest(klass=None, helper_name=None, test_method_name=None,
     return insertTest(klass=klass, helper_name=helper_name, 
                       test_method_name=test_method_name,
                       test_method_doc=test_method_doc, 
-                      tested_name = klass.macro_name, 
-                      **helper_kwargs)
+                      tested_name = helper_kwargs.get("macro_name") or \
+                      klass.macro_name, **helper_kwargs)
 
 
 #Definition of specializations of the macroTest decorator:
@@ -141,7 +140,7 @@ class BaseMacroTestCase(object):
     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)
+      - macro_name (string) name of the macro to be tested
       - 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
@@ -154,10 +153,6 @@ class BaseMacroTestCase(object):
     def setUp(self):
         """ A macro_executor instance must be created
         """
-        if self.macro_name is None:
-            msg = '%s does not define macro_name' % self.__class__.__name__
-            raise NotImplementedError(msg)
-
         mefact = MacroExecutorFactory()
         self.macro_executor = mefact.getMacroExecutor(self.door_name)
 
@@ -196,12 +191,14 @@ class RunMacroTestCase(BaseMacroTestCase):
         BaseMacroTestCase.setUp(self)
         self.macro_executor.registerAll()
 
-    def macro_runs(self, macro_params=None, wait_timeout=float("inf"),
-                   data=_NOT_PASSED):
+    def macro_runs(self, macro_name=None, macro_params=None,
+                   wait_timeout=float("inf"), data=_NOT_PASSED):
         """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.
 
+        :param macro_name: (str) macro name (takes precedence over macro_name
+                             class member)
         :param macro_params: (seq<str>): parameters for running the macro.
                              If passed, they must be given as a sequence of
                              their string representations.
@@ -210,10 +207,11 @@ class RunMacroTestCase(BaseMacroTestCase):
         :param data: (obj) Optional. If passed, the macro data after the
                      execution is tested to be equal to this.
         """
-        self.macro_executor.run(macro_name=self.macro_name,
+        macro_name = macro_name or self.macro_name
+        self.macro_executor.run(macro_name=macro_name,
                                 macro_params=macro_params,
                                 sync=True, timeout=wait_timeout)
-        self.assertFinished('Macro %s did not finish' % self.macro_name)
+        self.assertFinished('Macro %s did not finish' % macro_name)
 
         #check if the data of the macro is the expected one
         if data is not _NOT_PASSED:
@@ -225,10 +223,12 @@ class RunMacroTestCase(BaseMacroTestCase):
         #TODO: implement generic asserts for macro result and macro output, etc
         #      in a similar way to what is done for macro data
 
-    def macro_fails(self, macro_params=None, wait_timeout=float("inf"),
-                    exception=None):
+    def macro_fails(self, macro_name=None, macro_params=None,
+                    wait_timeout=float("inf"), exception=None):
         """Check that the macro fails to run for the given input parameters
 
+        :param macro_name: (str) macro name (takes precedence over macro_name
+                             class member)
         :param macro_params: (seq<str>) input parameters for the macro
         :param wait_timeout: maximum allowed time for the macro to fail. By
                              default infinite timeout is used.
@@ -237,7 +237,7 @@ class RunMacroTestCase(BaseMacroTestCase):
                         (IMPORTANT: this is just a comparison of str
                         representations of exception objects)
         """
-        self.macro_executor.run(macro_name=self.macro_name,
+        self.macro_executor.run(macro_name=macro_name or self.macro_name,
                                 macro_params=macro_params,
                                 sync=True, timeout=wait_timeout)
         state = self.macro_executor.getState()
@@ -270,11 +270,13 @@ class RunStopMacroTestCase(RunMacroTestCase):
         msg = msg + '; State buffer was %s' % state_buffer
         self.assertIn(state, stoppedStates, msg)
 
-    def macro_stops(self, macro_params=None, stop_delay=0.1,
+    def macro_stops(self, macro_name=None, macro_params=None, stop_delay=0.1,
                     wait_timeout=float("inf")):
         """A helper method to create tests that check if the macro can be
         successfully stoped (a.k.a. aborted) after it has been launched.
 
+        :param macro_name: (str) macro name (takes precedence over macro_name
+                             class member)
         :param macro_params: (seq<str>): parameters for running the macro.
                              If passed, they must be given as a sequence of
                              their string representations.
@@ -283,7 +285,7 @@ class RunStopMacroTestCase(RunMacroTestCase):
         :param wait_timeout: (float) maximum allowed time (in s) for the macro
                              to finish. By default infinite timeout is used.
         """
-        self.macro_executor.run(macro_name=self.macro_name,
+        self.macro_executor.run(macro_name=macro_name or self.macro_name,
                                 macro_params=macro_params,
                                 sync=False)
 
@@ -291,7 +293,7 @@ class RunStopMacroTestCase(RunMacroTestCase):
             time.sleep(stop_delay)
         self.macro_executor.stop()
         self.macro_executor.wait(timeout=wait_timeout)
-        self.assertStopped('Macro %s did not stop' % self.macro_name)
+        self.assertStopped('Macro %s did not stop' % macro_name)
 
 
 if __name__ == '__main__':
diff --git a/src/sardana/macroserver/macros/test/sardemoenv.py b/src/sardana/macroserver/macros/test/sardemoenv.py
index 56d69c4..953c07e 100644
--- a/src/sardana/macroserver/macros/test/sardemoenv.py
+++ b/src/sardana/macroserver/macros/test/sardemoenv.py
@@ -23,6 +23,10 @@
 ##
 #############################################################################
 
+__all__ = ("SarDemoEnv", "getElements", "getControllers", "getMotors",
+           "getPseudoMotors", "getMoveables", "getCTs", "getZerods",
+           "getOneds", "getTwods", "getIORs")
+
 from taurus import Device
 from sardana.taurus.core.tango.sardana import registerExtensions
 from taurus.core.util.singleton import Singleton
@@ -152,6 +156,56 @@ class SarDemoEnv(Singleton):
         self.__init__(door_name)
 
 
+def getElements(elem_type="all", fallback_name="element_not_defined",
+                fallback_elements_len=5):
+    if fallback_name is None:
+        fallback_name = elem_type + "_not_defined"
+    try:
+        elements = SarDemoEnv().getElements(elem_type)
+    except RuntimeError:
+        import taurus
+        from sardana import sardanacustomsettings
+        door_name = getattr(sardanacustomsettings, 'UNITTEST_DOOR_NAME',
+                            'UNDEFINED')
+        taurus.warning("The door %s is not running. " % (door_name) +
+                       "Ignore this message if you are building the documentation.")
+        elements = [fallback_name] * fallback_elements_len
+    except Exception, e:
+        import taurus
+        taurus.debug(e)
+        taurus.warning("It was not possible to retrieve the motor names. " +
+                     "Ignore this message if you are building the documentation.")
+        elements = [fallback_name] * fallback_elements_len
+    return elements
+
+def getControllers():
+    return getElements(elem_type="controller")
+
+def getCTs():
+    return getElements(elem_type="ctexpchannel")
+
+def getMotors():
+    return getElements(elem_type="motor")
+
+def getPseudoMotors():
+    return getElements(elem_type="pseudomotor")
+
+def getMoveables():
+    return getElements(elem_type="moveable")
+
+def getZerods():
+    return getElements(elem_type="zerodexpchannel")
+
+def getOneds():
+    return getElements(elem_type="onedexpchannel")
+
+def getTwods():
+    return getElements(elem_type="twodexpchannel")
+
+def getIORs():
+    return getElements(elem_type="ioregister")
+
+
 if __name__ == '__main__':
     s = SarDemoEnv()
     print s.env
diff --git a/src/sardana/macroserver/macros/test/test_ct.py b/src/sardana/macroserver/macros/test/test_ct.py
index 2f46e0d..b76f3d2 100644
--- a/src/sardana/macroserver/macros/test/test_ct.py
+++ b/src/sardana/macroserver/macros/test/test_ct.py
@@ -31,9 +31,14 @@ from sardana.macroserver.macros.test import testRun
 from sardana.macroserver.macros.test import testStop
 
 
- at testRun(macro_params=['.1'], wait_timeout=2)
- at testRun(macro_params=['.3'], wait_timeout=2)
- at testStop(macro_params=['1'], stop_delay=.1, wait_timeout=3)
+ at testRun(macro_name="ct", macro_params=['.1'], wait_timeout=2)
+ at testRun(macro_name="ct", macro_params=['.3'], wait_timeout=2)
+ at testStop(macro_name="ct", macro_params=['1'], stop_delay=.1, wait_timeout=3)
+# TODO: uncomment these test when bug-474 is fixed:
+# https://sourceforge.net/p/sardana/tickets/474/
+#@testRun(macro_name="uct", macro_params=['.1'], wait_timeout=2)
+#@testRun(macro_name="uct", macro_params=['.3'], wait_timeout=2)
+#@testStop(macro_name="uct", macro_params=['1'], stop_delay=.1, wait_timeout=3)
 class CtTest(RunStopMacroTestCase, unittest.TestCase):
 
     """Test of ct macro. It verifies that macro ct can be executed.
@@ -43,4 +48,4 @@ class CtTest(RunStopMacroTestCase, unittest.TestCase):
     Then it does another execution and it tests if the execution can be
     aborted.
     """
-    macro_name = "ct"
+    pass
diff --git a/src/sardana/macroserver/macros/test/test_env.py b/src/sardana/macroserver/macros/test/test_env.py
new file mode 100644
index 0000000..798caa8
--- /dev/null
+++ b/src/sardana/macroserver/macros/test/test_env.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+
+##############################################################################
+##
+## 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/>.
+##
+##############################################################################
+
+"""Tests for environment macros"""
+
+from taurus.external import unittest
+from sardana.macroserver.macros.test import RunMacroTestCase, testRun
+
+ at testRun
+class DumpenvTest(RunMacroTestCase, unittest.TestCase):
+    """Test case for dumpenv macro
+    """
+    macro_name = "dumpenv"
+
+
+ at testRun
+class LsvoTest(RunMacroTestCase, unittest.TestCase):
+    """Test case for lsvo macro
+    """
+    macro_name = "lsvo"
+
+
+ at testRun(macro_params=["PosFormat", "3"])
+class SetvoTest(RunMacroTestCase, unittest.TestCase):
+    """Test case for setvo macro
+    """
+    macro_name = "setvo"
+
+
+ at testRun(macro_params=["PosFormat"])
+class UsetvoTest(RunMacroTestCase, unittest.TestCase):
+    """Test case for usetvo macro
+    """
+    macro_name = "usetvo"
+
+
+ at testRun
+ at testRun(macro_params=["ascan"])
+ at testRun(macro_params=["ascan", "dscan"]) 
+class LsenvTest(RunMacroTestCase, unittest.TestCase):
+    """Test case for lsvo macro
+    """
+    macro_name = "lsenv"
+
+
+ at testRun(macro_params=["MyEnvVar", "test.dat"]) 
+class SenvTest(RunMacroTestCase, unittest.TestCase):
+    """Test case for senv macro
+    """
+    macro_name = "senv"
+
+
+ at testRun(macro_params=["MyEnvVar"])
+class UsenvTest(RunMacroTestCase, unittest.TestCase):
+    """Test case for usenv macro
+    """
+    macro_name = "usenv"
diff --git a/src/sardana/macroserver/macros/test/test_expert.py b/src/sardana/macroserver/macros/test/test_expert.py
new file mode 100644
index 0000000..c9708bf
--- /dev/null
+++ b/src/sardana/macroserver/macros/test/test_expert.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python
+
+##############################################################################
+##
+## 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/>.
+##
+##############################################################################
+
+"""Tests for expert macros"""
+
+from taurus.external import unittest
+from sardana.macroserver.macros.test import RunMacroTestCase, testRun, getCTs,\
+    getControllers
+
+CTRL_NAME1 = getControllers()[0]
+CT_NAME1, CT_NAME2 = getCTs()[:2]
+
+class ExpertTest(RunMacroTestCase, unittest.TestCase):
+    """Test case for some of the expert macros.
+    """
+    def test_expert(self):
+        CTRL_NAME = "unittestmotctrl01"
+        MOT_NAME1 = "unittestmot01"
+        MOT_NAME2 = "unittestmot02"
+        try:
+            self.macro_runs(macro_name="defctrl",
+                            macro_params=["DummyMotorController", CTRL_NAME],
+                            wait_timeout=1)
+            self.macro_runs(macro_name="defm",
+                            macro_params=[MOT_NAME1, CTRL_NAME, "1"],
+                            wait_timeout=1)
+            self.macro_runs(macro_name="defelem",
+                            macro_params=[MOT_NAME2, CTRL_NAME, "2"],
+                            wait_timeout=1)
+#     add test of renameelem when bug-471 is fixed
+#     https://sourceforge.net/p/sardana/tickets/471/
+#             self.macro_runs(macro_name="renameelem",
+#                             macro_params=[MOT_NAME1, MOT_NAME2],
+#                             wait_timeout=1)
+            self.macro_runs(macro_name="udefelem",
+                            macro_params=[MOT_NAME1, MOT_NAME2],
+                            wait_timeout=1)
+            self.macro_runs(macro_name="udefctrl",
+                            macro_params = [CTRL_NAME],
+                            wait_timeout=1)
+        except Exception, e:
+            import taurus
+            taurus.warning("Your system may stay dirty due to an unexpected"
+                           " exception during the test.")
+            raise e
+
+
+class MeasTest(RunMacroTestCase, unittest.TestCase):
+    """Test case for measurement group related expert macros.
+    """
+    def test_meas(self):
+        MNTGRP_NAME = "unittestmntgrp01"
+        try:
+            self.macro_runs(macro_name="defmeas",
+                            macro_params=[MNTGRP_NAME, CT_NAME1, CT_NAME2])
+            self.macro_runs(macro_name="udefmeas",
+                            macro_params=[MNTGRP_NAME])
+        except Exception, e:
+            import taurus
+            taurus.warning("Your system may stay dirty due to an unexpected"
+                           " exception during the test.")
+            raise e
+
+#TODO: improve this test: not all sardana controller implement SendToCtrl
+# @testRun(macro_params=[CTRL_NAME1, "blabla"], wait_timeout=1)
+# class Send2ctrlTest(RunMacroTestCase, unittest.TestCase):
+#     """Test case for send2ctrl macro.
+#     """
+#     macro_name = "send2ctrl"
+
+
+# This is a known failure until bug-472 is fixed:
+# https://sourceforge.net/p/sardana/tickets/472/
+# @testRun(macro_params=["DummyMotorController"])
+# class EdctrlTest(RunMacroTestCase, unittest.TestCase):
+#     """Test case for edctrl macro.
+#     """
+#     macro_name = "edctrl"
+
+
+ at testRun(macro_name="prdef", macro_params=["wa"], wait_timeout=1)
+ at testRun(macro_name="relmaclib", macro_params=["standard"], wait_timeout=1)
+ at testRun(macro_name="relmac", macro_params=["wa"], wait_timeout=1)
+class MacroTest(RunMacroTestCase, unittest.TestCase):
+    """Test case for macro related expert macros.
+    """
+
+
+ at testRun(macro_params=["wa"], wait_timeout=1)
+ at testRun(macro_params=[CTRL_NAME1], wait_timeout=1)
+ at testRun(macro_params=[CT_NAME1], wait_timeout=1)
+class SarinfoTest(RunMacroTestCase, unittest.TestCase):
+    """Test case for send2ctrl macro.
+    """
+    macro_name = "sar_info"
diff --git a/src/sardana/macroserver/macros/test/__init__.py b/src/sardana/macroserver/macros/test/test_ioregister.py
similarity index 68%
copy from src/sardana/macroserver/macros/test/__init__.py
copy to src/sardana/macroserver/macros/test/test_ioregister.py
index 701d235..6a738f9 100644
--- a/src/sardana/macroserver/macros/test/__init__.py
+++ b/src/sardana/macroserver/macros/test/test_ioregister.py
@@ -23,7 +23,17 @@
 ##
 ##############################################################################
 
-from macroexecutor import BaseMacroExecutor, MacroExecutorFactory
-from base import (macroTest, BaseMacroTestCase, RunMacroTestCase,
-                  RunStopMacroTestCase, testRun, testFail, testStop)
-from sardemoenv import SarDemoEnv
+"""Tests for ioregister macros"""
+
+from taurus.external import unittest
+from sardana.macroserver.macros.test import RunMacroTestCase, testRun, getIORs
+
+IOR_NAME = getIORs()[0]
+
+ at testRun(macro_name="write_ioreg", macro_params=[IOR_NAME, "1"],
+         wait_timeout=1)
+ at testRun(macro_name="read_ioreg", macro_params=[IOR_NAME], wait_timeout=1)
+class IORegisterTest(RunMacroTestCase, unittest.TestCase):
+    """Test case for ioregister macros
+    """
+    pass
\ No newline at end of file
diff --git a/src/sardana/macroserver/macros/test/test_list.py b/src/sardana/macroserver/macros/test/test_list.py
index cae0f7f..181b55a 100644
--- a/src/sardana/macroserver/macros/test/test_list.py
+++ b/src/sardana/macroserver/macros/test/test_list.py
@@ -162,3 +162,52 @@ class Ls2dTest(LsTest, unittest.TestCase):
     """
     macro_name = "ls2d"
     elem_type = "twodexpchannel"
+
+
+ at testRun
+ at testRun(macro_params=["w.*"])
+class LsdefTest(RunMacroTestCase, unittest.TestCase):
+
+    macro_name = "lsdef"
+
+
+ at testRun
+ at testRun(macro_params=["Dummy.*"])
+class LsctrllibTest(RunMacroTestCase, unittest.TestCase):
+
+    macro_name = "lsctrllib"
+
+
+ at testRun
+ at testRun(macro_params=["mot.*"])
+class LsiTest(RunMacroTestCase, unittest.TestCase):
+
+    macro_name = "lsi"
+
+
+ at testRun
+ at testRun(macro_params=["mot.*"])
+class LsaTest(RunMacroTestCase, unittest.TestCase):
+
+    macro_name = "lsa"
+
+
+ at testRun
+ at testRun(macro_params=["mot.*"])
+class LsmeasTest(RunMacroTestCase, unittest.TestCase):
+
+    macro_name = "lsmeas"
+
+
+ at testRun
+ at testRun(macro_params=["w.*"])
+class LsmacTest(RunMacroTestCase, unittest.TestCase):
+
+    macro_name = "lsmac"
+
+
+ at testRun
+ at testRun(macro_params=["s.*"])
+class LsmaclibTest(RunMacroTestCase, unittest.TestCase):
+
+    macro_name = "lsmaclib"
diff --git a/src/sardana/macroserver/macros/test/test_scan.py b/src/sardana/macroserver/macros/test/test_scan.py
index 03ee1c0..e92802f 100644
--- a/src/sardana/macroserver/macros/test/test_scan.py
+++ b/src/sardana/macroserver/macros/test/test_scan.py
@@ -27,27 +27,10 @@
 
 from taurus.external import unittest
 from sardana.macroserver.macros.test import (RunStopMacroTestCase,
-                                             testRun, testStop, SarDemoEnv)
+                                             testRun, testStop, getMotors)
 
 #get handy motor names from sardemo
-try:
-    _MOTORS = SarDemoEnv().getMotors()
-    _m1, _m2 = _MOTORS[:2]
-except RuntimeError:
-    import taurus
-    from sardana import sardanacustomsettings
-    door_name = getattr(sardanacustomsettings, 'UNITTEST_DOOR_NAME',
-                        'UNDEFINED')
-    taurus.warning("The door %s is not running. " % (door_name) +
-                   "Ignore this message if you are building the documentation")
-    _m1 = _m2 = 'motor_not_defined'
-except Exception, e:
-    import taurus
-    taurus.debug(e)
-    taurus.warning("It was not possible to retrieve the motor names. " +
-                 "Ignore this message if you are building the documentation.")
-    _m1 = _m2 = 'motor_not_defined'
-
+_m1, _m2 = getMotors()[:2]
 
 def parsing_log_output(log_output):
     """A helper method to parse log output of an executed scan macro.
diff --git a/src/sardana/macroserver/macros/test/test_standard.py b/src/sardana/macroserver/macros/test/test_standard.py
new file mode 100644
index 0000000..f9143d1
--- /dev/null
+++ b/src/sardana/macroserver/macros/test/test_standard.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+
+##############################################################################
+##
+## 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/>.
+##
+##############################################################################
+
+"""Tests for standard macros"""
+
+from taurus.external import unittest
+from sardana.macroserver.macros.test import RunMacroTestCase, testRun,\
+    getMotors
+
+MOT_NAME1, MOT_NAME2 = getMotors()[:2]
+
+#TODO: these tests randomly causes segfaults. fix it!
+# @testRun(macro_name="wu", wait_timeout=1)
+# @testRun(macro_name="wa", wait_timeout=1)
+# @testRun(macro_name="wa", macro_params=["mot.*"], wait_timeout=1)
+# @testRun(macro_name="pwa", wait_timeout=1)
+# @testRun(macro_name="wm", macro_params=[MOT_NAME1], wait_timeout=1)
+# @testRun(macro_name="wm", macro_params=[MOT_NAME1, MOT_NAME2], wait_timeout=1)
+# @testRun(macro_name="wum", macro_params=[MOT_NAME1], wait_timeout=1)
+# @testRun(macro_name="wum", macro_params=[MOT_NAME1, MOT_NAME2], wait_timeout=1)
+# @testRun(macro_name="pwm", macro_params=[MOT_NAME1], wait_timeout=1)
+# @testRun(macro_name="pwm", macro_params=[MOT_NAME1, MOT_NAME2], wait_timeout=1)
+# class WhereTest(RunMacroTestCase, unittest.TestCase):
+#     """Test case for where macros
+#     """
+#     pass
+
+ at testRun(macro_name="set_lim", macro_params=[MOT_NAME1, "-100", "100"],
+         wait_timeout=1)
+ at testRun(macro_name="set_lm", macro_params=[MOT_NAME1, "-1000", "1000"],
+         wait_timeout=1)
+class LimTest(RunMacroTestCase, unittest.TestCase):
+    """Test case for limit macros
+    """
+    pass
+
+
+ at testRun(macro_name="set_pos", macro_params=[MOT_NAME1, "0"],
+         wait_timeout=1)
+ at testRun(macro_name="set_user_pos", macro_params=[MOT_NAME1, "0"],
+         wait_timeout=1)
+class PosTest(RunMacroTestCase, unittest.TestCase):
+    """Test case for position macros
+    """
+    pass
+
+
+class MoveTest(RunMacroTestCase, unittest.TestCase):
+    """Test case for position macros
+    """
+    def test_move(self):
+        self.macro_runs("set_user_pos", macro_params=[MOT_NAME1, "0"],
+                        wait_timeout=1)
+        self.macro_runs("mv", macro_params=[MOT_NAME1, "1"], wait_timeout=3)
+        self.macro_runs("umv", macro_params=[MOT_NAME1, "0"], wait_timeout=3)
+        self.macro_runs("mvr", macro_params=[MOT_NAME1, "1"], wait_timeout=3)
+        self.macro_runs("umvr", macro_params=[MOT_NAME1, "-1"], wait_timeout=3)
+
+
+ at testRun(macro_params=[MOT_NAME1])
+class MstateTest(RunMacroTestCase, unittest.TestCase):
+
+    macro_name = "mstate"
+
+
+ at testRun(macro_params=["blabla"])
+class ReportTest(RunMacroTestCase, unittest.TestCase):
+
+    macro_name = "report"
diff --git a/src/sardana/macroserver/macros/test/test_wm.py b/src/sardana/macroserver/macros/test/test_wm.py
index f6db360..5f2a898 100644
--- a/src/sardana/macroserver/macros/test/test_wm.py
+++ b/src/sardana/macroserver/macros/test/test_wm.py
@@ -27,26 +27,10 @@
 
 from taurus.external import unittest
 from sardana.macroserver.macros.test import (RunMacroTestCase, testRun,
-                                             SarDemoEnv)
-
-try:
-    _MOTORS = SarDemoEnv().getMotors()
-    _m1, _m2 = _MOTORS[:2]
-except RuntimeError:
-    import taurus
-    from sardana import sardanacustomsettings
-    door_name = getattr(sardanacustomsettings, 'UNITTEST_DOOR_NAME',
-                        'UNDEFINED')
-    taurus.warning("The door %s is not running. " % (door_name) +
-                   "Ignore this message if you are building the documentation.")
-    _m1 = _m2 = 'motor_not_defined'
-except Exception, e:
-    import taurus
-    taurus.debug(e)
-    taurus.warning("It was not possible to retrieve the motor names. " +
-                 "Ignore this message if you are building the documentation.")
-    _m1 = _m2 = 'motor_not_defined'
+                                             getMotors)
 
+#get handy motor names from sardemo
+_m1, _m2 = getMotors()[:2]
 
 class WBase(RunMacroTestCase):
 
diff --git a/src/sardana/macroserver/macroserver.py b/src/sardana/macroserver/macroserver.py
index 0fab6bb..afc839a 100644
--- a/src/sardana/macroserver/macroserver.py
+++ b/src/sardana/macroserver/macroserver.py
@@ -49,6 +49,7 @@ from sardana.macroserver.msbase import MSObject
 from sardana.macroserver.mscontainer import MSContainer
 from sardana.macroserver.msdoor import MSDoor
 from sardana.macroserver.msmacromanager import MacroManager
+from sardana.macroserver.msrecordermanager import RecorderManager
 from sardana.macroserver.mstypemanager import TypeManager
 from sardana.macroserver.msenvmanager import EnvironmentManager
 from sardana.macroserver.msparameter import ParamType
@@ -137,7 +138,7 @@ class MacroServer(MSContainer, MSObject, SardanaElementManager, SardanaIDManager
     logReportKlass = NonOverlappingTimedRotatingFileHandler
     
     def __init__(self, full_name, name=None, macro_path=None,
-                 environment_db=None):
+                 environment_db=None, recorder_path=None):
         # dict<str, Pool>
         # key   - device name (case insensitive)
         # value - Pool object representing the device name
@@ -155,6 +156,8 @@ class MacroServer(MSContainer, MSObject, SardanaElementManager, SardanaIDManager
         self._environment_manager = EnvironmentManager(self,
                                         environment_db=environment_db)
         self._macro_manager = MacroManager(self, macro_path=macro_path)
+        self._recorder_manager = RecorderManager(self,
+                                                 recorder_path=recorder_path)
 
     def serialize(self, *args, **kwargs):
         kwargs = MSObject.serialize(self, *args, **kwargs)
@@ -206,6 +209,20 @@ class MacroServer(MSContainer, MSObject, SardanaElementManager, SardanaIDManager
         self.macro_manager.setMacroPath(macro_path)
 
     # --------------------------------------------------------------------------
+    # Recorder path related methods
+    # --------------------------------------------------------------------------
+
+    def set_recorder_path(self, recorder_path):
+        """Sets the recorder path.
+
+        :param recorder_path:
+            recorder path
+        :type recorder_path:
+            seq<str>
+        """
+        self.recorder_manager.setRecorderPath(recorder_path)
+
+    # --------------------------------------------------------------------------
     # Report related methods
     # --------------------------------------------------------------------------
 
@@ -399,7 +416,11 @@ class MacroServer(MSContainer, MSObject, SardanaElementManager, SardanaIDManager
     @property
     def macro_manager(self):
         return self._macro_manager
-    
+
+    @property
+    def recorder_manager(self):
+        return self._recorder_manager
+
     @property
     def environment_manager(self):
         return self._environment_manager
@@ -426,7 +447,9 @@ class MacroServer(MSContainer, MSObject, SardanaElementManager, SardanaIDManager
         new_elements, changed_elements, deleted_elements = [], [], []
         
         new_lib = manager.reloadMacroLib(lib_name)
-        
+        if new_lib.has_errors():
+            return new_lib
+
         if old_lib is None:
             new_elements.extend(new_lib.get_macros())
             new_elements.append(new_lib)
@@ -437,7 +460,7 @@ class MacroServer(MSContainer, MSObject, SardanaElementManager, SardanaIDManager
             changed_names = set.intersection(new_names, old_names)
             deleted_names = old_names.difference(new_names)
             new_names = new_names.difference(old_names)
-            
+
             for new_name in new_names:
                 new_elements.append(new_lib.get_macro(new_name))
             for changed_name in changed_names:
@@ -459,7 +482,7 @@ class MacroServer(MSContainer, MSObject, SardanaElementManager, SardanaIDManager
     def reload_macro(self, macro_name):
         macro_info = self.macro_manager.getMacro(macro_name)
         lib_name = macro_info.module_name
-        self.reload_macro_lib(lib_name)
+        return self.reload_macro_lib(lib_name)
     
     def reload_macros(self, macro_names):
         lib_names = set()
@@ -676,6 +699,8 @@ class MacroServer(MSContainer, MSObject, SardanaElementManager, SardanaIDManager
         
         :param key: the key for the environment to be unset"""
         ret = self.environment_manager.unsetEnv(key)
+        # list is unhashable - convert to a tuple
+        if isinstance(key, list): key = tuple(key)
         evt = { 'del' : { key : None } }
         self.fire_event(EventType("EnvironmentChanged"), evt)
         return ret
@@ -718,7 +743,7 @@ class MacroServer(MSContainer, MSObject, SardanaElementManager, SardanaIDManager
         for type_name in type_name_list:
             type_class_name = type_name
             if type_class_name.endswith('*'):
-               type_class_name = type_class_name[:-1]
+                type_class_name = type_class_name[:-1]
             type_inst = self.get_data_type(type_class_name)
             if not type_inst.hasCapability(ParamType.ItemList):
                 continue
diff --git a/src/sardana/macroserver/msexception.py b/src/sardana/macroserver/msexception.py
index 1eca210..66c6e5c 100644
--- a/src/sardana/macroserver/msexception.py
+++ b/src/sardana/macroserver/msexception.py
@@ -28,7 +28,7 @@ manager"""
 
 __all__ = ["MacroServerException", "MacroServerExceptionList", "MissingEnv",
            "UnknownEnv", "UnknownMacro", "UnknownMacroLibrary",
-           "MacroWrongParameterType", "LibraryError",
+           "UnknownRecorder", "MacroWrongParameterType", "LibraryError",
            "InterruptException", "StopException", "AbortException",
            "InputCancelled"]
 
@@ -61,6 +61,10 @@ class UnknownMacro(UnknownCode):
     pass
 
 
+class UnknownRecorder(UnknownCode):
+    pass
+
+
 class UnknownMacroLibrary(UnknownLibrary):
     pass
 
diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py
index af12d9f..cc99d9c 100644
--- a/src/sardana/macroserver/msmacromanager.py
+++ b/src/sardana/macroserver/msmacromanager.py
@@ -42,6 +42,7 @@ from lxml import etree
 
 from PyTango import DevFailed
 
+from taurus.external.ordereddict import OrderedDict
 from taurus.core.util.log import Logger
 from taurus.core.util.codecs import  CodecFactory
 
@@ -59,6 +60,10 @@ from sardana.macroserver.msexception import UnknownMacroLibrary, \
     LibraryError, UnknownMacro, MissingEnv, AbortException, StopException, \
     MacroServerException, UnknownEnv
 
+# These classes are imported from the "client" part of sardana, if finally
+# both the client and the server side needs them, place them in some common location 
+from sardana.taurus.core.tango.sardana.macro import createMacroNode
+
 _BASE_DIR = os.path.dirname(os.path.abspath(__file__))
 
 
@@ -120,6 +125,14 @@ def is_macro(macro, abs_file=None, logger=None):
         return False
     return True
 
+def recur_map(fun, data):
+    """Recursive map. Similar to map, but maintains the list objects structure
+    """
+    if hasattr(data, "__iter__"):
+        return [recur_map(fun, elem) for elem in data]
+    else:
+        return fun(data)
+
 
 class MacroManager(MacroServerManager):
 
@@ -216,7 +229,7 @@ class MacroManager(MacroServerManager):
 
     def _findMacroLibNames(self, path=None):
         path = path or self.getMacroPath()
-        ret = {}
+        ret = OrderedDict()
         for p in reversed(path):
             try:
                 for f in os.listdir(p):
@@ -440,14 +453,20 @@ class MacroManager(MacroServerManager):
             path = copy.copy(path)
             path.reverse()
 
+        mod_manager = ModuleManager()
+        m, exc_info = None, None
+        valid, exc_info = mod_manager.isValidModule(module_name, path)
+        if not valid:
+            params = dict(module=m, name=module_name,
+                          macro_server=self.macro_server, exc_info=exc_info)
+            return MacroLibrary(**params)
+
         # if there was previous Macro Library info remove it
         old_macro_lib = self._modules.pop(module_name, None)
         if old_macro_lib is not None:
             for macro in old_macro_lib.get_macros():
                 self._macro_dict.pop(macro.name)
 
-        mod_manager = ModuleManager()
-        m, exc_info = None, None
         try:
             m = mod_manager.reloadModule(module_name, path)
         except:
@@ -631,14 +650,32 @@ class MacroManager(MacroServerManager):
             macro_meta = self.getMacro(macro_name)
             ret.append(json_codec.encode(('', macro_meta.serialize()))[1])
         return ret
-
-    def decodeMacroParameters(self, door, in_par_list):
-        if len(in_par_list) == 0:
-            raise RuntimeError('Macro name not specified')
-        macro_name = in_par_list[0]
+    
+    def _createMacroNode(self, macro_name, macro_params):
+        macro = self.getMacro(macro_name)
+        params_def = macro.get_parameter()
+        return createMacroNode(macro_name, params_def, macro_params)
+
+    def decodeMacroParameters(self, door, raw_params):
+        """Decode macro parameters
+
+        :param door: (sardana.macroserver.msdoor.MSDoor) door object
+        :param raw_params: (lxml.etree._Element or list) xml element representing
+                          macro with subelements representing parameters or list
+                          with macro name followed by parameter values
+        """
+        if isinstance(raw_params, etree._Element):
+            macro_name = raw_params.get("name")
+        elif isinstance(raw_params, list):
+            # leave only macro parameters in the list
+            macro_name = raw_params.pop(0)
+        else:
+            raise Exception("Wrong format of raw_params object")
         macro_meta = self.getMacro(macro_name)
-        out_par_list = ParamDecoder(door, macro_meta, in_par_list)
-        return macro_meta, in_par_list, out_par_list
+        params_def = macro_meta.get_parameter()
+        type_manager = door.type_manager
+        out_par_list = ParamDecoder(type_manager, params_def, raw_params)
+        return macro_meta, raw_params, out_par_list
 
     def strMacroParamValues(self, par_list):
         """strMacroParamValues(list<string> par_list) -> list<string>
@@ -784,13 +821,18 @@ class MacroExecutor(Logger):
     def getNewMacroID(self):
         self._macro_counter -= 1
         return self._macro_counter
+    
+    def _createMacroNode(self, macro_name, macro_params):
+        return self.macro_manager._createMacroNode(macro_name, macro_params)
 
     def _preprocessParameters(self, par_str_list):
         if not par_str_list[0].lstrip().startswith('<'):
             xml_root = xml_seq = etree.Element('sequence')
-            xml_macro = etree.SubElement(xml_seq, 'macro', name=par_str_list[0])
-            for p in par_str_list[1:]:
-                etree.SubElement(xml_macro, 'param', value=p)
+            macro_name = par_str_list[0]
+            macro_params = par_str_list[1:]
+            macro_node = self._createMacroNode(macro_name, macro_params)
+            xml_macro = macro_node.toXml()
+            xml_seq.append(xml_macro)
         else:
             xml_root = etree.fromstring(par_str_list[0])
 
@@ -815,25 +857,6 @@ class MacroExecutor(Logger):
             if eid is None:
                 eid = str(self.getNewMacroID())
                 macro.set('id', eid)
-            name = macro.get('name')
-            params = []
-            
-            # SEEMS THERE IS A MEMORY LEAK IN lxml.etree Element.xpath :
-            # https://bugs.launchpad.net/lxml/+bug/397933
-            # https://mailman-mail5.webfaction.com/pipermail/lxml/2011-October/006205.html
-            # We work around it using findall:
-            
-            #for p in macro.xpath('param|paramrepeat'):
-            #    if p.tag == 'param':
-            #        params.append(p.get('value'))
-            #    else:
-            #        params.extend([ p2.get('value') for p2 in p.findall(".//param")])
-            for p in macro.findall('*'):
-                if p.tag == 'param':
-                    params.append(p.get('value'))
-                elif p.tag == 'paramrepeat':
-                    params.extend([ p2.get('value') for p2 in p.findall(".//param")])
-            macro.set('macro_line', "%s(%s)" % (name, ", ".join(params)))
 
     def __preprocessResult(self, result):
         """decodes the given output from a macro in order to be able to send to
@@ -851,24 +874,32 @@ class MacroExecutor(Logger):
             result = (str(result),)
         return result
 
-    def _decodeXMLMacroParameters(self, xml_macro):
-        str_params = [xml_macro.get('name')]
-        for param in xml_macro.findall('.//param'):
-            str_params.append(param.get('value'))
-        return self._decodeMacroParameters(str_params)
-
     def _decodeMacroParameters(self, params):
         return self.macro_manager.decodeMacroParameters(self.door, params)
+    
+    def _composeMacroLine(self, macro_name, macro_params, macro_id):
+        # recursive map to maintain the list objects structure
+        params_str_list = recur_map(str, macro_params)
+        # plain map to be able to perform join (only strings may be joined)
+        params_str_list = map(str, params_str_list)
+        params_str = ', '.join(params_str_list)
+        macro_id = macro_id
+        # create macro_line - string representation of macro, its parameters and id
+        macro_line = "%s(%s) -> %s" % (macro_name, params_str, macro_id)
+        return macro_line
 
     def _prepareXMLMacro(self, xml_macro, parent_macro=None):
-        macro_meta, _, params = self._decodeXMLMacroParameters(xml_macro)
+        macro_meta, _, macro_params = self._decodeMacroParameters(xml_macro)
+        macro_name = macro_meta.name
+        macro_id = xml_macro.get("id")
+        macro_line = self._composeMacroLine(macro_name, macro_params, macro_id)
         init_opts = {
-            'id'           : xml_macro.get('id'),
-            'macro_line'   : xml_macro.get('macro_line'),
+            'id'           : macro_id,
+            'macro_line'   : macro_line,
             'parent_macro' : parent_macro,
         }
 
-        macro_obj = self._createMacroObj(macro_meta, params, init_opts)
+        macro_obj = self._createMacroObj(macro_meta, macro_params, init_opts)
         for macro in xml_macro.findall('macro'):
             hook = MacroExecutor.RunSubXMLHook(self, macro)
             hook_hints = macro.findall('hookPlace')
@@ -877,7 +908,7 @@ class MacroExecutor(Logger):
             else:
                 hook_places = [ h.text for h in hook_hints ]
                 macro_obj.hooks = [ (hook, hook_places) ]
-        prepare_result = self._prepareMacroObj(macro_obj, params)
+        prepare_result = self._prepareMacroObj(macro_obj, macro_params)
         return macro_obj, prepare_result
 
     def _createMacroObj(self, macro_name_or_meta, pars, init_opts={}):
@@ -922,16 +953,25 @@ class MacroExecutor(Logger):
            Several different parameter formats are supported:
            1. several parameters:
              1.1 executor.prepareMacro('ascan', 'th', '0', '100', '10', '1.0')
+                 executor.prepareMacro('mv', [['th', '0']])
              1.2 executor.prepareMacro('ascan', 'th', 0, 100, 10, 1.0)
+                 executor.prepareMacro('mv', [['th', 0]])
              1.3 th = self.getObj('th');
                  executor.prepareMacro('ascan', th, 0, 100, 10, 1.0)
+                 executor.prepareMacro('mv', [[th, 0]])
            2. a sequence of parameters:
               2.1 executor.prepareMacro(['ascan', 'th', '0', '100', '10', '1.0')
+                  executor.prepareMacro(['mv', [['th', '0']]])
               2.2 executor.prepareMacro(('ascan', 'th', 0, 100, 10, 1.0))
+                  executor.prepareMacro(['mv', [['th', 0]]])
               2.3 th = self.getObj('th');
                   executor.prepareMacro(['ascan', th, 0, 100, 10, 1.0])
-           3. a space separated string of parameters:
+                  executor.prepareMacro(['mv', [[th, 0]]])
+           3. a space separated string of parameters (this is not compatible
+              with multiple or nested repeat parameters, furthermore the repeat
+              parameter must be the last one):
               executor.prepareMacro('ascan th 0 100 10 1.0')
+              executor.prepareMacro('mv %s 0' % motor.getName())
 
         :param pars: the command parameters as explained above
         :param opts: keyword optional parameters for prepare
@@ -940,17 +980,33 @@ class MacroExecutor(Logger):
         par0 = pars[0]
         if len(pars) == 1:
             if is_pure_str(par0):
-                pars = par0.split(' ')
+                # dealing with sth like args = ('ascan th 0 100 10 1.0',)
+                pars = par0.split()
+                macro_name, macro_params = pars[0], pars[1:]
+                macro_node = self._createMacroNode(macro_name, macro_params)
+                pars = macro_node.toList()
             elif is_non_str_seq(par0):
+                # dealing with sth like args = (['ascan', 'th', '0', '100', '10', '1.0'],)
+                # or args = (['mv', [[mot01, 0], [mot02, 0]]])
                 pars = par0
-        pars = map(str, pars)
+        # dealing with sth like args = ('ascan', 'th', '0', '100', '10', '1.0')
+        # or args = ('mv', [[mot01, 0], [mot02, 0]])
+
+        # in case parameters were passed as objects cast them to strings
+        pars = recur_map(str, pars)
 
-        macro_klass, str_pars, pars = self._decodeMacroParameters(pars)
+        meta_macro, _, macro_params = self._decodeMacroParameters(pars)
+        macro_name = meta_macro.name
+        macro_id = init_opts.get("id")
+        if macro_id is None:
+            macro_id = str(self.getNewMacroID())
+            init_opts["id"] = macro_id
+        macro_line = self._composeMacroLine(macro_name, macro_params, macro_id)
 
-        init_opts['macro_line'] = "%s(%s) -> [%s]" % (str_pars[0], ", ".join(str_pars[1:]), id)
-        if not init_opts.has_key('id'):
-            init_opts['id'] = str(self.getNewMacroID())
-        return self.prepareMacroObj(macro_klass, pars, init_opts, prepare_opts)
+        init_opts['macro_line'] = macro_line
+
+        return self.prepareMacroObj(meta_macro, macro_params, init_opts, 
+                                    prepare_opts)
 
     def getRunningMacro(self):
         return self._macro_pointer
@@ -1074,10 +1130,10 @@ class MacroExecutor(Logger):
         try:
             self.__runStatelessXML()
             self.sendState(Macro.Finished)
-        except AbortException:
+        except (StopException, AbortException):
             self.sendState(Macro.Abort)
         except Exception:
-            self.sendState(Macro.Abort)
+            self.sendState(Macro.Exception)
         finally:
             self._macro_stack = None
             self._xml_stack = None
diff --git a/src/sardana/macroserver/msmetarecorder.py b/src/sardana/macroserver/msmetarecorder.py
new file mode 100644
index 0000000..577748b
--- /dev/null
+++ b/src/sardana/macroserver/msmetarecorder.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+
+##############################################################################
+##
+## 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 the class definition for the MacroServer meta recorder
+information"""
+
+__all__ = ["RECORDER_TEMPLATE", "RecorderLibrary", "RecorderClass"]
+
+__docformat__ = 'restructuredtext'
+
+from sardana import InvalidId, ElementType
+from sardana.sardanameta import SardanaLibrary, SardanaClass
+
+#: String containing template code for a controller class
+RECORDER_TEMPLATE = """class @recorder_name@(BaseFileRecorder):
+    \"\"\"@recorder_name@ description.\"\"\"
+
+"""
+
+
+class RecorderLibrary(SardanaLibrary):
+    """Object representing a python module containing recorder classes and/or
+    recorder 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)
+        - recorder_list - list<RecorderClass>
+        - exc_info - exception information if an error occurred when loading
+                    the module"""
+
+    def __init__(self, **kwargs):
+        kwargs['manager'] = kwargs.pop('macro_server')
+        kwargs['elem_type'] = ElementType.RecorderLibrary
+        SardanaLibrary.__init__(self, **kwargs)
+
+    def serialize(self, *args, **kwargs):
+        kwargs = SardanaLibrary.serialize(self, *args, **kwargs)
+        kwargs['macro_server'] = self.get_manager().name
+        kwargs['id'] = InvalidId
+        return kwargs
+
+    add_recorder = SardanaLibrary.add_meta_class
+    get_recorder = SardanaLibrary.get_meta_class
+    get_recorders = SardanaLibrary.get_meta_classes
+    has_recorder = SardanaLibrary.has_meta_class
+
+    @property
+    def recorders(self):
+        return self.meta_classes
+
+
+class RecorderClass(SardanaClass):
+    """Object representing a python recorder class.
+       Public members:
+
+           - name - class name
+           - klass - python class object
+           - lib - RecorderLibrary object representing the module where the
+             recorder is."""
+
+    def __init__(self, **kwargs):
+        kwargs['manager'] = kwargs.pop('macro_server')
+        kwargs['elem_type'] = ElementType.RecorderClass
+        SardanaClass.__init__(self, **kwargs)
+
+    def serialize(self, *args, **kwargs):
+        kwargs = SardanaClass.serialize(self, *args, **kwargs)
+        kwargs['id'] = InvalidId
+        kwargs['hints'] = self.code_object.hints
+        kwargs['macro_server'] = self.get_manager().name
+        return kwargs
+
+    @property
+    def recorder_class(self):
+        return self.klass
diff --git a/src/sardana/macroserver/msparameter.py b/src/sardana/macroserver/msparameter.py
index 0d59596..a888582 100644
--- a/src/sardana/macroserver/msparameter.py
+++ b/src/sardana/macroserver/msparameter.py
@@ -33,6 +33,8 @@ __all__ = ["WrongParam", "MissingParam", "UnknownParamObj", "WrongParamType",
 
 __docformat__ = 'restructuredtext'
 
+from lxml import etree
+
 from taurus.core.util.containers import CaselessDict
 
 from sardana import ElementType, INTERFACES_EXPANDED
@@ -299,105 +301,130 @@ AbstractParamTypes = ParamType, ElementParamType, ElementParamInterface, AttrPar
 
 class ParamDecoder:
 
-    def __init__(self, door, macro_meta, param_str_list):
-        self.door = door
-        self.macro_meta = macro_meta
-        self.param_str_list = param_str_list
-        self.param_list = None
+    def __init__(self, type_manager, params_def, raw_params):
+        """Create ParamDecorder object and decode macro parameters
+
+        :param type_manager: (sardana.macroserver.mstypemanager.TypeManager)
+            type manager object
+        :param params_def: list<list> macro parameter definition
+        :param raw_params: (lxml.etree._Element or list) xml element
+            representing macro with subelements representing parameters or list
+            with parameter values
+        """
+        self.type_manager = type_manager
+        self.params_def = params_def
+        self.raw_params = raw_params
+        self.params = None
         self.decode()
 
-    @property
-    def type_manager(self):
-        return self.door.type_manager
-
     def decode(self):
-        macro_meta = self.macro_meta
-        macro_type = macro_meta.get_type()
-        pars_str = self.param_str_list[1:]
-        pars_def = macro_meta.get_parameter()
-        if macro_type == ElementType.MacroClass:
-            _, self.param_list = self.decodeNormal(pars_str, pars_def)
-        elif macro_type == ElementType.MacroFunction:
-            if macro_meta.function.param_def is None:
-                self.param_list = self.param_str_list[1:]
-            else:
-                _, self.param_list = self.decodeNormal(pars_str, pars_def)
-        return self.param_list
-
-    def decodeNormal(self, str_list, def_list):
-        str_len = len(str_list)
-        obj_list = []
-        str_idx = 0
-        for i, par_def in enumerate(def_list):
-            name = par_def['name']
-            type_class = par_def['type']
-            def_val = par_def['default_value']
-            if str_idx == str_len:
-                if def_val is None:
-                    if not isinstance(type_class, list):
-                        raise MissingParam, "'%s' not specified" % name
-                    elif isinstance(type_class, list):
-                        min_rep = par_def['min']
-                        if min_rep > 0:
-                            msg = "'%s' demands at least %d values" %\
-                                  (name, min_rep)
-                            raise WrongParam, msg
-                new_obj_list = []
-                if not def_val is None:
-                    new_obj_list.append(def_val)
-            else:
-                if isinstance(type_class, list):
-                    data = self.decodeRepeat(str_list[str_idx:], par_def)
-                    dec_token, new_obj_list = data
+        """Decode raw representation of parameters to parameters as passed
+        to the prepare or run methods.
+        """
+        raw_params = self.raw_params
+        params_def = self.params_def
+        # ignore other tags than "param" and "paramRepeat"
+        # e.g. sequencer may create tags like "hookPlace"
+        if isinstance(raw_params, etree._Element):
+            for raw_param in raw_params:
+                if not raw_param.tag in ("param", "paramrepeat"):
+                    raw_params.remove(raw_param)
+
+        params = []
+        # iterate over definition since missing values may just mean using
+        # the default values
+        for i, param_def in enumerate(params_def):
+            try:
+                raw_param = raw_params[i]
+            except IndexError:
+                raw_param = None
+            obj = self.decodeNormal(raw_param, param_def)
+            params.append(obj)
+        self.params = params
+        return self.params
+
+    def decodeNormal(self, raw_param, param_def):
+        """Decode and validate parameter
+
+        :param raw_param: (lxml.etree._Element or list) xml element
+            representing parameter
+        :param param_def: (dict) parameter definition
+
+        :return: (list): list with decoded parameter repetitions
+        """
+        param_type = param_def["type"]
+        name = param_def["name"]
+        if isinstance(param_type, list):
+            param = self.decodeRepeat(raw_param, param_def)
+        else:
+            type_manager = self.type_manager
+            param_type = type_manager.getTypeObj(param_type)
+            try:
+                if isinstance(raw_param, etree._Element):
+                    value = raw_param.get("value")
                 else:
-                    type_manager = self.type_manager
-                    type_name = type_class
-                    type_class = type_manager.getTypeClass(type_name)
-                    par_type = type_manager.getTypeObj(type_name)
-                    par_str = str_list[str_idx]
-                    try:
-                        val = par_type.getObj(par_str)
-                    except ValueError, e:
-                        raise WrongParamType, e.message
-                    except UnknownParamObj, e:
-                        raise WrongParam, e.message
-                    if val is None:
-                        msg = 'Could not create %s parameter "%s" for "%s"' % \
-                              (par_type.getName(), name, par_str)
-                        raise WrongParam, msg
-                    dec_token = 1
-                    new_obj_list = [val]
-                str_idx += dec_token
-            obj_list += new_obj_list
-        return str_idx, obj_list
-
-    def decodeRepeat(self, str_list, par_def):
-        name = par_def['name']
-        param_def = par_def['type']
-        min_rep = par_def['min']
-        max_rep = par_def['max']
-
-        dec_token = 0
-        obj_list = []
-        rep_nr = 0
-        while dec_token < len(str_list):
-            if max_rep is not None and rep_nr == max_rep:
-                break
-            new_token, new_obj_list = self.decodeNormal(str_list[dec_token:],
-                                                        param_def)
-            dec_token += new_token
-            if len(new_obj_list) == 1:
-                new_obj_list = new_obj_list[0]
-            obj_list.append(new_obj_list)
-            rep_nr += 1
-        if rep_nr < min_rep:
+                    value = raw_param
+                if value is None:
+                    value = param_def['default_value']
+                if value is None:
+                    raise MissingParam, "'%s' not specified" % name
+                param = param_type.getObj(value)
+            except ValueError, e:
+                raise WrongParamType, e.message
+            except UnknownParamObj, e:
+                raise WrongParam, e.message
+            if param is None:
+                msg = 'Could not create %s parameter "%s" for "%s"' % \
+                      (param_type.getName(), name, raw_param)
+                raise WrongParam, msg
+        return param
+
+    def decodeRepeat(self, raw_param_repeat, param_repeat_def):
+        """Decode and validate repeat parameter
+
+        :param raw_param_repeat: (lxml.etree._Element or list) xml element
+            representing param repeat with subelements representing repetitions
+            or list representing repetitions
+        :param param_repeat_def: (dict) repeat parameter definition
+
+        :return: (list): list with decoded parameter repetitions
+        """
+        name = param_repeat_def['name']
+        param_type = param_repeat_def['type']
+        min_rep = param_repeat_def['min']
+        max_rep = param_repeat_def['max']
+        param_repeat = []
+        if raw_param_repeat is None:
+            raw_param_repeat = param_repeat_def['default_value']
+        if raw_param_repeat is None:
+            raw_param_repeat = []
+        len_rep = len(raw_param_repeat)
+        if min_rep and len_rep < min_rep:
             msg = 'Found %d repetitions of param %s, min is %d' % \
-                  (rep_nr, name, min_rep)
+                  (len_rep, name, min_rep)
+            raise RuntimeError, msg
+        if  max_rep and len_rep > max_rep:
+            msg = 'Found %d repetitions of param %s, max is %d' % \
+                  (len_rep, name, max_rep)
             raise RuntimeError, msg
-        return dec_token, obj_list
+        for raw_repeat in raw_param_repeat:
+            if len(param_type) > 1:
+                repeat = []
+                for i, member_raw in enumerate(raw_repeat):
+                    member_type = param_type[i]
+                    member = self.decodeNormal(member_raw, member_type)
+                    repeat.append(member)
+            else:
+                # if the repeat parameter is composed of just one member
+                # do not encapsulate it in list and pass directly the item
+                if isinstance(raw_repeat, etree._Element):
+                    raw_repeat = raw_repeat[0]
+                repeat = self.decodeNormal(raw_repeat, param_type[0])
+            param_repeat.append(repeat)
+        return param_repeat
 
     def getParamList(self):
-        return self.param_list
+        return self.params
 
     def __getattr__(self, name):
-        return getattr(self.param_list, name)
+        return getattr(self.params, name)
diff --git a/src/sardana/macroserver/msrecordermanager.py b/src/sardana/macroserver/msrecordermanager.py
new file mode 100644
index 0000000..b2ecb4c
--- /dev/null
+++ b/src/sardana/macroserver/msrecordermanager.py
@@ -0,0 +1,363 @@
+#!/usr/bin/env python
+
+##############################################################################
+##
+## 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 the class definition for the MacroServer macro
+manager"""
+
+__all__ = ["RecorderManager"]
+
+__docformat__ = 'restructuredtext'
+
+import os
+import sys
+import copy
+import inspect
+
+from taurus.external.ordereddict import OrderedDict
+
+from sardana import sardanacustomsettings
+from sardana.sardanaexception import format_exception_only_str
+from sardana.sardanamodulemanager import ModuleManager
+from sardana.macroserver.msmanager import MacroServerManager
+from sardana.macroserver.scan.recorder import DataRecorder
+from sardana.macroserver.msmetarecorder import RecorderLibrary, \
+    RecorderClass
+from sardana.macroserver.msexception import UnknownRecorder, LibraryError
+
+_BASE_DIR = os.path.dirname(os.path.abspath(__file__))
+
+
+class RecorderManager(MacroServerManager):
+
+    DEFAULT_RECORDER_DIRECTORIES = os.path.join(_BASE_DIR, 'recorders'),
+
+    def __init__(self, macro_server, recorder_path=None):
+        MacroServerManager.__init__(self, macro_server)
+        if recorder_path is not None:
+            self.setRecorderPath(recorder_path)
+
+    def reInit(self):
+        if self.is_initialized():
+            return
+
+        # dict<str, RecorderLibrary>
+        # key   - module name (without path and without extension)
+        # value - RecorderLibrary object representing the module
+        self._modules = {}
+
+        #: dict<str, <metarecorder.RecorderClass>
+        #: key   - recorder name
+        #: value - RecorderClass object representing the recorder
+        self._recorder_dict = {}
+
+        # list<str>
+        # elements are absolute paths
+        self._recorder_path = []
+
+        # custom map (per installation) allowing to avoid
+        # recorder class ambiguity problems (using extension filter)
+        #: dict<str, str>
+        #: key   - scan file extension
+        #: value - recorder name
+        self._custom_scan_recorder_map = getattr(sardanacustomsettings,
+                                                 "SCAN_RECORDER_MAP",
+                                                 None)
+        #: dict<str, str>
+        #: key   - scan file extension
+        #: value - list with recorder name(s)
+        self._scan_recorder_map = {}
+
+        MacroServerManager.reInit(self)
+
+    def cleanUp(self):
+        if self.is_cleaned():
+            return
+
+        if self._modules:
+            for _, types_dict in self._modules.items():
+                for type_name in types_dict:
+                    Type.removeType(type_name)
+
+        self._recorder_path = None
+        self._modules = None
+        MacroServerManager.cleanUp(self)
+
+    def setScanRecorderMap(self, recorder_map):
+        """Registers a new map of recorders in this manager.
+        """
+        self._scan_recorder_map = dict(recorder_map)
+
+    def setRecorderPath(self, recorder_path):
+        """Registers a new list of recorder directories in this manager.
+        """
+        _recorder_path = []
+        for paths in recorder_path:
+            splited_paths = paths.split(":")
+            for path in splited_paths:
+                # filter empty and commented paths
+                if not path.startswith("#"):
+                    _recorder_path.append(path)
+
+        for recorder_dir in self.DEFAULT_RECORDER_DIRECTORIES:
+            if recorder_dir not in _recorder_path:
+                _recorder_path.append(recorder_dir)
+
+        self._recorder_path = _recorder_path
+
+        recorder_file_names = self._findRecorderLibNames(
+            _recorder_path)
+
+        for mod_name, file_name in recorder_file_names.iteritems():
+            dir_name = os.path.dirname(file_name)
+            path = [dir_name]
+            try:
+                self.reloadRecorderLib(mod_name, path)
+            except:
+                pass
+        pass
+
+    def getRecorderPath(self):
+        return self._recorder_path
+
+    def getRecorderMetaClass(self, recorder_name):
+        """ Return the Recorder class for the given class name.
+        :param klass_name: Name of the recorder class.
+        :type klass_name: str
+        :return:  a :obj:`class` class of recorder or None if it does not exist
+        :rtype:
+            :obj:`class:`~sardana.macroserver.msmetarecorder.RecorderClass`\>
+        """
+        ret = self._recorder_dict.get(recorder_name)
+        if ret is None:
+            raise UnknownRecorder("Unknown recorder %s" % recorder_name)
+        return ret
+
+    def getRecorderMetaClasses(self, filter=None, extension=None):
+        """ Returns a :obj:`dict` containing information about recorder classes.
+
+        :param filter: a klass of a valid type of Recorder
+        :type filter: obj
+        :param filter: a scan file extension
+        :type filter: str
+        :return: a :obj:`dict` containing information about recorder classes
+        :rtype:
+            :obj:`dict`\<:obj:`str`\,
+            :class:`~sardana.macroserver.msmetarecorder.RecorderClass`\>
+        """
+        if filter is None:
+            filter = DataRecorder
+        ret = {}
+        for name, klass in self._recorder_dict.items():
+            if issubclass(klass.recorder_class, filter):
+                if extension:
+                    if self._custom_scan_recorder_map:
+                        _map = self._custom_scan_recorder_map
+                        name = _map.get(extension, None)
+                        if name:
+                            klass = self.getRecorderMetaClass(name)
+                            ret[name] = klass
+                    else:
+                        _map = self._scan_recorder_map
+                        if (extension in _map.keys() and
+                            klass in _map[extension]):
+                            ret[name] = klass
+                else:
+                    ret[name] = klass
+        return ret
+
+    def getRecorderClasses(self, filter=None, extension=None):
+        """ Returns a :obj:`dict` containing information about recorder classes.
+        :param filter: a klass of a valid type of Recorder
+        :type filter: obj
+        :param filter: a scan file extension
+        :type filter: str
+        :return: a :obj:`dict` containing information about recorder classes
+        :rtype:
+            :obj:`dict`\<:obj:`str`\, :class:`DataRecorder`\>
+        """
+        if filter is None:
+            filter = DataRecorder
+        meta_klasses = self.getRecorderMetaClasses(filter=filter,
+                                                   extension=extension)
+        return dict((key, value.klass)
+                    for (key, value) in meta_klasses.items())
+
+    def getRecorderClass(self, klass_name):
+        """ Return the Recorder class for the given class name.
+        :param klass_name: Name of the recorder class.
+        :type klass_name: str
+        :return:  a :obj:`class` class of recorder or None if it does not exist
+        :rtype:
+            :obj:`class:`DataRecorder`\>
+        """
+        meta_klass = self.getRecorderMetaClass(klass_name)
+        return meta_klass.klass
+
+    def _findRecorderLibName(self, lib_name, path=None):
+        path = path or self.getRecorderPath()
+        f_name = lib_name
+        if not f_name.endswith('.py'):
+            f_name += '.py'
+        for p in path:
+            try:
+                elems = os.listdir(p)
+                if f_name in elems:
+                    return os.path.abspath(os.path.join(p, f_name))
+            except:
+                self.debug("'%s' is not a valid path" % p)
+        return None
+
+    def _findRecorderLibNames(self, recorder_path=None):
+        path = recorder_path or self.getRecorderPath()
+        ret = OrderedDict()
+        for p in reversed(path):
+            try:
+                for fdir in os.listdir(p):
+                    name, ext = os.path.splitext(fdir)
+                    if name.startswith("_"):
+                        continue
+                    if ext.endswith('.py'):
+                        full_path = os.path.abspath(os.path.join(p, fdir))
+                        ret[name] = full_path
+            except:
+                self.debug("'%s' is not a valid path" % p)
+        return ret
+
+    def reloadRecorderLib(self, module_name, path=None):
+        """Reloads the given library(=module) names.
+
+        :param module_name: recorder library name (=python module name)
+        :param path:
+            a list of absolute path to search for libraries [default: None,
+            means the current RecorderPath will be used]"""
+        path = path or self.getRecorderPath()
+        # reverse the path order:
+        # more priority elements last. This way if there are repeated elements
+        # they first ones (lower priority) will be overwritten by the last ones
+        if path:
+            path = copy.copy(path)
+            path.reverse()
+
+        # if there was previous Recorder Library info remove it
+        old_recorder_lib = self._modules.pop(module_name, None)
+        if old_recorder_lib is not None:
+            for recorder in old_recorder_lib.get_recorders():
+                self._recorder_dict.pop(recorder.name)
+                # remove recorders from the map
+                for _, recorders in self._scan_recorder_map.iteritems():
+                    try:
+                        recorders.remove(recorder)
+                    except:
+                        pass
+
+        mod_manager = ModuleManager()
+        m, exc_info = None, None
+        try:
+            m = mod_manager.reloadModule(
+                module_name, path, reload=reload)
+        except:
+            exc_info = sys.exc_info()
+
+        params = dict(module=m, name=module_name,
+                      macro_server=self.macro_server, exc_info=exc_info)
+        if m is None:
+            file_name = self._findRecorderLibName(module_name)
+            if file_name is None:
+                if exc_info:
+                    msg = format_exception_only_str(*exc_info[:2])
+                else:
+                    msg = "Error (re)loading recorder library '%s'" \
+                        % module_name
+                raise LibraryError(msg, exc_info=exc_info)
+            params['file_path'] = file_name
+            recorder_lib = RecorderLibrary(**params)
+            self._modules[module_name] = recorder_lib
+        else:
+            recorder_lib = RecorderLibrary(**params)
+            lib_contains_recorders = False
+            abs_file = recorder_lib.file_path
+            for _, klass in inspect.getmembers(m, inspect.isclass):
+                if issubclass(klass, DataRecorder):
+                    # if it is a class defined in some other class forget it to
+                    # avoid replicating the same recorder in different
+                    # recorder files
+                    # use normcase to treat case insensitivity of paths on
+                    # certain platforms e.g. Windows
+                    if os.path.normcase(inspect.getabsfile(klass)) !=\
+                       os.path.normcase(abs_file):
+                        continue
+                    lib_contains_recorders = True
+                    self.addRecorder(recorder_lib, klass)
+            if lib_contains_recorders:
+                self._modules[module_name] = recorder_lib
+
+        return recorder_lib
+
+    def addRecorder(self, recorder_lib, klass):
+        """Adds a new recorder class
+        """
+        recorder_name = klass.__name__
+        exists = recorder_lib.has_recorder(recorder_name)
+        if exists:
+            action = "Updating"
+        else:
+            action = "Adding"
+
+        self.debug("%s recorder %s" % (action, recorder_name))
+
+        try:
+            recorder_class = RecorderClass(lib=recorder_lib, klass=klass,
+                                           macro_server=self.macro_server)
+            recorder_lib.add_recorder(recorder_class)
+            self._recorder_dict[recorder_name] = recorder_class
+            if hasattr(klass, "formats"):
+                self._addRecorderToMap(recorder_class)
+        except:
+            self.warning("Failed to add recorder class %s", recorder_name,
+                         exc_info=1)
+
+        if exists:
+            action = "Updated"
+        else:
+            action = "Added"
+        self.debug("%s recorder %s" % (action, recorder_name))
+
+    def _addRecorderToMap(self, recorder_class):
+        klass = recorder_class.klass
+        for ext in klass.formats.values():
+            recorders = self._scan_recorder_map.get(ext, [])
+            if len(recorders) == 0:
+                recorders.append(recorder_class)
+            else:
+                recorder_from_map = recorders[-1]  # it could be any recorder
+                # recorders are on the same priority level (located in the same
+                # directory) - just append it to the list
+                if recorder_from_map.lib.path == recorder_class.lib.path:
+                    recorders.append(recorder_class)
+                # new recorder comes from another directory (it must be of
+                # higher priority) - forget about others and create new list
+                else:
+                    recorders = [recorder_class]
+            self._scan_recorder_map[ext] = recorders
diff --git a/src/sardana/macroserver/scan/recorder/__init__.py b/src/sardana/macroserver/recorders/__init__.py
similarity index 89%
copy from src/sardana/macroserver/scan/recorder/__init__.py
copy to src/sardana/macroserver/recorders/__init__.py
index 8f71c3f..0ce4033 100644
--- a/src/sardana/macroserver/scan/recorder/__init__.py
+++ b/src/sardana/macroserver/recorders/__init__.py
@@ -23,11 +23,10 @@
 ##
 ##############################################################################
 
-"""This is the macro server scan recorder module"""
+"""This package contains the standard macroserver recorders"""
 
 __docformat__ = 'restructuredtext'
 
-from .datarecorder import *
-from .output import *
 from .sharedmemory import *
-from .storage import *
\ No newline at end of file
+from .storage import *
+from .output import *
\ No newline at end of file
diff --git a/src/sardana/macroserver/scan/recorder/__init__.py b/src/sardana/macroserver/recorders/examples/__init__.py
similarity index 87%
copy from src/sardana/macroserver/scan/recorder/__init__.py
copy to src/sardana/macroserver/recorders/examples/__init__.py
index 8f71c3f..e4160c8 100644
--- a/src/sardana/macroserver/scan/recorder/__init__.py
+++ b/src/sardana/macroserver/recorders/examples/__init__.py
@@ -23,11 +23,9 @@
 ##
 ##############################################################################
 
-"""This is the macro server scan recorder module"""
+"""This package contains the macroserver example recorders"""
 
 __docformat__ = 'restructuredtext'
 
-from .datarecorder import *
-from .output import *
-from .sharedmemory import *
-from .storage import *
\ No newline at end of file
+from .dummy import *
+from .xas import *
diff --git a/src/sardana/test/testsuite.py b/src/sardana/macroserver/recorders/examples/dummy.py
similarity index 53%
copy from src/sardana/test/testsuite.py
copy to src/sardana/macroserver/recorders/examples/dummy.py
index 96d195b..686acfb 100644
--- a/src/sardana/test/testsuite.py
+++ b/src/sardana/macroserver/recorders/examples/dummy.py
@@ -23,40 +23,33 @@
 ##
 ##############################################################################
 
-"""
-This module defines the test suite for the whole Sardana package
-Usage::
+"""This are the macroserver dummy data recorders"""
 
-  from sardana.test import testsuite
-  testsuite.run()
-
-"""
+__all__ = ["DumbRecorder"]
 
 __docformat__ = 'restructuredtext'
 
-import sys, os
-from taurus.external import unittest
-import sardana
-
-
-def run():
-    '''Runs all tests for the taurus package
-
-    :returns: the test runner result
-    :rtype: unittest.result.TestResult
-    '''
-    # discover all tests within the sardana/src directory
-    loader = unittest.defaultTestLoader
-    suite = loader.discover(os.path.dirname(sardana.__file__))
-    # use the basic text test runner that outputs to sys.stderr
-    runner = unittest.TextTestRunner(descriptions=True, verbosity=2)
-    # run the test suite
-    result = runner.run(suite)
-    return result
-
-if __name__ == '__main__':
-    result = run()
-    exit_code = 0
-    if not result.wasSuccessful():
-        exit_code = 1
-    sys.exit(exit_code)
\ No newline at end of file
+import time
+
+from sardana.macroserver.scan.recorder import DataRecorder
+
+class DumbRecorder(DataRecorder):
+
+    def _startRecordList(self, recordlist):
+        print "Starting new recording"
+        print "# Title :     ", recordlist.getEnvironValue('title')
+        env = recordlist.getEnviron()
+        for envky in env.keys():
+            if envky != 'title' and envky != 'labels':
+                print "# %8s :    %s " % (envky, str(env[envky]))
+        print "# Started:    ", time.ctime(env['starttime'])
+        print "# L:  ",
+        print "  ".join(env['labels'])
+
+    def _writeRecord(self, record):
+        print record.data
+
+    def _endRecordList(self, recordlist):
+        print "Ending recording"
+        env = recordlist.getEnviron()
+        print "Recording ended at: ", time.ctime(env['endtime'])
\ No newline at end of file
diff --git a/src/sardana/macroserver/recorders/examples/xas.py b/src/sardana/macroserver/recorders/examples/xas.py
new file mode 100644
index 0000000..4cf664e
--- /dev/null
+++ b/src/sardana/macroserver/recorders/examples/xas.py
@@ -0,0 +1,283 @@
+#!/usr/bin/env python
+
+##############################################################################
+##
+## 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 is the macro server scan data output recorder module"""
+
+__all__ = ["NXxas_FileRecorder"]
+
+__docformat__ = 'restructuredtext'
+
+from sardana.macroserver.scan.recorder import BaseNEXUS_FileRecorder
+
+class NXxas_FileRecorder(BaseNEXUS_FileRecorder):
+    """saves data to a nexus file that follows the NXsas application definition
+    
+        """
+        
+    def __init__(self, filename=None, macro=None, overwrite=False, **pars):
+        BaseNEXUS_FileRecorder.__init__(self, filename=filename, macro=macro, overwrite=overwrite, **pars)
+        
+        
+    def _startRecordList(self, recordlist):
+        nxs = self.nxs
+        if self.filename is None:
+            return
+        
+        #get the recordlist environment
+        self.currentlist = recordlist
+        env = self.currentlist.getEnviron()
+        
+        #adapt the datadesc to the NeXus requirements
+        self.datadesc = []
+        for dd in env['datadesc']:
+            dd = dd.clone()
+            dd.label = self.sanitizeName(dd.label)
+            if dd.dtype == 'bool':
+                dd.dtype = 'int8'
+                self.debug('%s will be stored with type=%s',dd.name,dd.dtype)
+            if dd.dtype in self.supported_dtypes:
+                self.datadesc.append(dd)
+            else:
+                self.warning('%s will not be stored. Reason: type %s not supported',dd.name,dd.dtype)
+        
+        
+        serialno = env["serialno"]
+        nxfilemode = self.getFormat()
+        if not self.overwrite and os.path.exists(self.filename): nxfilemode='rw'               
+        
+        self.debug("starting new recording %d on file %s", serialno, self.filename)
+        
+        #create an nxentry and write it to file
+        self.nxentry = nxs.NXentry(name= "entry%d" % serialno)
+        self.nxentry.save(self.filename, format=nxfilemode)
+
+        #add fields to nxentry
+        import sardana.release
+        program_name = "%s (%s)" % (sardana.release.name, self.__class__.__name__)
+        self.nxentry.insert(nxs.NXfield(name='start_time', value=env['starttime'].isoformat()))
+        self.nxentry.insert(nxs.NXfield(name='title', value=env['title']))
+        self.nxentry.insert(nxs.NXfield(name='definition', value='NXxas'))
+        self.nxentry.insert(nxs.NXfield(name='epoch', value=time.mktime(env['starttime'].timetuple())))
+        self.nxentry.insert(nxs.NXfield(name='program_name', value=program_name, attrs={'version':sardana.release.version}))
+        self.nxentry.insert(nxs.NXfield(name='entry_identifier', value=env['serialno']))
+                
+        #add the "measurement" group (a NXcollection containing all counters from the mntgrp for convenience) 
+        measurement = nxs.NXcollection(name='measurement')
+        self.ddfieldsDict = {}
+        for dd in self.datadesc:
+            field = NXfield_comp(name=dd.label,
+                                 dtype=dd.dtype,
+                                 shape=[nxs.UNLIMITED] + list(dd.shape),
+                                 nxslab_dims=[1] + list(dd.shape)
+                                 )
+            if hasattr(dd,'data_units'):
+                field.attrs['units'] = dd.data_units
+            measurement.insert(field)
+            #create a dict of fields in the datadesc for easier access later on
+            self.ddfieldsDict[dd.label] = field
+        
+        self.nxentry.insert(measurement)
+        
+        #user group
+        nxuser = nxs.NXuser()
+        self.nxentry.insert(nxuser)
+        nxuser['name'] = env['user']
+
+        #sample group
+        nxsample = nxs.NXsample()
+        self.nxentry.insert(nxsample)
+        nxsample['name'] = env['SampleInfo'].get('name','Unknown')
+        
+        #monitor group
+        scan_acq_time = env.get('integ_time')
+        scan_monitor_mode = scan_acq_time>1 and 'timer' or 'monitor'
+        nxmonitor = nxs.NXmonitor(mode=scan_monitor_mode,
+                        preset=scan_acq_time)
+        self.nxentry.insert(nxmonitor)
+        monitor_data = self.ddfieldsDict[self.sanitizeName(env['monitor'])] #to be linked later on
+        
+        #instrument group
+        nxinstrument = nxs.NXinstrument()
+        self.nxentry.insert(nxinstrument)
+        
+        #monochromator  group
+        nxmonochromator = nxs.NXmonochromator()
+        nxinstrument.insert(nxmonochromator)
+        energy_data = self.ddfieldsDict[self.sanitizeName(env['monochromator'])] #to be linked later on
+        
+        #incoming_beam  group
+        nxincoming_beam = nxs.NXdetector(name='incoming_beam')
+        nxinstrument.insert(nxincoming_beam)
+        incbeam_data = self.ddfieldsDict[self.sanitizeName(env['incbeam'])] #to be linked later on
+        
+        #absorbed_beam  group
+        nxabsorbed_beam = nxs.NXdetector(name='absorbed_beam')
+        nxinstrument.insert(nxabsorbed_beam)
+        absbeam_data = self.ddfieldsDict[self.sanitizeName(env['absbeam'])] #to be linked later on
+        absbeam_data.attrs['signal'] = '1'
+        absbeam_data.attrs['axes'] = 'energy'
+        
+        #source group
+        nxsource = nxs.NXsource()
+        nxinstrument.insert(nxsource) 
+        nxinstrument['source']['name'] = env.get('SourceInfo',{}).get('name','Unknown')
+        nxinstrument['source']['type'] = env.get('SourceInfo',{}).get('type','Unknown')
+        nxinstrument['source']['probe'] = env.get('SourceInfo',{}).get('x-ray','Unknown')
+        
+        #data group
+        nxdata = nxs.NXdata()
+        self.nxentry.insert(nxdata)
+        
+        
+        #@todo create the PreScanSnapshot
+        #self._createPreScanSnapshot(env)   
+        
+        #write everything to file
+        self.nxentry.write() 
+        
+        #@todo: do this with the PyTree api instead(how to do named links with the PyTree API????)
+        self._nxln(monitor_data, nxmonitor, name='data')
+        self._nxln(incbeam_data, nxincoming_beam, name='data')
+        self._nxln(absbeam_data, nxabsorbed_beam, name='data')
+        self._nxln(energy_data, nxmonochromator, name='energy')
+        self._nxln(energy_data, nxdata, name='energy')
+        self._nxln(absbeam_data, nxdata, name='absorbed_beam')
+                
+        self.nxentry.nxfile.flush()
+        
+    
+    def _writeRecord(self, record):
+        # most used variables in the loop
+        fd, debug, warning = self.nxentry.nxfile, self.debug, self.warning
+        nparray, npshape = numpy.array, numpy.shape
+        rec_data, rec_nb = record.data, record.recordno
+                
+        for dd in self.datadesc:
+            if record.data.has_key( dd.name ):
+                data = rec_data[dd.name]
+                field = self.ddfieldsDict[dd.label]
+                
+                if data is None:
+                    data = numpy.zeros(dd.shape, dtype=dd.dtype)
+                if not hasattr(data, 'shape'):
+                    data = nparray([data], dtype=dd.dtype)
+                elif dd.dtype != data.dtype.name:
+                    debug('%s casted to %s (was %s)', dd.label, dd.dtype,
+                                                      data.dtype.name)
+                    data = data.astype(dd.dtype)
+
+                slab_offset = [rec_nb] + [0] * len(dd.shape)
+                shape = [1] + list(npshape(data))
+                try:
+                    field.put(data, slab_offset, shape)
+                    field.write()
+                except:
+                    warning("Could not write <%s> with shape %s", data, shape)
+                    raise
+            else:
+                debug("missing data for label '%s'", dd.label)
+        self.nxentry.nxfile.flush()
+
+
+    def _endRecordList(self, recordlist):
+        env=self.currentlist.getEnviron()
+        self.nxentry.insert(nxs.NXfield(name='end_time', value=env['endtime'].isoformat()))
+        #self._populateInstrumentInfo()
+        #self._createNXData()
+        self.nxentry.write()
+        self.nxentry.nxfile.flush()
+        self.debug("Finishing recording %d on file %s:", env['serialno'], self.filename)
+        return
+        
+
+
+#===============================================================================
+# BEGIN: THIS BLOCK SHOULD BE REMOVED IF NEXUS ACCEPTS THE PATCH TO NXfield
+#===============================================================================
+try:
+    from nxs import NXfield #needs Nexus v>=4.3
+    from nxs import napi, NeXusError
+    
+    class NXfield_comp(NXfield):
+        
+        #NOTE: THE CONSTRUCTOR IS OPTIONAL. IF NOT IMPLEMENTED, WE CAN STILL USE THE nxslab_dims PROPERTY
+        def __init__(self, value=None, name='field', dtype=None, shape=(), group=None,
+                     attrs={}, nxslab_dims=None, **attr):
+            NXfield.__init__(self, value=value, name=name, dtype=dtype, shape=shape, group=group,
+                     attrs=attrs, **attr)
+            self._slab_dims = nxslab_dims
+            
+        def write(self):
+            """
+            Write the NXfield, including attributes, to the NeXus file.
+            """
+            if self.nxfile:
+                if self.nxfile.mode == napi.ACC_READ:
+                    raise NeXusError("NeXus file is readonly")
+                if not self.infile:
+                    shape = self.shape
+                    if shape == (): shape = (1,)
+                    with self.nxgroup as path:
+                        if self.nxslab_dims is not None:
+                        #compress
+                            path.compmakedata(self.nxname, self.dtype, shape, 'lzw', 
+                                              self.nxslab_dims)
+                        else:
+                        # Don't use compression
+                            path.makedata(self.nxname, self.dtype, shape)
+                    self._infile = True
+                if not self.saved:            
+                    with self as path:
+                        path._writeattrs(self.attrs)
+                        value = self.nxdata
+                        if value is not None:
+                            path.putdata(value)
+                    self._saved = True
+            else:
+                raise IOError("Data is not attached to a file")
+        
+        def _getnxslabdims(self):
+            try:
+                return self._nxslab_dims
+            except:
+                slab_dims = None
+            #even if slab_dims have not been set, check if the dataset is large 
+            shape = self.shape or (1,)
+            if numpy.prod(shape) > 10000:
+                slab_dims = numpy.ones(len(shape),'i')
+                slab_dims[-1] = min(shape[-1], 100000)
+            return slab_dims
+        
+        def _setnxslabdims(self, slab_dims):
+            self._nxslab_dims = slab_dims
+        
+        nxslab_dims = property(_getnxslabdims,_setnxslabdims,doc="Slab (a.k.a. chunk) dimensions for compression")
+except:
+    pass #NXxas_FileRecorder won't be usable
+
+
+#==============================================================================
+# END: THE ABOVE BLOCK SHOULD BE REMOVED IF NEXUS ACCEPTS THE PATCH TO NXfield
+#==============================================================================
diff --git a/src/sardana/macroserver/scan/recorder/output.py b/src/sardana/macroserver/recorders/output.py
similarity index 96%
rename from src/sardana/macroserver/scan/recorder/output.py
rename to src/sardana/macroserver/recorders/output.py
index 4366725..a25b898 100644
--- a/src/sardana/macroserver/scan/recorder/output.py
+++ b/src/sardana/macroserver/recorders/output.py
@@ -153,8 +153,13 @@ class OutputRecorder(DataRecorder):
         dh = recordlist.getDataHandler()
 
         for fr in [r for r in dh.recorders if isinstance(r, BaseFileRecorder)]:
+            if not hasattr(self._stream, "getAllEnv") or \
+               "ScanRecorder" in self._stream.getAllEnv().keys():
+                message = "%s from %s" % (fr.getFormat(), fr.__class__.__name__)
+            else:
+                message = "%s" % (fr.getFormat())
             self._stream.info('Operation will be saved in %s (%s)',
-                              fr.getFileName(), fr.getFormat())
+                              fr.getFileName(), message)
 
         msg = "Scan #%d started at %s." % (serialno, starttime)
         if not estimatedtime is None:
diff --git a/src/sardana/macroserver/scan/recorder/sharedmemory.py b/src/sardana/macroserver/recorders/sharedmemory.py
similarity index 81%
copy from src/sardana/macroserver/scan/recorder/sharedmemory.py
copy to src/sardana/macroserver/recorders/sharedmemory.py
index a5f3b63..d913d95 100644
--- a/src/sardana/macroserver/scan/recorder/sharedmemory.py
+++ b/src/sardana/macroserver/recorders/sharedmemory.py
@@ -25,7 +25,7 @@
 
 """This is the macro server scan data output recorder module"""
 
-__all__ = ["SharedMemoryRecorder"]
+__all__ = ["SPSRecorder", "ShmRecorder"]
 
 __docformat__ = 'restructuredtext'
 
@@ -33,22 +33,11 @@ import time
 import numpy
 import operator
 
+from sardana.macroserver.scan.recorder import BaseSharedMemoryRecorder
 from sardana.macroserver.scan.recorder.datarecorder import DataRecorder
 
-SPS_AVAILABLE = False
-try:
-    import sps
-    SPS_AVAILABLE = True
-except:
-    pass
 
-class _SharedMemoryRecorder(DataRecorder):
-
-    def __init__(self, **pars):
-        DataRecorder.__init__(self, **pars)
-
-
-class SPSRecorder(_SharedMemoryRecorder):
+class SPSRecorder(BaseSharedMemoryRecorder):
 
     maxenv = 50
     envlen = 1024
@@ -59,7 +48,12 @@ class SPSRecorder(_SharedMemoryRecorder):
             @param[in] shape tuple (cols, rows)
             @param[in] pars keyword extra parameters
         """
-        _SharedMemoryRecorder.__init__(self, **kwpars)
+        BaseSharedMemoryRecorder.__init__(self, **kwpars)
+        try:
+            import sps  #check if sps format is supported by this system
+            self.sps = sps
+        except ImportError:
+            raise Exception("SPS is not available")
         self.shape = shape
         self.owner = False
         self.owner_ENV = False
@@ -89,31 +83,31 @@ class SPSRecorder(_SharedMemoryRecorder):
 
     def putEnv(self, name, value):
         if not self.isInitialized(): return
-        sps.putenv(self.program, self.array_ENV, name, str(value))
+        self.sps.putenv(self.program, self.array_ENV, name, str(value))
 
     def putAllEnv(self, d):
         if not self.isInitialized(): return
         p, a = self.program, self.array_ENV
         for k, v in d.iteritems():
-            sps.putenv(p, a, k, str(v))
+            self.sps.putenv(p, a, k, str(v))
 
     def _startRecordList(self, recordlist):
         if not self.isInitialized(): return
 
-        arraylist = sps.getarraylist(self.program)
+        arraylist = self.sps.getarraylist(self.program)
 
         if self.array in arraylist:
-            shm = sps.attach(self.program, self.array)
+            shm = self.sps.attach(self.program, self.array)
         else:
             cols, rows = self.shape
-            sps.create(self.program, self.array, rows, cols, sps.DOUBLE)
+            self.sps.create(self.program, self.array, rows, cols, self.sps.DOUBLE)
             self.owner = True
 
         if self.array_ENV in arraylist:
-            shm_env = sps.attach(self.program, self.array_ENV)
+            shm_env = self.sps.attach(self.program, self.array_ENV)
         else:
-            sps.create(self.program, self.array_ENV, self.maxenv, self.envlen,
-                       sps.STRING)
+            self.sps.create(self.program, self.array_ENV, self.maxenv, self.envlen,
+                       self.sps.STRING)
             self.owner_ENV = True
 
         self.nopts = 0
@@ -152,12 +146,12 @@ class SPSRecorder(_SharedMemoryRecorder):
                 sufix = "1D"
                 if self.array.endswith(sufix):
                     valsmca = numpy.array(valsmca)
-                    sps.putdatarow(self.program, self.array, record.recordno, valsmca)
+                    self.sps.putdatarow(self.program, self.array, record.recordno, valsmca)
 
         sufix = "0D"
         if self.array.endswith(sufix):
             vals = numpy.array(vals)
-            sps.putdatarow(self.program, self.array, record.recordno, vals)
+            self.sps.putdatarow(self.program, self.array, record.recordno, vals)
 
         self.nopts += 1
 
@@ -207,7 +201,7 @@ class ShmRecorder(DataRecorder):
         self.cols = cols
 
     def putenv(self, name, value):
-        sps.putenv(self.progname, self.shm_id_env, name, str(value))
+        self.sps.putenv(self.progname, self.shm_id_env, name, str(value))
 
     def setChanDimList(self, chandimlist):
         self.chandimlist = chandimlist
@@ -216,19 +210,19 @@ class ShmRecorder(DataRecorder):
 
         if not self.isInitialized(): return
 
-        arraylist = sps.getarraylist(self.progname)
+        arraylist = self.sps.getarraylist(self.progname)
 
         if self.shm_id in arraylist:
-            shm = sps.attach(self.progname, self.shm_id)
+            shm = self.sps.attach(self.progname, self.shm_id)
         else:
-            sps.create(self.progname, self.shm_id, self.rows, self.cols,
-                       sps.DOUBLE)
+            self.sps.create(self.progname, self.shm_id, self.rows, self.cols,
+                       self.sps.DOUBLE)
 
         if self.shm_id_env in arraylist:
-            shm_env = sps.attach(self.progname, self.shm_id_env)
+            shm_env = self.sps.attach(self.progname, self.shm_id_env)
         else:
-            sps.create(self.progname, self.shm_id_env, self.maxenv, self.envlen,
-                       sps.STRING)
+            self.sps.create(self.progname, self.shm_id_env, self.maxenv, self.envlen,
+                       self.sps.STRING)
 
         print "Starting new SHM recording"
 
@@ -288,13 +282,13 @@ class ShmRecorder(DataRecorder):
                  tmp_name = self.mnt_grp + "_1D"
                  if self.shm_id == tmp_name:
                     valsmca = numpy.array(valsmca)
-                    sps.putdatarow(self.progname, self.shm_id, record.recordno, valsmca)
+                    self.sps.putdatarow(self.progname, self.shm_id, record.recordno, valsmca)
            myj = myj + 1
 
         vals = numpy.array(vals)
         tmp_name = self.mnt_grp + "_0D"
         if self.shm_id == tmp_name:
-           sps.putdatarow(self.progname, self.shm_id, record.recordno, vals)
+           self.sps.putdatarow(self.progname, self.shm_id, record.recordno, vals)
 
         self.nopts += 1
         self.putenv('nopts', self.nopts)
@@ -309,12 +303,3 @@ class ShmRecorder(DataRecorder):
         if not self.isInitialized(): return
         self.putenv('ended', time.ctime(recordlist.getEnvironValue('endtime')))
 
-
-def SharedMemoryRecorder(type, **pars):
-    global SPS_AVAILABLE
-    if type == 'sps' and SPS_AVAILABLE:
-        klass = SPSRecorder
-    else:
-        return
-
-    return klass(**pars)
diff --git a/src/sardana/macroserver/scan/recorder/storage.py b/src/sardana/macroserver/recorders/storage.py
similarity index 60%
copy from src/sardana/macroserver/scan/recorder/storage.py
copy to src/sardana/macroserver/recorders/storage.py
index f4c2dab..d6d0b9d 100644
--- a/src/sardana/macroserver/scan/recorder/storage.py
+++ b/src/sardana/macroserver/recorders/storage.py
@@ -25,7 +25,7 @@
 
 """This is the macro server scan data output recorder module"""
 
-__all__ = ["BaseFileRecorder", "FileRecorder"]
+__all__ = ["FIO_FileRecorder", "NXscan_FileRecorder", "SPEC_FileRecorder"]
 
 __docformat__ = 'restructuredtext'
 
@@ -40,31 +40,16 @@ import PyTango
 
 from sardana.taurus.core.tango.sardana import PlotType
 from sardana.macroserver.macro import Type
-from sardana.macroserver.scan.recorder.datarecorder import DataRecorder, \
-    DataFormats, SaveModes
+from sardana.macroserver.scan.recorder import (BaseFileRecorder,
+                                               BaseNAPI_FileRecorder,
+                                               SaveModes)
 from taurus.core.util.containers import chunks
 
 
-class BaseFileRecorder(DataRecorder):
-    def __init__(self, **pars):
-        DataRecorder.__init__(self, **pars)
-        self.filename = None
-        self.fd       = None 
-        
-    def getFileName(self):
-        return self.filename
-
-    def getFileObj(self):
-        return self.fd
-
-    def getFormat(self):
-        return '<unknown>'
-
-
 class FIO_FileRecorder(BaseFileRecorder):
     """ Saves data to a file """
 
-    formats = { DataFormats.fio : '.fio' }
+    formats = {'fio': '.fio'}
 
     def __init__(self, filename=None, macro=None, **pars):
         BaseFileRecorder.__init__(self)
@@ -103,7 +88,7 @@ class FIO_FileRecorder(BaseFileRecorder):
             self.filename = "%s_%s.%s" % (tpl[0], "[ScanId]", tpl[2])
 
     def getFormat(self):
-        return DataFormats.whatis(DataFormats.fio)
+        return self.formats.keys()[0]
     
     def _startRecordList(self, recordlist):
 
@@ -280,7 +265,7 @@ class FIO_FileRecorder(BaseFileRecorder):
 class SPEC_FileRecorder(BaseFileRecorder):
     """ Saves data to a file """
 
-    formats = { DataFormats.Spec : '.spec' }
+    formats = {'Spec': '.spec'}
     supported_dtypes = ('float32','float64','int8',
                         'int16','int32','int64','uint8',
                         'uint16','uint32','uint64')
@@ -306,7 +291,7 @@ class SPEC_FileRecorder(BaseFileRecorder):
         self.currentlist = None
 
     def getFormat(self):
-        return DataFormats.whatis(DataFormats.Spec)
+        return self.formats.keys()[0]
     
     def _startRecordList(self, recordlist):
         '''Prepares and writes the scan header.'''
@@ -470,230 +455,8 @@ class SPEC_FileRecorder(BaseFileRecorder):
         self.fd.flush()
         if fileWasClosed:
             self.fd.close() #leave the file descriptor as found
-        
-        
-
-class BaseNEXUS_FileRecorder(BaseFileRecorder):
-    """Base class for NeXus file recorders"""   
-    
-    formats = { DataFormats.w5 : '.h5', 
-                DataFormats.w4 : '.h4', 
-                DataFormats.wx : '.xml' }
-    supported_dtypes = ('float32','float64','int8',
-                        'int16','int32','int64','uint8',
-                        'uint16','uint32','uint64') #note that 'char' is not supported yet!
-    _dataCompressionRank = -1
-        
-    def __init__(self, filename=None, macro=None, overwrite=False, **pars):
-        BaseFileRecorder.__init__(self, **pars)
-
-        try:
-            import nxs  #check if Nexus data format is supported by this system
-            self.nxs = nxs
-        except ImportError:
-            raise Exception("NeXus is not available")
-        
-        self.macro = macro
-        self.overwrite = overwrite
-        if filename:
-            self.setFileName(filename)
-            
-        self.instrDict = {}
-        self.entryname = 'entry'
-    
-    def setFileName(self, filename):
-        if self.fd  is not None:
-            self.fd.close()
-   
-        self.filename = filename
-        #obtain preferred nexus file mode for writing from the filename extension (defaults to hdf5)
-        extension = os.path.splitext(filename)[1]
-        inv_formats = dict(itertools.izip(self.formats.itervalues(), self.formats.iterkeys()))
-        self.nxfilemode = inv_formats.get(extension.lower(), DataFormats.w5)
-        self.currentlist = None
-    
-    def getFormat(self):
-        return DataFormats.whatis(self.nxfilemode)
-    
-    def sanitizeName(self, name):
-        '''It returns a version of the given name that can be used as a python
-        variable (and conforms to NeXus best-practices for dataset names)'''
-        #make sure the name does not start with a digit
-        if name[0].isdigit(): name = "_%s" % name
-        #substitute whitespaces by underscores and remove other non-alphanumeric characters
-        return "".join(x for x in name.replace(' ','_') if x.isalnum() or x=='_')
-    
-    
-    def _nxln(self, src, dst, name=None):
-        '''convenience function to create NX links with just one call. On successful return, dst will be open.
-        
-        :param src: (str or NXgroup or NXfield) source group or dataset (or its path)
-        :param dst: (str or NXgroup) the group that will hold the link (or its path)
-        :param name: (str) name for the link. If not given, the name of the source is used
-        
-        .. note:: `groupname:nxclass` notation can be used for both paths for better performance
-        '''
-        
-        fd = getattr(self, 'fd')
-        if fd is None:
-            fd = getattr(src,'nxfile', getattr(dst,'nxfile'))
-        if fd is None:
-            raise NeXusError('Cannot get a file handle')
-        
-        if isinstance(src, self.nxs.NXobject):
-            src = src.nxpath
-        if isinstance(dst, self.nxs.NXobject):
-            dst = dst.nxpath
-            
-        fd.openpath(src)
-        try:
-            nid = fd.getdataID()
-        except self.nxs.NeXusError:
-            nid = fd.getgroupID()
-        fd.openpath(dst)
-        if name is None:
-            fd.makelink(nid)
-        else:
-            fd.makenamedlink(name,nid)
-
-    #===========================================================================
-    # Unimplemented methods that must be implemented in derived classes    
-    #===========================================================================
-    
-    def _startRecordList(self, recordlist):
-        raise NotImplementedError('_startRecordList must be implemented in BaseNEXUS_FileRecorder derived classes')    
-    
-    def _writeRecord(self, record):
-        raise NotImplementedError('_writeRecord must be implemented in BaseNEXUS_FileRecorder derived classes')  
-    
-    def _endRecordList(self, recordlist):
-        raise NotImplementedError('_endRecordList must be implemented in BaseNEXUS_FileRecorder derived classes')  
 
 
-class BaseNAPI_FileRecorder(BaseNEXUS_FileRecorder):
-    """Base class for NeXus file recorders (NAPI-based)"""
-    
-    #===========================================================================
-    # Convenience methods to make NAPI less tedious
-    #===========================================================================
-    
-    _nxentryInPath = re.compile(r'/[^/:]+:NXentry')
-    
-    def _makedata(self, name, dtype=None, shape=None, mode='lzw', chunks=None, comprank=None):
-        '''
-        combines :meth:`nxs.NeXus.makedata` and :meth:`nxs.NeXus.compmakedata` by selecting between 
-        using compression or not based on the comprank parameter and the rank of the data.
-        Compression will be used only if the shape of the data is given and its length is larger 
-        than comprank. If comprank is not passed (or None is passed) the default dataCompressionRank 
-        will be used
-        '''
-        if comprank is None: 
-            comprank = self._dataCompressionRank
-        
-        if shape is None or comprank<0 or (len(shape) < comprank):
-            return self.fd.makedata(name, dtype=dtype, shape=shape)
-        else:
-            try:
-                self.fd.compmakedata(name, dtype=dtype, shape=shape, mode=mode, chunks=chunks)
-            except ValueError: #workaround for bug in nxs<4.3 (compmakedatafails if chunks is not explicitly passed)
-                chunks = [1]*len(shape)
-                chunks[-1] = shape[-1]
-                self.fd.compmakedata(name, dtype=dtype, shape=shape, mode=mode, chunks=chunks)
-              
-    def _writeData(self, name, data, dtype, shape=None, chunks=None, attrs=None):
-        '''
-        convenience method that creates datasets (calling self._makedata), opens
-        it (napi.opendata) and writes the data (napi.putdata).
-        It also writes attributes (napi.putattr) if passed in a dictionary and 
-        it returns the data Id (useful for linking). The dataset is left closed. 
-        '''
-        if shape is None:
-            if dtype == 'char':
-                shape = [len(data)]
-                chunks = chunks or list(shape) #for 'char', write the whole block in one chunk
-            else:
-                shape = getattr(data,'shape',[1])
-        self._makedata(name, dtype=dtype, shape=shape, chunks=chunks)
-        self.fd.opendata(name)
-        self.fd.putdata(data)
-        if attrs is not None:
-            for k,v in attrs.items():
-                self.fd.putattr(k,v)
-        nid = self.fd.getdataID()
-        self.fd.closedata()
-        return nid
-
-    def _newentryname(self, prefix='entry', suffix='', offset=1):
-        '''Returns a str representing the name for a new entry.
-        The name is formed by the prefix and an incremental numeric suffix.
-        The offset indicates the start of the numeric suffix search'''
-        i = offset
-        while True:
-            entry = "%s%i" % (prefix, i)
-            if suffix:
-                entry += " - " + suffix
-            try:
-                self.fd.opengroup(entry,'NXentry')
-                self.fd.closegroup()
-                i += 1
-            except ValueError:  #no such group name exists
-                return entry
-        
-    def _nxln(self, src, dst):
-        '''convenience function to create NX links with just one call. On successful return, dst will be open.
-        
-        :param src: (str) the nxpath to the source group or dataset
-        :param dst: (str) the nxpath to the group that will hold the link
-        
-        .. note:: `groupname:nxclass` notation can be used for both paths for better performance
-        '''
-        self.fd.openpath(src)
-        try:
-            nid = self.fd.getdataID()
-        except self.nxs.NeXusError:
-            nid = self.fd.getgroupID()
-        self.fd.openpath(dst)
-        self.fd.makelink(nid)
-            
-    def _createBranch(self, path):
-        """
-        Navigates the nexus tree starting in / and finishing in path. 
-        
-        If path does not start with `/<something>:NXentry`, the current entry is
-        prepended to it.
-        
-        This method creates the groups if they do not exist. If the
-        path is given using `name:nxclass` notation, the given nxclass is used.
-        Otherwise, the class name is obtained from self.instrDict values (and if
-        not found, it defaults to NXcollection). If successful, path is left
-        open
-        """
-        m = self._nxentryInPath.match(path)
-        if m is None:
-            self._createBranch("/%s:NXentry" % self.entryname)  #if at all, it will recurse just once
-#            self.fd.openpath("/%s:NXentry" % self.entryname)
-        else:
-            self.fd.openpath("/")
-
-        relpath = ""
-        for g in path.split('/'):
-            if len(g) == 0:
-                continue
-            relpath = relpath + "/"+ g
-            if ':' in g:
-                g,group_type = g.split(':')
-            else:
-                try:
-                    group_type = self.instrDict[relpath].klass
-                except:
-                    group_type = 'NXcollection'
-            try:
-                self.fd.opengroup(g, group_type)
-            except:
-                self.fd.makegroup(g, group_type)
-                self.fd.opengroup(g, group_type)
-                
-
 class NXscan_FileRecorder(BaseNAPI_FileRecorder):
     """saves data to a nexus file that follows the NXscan application definition
     
@@ -719,7 +482,7 @@ class NXscan_FileRecorder(BaseNAPI_FileRecorder):
         self.entryname = "entry%d" % serialno
         try:
             self.fd.makegroup(self.entryname,"NXentry")
-        except NeXusError:
+        except self.nxs.NeXusError:
             entrynames = self.fd.getentries().keys()
             
             #===================================================================
@@ -1030,271 +793,3 @@ class NXscan_FileRecorder(BaseNAPI_FileRecorder):
         #leave the file as it was
         if fileWasClosed:
             self.fd.close()
-        
-            
-class NXxas_FileRecorder(BaseNEXUS_FileRecorder):
-    """saves data to a nexus file that follows the NXsas application definition
-    
-        """
-        
-    def __init__(self, filename=None, macro=None, overwrite=False, **pars):
-        BaseNEXUS_FileRecorder.__init__(self, filename=filename, macro=macro, overwrite=overwrite, **pars)
-        
-        
-    def _startRecordList(self, recordlist):
-        nxs = self.nxs
-        if self.filename is None:
-            return
-        
-        #get the recordlist environment
-        self.currentlist = recordlist
-        env = self.currentlist.getEnviron()
-        
-        #adapt the datadesc to the NeXus requirements
-        self.datadesc = []
-        for dd in env['datadesc']:
-            dd = dd.clone()
-            dd.label = self.sanitizeName(dd.label)
-            if dd.dtype == 'bool':
-                dd.dtype = 'int8'
-                self.debug('%s will be stored with type=%s',dd.name,dd.dtype)
-            if dd.dtype in self.supported_dtypes:
-                self.datadesc.append(dd)
-            else:
-                self.warning('%s will not be stored. Reason: type %s not supported',dd.name,dd.dtype)
-        
-        
-        serialno = env["serialno"]
-        nxfilemode = self.getFormat()
-        if not self.overwrite and os.path.exists(self.filename): nxfilemode='rw'               
-        
-        self.debug("starting new recording %d on file %s", serialno, self.filename)
-        
-        #create an nxentry and write it to file
-        self.nxentry = nxs.NXentry(name= "entry%d" % serialno)
-        self.nxentry.save(self.filename, format=nxfilemode)
-
-        #add fields to nxentry
-        import sardana.release
-        program_name = "%s (%s)" % (sardana.release.name, self.__class__.__name__)
-        self.nxentry.insert(nxs.NXfield(name='start_time', value=env['starttime'].isoformat()))
-        self.nxentry.insert(nxs.NXfield(name='title', value=env['title']))
-        self.nxentry.insert(nxs.NXfield(name='definition', value='NXxas'))
-        self.nxentry.insert(nxs.NXfield(name='epoch', value=time.mktime(env['starttime'].timetuple())))
-        self.nxentry.insert(nxs.NXfield(name='program_name', value=program_name, attrs={'version':sardana.release.version}))
-        self.nxentry.insert(nxs.NXfield(name='entry_identifier', value=env['serialno']))
-                
-        #add the "measurement" group (a NXcollection containing all counters from the mntgrp for convenience) 
-        measurement = nxs.NXcollection(name='measurement')
-        self.ddfieldsDict = {}
-        for dd in self.datadesc:
-            field = NXfield_comp(name=dd.label,
-                                 dtype=dd.dtype,
-                                 shape=[nxs.UNLIMITED] + list(dd.shape),
-                                 nxslab_dims=[1] + list(dd.shape)
-                                 )
-            if hasattr(dd,'data_units'):
-                field.attrs['units'] = dd.data_units
-            measurement.insert(field)
-            #create a dict of fields in the datadesc for easier access later on
-            self.ddfieldsDict[dd.label] = field
-        
-        self.nxentry.insert(measurement)
-        
-        #user group
-        nxuser = nxs.NXuser()
-        self.nxentry.insert(nxuser)
-        nxuser['name'] = env['user']
-
-        #sample group
-        nxsample = nxs.NXsample()
-        self.nxentry.insert(nxsample)
-        nxsample['name'] = env['SampleInfo'].get('name','Unknown')
-        
-        #monitor group
-        scan_acq_time = env.get('integ_time')
-        scan_monitor_mode = scan_acq_time>1 and 'timer' or 'monitor'
-        nxmonitor = nxs.NXmonitor(mode=scan_monitor_mode,
-                        preset=scan_acq_time)
-        self.nxentry.insert(nxmonitor)
-        monitor_data = self.ddfieldsDict[self.sanitizeName(env['monitor'])] #to be linked later on
-        
-        #instrument group
-        nxinstrument = nxs.NXinstrument()
-        self.nxentry.insert(nxinstrument)
-        
-        #monochromator  group
-        nxmonochromator = nxs.NXmonochromator()
-        nxinstrument.insert(nxmonochromator)
-        energy_data = self.ddfieldsDict[self.sanitizeName(env['monochromator'])] #to be linked later on
-        
-        #incoming_beam  group
-        nxincoming_beam = nxs.NXdetector(name='incoming_beam')
-        nxinstrument.insert(nxincoming_beam)
-        incbeam_data = self.ddfieldsDict[self.sanitizeName(env['incbeam'])] #to be linked later on
-        
-        #absorbed_beam  group
-        nxabsorbed_beam = nxs.NXdetector(name='absorbed_beam')
-        nxinstrument.insert(nxabsorbed_beam)
-        absbeam_data = self.ddfieldsDict[self.sanitizeName(env['absbeam'])] #to be linked later on
-        absbeam_data.attrs['signal'] = '1'
-        absbeam_data.attrs['axes'] = 'energy'
-        
-        #source group
-        nxsource = nxs.NXsource()
-        nxinstrument.insert(nxsource) 
-        nxinstrument['source']['name'] = env.get('SourceInfo',{}).get('name','Unknown')
-        nxinstrument['source']['type'] = env.get('SourceInfo',{}).get('type','Unknown')
-        nxinstrument['source']['probe'] = env.get('SourceInfo',{}).get('x-ray','Unknown')
-        
-        #data group
-        nxdata = nxs.NXdata()
-        self.nxentry.insert(nxdata)
-        
-        
-        #@todo create the PreScanSnapshot
-        #self._createPreScanSnapshot(env)   
-        
-        #write everything to file
-        self.nxentry.write() 
-        
-        #@todo: do this with the PyTree api instead(how to do named links with the PyTree API????)
-        self._nxln(monitor_data, nxmonitor, name='data')
-        self._nxln(incbeam_data, nxincoming_beam, name='data')
-        self._nxln(absbeam_data, nxabsorbed_beam, name='data')
-        self._nxln(energy_data, nxmonochromator, name='energy')
-        self._nxln(energy_data, nxdata, name='energy')
-        self._nxln(absbeam_data, nxdata, name='absorbed_beam')
-                
-        self.nxentry.nxfile.flush()
-        
-    
-    def _writeRecord(self, record):
-        # most used variables in the loop
-        fd, debug, warning = self.nxentry.nxfile, self.debug, self.warning
-        nparray, npshape = numpy.array, numpy.shape
-        rec_data, rec_nb = record.data, record.recordno
-                
-        for dd in self.datadesc:
-            if record.data.has_key( dd.name ):
-                data = rec_data[dd.name]
-                field = self.ddfieldsDict[dd.label]
-                
-                if data is None:
-                    data = numpy.zeros(dd.shape, dtype=dd.dtype)
-                if not hasattr(data, 'shape'):
-                    data = nparray([data], dtype=dd.dtype)
-                elif dd.dtype != data.dtype.name:
-                    debug('%s casted to %s (was %s)', dd.label, dd.dtype,
-                                                      data.dtype.name)
-                    data = data.astype(dd.dtype)
-
-                slab_offset = [rec_nb] + [0] * len(dd.shape)
-                shape = [1] + list(npshape(data))
-                try:
-                    field.put(data, slab_offset, shape)
-                    field.write()
-                except:
-                    warning("Could not write <%s> with shape %s", data, shape)
-                    raise
-            else:
-                debug("missing data for label '%s'", dd.label)
-        self.nxentry.nxfile.flush()
-
-
-    def _endRecordList(self, recordlist):
-        env=self.currentlist.getEnviron()
-        self.nxentry.insert(nxs.NXfield(name='end_time', value=env['endtime'].isoformat()))
-        #self._populateInstrumentInfo()
-        #self._createNXData()
-        self.nxentry.write()
-        self.nxentry.nxfile.flush()
-        self.debug("Finishing recording %d on file %s:", env['serialno'], self.filename)
-        return
-        
-
-
-#===============================================================================
-# BEGIN: THIS BLOCK SHOULD BE REMOVED IF NEXUS ACCEPTS THE PATCH TO NXfield
-#===============================================================================
-try:
-    from nxs import NXfield #needs Nexus v>=4.3
-    from nxs import napi, NeXusError
-    
-    class NXfield_comp(NXfield):
-        
-        #NOTE: THE CONSTRUCTOR IS OPTIONAL. IF NOT IMPLEMENTED, WE CAN STILL USE THE nxslab_dims PROPERTY
-        def __init__(self, value=None, name='field', dtype=None, shape=(), group=None,
-                     attrs={}, nxslab_dims=None, **attr):
-            NXfield.__init__(self, value=value, name=name, dtype=dtype, shape=shape, group=group,
-                     attrs=attrs, **attr)
-            self._slab_dims = nxslab_dims
-            
-        def write(self):
-            """
-            Write the NXfield, including attributes, to the NeXus file.
-            """
-            if self.nxfile:
-                if self.nxfile.mode == napi.ACC_READ:
-                    raise NeXusError("NeXus file is readonly")
-                if not self.infile:
-                    shape = self.shape
-                    if shape == (): shape = (1,)
-                    with self.nxgroup as path:
-                        if self.nxslab_dims is not None:
-                        #compress
-                            path.compmakedata(self.nxname, self.dtype, shape, 'lzw', 
-                                              self.nxslab_dims)
-                        else:
-                        # Don't use compression
-                            path.makedata(self.nxname, self.dtype, shape)
-                    self._infile = True
-                if not self.saved:            
-                    with self as path:
-                        path._writeattrs(self.attrs)
-                        value = self.nxdata
-                        if value is not None:
-                            path.putdata(value)
-                    self._saved = True
-            else:
-                raise IOError("Data is not attached to a file")
-        
-        def _getnxslabdims(self):
-            try:
-                return self._nxslab_dims
-            except:
-                slab_dims = None
-            #even if slab_dims have not been set, check if the dataset is large 
-            shape = self.shape or (1,)
-            if numpy.prod(shape) > 10000:
-                slab_dims = numpy.ones(len(shape),'i')
-                slab_dims[-1] = min(shape[-1], 100000)
-            return slab_dims
-        
-        def _setnxslabdims(self, slab_dims):
-            self._nxslab_dims = slab_dims
-        
-        nxslab_dims = property(_getnxslabdims,_setnxslabdims,doc="Slab (a.k.a. chunk) dimensions for compression")
-except:
-    pass #NXxas_FileRecorder won't be usable
-
-
-#===============================================================================
-# END: THE ABOVE BLOCK SHOULD BE REMOVED IF NEXUS ACCEPTS THE PATCH TO NXfield
-#===============================================================================
-
-
-def FileRecorder(filename, macro, **pars):
-    ext = os.path.splitext(filename)[1].lower() or '.spec'
-    
-    hintedklass = globals().get(getattr(macro,'hints',{}).get('FileRecorder',None))
-    
-    if hintedklass is not None and issubclass(hintedklass, BaseFileRecorder): 
-        klass = hintedklass
-    elif ext in NXscan_FileRecorder.formats.values():
-        klass = NXscan_FileRecorder
-    elif ext in FIO_FileRecorder.formats.values():
-        klass = FIO_FileRecorder
-    else:
-        klass = SPEC_FileRecorder
-    return klass(filename=filename, macro=macro, **pars)
diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py
index 6192480..8de7866 100644
--- a/src/sardana/macroserver/scan/gscan.py
+++ b/src/sardana/macroserver/scan/gscan.py
@@ -7,17 +7,17 @@
 ## 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/>.
 ##
@@ -52,8 +52,9 @@ from sardana.macroserver.msexception import MacroServerException, UnknownEnv, \
 from sardana.macroserver.msparameter import Type
 from sardana.macroserver.scan.scandata import ColumnDesc, MoveableDesc, \
     ScanFactory, ScanDataEnvironment
-from sardana.macroserver.scan.recorder import OutputRecorder, JsonRecorder, \
-    SharedMemoryRecorder, FileRecorder
+from sardana.macroserver.scan.recorder import (AmbiguousRecorderError,
+                                               SharedMemoryRecorder,
+                                               FileRecorder)
 from sardana.taurus.core.tango.sardana.pool import Ready
 
 
@@ -66,13 +67,13 @@ class ScanException(MacroServerException):
 
 
 class ExtraData(object):
-    
+
     def __init__(self, **kwargs):
         """Expected keywords are:
             - model (str, mandatory): represents data source (ex.: a/b/c/d)
             - label (str, mandatory): column label
             - name (str, optional): unique name (defaults to model)
-            - shape (seq, optional): data shape 
+            - shape (seq, optional): data shape
             - dtype (numpy.dtype, optional): data type
             - instrument (str, optional): full instrument name"""
         self._label = kwargs['label']
@@ -84,19 +85,19 @@ class ExtraData(object):
         if not kwargs.has_key('name'):
             kwargs['name'] = self._model
         self._column = ColumnDesc(**kwargs)
-    
+
     def getLabel(self):
         return self._label
 
     def getName(self):
         return self._label
-    
+
     def getColumnDesc(self):
         return self._column
-    
+
     def getType(self):
         raise Exception("Must be implemented in subclass")
-    
+
     def getShape(self):
         raise Exception("Must be implemented in subclass")
 
@@ -105,23 +106,23 @@ class ExtraData(object):
 
 
 class TangoExtraData(ExtraData):
-    
+
     def __init__(self, **kwargs):
         self._attribute = None
         ExtraData.__init__(self, **kwargs)
-    
+
     @property
     def attribute(self):
         if self._attribute is None:
             self._attribute = taurus.Attribute(self._model)
         return self._attribute
-        
+
     def getType(self):
         t = self.attribute.getType()
         if t is None:
             raise Exception("Could not determine type for unknown attribute '%s'" % self._model)
         return FROM_TANGO_TO_STR_TYPE[t]
-    
+
     def getShape(self):
         s = self.attribute.getShape()
         if s is None:
@@ -138,20 +139,20 @@ class TangoExtraData(ExtraData):
 
 
 class GScan(Logger):
-    """Generic Scan object. 
-    The idea is that the scan macros create an instance of this Generic Scan, 
+    """Generic Scan object.
+    The idea is that the scan macros create an instance of this Generic Scan,
     supplying in the constructor a reference to the macro that created the scan,
-    a generator function pointer, a list of moveable items, an extra 
+    a generator function pointer, a list of moveable items, an extra
     environment and a sequence of constrains.
-    
+
     If the referenced macro is hookable, 'pre-scan' and 'post-scan' hook hints
     will be used to execute callables before the start and after the end of the
     scan, respectively
-    
+
     The generator must be a function yielding a dictionary with the following
     content (minimum) at each step of the scan:
       - 'positions'  : In a step scan, the position where the moveables should go
-      - 'integ_time' : In a step scan, a number representing the integration time for the step 
+      - 'integ_time' : In a step scan, a number representing the integration time for the step
                      (in seconds)
       - 'integ_time' : In a continuous scan, the time between acquisitions
       - 'pre-move-hooks' : (optional) a sequence of callables to be called in strict order before starting to move.
@@ -164,21 +165,21 @@ class GScan(Logger):
       - 'check_func' : (optional) a list of callable objects. callable(moveables, counters)
       - 'extravalues': (optional) a dictionary containing the values for each extra info
                        field. The extra information fields must be described in
-                       extradesc (passed in the constructor of the Gscan) 
-    
-    
+                       extradesc (passed in the constructor of the Gscan)
+
+
     The moveables must be a sequence Motion or MoveableDesc objects.
-    
+
     The environment is a dictionary of extra environment to be added specific
     to the macro in question.
-    
-    Each constrain must be a callable which must receive a two parameters: the 
+
+    Each constrain must be a callable which must receive a two parameters: the
     current point and the next point. It should return True or False
-    
+
     The extradesc optional argument consists of a list of ColumnDesc objects
     which describe the data fields that will be filled using step['extravalues'],
     where step is what the generator yields.
-    
+
     The Generic Scan will create:
       - a ScanData
       - DataHandler with the following recorders:
@@ -189,9 +190,9 @@ class GScan(Logger):
         - 'serialno' : a integer identifier for the scan operation
         - 'user' : the user which started the scan
         - 'title' : the scan title (build from macro.getCommand)
-        - 'datadesc' : a seq<ColumnDesc> describing each column of data 
+        - 'datadesc' : a seq<ColumnDesc> describing each column of data
                      (labels, data format, data shape, etc)
-        - 'estimatedtime' : a float representing an estimation for 
+        - 'estimatedtime' : a float representing an estimation for
                           the duration of the scan (in seconds). Negative means
                           the time estimation is known not to be accurate. Anyway,
                           time estimation has 'at least' semantics.
@@ -205,38 +206,41 @@ class GScan(Logger):
         (at the end of the scan, extra keys 'endtime' and 'deadtime' will be added
         representing the time at the end of the scan and the dead time)
 
-        This object is passed to all recorders at the beginning and at the end 
+        This object is passed to all recorders at the beginning and at the end
         of the scan (when startRecordList and endRecordList is called)
-    
+
     At each step of the scan, for each Recorder, the writeRecord method will
     be called with a Record object as parameter. The Record.data member will be
     a dictionary containing:
       - 'point_nb' : the point number of the scan
-      - for each column of the scan (motor or counter), a key with the 
+      - for each column of the scan (motor or counter), a key with the
       corresponding column name will contain the value"""
-    
+
     MAX_SCAN_HISTORY = 20
-    
-    env = ('ActiveMntGrp', 'ExtraColumns' 'ScanDir', 'ScanFile', 'SharedMemory', 'OutputCols')
-    
-    def __init__(self, macro, generator=None, moveables=[], env={}, constraints=[], extrainfodesc=[]):
+
+    env = ('ActiveMntGrp', 'ExtraColumns' 'ScanDir', 'ScanFile', 'ScanRecorder',
+           'SharedMemory', 'OutputCols')
+
+    def __init__(self, macro, generator=None, moveables=[], env={}, constraints=[],
+                 extrainfodesc=[]):
         self._macro = macro
         self._generator = generator
         self._extrainfodesc = extrainfodesc
-        
-        #nasty hack to make sure macro has access to gScan as soon as possible 
-        self._macro._gScan = self #TODO: CAUTION! this may be causing a circular reference! 
-        
+
+        #nasty hack to make sure macro has access to gScan as soon as possible
+        self._macro._gScan = self #TODO: CAUTION! this may be causing a circular reference!
+        self._rec_manager = macro.getMacroServer().recorder_manager
+
         self._moveables, moveable_names = [], []
         for moveable in moveables:
             if not isinstance(moveable, MoveableDesc):
                 moveable = MoveableDesc(moveable=moveable)
             moveable_names.append(moveable.moveable.getName())
             self._moveables.append(moveable)
-        
+
         name = self.__class__.__name__
         self.call__init__(Logger, name)
-        
+
         # ----------------------------------------------------------------------
         # Setup motion objects
         # ----------------------------------------------------------------------
@@ -270,21 +274,21 @@ class GScan(Logger):
 
         if self._master is None:
             raise ScanSetupError('%s has no timer defined' % mnt_grp.getName())
-        
+
         self._measurement_group = mnt_grp
-        
+
         # ----------------------------------------------------------------------
         # Setup extra columns
         # ----------------------------------------------------------------------
         self._extra_columns = self._getExtraColumns()
-        
+
         # ----------------------------------------------------------------------
         # Setup data management
         # ----------------------------------------------------------------------
-        
+
         # Generate data handler
         data_handler = ScanFactory().getDataHandler()
-        
+
         # The Scan data object
         data = ScanFactory().getScanData(data_handler)
 
@@ -293,26 +297,26 @@ class GScan(Logger):
 
         # The Output recorder (if any)
         json_recorder = self._getJsonRecorder()
-        
+
         # The File recorders (if any)
         file_recorders = self._getFileRecorders()
-        
+
         # The Shared memory recorder (if any)
         shm_recorder = self._getSharedMemoryRecorder(0)
         shm_recorder_1d = None
         if shm_recorder is not None:
             shm_recorder_1d = self._getSharedMemoryRecorder(1)
-        
+
         data_handler.addRecorder(output_recorder)
         data_handler.addRecorder(json_recorder)
         for file_recorder in file_recorders:
             data_handler.addRecorder(file_recorder)
         data_handler.addRecorder(shm_recorder)
         data_handler.addRecorder(shm_recorder_1d)
-        
+
         self._data = data
         self._data_handler = data_handler
-                
+
         # ----------------------------------------------------------------------
         # Setup environment
         # ----------------------------------------------------------------------
@@ -327,7 +331,7 @@ class GScan(Logger):
         except:
             self.info('ExtraColumns is not defined')
             return ret
-        
+
         try:
             for i, kwargs in enumerate(cols):
                 kw = dict(kwargs)
@@ -355,7 +359,8 @@ class GScan(Logger):
         try:
             json_enabled = self.macro.getEnv('JsonRecorder')
             if json_enabled:
-                return JsonRecorder(self.macro)
+                return self._rec_manager.getRecorderClass("JsonRecorder")(
+                    self.macro)
         except InterruptException:
             raise
         except Exception:
@@ -371,8 +376,9 @@ class GScan(Logger):
             raise
         except:
             pass
-        return OutputRecorder(self.macro, cols=cols, number_fmt='%g')
-    
+        return self._rec_manager.getRecorderClass("OutputRecorder")(
+            self.macro, cols=cols, number_fmt='%g')
+
     def _getFileRecorders(self):
         macro = self.macro
         try:
@@ -384,11 +390,11 @@ class GScan(Logger):
                           'stored persistently. Use Use "expconf" (or "senv ScanDir '
                           '<abs directory>") to enable it')
             return ()
-        
+
         if not isinstance(scan_dir, (str, unicode)):
             scan_dir_t = type(scan_dir).__name__
             raise TypeError("ScanDir MUST be string. It is '%s'" % scan_dir_t)
-        
+
         try:
             file_names = macro.getEnv('ScanFile')
         except InterruptException:
@@ -399,32 +405,57 @@ class GScan(Logger):
                           'file(s)>") to enable it')
             return ()
 
+        scan_recorders = []
+        try:
+            scan_recorders = macro.getEnv('ScanRecorder')
+        except InterruptException:
+            raise
+        except UnknownEnv:
+            pass
+
         if isinstance(file_names, (str, unicode)):
             file_names = (file_names,)
         elif not operator.isSequenceType(file_names):
             scan_file_t = type(file_names).__name__
             raise TypeError("ScanFile MUST be string or sequence of strings."\
                             " It is '%s'" % scan_file_t)
-            
+
+        if isinstance(scan_recorders, (str, unicode)):
+            scan_recorders = (scan_recorders,)
+        elif not operator.isSequenceType(scan_recorders):
+            scan_recorders_t = type(scan_recorders).__name__
+            raise TypeError("ScanRecorder MUST be string or sequence of strings."\
+                            " It is '%s'" % scan_recorders_t)
+
         file_recorders = []
-        for file_name in file_names:
+        for i, file_name in enumerate(file_names):
             abs_file_name = os.path.join(scan_dir, file_name)
             try:
-                file_recorder = FileRecorder(abs_file_name, macro=macro)
+                file_recorder = None
+                if len(scan_recorders) > i:
+                    file_recorder = self._rec_manager.getRecorderClass(
+                        scan_recorders[i])(abs_file_name, macro=macro)
+                if not file_recorder:
+                    file_recorder = FileRecorder(abs_file_name, macro=macro)
                 file_recorders.append(file_recorder)
             except InterruptException:
                 raise
+            except AmbiguousRecorderError, e:
+                macro.error('Select recorder that you would like to use '
+                            '(i.e. set ScanRecorder environment variable).')
+                raise e
             except Exception:
                 macro.warning("Error creating recorder for %s", abs_file_name)
                 macro.debug("Details:", exc_info=1)
-        
+
         if len(file_recorders) == 0:
             macro.warning("No valid recorder found. This operation will not be "
                           " stored persistently")
         return file_recorders
-    
+
     def _getSharedMemoryRecorder(self, eid):
         macro, mg, shm = self.macro, self.measurement_group, False
+        shmRecorder = None
         try:
             shm = macro.getEnv('SharedMemory')
         except InterruptException:
@@ -433,10 +464,10 @@ class GScan(Logger):
             self.info('SharedMemory is not defined. Use "senv '
                       'SharedMemory sps" to enable it')
             return
-        
-        if not shm: 
+
+        if not shm:
             return
-        
+
         kwargs = {}
         # For now we only support SPS shared memory format
         if shm.lower() == 'sps':
@@ -445,14 +476,14 @@ class GScan(Logger):
             ch_nb = len(mg.getChannels())
             oned_nb = 0
             array_prefix = mg.getName().upper()
-            
+
             try:
                 oned_nb = len(mg.OneDExpChannels)
             except InterruptException:
                 raise
             except:
                 oned_nb = 0
-            
+
             twod_nb = 0
             try:
                 twod_nb = len(mg.TwoDExpChannels)
@@ -460,12 +491,12 @@ class GScan(Logger):
                 raise
             except:
                 twod_nb = 0
-            
+
             if eid == 0:
                 cols += (ch_nb - oned_nb - twod_nb)    # counter/timer & 0D channel columns
             elif eid == 1:
                 cols = 1024
-                
+
             if eid == 0:
                 kwargs.update({ 'program' : macro.getDoorName(),
                                   'array' : "%s_0D" % array_prefix,
@@ -477,53 +508,55 @@ class GScan(Logger):
                     kwargs.update({ 'program' : macro.getDoorName(),
                                   'array' : "%s_1D" % array_prefix,
                                   'shape' : (cols, 99) } )
-            
-        shmRecorder = SharedMemoryRecorder(shm, **kwargs)
-        if shmRecorder is None:
-            self.info('SharedMemory %s is not available'%shm)
+        try:
+            shmRecorder = SharedMemoryRecorder(shm, macro, **kwargs)
+        except Exception:
+            macro.warning("Error creating %s SharedMemory recorder." % shm)
+            macro.debug("Details:", exc_info=1)
+        
         return shmRecorder
-    
+
     def _secsToTimedelta(self, secs):
         days, secs = divmod(secs, 86400)
         # we don't have to care about microseconds because if secs is a float
         # timedelta will do it for us
         return datetime.timedelta(days, secs)
-    
+
     def _timedeltaToSecs(self, td):
         return 86400*td.days + td.seconds + 1E-6*td.microseconds
-    
+
     def _setupEnvironment(self, additional_env):
         try:
             serialno = self.macro.getEnv("ScanID") + 1
         except UnknownEnv:
             serialno = 1
         self.macro.setEnv("ScanID", serialno)
-            
+
         env = ScanDataEnvironment(
                 { 'serialno' : serialno,
                       'user' : USER_NAME, #TODO: this should be got from self.measurement_group.getChannelsInfo()
                      'title' : self.macro.getCommand() } )
-        
+
         # Initialize the data_desc list (and add the point number column)
         data_desc = [
             ColumnDesc(name='point_nb', label='#Pt No', dtype='int64')
         ]
-        
+
         # add motor columns
         ref_moveables = []
         for moveable in self.moveables:
             data_desc.append(moveable)
             if moveable.is_reference:
                 ref_moveables.insert(0, moveable.name)
-        
+
         if not ref_moveables and len(self.moveables):
             ref_moveables.append(data_desc[-1].name)
         env['ref_moveables'] = ref_moveables
-        
+
         # add master column
         master = self._master
         instrument = master['instrument']
-        
+
         #add channels from measurement group
         channels_info = self.measurement_group.getChannelsInfo()
         counters = []
@@ -543,7 +576,7 @@ class GScan(Logger):
                     plotAxes.append(ref_moveables[i])
                     i += 1
                 else: plotAxes.append(a)
-                
+
             #create the ColumnDesc object
             column = ColumnDesc(name=ci.full_name,
                                 label=ci.label,
@@ -561,42 +594,42 @@ class GScan(Logger):
             counters.append(column.name)
         counters.remove(master['full_name'])
         env['counters'] = counters
-        
+
         for extra_column in self._extra_columns:
             data_desc.append(extra_column.getColumnDesc())
-        # add extra columns 
+        # add extra columns
         data_desc += self._extrainfodesc
         data_desc.append(ColumnDesc(name='timestamp', label='dt', dtype='float64'))
-        
+
         env['datadesc'] = data_desc
-        
+
         #set the data compression default
         try:
             env['DataCompressionRank'] = self.macro.getEnv('DataCompressionRank')
         except UnknownEnv:
             env['DataCompressionRank'] = -1
-        
+
         #set the sample information
         #@todo: use the instrument API to get this info
-        try: 
+        try:
             env['SampleInfo'] = self.macro.getEnv('SampleInfo')
         except UnknownEnv:
             env['SampleInfo'] = {}
-            
+
         #set the source information
         #@todo: use the instrument API to get this info
         try:
             env['SourceInfo'] = self.macro.getEnv('SourceInfo')
         except UnknownEnv:
             env['SourceInfo'] = {}
-        
+
         #take the pre-scan snapshot
         try:
             preScanSnapShot = self.macro.getEnv('PreScanSnapshot')
         except UnknownEnv:
             preScanSnapShot = []
         env['preScanSnapShot'] = self.takeSnapshot(elements=preScanSnapShot)
-        
+
         env['macro_id'] = self.macro.getID()
         try:
             env['ScanFile'] = self.macro.getEnv('ScanFile')
@@ -611,22 +644,22 @@ class GScan(Logger):
         except:
             env['ScanDir'] = None
         env['estimatedtime'], env['total_scan_intervals'] = self._estimate()
-        env['instrumentlist'] = self._macro.findObjs('.*', type_class=Type.Instrument) 
+        env['instrumentlist'] = self._macro.findObjs('.*', type_class=Type.Instrument)
 
         #env.update(self._getExperimentConfiguration) #add all the info from the experiment configuration to the environment
         env.update(additional_env)
         self._env = env
-        
+
         # Give the environment to the ScanData
         self.data.setEnviron(env)
 
     def takeSnapshot(self, elements=[]):
         '''reads the current values of the given elements
-        
+
         :param elements: (list<str,str>) list of tuples of label,src for the elements to read
                          (can be pool elements or Taurus attribute names).
-        
-        :return: (list<ColumnDesc>) a list of :class:`ColumnDesc`, each including a 
+
+        :return: (list<ColumnDesc>) a list of :class:`ColumnDesc`, each including a
                  "pre_scan_value" attribute with the read value for that attr
         '''
         manager = self.macro.getManager()
@@ -675,7 +708,7 @@ class GScan(Logger):
         if with_time and with_interval:
             t, i = self.macro.getTimeEstimation(), self.macro.getIntervalEstimation()
             return t, i
-        
+
         max_iter = max_iter or self.MAX_ITER
         iterator = self.generator()
         total_time = 0.0
@@ -694,7 +727,7 @@ class GScan(Logger):
                         max_path_duration = max(max_path_duration, path.duration)
                     integ_time = step.get("integ_time", 0.0)
                     acq_time += integ_time
-                    motion_time += max_path_duration 
+                    motion_time += max_path_duration
                     total_time += integ_time + max_path_duration
                     interval_nb += 1
                     start_pos = end_pos
@@ -709,11 +742,11 @@ class GScan(Logger):
             return total_time, interval_nb
         # max iteration reached.
         return -total_time, -interval_nb
-    
+
     @property
     def data(self):
         return self._data
-    
+
     @property
     def macro(self):
         return self._macro
@@ -729,17 +762,17 @@ class GScan(Logger):
     @property
     def motion(self):
         return self._motion
-    
+
     @property
     def moveables(self):
         return self._moveables
-    
+
     @property
     def steps(self):
         if not hasattr(self, '_steps'):
             self._steps = enumerate(self.generator())
         return self._steps
-    
+
     def start(self):
         self.do_backup()
         env = self._env
@@ -758,23 +791,23 @@ class GScan(Logger):
         estimated = env['estimatedtime']
         acq_time = env['acqtime']
         #env['deadtime'] = 100.0 * (total_time - estimated) / total_time
-        
+
         env['deadtime'] = total_time - acq_time
         if 'delaytime' in env:
             env['motiontime'] = total_time - acq_time - env['delaytime']
         elif 'motiontime' in env:
             env['delaytime'] = total_time - acq_time - env['motiontime']
-                  
+
         self.data.end()
         try:
             scan_history = self.macro.getEnv('ScanHistory')
         except UnknownEnv:
             scan_history = []
-        
+
         scan_file = env['ScanFile']
         if isinstance(scan_file, (str, unicode)):
             scan_file = scan_file,
-        
+
         names = [ col.name for col in env['datadesc'] ]
         history = dict(startts=env['startts'], endts=env['endts'],
                        estimatedtime=env['estimatedtime'],
@@ -790,7 +823,7 @@ class GScan(Logger):
     def scan(self):
         for _ in self.step_scan():
             pass
-        
+
     def step_scan(self):
         self.start()
         try:
@@ -806,7 +839,7 @@ class GScan(Logger):
             if not ex is None: raise e
         finally:
             self.do_restore()
-                
+
     def scan_loop(self):
         raise NotImplementedError('Scan method cannot be called by '
                                   'abstract class')
@@ -818,37 +851,37 @@ class GScan(Logger):
         except:
             self.macro.warning("Failed to execute macro 'do_backup'")
             self.debug("Details:", exc_info=1)
-        
+
     def do_restore(self):
         try:
             if hasattr(self.macro, 'do_restore'):
                 self.macro.do_restore()
         except:
-            self.macro.warning("Failed to execute macro 'do_restore'")        
+            self.macro.warning("Failed to execute macro 'do_restore'")
             self.debug("Details:", exc_info=1)
 
 
 class SScan(GScan):
     """Step scan"""
-    
+
     def scan_loop(self):
         lstep = None
         macro = self.macro
         scream = False
-        
+
         if hasattr(macro, "nr_points"):
             nr_points = float(macro.nr_points)
             scream = True
         else:
             yield 0.0
-        
+
         if hasattr(macro, 'getHooks'):
             for hook in macro.getHooks('pre-scan'):
                 hook()
-        
+
         self._sum_motion_time = 0
         self._sum_acq_time = 0
-        
+
         for i, step in self.steps:
             # allow scan to be stopped between points
             macro.checkPoint()
@@ -863,14 +896,14 @@ class SScan(GScan):
 
         if not scream:
             yield 100.0
-            
+
         self._env['motiontime'] = self._sum_motion_time
         self._env['acqtime'] = self._sum_acq_time
-        
+
     def stepUp(self, n, step, lstep):
         motion, mg = self.motion, self.measurement_group
         startts = self._env['startts']
-        
+
         #pre-move hooks
         for hook in step.get('pre-move-hooks',()):
             hook()
@@ -880,7 +913,7 @@ class SScan(GScan):
                 raise
             except:
                 pass
-        
+
         # Move
         self.debug("[START] motion")
         move_start_time = time.time()
@@ -893,10 +926,10 @@ class SScan(GScan):
             self.dump_information(n, step)
             raise
         self.debug("[ END ] motion")
-        
+
         curr_time = time.time()
         dt = curr_time - startts
-        
+
         #post-move hooks
         for hook in step.get('post-move-hooks',()):
             hook()
@@ -906,16 +939,16 @@ class SScan(GScan):
                 raise
             except:
                 pass
-        
+
         # allow scan to be stopped between motion and data acquisition
         self.macro.checkPoint()
-        
+
         if state != Ready:
             self.dump_information(n, step)
             m = "Scan aborted after problematic motion: " \
                 "Motion ended with %s\n" % str(state)
             raise ScanException({ 'msg' : m })
-        
+
         #pre-acq hooks
         for hook in step.get('pre-acq-hooks',()):
             hook()
@@ -924,7 +957,7 @@ class SScan(GScan):
             except InterruptException:
                 raise
             except: pass
-        
+
         integ_time = step['integ_time']
         # Acquire data
         self.debug("[START] acquisition")
@@ -943,7 +976,7 @@ class SScan(GScan):
                 raise
             except:
                 pass
-        
+
         #hooks for backwards compatibility:
         if step.has_key('hooks'):
             self.macro.info('Deprecation warning: you should use '
@@ -957,18 +990,18 @@ class SScan(GScan):
                     raise
                 except:
                     pass
-        
+
         # Add final moveable positions
         data_line['point_nb'] = n
         data_line['timestamp'] = dt
         for i, m in enumerate(self.moveables):
             data_line[m.moveable.getName()] = positions[i]
-        
+
         #Add extra data coming in the step['extrainfo'] dictionary
         if step.has_key('extrainfo'): data_line.update(step['extrainfo'])
-        
+
         self.data.addRecord(data_line)
-    
+
         #post-step hooks
         for hook in step.get('post-step-hooks', ()):
             hook()
@@ -978,18 +1011,18 @@ class SScan(GScan):
                 raise
             except:
                 pass
-        
+
     def dump_information(self, n, step):
         moveables = self.motion.moveable_list
         msg = ["Report: Stopped at step #" + str(n) + " with:"]
         for moveable in moveables:
             msg.append(moveable.information())
         self.macro.info("\n".join(msg))
-        
+
 
 class CScan(GScan):
     """Continuous scan abstract class. Implements helper methods."""
-    
+
     def __init__(self, macro, generator=None, moveables=[],
                  env={}, constraints=[], extrainfodesc=[]):
         GScan.__init__(self, macro, generator=generator,
@@ -1010,19 +1043,19 @@ class CScan(GScan):
         # pseudomotors' underneeth physical motors on on their constant
         # velocity in contrary to the the CScan which do not coordinate them
         self._physical_motion = self.macro.getMotion(physical_moveables_names)
-        
+
     def populate_moveables_data_structures(self, moveables):
         '''Populates moveables data structures.
-        :param moveables: (list<Moveable>) data structures will be generated 
+        :param moveables: (list<Moveable>) data structures will be generated
                           for these moveables
         :return (moveable_trees, physical_moveables_names, physical_moveables)
                 - moveable_trees (list<Tree>) - each tree represent one Moveables
                             with its hierarchy of inferior moveables.
-                - physical_moveables_names (list<str> - list of the names of the 
+                - physical_moveables_names (list<str> - list of the names of the
                             physical moveables. List order is important and preserved.
                 - physical_moveables (list<Moveable> - list of the moveable objects.
                             List order is important and preserved.'''
-        
+
         def generate_moveable_node(macro, moveable):
             '''Function to generate a moveable data structures based on moveable object.
             Internally can be recursively called if moveable is a PseudoMotor.
@@ -1030,7 +1063,7 @@ class CScan(GScan):
             :return (moveable_node, physical_moveables_names, physical_moveables)
                 - moveable_node (BaseNode) - can be a BranchNode if moveable is a PseudoMotor
                                       or a LeafNode if moveable is a PhysicalMotor.
-                - physical_moveables_names (list<str> - list of the names of the 
+                - physical_moveables_names (list<str> - list of the names of the
                             physical moveables. List order is important and preserved.
                 - physical_moveables (list<Moveable> - list of the moveable objects.
                             List order is important and preserved.'''
@@ -1039,8 +1072,8 @@ class CScan(GScan):
             physical_moveables = []
             moveable_type = moveable.getType()
             if moveable_type == "PseudoMotor":
-                moveable_node = BranchNode(moveable)    
-                moveables_names = moveable.elements                
+                moveable_node = BranchNode(moveable)
+                moveables_names = moveable.elements
                 sub_moveables = [macro.getMoveable(name) \
                                  for name in moveables_names]
                 for sub_moveable in sub_moveables:
@@ -1057,24 +1090,24 @@ class CScan(GScan):
                 physical_moveables_names.append(moveable_name)
                 physical_moveables.append(moveable)
             return moveable_node, physical_moveables_names, physical_moveables
-        
+
         moveable_trees = []
-        physical_moveables_names = [] 
+        physical_moveables_names = []
         physical_moveables = []
-                
+
         for moveable in moveables:
             moveable_root_node, _physical_moveables_names, _physical_moveables = \
-                      generate_moveable_node(self.macro, moveable.moveable) 
+                      generate_moveable_node(self.macro, moveable.moveable)
             moveable_tree = Tree(moveable_root_node)
             moveable_trees.append(moveable_tree)
             physical_moveables_names += _physical_moveables_names
-            physical_moveables += _physical_moveables 
+            physical_moveables += _physical_moveables
         return moveable_trees, physical_moveables_names, physical_moveables
-    
+
     def get_moveables_trees(self):
         '''Returns reference to the list of the moveables trees'''
         return self._moveables_trees
-                    
+
     def on_waypoints_end(self, restore_positions=None):
         """To be called by the waypoint thread to handle the end of waypoints
         (either because no more waypoints or because a macro abort was
@@ -1085,9 +1118,9 @@ class CScan(GScan):
             self.macro.info("Correcting overshoot...")
             self.motion.move(restore_positions)
         self.do_restore()
-        self.motion_end_event.set()        
+        self.motion_end_event.set()
         self.motion_event.set()
-            
+
     def go_through_waypoints(self, iterate_only=False):
         """Go through the different waypoints."""
         try:
@@ -1101,7 +1134,7 @@ class CScan(GScan):
         """Internal, unprotected method to go through the different waypoints."""
         raise NotImplementedError("_go_through_waypoints must be implemented " +
                             "in CScan derived classes")
-                
+
     def waypoint_estimation(self):
         """Internal, unprotected method to go through the different waypoints."""
         motion, waypoints = self.motion, self.generator()
@@ -1114,16 +1147,16 @@ class CScan(GScan):
             if start_positions is None:
                 last_end_positions = positions
                 continue
-            
+
             waypoint_info = self.prepare_waypoint(waypoint, start_positions,
                                                   iterate_only=True)
             motion_paths, delta_start, acq_duration = waypoint_info
-    
+
             start_path, end_path = [] , []
             for path in motion_paths:
                 start_path.append(path.initial_user_pos)
                 end_path.append(path.final_user_pos)
-                       
+
             # move from last waypoint to start position of this waypoint
             first_duration = 0
             if i == 1:
@@ -1136,32 +1169,32 @@ class CScan(GScan):
                 v_motor = _path.motor
                 path = MotionPath(v_motor, start, end)
                 first_duration = max(first_duration, path.duration)
-                        
+
             # move from waypoint start position to waypoint end position
             second_duration = 0
             for _path, start, end in zip(motion_paths, start_path, end_path):
                 v_motor = _path.motor
                 path = MotionPath(v_motor, start, end)
                 second_duration = max(second_duration, path.duration)
-            
+
             total_duration += first_duration + second_duration
-            
+
             last_end_positions = end_path
-        
+
         # add correct overshoot time
         overshoot_duration = 0
         for _path, start, end in zip(motion_paths, last_end_positions, positions):
             v_motor = _path.motor
             path = MotionPath(v_motor, start, end)
             overshoot_duration = max(overshoot_duration, path.duration)
-        
+
         total_duration += overshoot_duration
-        return total_duration       
-    
+        return total_duration
+
     def prepare_waypoint(self, waypoint, start_positions, iterate_only=False):
-        raise NotImplementedError("prepare_waypoint must be implemented in " + 
+        raise NotImplementedError("prepare_waypoint must be implemented in " +
                                   "CScan derived classes")
-        
+
     def set_all_waypoints_finished(self, v):
         self._all_waypoints_finished = v
 
@@ -1182,7 +1215,7 @@ class CScan(GScan):
             except AttributeError:
                 motor_backup = None
             backup.append(motor_backup)
-           
+
     def do_restore(self):
         super(CScan, self).do_restore()
         # restore changed motors to initial state
@@ -1198,12 +1231,12 @@ class CScan(GScan):
             except:
                 self.macro.warning("Failed to restore %s", motor)
                 self.debug("Details:", exc_info=1)
-                
+
     def _setFastMotions(self, motors=None):
         '''make given motors go at their max speed and accel'''
         if motors is None:
             motors = [b.get('moveable') for b in self._backup if b is not None]
-            
+
         for motor in motors:
             try:
                 motor.setVelocity(self.get_max_top_velocity(motor))
@@ -1213,12 +1246,12 @@ class CScan(GScan):
             except:
                 self.macro.warning("Failed to put %s into fast motion", motor)
                 self.debug("Details:", exc_info=1)
-                
+
     def get_max_top_velocity(self, motor):
         """Helper method to find the maximum top velocity for the motor.
         If the motor doesn't have a defined range for top velocity,
         then use the current top velocity"""
-        
+
         top_vel_obj = motor.getVelocityObj()
         min_top_vel, max_top_vel = top_vel_obj.getRange()
         try:
@@ -1233,12 +1266,12 @@ class CScan(GScan):
             except AttributeError:
                 pass
         return max_top_vel
-    
+
     def get_min_acc_time(self, motor):
         """Helper method to find the minimum acceleration time for the motor.
         If the motor doesn't have a defined range for the acceleration time,
         then use the current acceleration time"""
-        
+
         acc_time_obj = motor.getAccelerationObj()
         min_acc_time, max_acc_time = acc_time_obj.getRange()
         try:
@@ -1246,12 +1279,12 @@ class CScan(GScan):
         except ValueError:
             min_acc_time = motor.getAcceleration()
         return min_acc_time
-    
+
     def get_min_dec_time(self, motor):
         """Helper method to find the minimum deceleration time for the motor.
         If the motor doesn't have a defined range for the acceleration time,
         then use the current acceleration time"""
-        
+
         dec_time_obj = motor.getDecelerationObj()
         min_dec_time, max_dec_time = dec_time_obj.getRange()
         try:
@@ -1259,58 +1292,58 @@ class CScan(GScan):
         except ValueError:
             min_dec_time = motor.getDeceleration()
         return min_dec_time
- 
+
     def set_max_top_velocity(self, motor):
-        """Helper method to set the maximum top velocity for the motor to 
+        """Helper method to set the maximum top velocity for the motor to
         its maximum allowed limit."""
-        
+
         v = self.get_max_top_velocity(motor)
         try:
             motor.setVelocity(v)
         except:
             pass
 
-                
+
 class CSScan(CScan):
     """Continuous scan controlled by software"""
-    
+
     def __init__(self, macro, waypointGenerator=None, periodGenerator=None,
                  moveables=[], env={}, constraints=[], extrainfodesc=[]):
         CScan.__init__(self, macro, generator=waypointGenerator,
                        moveables=moveables, env=env, constraints=constraints,
                        extrainfodesc=extrainfodesc)
         self._periodGenerator = periodGenerator
-        
+
 
     def _calculateTotalAcquisitionTime(self):
         return None
-        
+
     @property
     def period_generator(self):
         return self._periodGenerator
-    
+
     @property
     def period_steps(self):
         if not hasattr(self, '_period_steps'):
             self._period_steps = enumerate(self.period_generator())
         return self._period_steps
-    
+
     def prepare_waypoint(self, waypoint, start_positions, iterate_only=False):
         slow_down = waypoint.get('slow_down', 1)
         positions = waypoint['positions']
-        
+
         duration, cruise_duration, delta_start = 0, 0, 0
         ideal_paths, real_paths = [], []
         for i, (moveable, position) in enumerate(zip(self.moveables, positions)):
             motor = moveable.moveable
-            
+
             coordinate = True
             try:
                 base_vel, top_vel = motor.getBaseRate(), motor.getVelocity()
                 accel_time, decel_time = motor.getAcceleration(), motor.getDeceleration()
 
                 if slow_down > 0:
-                    # find and set the maximum top velocity for the motor. 
+                    # find and set the maximum top velocity for the motor.
                     # If the motor doesn't have a defined range for top velocity,
                     # then use the current top velocity
                     max_top_vel = self.get_max_top_velocity(motor)
@@ -1326,50 +1359,50 @@ class CSScan(CScan):
                 coordinate = False
 
             last_user_pos = start_positions[i]
-                        
+
             real_vmotor = VMotor(min_vel=base_vel, max_vel=max_top_vel,
                                  accel_time=accel_time,
                                  decel_time=decel_time)
             real_path = MotionPath(real_vmotor, last_user_pos, position)
-            real_path.moveable = moveable            
+            real_path.moveable = moveable
             real_path.apply_correction = coordinate
-            
+
             # Find the cruise duration of motion at top velocity. For this create a
             # virtual motor which has instantaneous acceleration and deceleration
             ideal_vmotor = VMotor(min_vel=base_vel, max_vel=max_top_vel,
                                   accel_time=0, decel_time=0)
-            
+
             # create a path which will tell us which is the cruise duration of this
             # motion at top velocity
             ideal_path = MotionPath(ideal_vmotor, last_user_pos, position)
             ideal_path.moveable = moveable
             ideal_path.apply_correction = coordinate
-            
+
             # if really motor is moving in this waypoint
             if ideal_path.displacement > 0:
                 # recalculate time to reach maximum velocity
                 delta_start = max(delta_start, accel_time)
-            
+
             # recalculate cruise duration of motion at top velocity
             cruise_duration = max(cruise_duration, ideal_path.duration)
             duration = max(duration, real_path.duration)
-            
+
             ideal_paths.append(ideal_path)
             real_paths.append(real_path)
-            
+
         if slow_down <= 0:
             return real_paths, 0, duration
-        
+
         # after finding the duration, introduce the slow down factor added
         # by the user
         cruise_duration /= slow_down
-        
+
         if cruise_duration == 0:
             cruise_duration = float('+inf')
-        
+
         # now that we have the appropriate top velocity for all motors, the
         # cruise duration of motion at top velocity, and the time it takes to
-        # recalculate 
+        # recalculate
         for path in ideal_paths:
             vmotor = path.motor
             # in the case of pseudo motors or not moving a motor...
@@ -1388,10 +1421,10 @@ class CSScan(CScan):
             path.setInitialUserPos(new_initial_pos)
             new_final_pos = path.final_user_pos + disp_sign * vmotor.displacement_reach_min_vel
             path.setFinalUserPos(new_final_pos)
-        
+
         return ideal_paths, delta_start, cruise_duration
 
-    
+
     def go_through_waypoints(self, iterate_only=False):
         """go through the different waypoints."""
         try:
@@ -1406,7 +1439,7 @@ class CSScan(CScan):
         """Internal, unprotected method to go through the different waypoints."""
         macro, motion, waypoints = self.macro, self.motion, self.steps
         self.macro.debug("_go_through_waypoints() entering...")
-        
+
         last_positions = None
         for _, waypoint in waypoints:
             self.macro.debug("Waypoint iteration...")
@@ -1417,16 +1450,16 @@ class CSScan(CScan):
             if start_positions is None:
                 last_positions = positions
                 continue
-            
+
             waypoint_info = self.prepare_waypoint(waypoint, start_positions)
             motion_paths, delta_start, acq_duration = waypoint_info
-            
+
             self.acq_duration = acq_duration
-                        
+
             #execute pre-move hooks
-            for hook in waypoint.get('pre-move-hooks',[]): 
+            for hook in waypoint.get('pre-move-hooks',[]):
                 hook()
-    
+
             start_pos, final_pos = [] , []
             for path in motion_paths:
                 start_pos.append(path.initial_user_pos)
@@ -1434,16 +1467,16 @@ class CSScan(CScan):
 
             if macro.isStopped():
                 self.on_waypoints_end()
-                return 
-                      
+                return
+
             # move to start position
             self.macro.debug("Moving to start position: %s" % repr(start_pos))
             motion.move(start_pos)
-            
+
             if macro.isStopped():
                 self.on_waypoints_end()
                 return
-            
+
             # prepare motor(s) with the velocity required for synchronization
             for path in motion_paths:
                 if not path.apply_correction:
@@ -1455,10 +1488,10 @@ class CSScan(CScan):
             if macro.isStopped():
                 self.on_waypoints_end()
                 return
-                        
+
             self.timestamp_to_start = time.time() + delta_start
             self.motion_event.set()
-            
+
             # move to waypoint end position
             motion.move(final_pos)
 
@@ -1466,17 +1499,17 @@ class CSScan(CScan):
 
             if macro.isStopped():
                 return self.on_waypoints_end()
-            
+
             #execute post-move hooks
             for hook in waypoint.get('post-move-hooks',[]):
                 hook()
-            
-            if start_positions is None:  
+
+            if start_positions is None:
                 last_positions = positions
-        
+
         self.on_waypoints_end(positions)
 
-    
+
     def scan_loop(self):
         motion, mg, waypoints = self.motion, self.measurement_group, self.steps
         macro = self.macro
@@ -1484,10 +1517,10 @@ class CSScan(CScan):
         scream = False
         motion_event = self.motion_event
         startts = self._env['startts']
-        
+
         sum_delay = 0
         sum_integ_time = 0
-        
+
         if hasattr(macro, "nr_points"):
             nr_points = float(macro.nr_points)
             scream = True
@@ -1498,26 +1531,26 @@ class CSScan(CScan):
         period_steps = self.period_steps
         point_nb, step = -1, None
         data = self.data
-        
+
         if hasattr(macro, 'getHooks'):
             for hook in macro.getHooks('pre-scan'):
                 hook()
-        
+
         # start move & acquisition as close as possible
         # from this point on synchronization becomes critical
         manager.add_job(self.go_through_waypoints)
-        
+
         while not self._all_waypoints_finished:
-        
+
             # wait for motor to reach start position
             motion_event.wait()
-            
+
             # allow scan to stop
             macro.checkPoint()
-            
+
             if self._all_waypoints_finished:
                 break
-            
+
             # wait for motor to reach max velocity
             start_time = time.time()
             deltat = self.timestamp_to_start - start_time
@@ -1525,7 +1558,7 @@ class CSScan(CScan):
                 time.sleep(deltat)
             curr_time = acq_start_time = time.time()
             integ_time = 0
-            
+
             # Acquisition loop: acquire consecutively until waypoint asks to
             # stop or we see that we will enter deceleration time in next
             # acquisition
@@ -1533,7 +1566,7 @@ class CSScan(CScan):
 
                 # allow scan to stop
                 macro.checkPoint()
-            
+
                 try:
                     point_nb, step = period_steps.next()
                 except StopIteration:
@@ -1541,13 +1574,13 @@ class CSScan(CScan):
                     break
 
                 integ_time = step['integ_time']
-                
+
                 # If there is no more time to acquire... stop!
                 elapsed_time = time.time() - acq_start_time
                 if elapsed_time + integ_time > self.acq_duration:
                     motion_event.clear()
-                    break;                
-                
+                    break;
+
                 #pre-acq hooks
                 for hook in step.get('pre-acq-hooks',()):
                     hook()
@@ -1560,7 +1593,7 @@ class CSScan(CScan):
 
                 # allow scan to stop
                 macro.checkPoint()
-                
+
                 positions = motion.readPosition(force=True)
 
                 dt = time.time() - startts
@@ -1568,19 +1601,19 @@ class CSScan(CScan):
                 # Acquire data
                 self.debug("[START] acquisition")
                 state, data_line = mg.count(integ_time)
-                
+
                 sum_integ_time += integ_time
-                
+
                 # allow scan to stop
                 macro.checkPoint()
-                
+
                 # After acquisition, test if we are asked to stop, probably because
                 # the motor are stopped. In this case discard the last acquisition
                 if not self._all_waypoints_finished:
                     for ec in self._extra_columns:
                         data_line[ec.getName()] = ec.read()
                     self.debug("[ END ] acquisition")
-                    
+
                     #post-acq hooks
                     for hook in step.get('post-acq-hooks',()):
                         hook()
@@ -1597,12 +1630,12 @@ class CSScan(CScan):
                     data_line['timestamp'] = dt
                     for i, m in enumerate(self.moveables):
                         data_line[m.moveable.getName()] = positions[i]
-                    
+
                     #Add extra data coming in the step['extrainfo'] dictionary
                     if step.has_key('extrainfo'): data_line.update(step['extrainfo'])
-                    
+
                     self.data.addRecord(data_line)
-                    
+
                     if scream:
                         yield ((point_nb + 1) / nr_points) * 100.0
                 else:
@@ -1616,58 +1649,58 @@ class CSScan(CScan):
         if hasattr(macro, 'getHooks'):
             for hook in macro.getHooks('post-scan'):
                 hook()
-        
-        
+
+
         env = self._env
         env['acqtime'] = sum_integ_time
         env['delaytime'] = sum_delay
-        
+
         if not scream:
-            yield 100.0   
+            yield 100.0
 
 
 class CTScan(CScan):
-    '''Continuous scan controlled by hardware trigger signals. 
+    '''Continuous scan controlled by hardware trigger signals.
     Sequence of trigger signals is programmed in time. '''
-    
+
     class ExtraTrigger:
         '''Helper class and temporary solution for configuring trigger device.
            It is used to configure any Tango device name implementing:
-           +) following attributes: 
-           - InitialDelayTime [s] - delay time from calling Start to generating first pulse 
+           +) following attributes:
+           - InitialDelayTime [s] - delay time from calling Start to generating first pulse
            - HighTime [s] - time interval while signal will maintain its high state
            - LowTime [s] - time interval while signal will maintain its low state
            - SampPerChan - nr of pulses to be generated
            - IdleState - state (high or low) which signal will take after the Start command
-                         and which will maintain during the InitialDelayTime.  
+                         and which will maintain during the InitialDelayTime.
            +) following commands:
-           - Start 
+           - Start
            - Stop)'''
-         
+
         MIN_HIGH_TIME = 0.0000002
         MIN_TIME_PER_TRIGGER = 0.000001
-    
+
         def __init__(self, macro):
             self.macro = macro
-            
+
             triggerDeviceName = self.macro.getEnv("TriggerDevice")
             self.master = None
             self.slaves = []
             masterName = None
             slaveNames = []
-            
+
             if isinstance(triggerDeviceName, str):
                 masterName = triggerDeviceName
             elif isinstance(triggerDeviceName, list):
                 masterName = triggerDeviceName[0]
                 slaveNames = triggerDeviceName[1:]
-            
+
             for name in slaveNames:
                 slave = PyTango.DeviceProxy(name)
                 self.slaves.append(slave)
             if masterName != None:
-                self.master = PyTango.DeviceProxy(masterName)        
-    
+                self.master = PyTango.DeviceProxy(masterName)
+
         def configure(self, scanTime=None, nrOfTriggers=None, idleState="Low", lowTime=None, highTime=None, delayTime=0):
             if not None in (scanTime, nrOfTriggers, delayTime, idleState):
                 timePerTrigger = scanTime / nrOfTriggers
@@ -1679,33 +1712,33 @@ class CTScan(CScan):
                 pass
             else:
                 raise Exception("Missing parameters.")
-            
+
             self.master.write_attribute("InitialDelayTime", delayTime)
             self.master.write_attribute("HighTime", highTime) # 162.5 ns
             self.master.write_attribute("LowTime", lowTime) # 2.75 ms
             self.master.write_attribute("SampPerChan", long(nrOfTriggers))
             self.master.write_attribute("IdleState", idleState)
             self.master.write_attribute("SampleTimingType", "Implicit")
-    
+
             for slave in self.slaves:
                 slave.write_attribute("HighTime", highTime) # 162.5 ns
                 slave.write_attribute("LowTime", lowTime) # 2.75 ms
                 slave.write_attribute("SampPerChan", long(nrOfTriggers))
                 slave.write_attribute("IdleState", idleState)
                 slave.write_attribute("SampleTimingType", "Implicit")
-                
+
             return timePerTrigger
-    
+
         def getConfiguration(self):
             return None, None, None, None
-    
+
         def start(self):
             for slave in self.slaves:
                 self.macro.debug("Staring  %s" % slave.name())
                 slave.Start()
             if self.master != None:
                 self.master.Start()
-    
+
         def stop(self):
             for slave in self.slaves:
                 self.macro.debug("Stopping  %s" % slave.name())
@@ -1713,7 +1746,7 @@ class CTScan(CScan):
             if self.master != None:
                 self.master.Stop()
 
-    
+
     class ExtraMntGrp:
         '''Helper class and temporary solution for configuring experimental channels.
         It assumes that experimental channels are implementing:
@@ -1727,11 +1760,11 @@ class CTScan(CScan):
         - "start"
         - "pre-stop"
         - "stop"'''
-        
+
         def __init__(self, macro):
             self.macro = macro
             activeMntGrpName = self.macro.getEnv("ActiveMntGrp")
-            self.mntGrp = self.macro.getMeasurementGroup(activeMntGrpName)        
+            self.mntGrp = self.macro.getMeasurementGroup(activeMntGrpName)
             self.activeChannels = []
             self.nrOfTriggers = 0
             channels = self.mntGrp.getChannels()
@@ -1740,13 +1773,13 @@ class CTScan(CScan):
                 expChannel = self.macro.getExpChannel(channelName)
                 expChannel.getHWObj().set_timeout_millis(120000) #in case of readout of position channels, it can take really long...
                 self.activeChannels.append(expChannel)
-    
+
         def isMoving(self):
             for channel in self.activeChannels:
                 if channel.State() == PyTango.DevState.MOVING:
                     return True
             return False
-    
+
         def start(self):
             for channel in self.activeChannels:
                 pool = channel.getPoolObj()
@@ -1754,14 +1787,14 @@ class CTScan(CScan):
                 axis = channel.getAxis()
                 self.macro.debug("Pre-starting controller: %s, axis: %d", ctrlName, axis)
                 pool.SendToController([ctrlName, 'pre-start %d' % axis])
-    
+
             for channel in self.activeChannels:
                 pool = channel.getPoolObj()
                 ctrlName = channel.getControllerName()
                 axis = channel.getAxis()
                 self.macro.debug("Starting controller: %s, axis: %d", ctrlName, axis)
                 pool.SendToController([ctrlName, 'start %d' % axis])
-     
+
         def stop(self):
             for channel in self.activeChannels:
                 pool = channel.getPoolObj()
@@ -1769,14 +1802,14 @@ class CTScan(CScan):
                 axis = channel.getAxis()
                 self.macro.debug("Pre-stopping controller: %s, axis: %d", ctrlName, axis)
                 pool.SendToController([ctrlName, 'pre-stop %d' % axis])
-    
+
             for channel in self.activeChannels:
                 pool = channel.getPoolObj()
                 ctrlName = channel.getControllerName()
                 axis = channel.getAxis()
                 self.macro.debug("Stopping controller: %s, axis: %d", ctrlName, axis)
                 pool.SendToController([ctrlName, 'stop %d' % axis])
-    
+
         def getDataList(self):
             dataList = [ {"point_nb" : i, "timestamp" : 0} for i in xrange(self.nrOfTriggers) ]
             for channel in self.activeChannels:
@@ -1785,26 +1818,26 @@ class CTScan(CScan):
                 for i, data in enumerate(channelData):
                     dataList[i][dataDesc] = data
             return dataList
-    
+
         def setSamplingFrequency(self, freq):
             for channel in self.activeChannels:
                 channel.getAttribute('SamplingFrequency').write(freq)
-    
+
         def setAcquisitionTime(self, acqTime):
             for channel in self.activeChannels:
                 channel.getAttribute('AcquisitionTime').write(acqTime)
-    
+
         def setTriggerMode(self, mode):
             if mode not in ["soft", "gate"]:
                 raise Exception("Trigger mode must be either soft or gate.")
             for channel in self.activeChannels:
                 channel.getAttribute('TriggerMode').write(mode)
-    
+
         def setNrOfTriggers(self, nrOfTriggers):
             self.nrOfTriggers = nrOfTriggers
             for channel in self.activeChannels:
                 channel.getAttribute('NrOfTriggers').write(nrOfTriggers)
-    
+
         def configure(self, nrOfTriggers, acqTime, timePerTrigger, sampFreq=-1, triggerMode="gate"):
             self.macro.debug("acqTime: %s" % acqTime)
             if timePerTrigger == None:
@@ -1815,41 +1848,41 @@ class CTScan(CScan):
             self.setSamplingFrequency(sampFreq)
             self.setAcquisitionTime(acqTime)
             self.macro.debug("MG: nrOfTriggers: %s, timePerTrigger: %s, acqTime: %s, sampFreq: %s" % (nrOfTriggers,timePerTrigger,acqTime,sampFreq))
-    
+
         def getConfiguration(self):
             return None
-    
+
         def setConfiguration(self, configuration):
             pass
-    
+
     def __init__(self, macro, generator=None,
                  moveables=[], env={}, constraints=[], extrainfodesc=[]):
         CScan.__init__(self, macro, generator=generator,
                        moveables=moveables, env=env, constraints=constraints,
                        extrainfodesc=extrainfodesc)
         self._measurement_group = self.ExtraMntGrp(macro)
-        self.extraTrigger = self.ExtraTrigger(macro)            
-        
+        self.extraTrigger = self.ExtraTrigger(macro)
+
     def prepare_waypoint(self, waypoint, start_positions, iterate_only=False):
-        '''Prepare list of MotionPath objects per each physical motor. 
+        '''Prepare list of MotionPath objects per each physical motor.
         :param waypoint: (dict) waypoint dictionary with necessary information
         :param start_positions: (list<float>) list of starting position per each
                                  physical motor
         :return (ideal_paths, acc_time, active_time)
-                - ideal_paths: (list<MotionPath> representing motion attributes 
+                - ideal_paths: (list<MotionPath> representing motion attributes
                                of each physical motor)
                 - acc_time: acceleration time which will be used during the scan
-                            it corresponds to the longest acceleration time of 
+                            it corresponds to the longest acceleration time of
                             all the motors
                 - active_time: time interval while all the physical motors will
                                maintain constant velocity'''
 
         positions = waypoint['positions']
         active_time = waypoint["active_time"]
-        
+
         ideal_paths = []
-        
-        max_acc_time, max_dec_time = 0, 0                
+
+        max_acc_time, max_dec_time = 0, 0
         for moveable, end_position in zip(self._physical_moveables, positions):
             motor = moveable
             self.macro.debug("Motor: %s" % motor.getName())
@@ -1857,33 +1890,33 @@ class CTScan(CScan):
             self.macro.debug("DecTime: %f" % self.get_min_dec_time(motor))
             max_acc_time = max(self.get_min_acc_time(motor), max_acc_time)
             max_dec_time = max(self.get_min_dec_time(motor), max_dec_time)
-            
+
         acc_time = max_acc_time
         dec_time = max_dec_time
-            
+
         for moveable, start_position, end_position in \
                       zip(self._physical_moveables, start_positions, positions):
-            base_vel = moveable.getBaseRate()        
+            base_vel = moveable.getBaseRate()
             ideal_vmotor = VMotor(accel_time=acc_time,
                                   decel_time=dec_time,
                                   min_vel=base_vel)
-            ideal_path = MotionPath(ideal_vmotor, 
-                                    start_position, 
-                                    end_position, 
-                                    active_time)            
+            ideal_path = MotionPath(ideal_vmotor,
+                                    start_position,
+                                    end_position,
+                                    active_time)
             ideal_path.moveable = moveable
-            ideal_path.apply_correction = True        
-            ideal_paths.append(ideal_path)                                 
-        
+            ideal_path.apply_correction = True
+            ideal_paths.append(ideal_path)
+
         return ideal_paths, acc_time, active_time
-    
+
     def _go_through_waypoints(self):
         """Internal, unprotected method to go through the different waypoints.
            It controls all the three objects: motion, trigger and measurement
            group."""
         macro, motion, waypoints = self.macro, self._physical_motion, self.steps
         self.macro.debug("_go_through_waypoints() entering...")
-        
+
         last_positions = None
         for _, waypoint in waypoints:
             self.macro.debug("Waypoint iteration...")
@@ -1894,21 +1927,21 @@ class CTScan(CScan):
             if start_positions is None:
                 last_positions = positions
                 continue
-            
+
             waypoint_info = self.prepare_waypoint(waypoint, start_positions)
             motion_paths, delta_start, acq_duration = waypoint_info
-            
+
             self.acq_duration = acq_duration
-                        
+
             #execute pre-move hooks
-            for hook in waypoint.get('pre-move-hooks',[]): 
+            for hook in waypoint.get('pre-move-hooks',[]):
                 hook()
-    
+
             start_pos, final_pos = [] , []
             for path in motion_paths:
                 start_pos.append(path.initial_user_pos)
                 final_pos.append(path.final_user_pos)
-            
+
             if macro.isStopped():
                 self.on_waypoints_end()
                 return
@@ -1917,20 +1950,20 @@ class CTScan(CScan):
             self.__triggerConfigured = False
             self.__mntGrpStarted = False
             self.__triggerStarted = False
-            
+
             #validation of parameters
             for start, end in zip(self.macro.starts, self.macro.finals):
                 if start == end:
                     raise Exception("Start and End can not be equal.")
 
             startTimestamp = time.time()
-        
+
             #extra pre configuration
             if hasattr(macro, 'getHooks'):
                 for hook in macro.getHooks('pre-configuration'):
                     hook()
             self.macro.checkPoint()
-    
+
             #configuring trigger lines
             oldHighTime, oldLowTime, oldDelay, oldNrOfTriggers = \
                                         self.extraTrigger.getConfiguration()
@@ -1939,32 +1972,32 @@ class CTScan(CScan):
                                            scanTime=acq_duration,
                                            nrOfTriggers=self.macro.nr_of_points)
             self.macro.checkPoint()
-    
+
             #configuring measurementGroup
             self.mntGrpConfiguration = self._measurement_group.getConfiguration()
             self.__mntGrpConfigured = True
-            self._measurement_group.configure(self.macro.nr_of_points, 
-                                       self.macro.acq_time, 
+            self._measurement_group.configure(self.macro.nr_of_points,
+                                       self.macro.acq_time,
                                        timePerTrigger)
             self.macro.checkPoint()
-    
+
             #extra post configuration
             if hasattr(macro, 'getHooks'):
                 for hook in macro.getHooks('post-configuration'):
                     hook()
             self.macro.checkPoint()
-    
+
             endTimestamp = time.time()
             self.macro.info("Configuration took %s time." % repr(endTimestamp - startTimestamp))
 
             # move to start position
             self.macro.debug("Moving to start position: %s" % repr(start_pos))
             motion.move(start_pos)
-            
+
             if macro.isStopped():
                 self.on_waypoints_end()
                 return
-            
+
             # prepare motor(s) to move with their maximum velocity
             for path in motion_paths:
                 motor = path.moveable
@@ -1976,79 +2009,79 @@ class CTScan(CScan):
                 #if 0 in [path.max_vel, path.max_vel_time, path.min_vel_time]:
                 #    continue
                 motor.setVelocity(path.max_vel)
-                motor.setAcceleration(path.max_vel_time)                
+                motor.setAcceleration(path.max_vel_time)
                 motor.setDeceleration(path.min_vel_time)
-                
+
             if macro.isStopped():
                 self.on_waypoints_end()
                 return
-            
+
             if hasattr(macro, 'getHooks'):
                 for hook in macro.getHooks('pre-start'):
                     hook()
             self.macro.checkPoint()
-    
+
             self.macro.debug("Starting measurement group")
             self.__mntGrpStarted = True
-            self._measurement_group.start()            
-            
+            self._measurement_group.start()
+
             self.timestamp_to_start = time.time() + delta_start
-                        
+
             self.motion_event.set()
-            
+
             # move to waypoint end position
             self.macro.debug("Moving to waypoint position: %s" % repr(final_pos))
             self.macro.debug("Starting triggers")
             self.__triggerStarted = True
             self.extraTrigger.start()
             motion.move(final_pos)
-                        
+
             self.motion_event.clear()
 
             if macro.isStopped():
                 self.on_waypoints_end()
                 return
-            
+
             #execute post-move hooks
             for hook in waypoint.get('post-move-hooks',[]):
                 hook()
-                    
-            self.macro.debug("Waiting for measurement group to finish")            
+
+            self.macro.debug("Waiting for measurement group to finish")
             while self._measurement_group.isMoving():
                 self.macro.checkPoint()
                 time.sleep(0.1)
-                
-            self.macro.debug("Getting data")                
+
+            self.macro.debug("Getting data")
             data_list = self._measurement_group.getDataList()
-            
+
             def populate_ideal_positions():
                 moveables = self.moveables
                 nr_of_points = self.macro.nr_of_points
                 starts = self.macro.starts
                 finals = self.macro.finals
                 positions_records = [{} for i in xrange(nr_of_points)]
-                
+
                 for moveable, start, final in zip(moveables, starts, finals):
                     name = moveable.moveable.getName()
                     for point_nr, position in enumerate(np.linspace(start, \
                                                         final, nr_of_points)):
-                        positions_records[point_nr][name] = position    
-                    
+                        positions_records[point_nr][name] = position
+
                 return positions_records
-            
+
             #TODO: decide what to do with moveables
             position_list = populate_ideal_positions()
 
             self.macro.debug("Storing data")
             for data_dict, position_dict in zip(data_list,position_list):
                 data_dict.update(position_dict)
-                self.data.addRecord(data_dict)    
-            
-            if start_positions is None:  
+                self.data.addRecord(data_dict)
+
+            if start_positions is None:
                 last_positions = positions
-        
+
         self.on_waypoints_end(positions)
-        
+
     def on_waypoints_end(self, restore_positions=None):
         """To be called by the waypoint thread to handle the end of waypoints
         (either because no more waypoints or because a macro abort was
@@ -2066,20 +2099,20 @@ class CTScan(CScan):
             self.macro.info("Correcting overshoot...")
             self._physical_motion.move(restore_positions)
         self.do_restore()
-        self.motion_end_event.set()        
+        self.motion_end_event.set()
         self.motion_event.set()
         self.cleanup()
 
-    def scan_loop(self):        
+    def scan_loop(self):
         macro = self.macro
         manager = macro.getManager()
         scream = False
         motion_event = self.motion_event
         startts = self._env['startts']
-        
+
         sum_delay = 0
         sum_integ_time = 0
-        
+
         if hasattr(macro, "nr_points"):
             nr_points = float(macro.nr_points)
             scream = True
@@ -2087,30 +2120,30 @@ class CTScan(CScan):
             yield 0.0
 
         moveables = [ m.moveable for m in self.moveables ]
-        
+
         point_nb, step = -1, None
         data = self.data
-        
+
         if hasattr(macro, 'getHooks'):
             for hook in macro.getHooks('pre-scan'):
                 hook()
-                
+
         self.go_through_waypoints()
-        
-        
+
+
         if hasattr(macro, 'getHooks'):
             for hook in macro.getHooks('post-scan'):
                 hook()
-        
+
         env = self._env
         env['acqtime'] = sum_integ_time
         env['delaytime'] = sum_delay
-        
+
         if not scream:
-            yield 100.0   
+            yield 100.0
 
     def cleanup(self):
-        '''This method is responsible for restoring state of measurement group 
+        '''This method is responsible for restoring state of measurement group
         and trigger to its state before the scan.'''
         startTimestamp = time.time()
 
@@ -2163,11 +2196,11 @@ class CTScan(CScan):
 
 class HScan(SScan):
     """Hybrid scan"""
-    
+
     def stepUp(self, n, step, lstep):
         motion, mg = self.motion, self.measurement_group
         startts = self._env['startts']
-        
+
         #pre-move hooks
         for hook in step.get('pre-move-hooks',()):
             hook()
@@ -2177,12 +2210,12 @@ class HScan(SScan):
                 raise
             except:
                 pass
-                
+
         positions, integ_time = step['positions'], step['integ_time']
-        
+
         try:
             m_ID = motion.startMove(positions)
-            mg_ID = mg.startCount(integ_time)    
+            mg_ID = mg.startCount(integ_time)
         except InterruptException:
             raise
         except:
@@ -2198,12 +2231,12 @@ class HScan(SScan):
             self.dump_information(n, step)
             raise
         self._sum_acq_time += integ_time
-        
+
         curr_time = time.time()
         dt = curr_time - startts
-                
-        m_state, m_positions = motion.readState(), motion.readPosition()       
-         
+
+        m_state, m_positions = motion.readState(), motion.readPosition()
+
         if m_state != Ready:
             self.dump_information(n, step)
             m = "Scan aborted after problematic motion: " \
@@ -2211,18 +2244,18 @@ class HScan(SScan):
             raise ScanException({ 'msg' : m })
 
         data_line = mg.getValues()
-        
+
         # Add final moveable positions
         data_line['point_nb'] = n
         data_line['timestamp'] = dt
         for i, m in enumerate(self.moveables):
             data_line[m.moveable.getName()] = m_positions[i]
-        
+
         #Add extra data coming in the step['extrainfo'] dictionary
         if step.has_key('extrainfo'): data_line.update(step['extrainfo'])
-        
+
         self.data.addRecord(data_line)
-    
+
         #post-step hooks
         for hook in step.get('post-step-hooks',()):
             hook()
@@ -2232,7 +2265,7 @@ class HScan(SScan):
                 raise
             except:
                 pass
-        
+
     def dump_information(self, n, step):
         moveables = self.motion.moveable_list
         msg = ["Report: Stopped at step #" + str(n) + " with:"]
diff --git a/src/sardana/macroserver/scan/recorder/__init__.py b/src/sardana/macroserver/scan/recorder/__init__.py
index 8f71c3f..5c8ec1b 100644
--- a/src/sardana/macroserver/scan/recorder/__init__.py
+++ b/src/sardana/macroserver/scan/recorder/__init__.py
@@ -28,6 +28,5 @@
 __docformat__ = 'restructuredtext'
 
 from .datarecorder import *
-from .output import *
 from .sharedmemory import *
 from .storage import *
\ No newline at end of file
diff --git a/src/sardana/macroserver/scan/recorder/datarecorder.py b/src/sardana/macroserver/scan/recorder/datarecorder.py
index ceb8538..d5ddc68 100644
--- a/src/sardana/macroserver/scan/recorder/datarecorder.py
+++ b/src/sardana/macroserver/scan/recorder/datarecorder.py
@@ -25,8 +25,7 @@
 
 """This is the macro server scan data recorder module"""
 
-__all__ = ["DataFormats", "SaveModes", "RecorderStatus", "DataHandler",
-           "DataRecorder", "DumbRecorder"]
+__all__ = ["SaveModes", "RecorderStatus", "DataHandler", "DataRecorder"]
 
 __docformat__ = 'restructuredtext'
 
@@ -35,8 +34,6 @@ import time
 from taurus.core.util.log import Logger
 from taurus.core.util.enumeration import Enumeration
 
-DataFormats = Enumeration('DataFormats', ('Spec', 'CSV', 'XLS', 'w5', 'w4',
-                                          'wx', 'fio'))
 SaveModes = Enumeration('SaveModes', ('Record', 'Block'))
 RecorderStatus = Enumeration('RecorderStatus', ('Idle', 'Active', 'Disable'))
 
@@ -157,24 +154,3 @@ class DataRecorder(Logger):
 
     def _addCustomData(self, value, name, **kwargs):
         pass
-
-
-class DumbRecorder(DataRecorder):
-    def _startRecordList(self, recordlist):
-        print "Starting new recording"
-        print "# Title :     ", recordlist.getEnvironValue('title')
-        env = recordlist.getEnviron()
-        for envky in env.keys():
-            if envky != 'title' and envky != 'labels':
-                print "# %8s :    %s " % (envky, str(env[envky]))
-        print "# Started:    ", time.ctime(env['starttime'])
-        print "# L:  ",
-        print "  ".join(env['labels'])
-
-    def _writeRecord(self, record):
-        print record.data
-
-    def _endRecordList(self, recordlist):
-        print "Ending recording"
-        env = recordlist.getEnviron()
-        print "Recording ended at: ", time.ctime(env['endtime'])
diff --git a/src/sardana/macroserver/scan/recorder/sharedmemory.py b/src/sardana/macroserver/scan/recorder/sharedmemory.py
index a5f3b63..6bc1d7e 100644
--- a/src/sardana/macroserver/scan/recorder/sharedmemory.py
+++ b/src/sardana/macroserver/scan/recorder/sharedmemory.py
@@ -25,296 +25,26 @@
 
 """This is the macro server scan data output recorder module"""
 
-__all__ = ["SharedMemoryRecorder"]
+__all__ = ["BaseSharedMemoryRecorder", "SharedMemoryRecorder"]
 
 __docformat__ = 'restructuredtext'
 
-import time
-import numpy
-import operator
-
 from sardana.macroserver.scan.recorder.datarecorder import DataRecorder
 
-SPS_AVAILABLE = False
-try:
-    import sps
-    SPS_AVAILABLE = True
-except:
-    pass
 
-class _SharedMemoryRecorder(DataRecorder):
+class BaseSharedMemoryRecorder(DataRecorder):
 
     def __init__(self, **pars):
         DataRecorder.__init__(self, **pars)
 
+_SharedMemoryRecorder = BaseSharedMemoryRecorder # for backwards compatibility
 
-class SPSRecorder(_SharedMemoryRecorder):
-
-    maxenv = 50
-    envlen = 1024
-
-    def __init__(self, program=None, array=None, shape=None, **kwpars):
-        """ @param[in] program SPS program name
-            @param[in] array SPS array name
-            @param[in] shape tuple (cols, rows)
-            @param[in] pars keyword extra parameters
-        """
-        _SharedMemoryRecorder.__init__(self, **kwpars)
-        self.shape = shape
-        self.owner = False
-        self.owner_ENV = False
-        self.setID(program, array)
-
-    def setID(self, program, array):
-        self.program = program.replace('/', '')
-        self.array = array.replace('_', '')
-        if not array is None:
-            self.array_ENV = "%s_ENV" % self.array
-        else:
-            self.array_ENV = None
-        if program and array:
-            self.init()
-
-    def init(self):
-        pass
-
-    def setSize(self, rows, cols):
-        self.shape = (cols, rows)
-        self.rows = rows
-        self.cols = cols
-
-    def isInitialized(self):
-        ret = not (self.program is None or self.array_ENV is None or self.array is None)
-        return ret and not self.shape is None
-
-    def putEnv(self, name, value):
-        if not self.isInitialized(): return
-        sps.putenv(self.program, self.array_ENV, name, str(value))
-
-    def putAllEnv(self, d):
-        if not self.isInitialized(): return
-        p, a = self.program, self.array_ENV
-        for k, v in d.iteritems():
-            sps.putenv(p, a, k, str(v))
-
-    def _startRecordList(self, recordlist):
-        if not self.isInitialized(): return
-
-        arraylist = sps.getarraylist(self.program)
-
-        if self.array in arraylist:
-            shm = sps.attach(self.program, self.array)
-        else:
-            cols, rows = self.shape
-            sps.create(self.program, self.array, rows, cols, sps.DOUBLE)
-            self.owner = True
-
-        if self.array_ENV in arraylist:
-            shm_env = sps.attach(self.program, self.array_ENV)
-        else:
-            sps.create(self.program, self.array_ENV, self.maxenv, self.envlen,
-                       sps.STRING)
-            self.owner_ENV = True
-
-        self.nopts = 0
-
-        env = recordlist.getEnviron()
-
-        self.labels = [ col.label for col in env['datadesc'] ]
-
-        env = {     'title' : env['title'],
-                  'started' : env['starttime'].ctime(),
-                    'ended' : '',
-               'axistitles' : ' '.join(self.labels),
-                   'ylabel' : 'Counts',
-                    'nopts' : self.nopts,
-                     'xbeg' : 0,
-                     'xend' : 200,
-                  'aborted' : 0,
-                  'command' : 'done',
-                'fitresult' : '0' }
-
-        self.putAllEnv(env)
-
-    def _writeRecord(self, record):
-        if not self.isInitialized(): return
-
-        vals = []
-
-        for colname in self.labels:
-            val = record.data.get(colname)
-            if (not val is None) and (operator.isNumberType(val) and (type(val) in [int, float, long])):
-                vals.append(val)
-            elif (not val is None) and (operator.isNumberType(val)):
-                valsmca = []
-                for i in range(0, len(val)):
-                    valsmca.append(val[i])
-                sufix = "1D"
-                if self.array.endswith(sufix):
-                    valsmca = numpy.array(valsmca)
-                    sps.putdatarow(self.program, self.array, record.recordno, valsmca)
-
-        sufix = "0D"
-        if self.array.endswith(sufix):
-            vals = numpy.array(vals)
-            sps.putdatarow(self.program, self.array, record.recordno, vals)
-
-        self.nopts += 1
-
-        env = {   'nopts' : self.nopts,
-                   'peak' : 111,
-                'peakpos' : 34,
-                   'fwhm' : 12.3,
-                'fwhmpos' : 45,
-                    'com' : 23 }
-
-        self.putAllEnv(env)
-
-    def _endRecordList(self, recordlist):
-        if not self.isInitialized(): return
-        env = recordlist.getEnviron()
-        self.putEnv('ended', env.get('endtime').ctime())
-
-
-class ShmRecorder(DataRecorder):
-
-    """ Sets data in shared memory to be used by sps """
-
-    maxenv = 50
-    envlen = 1024
-
-    def setShmID(self, shmid):
-        self.shm_id = shmid
-        self.shm_id_ENV = shmid + "_ENV"
-
-    def setShmMntGrp(self, mnt_grp):
-        self.mnt_grp = mnt_grp
-
-    def setProgram(self, progname):
-        self.progname = progname
-
-    def isInitialized(self):
-        try:
-            getattr(self, "shm_id")
-            getattr(self, "shm_id_env")
-            getattr(self, "progname")
-            return True
-        except:
-            return False
-
-    def setSize(self, rows, cols):
-        self.rows = rows
-        self.cols = cols
-
-    def putenv(self, name, value):
-        sps.putenv(self.progname, self.shm_id_env, name, str(value))
-
-    def setChanDimList(self, chandimlist):
-        self.chandimlist = chandimlist
-
-    def _startRecordList(self, recordlist):
-
-        if not self.isInitialized(): return
-
-        arraylist = sps.getarraylist(self.progname)
-
-        if self.shm_id in arraylist:
-            shm = sps.attach(self.progname, self.shm_id)
-        else:
-            sps.create(self.progname, self.shm_id, self.rows, self.cols,
-                       sps.DOUBLE)
-
-        if self.shm_id_env in arraylist:
-            shm_env = sps.attach(self.progname, self.shm_id_env)
-        else:
-            sps.create(self.progname, self.shm_id_env, self.maxenv, self.envlen,
-                       sps.STRING)
-
-        print "Starting new SHM recording"
-
-        self.putenv('title', recordlist.getEnvironValue('title'))
-
-        for env, val in recordlist.getEnviron().items():
-           if env != 'title' and env != 'labels':
-               self.putenv(env , val)
-
-        self.nopts = 0
-
-        self.putenv('started', time.ctime(recordlist.getEnvironValue('starttime')))
-        self.putenv('ended', '')
-        self.putenv('axistitles', ' '.join(recordlist.getEnvironValue('labels')))
-        self.putenv('ylabel', 'Counts')
-        self.putenv('nopts', self.nopts)
-        self.putenv('xbeg', 100)
-        self.putenv('xend', 200)
-        self.putenv('aborted', 0)
-        self.putenv('command', 'done')
-        self.putenv('fitresult', '0')
-
-        self.labels = recordlist.getEnvironValue('labels')
-
-    def _writeRecord(self, record):
-        # uhmm. only numeric values can be written
-
-        if not self.isInitialized(): return
-
-        vals = []
-
-        dim_list = []
-        for colname in self.labels:
-            dim_list.append(0)
-            val = record.data.get(colname)
-            if (not val is None) and (type(val) in [int, float, long]):
-                vals.append(val)
-
-        myj = 0
-
-        for val2 in record.data:
-           tmp = val2 + '_value'
-           #esto me da el nombre del canal
-           for dim in self.chandimlist:
-              if tmp == dim:
-                 dim_list[myj] = self.chandimlist[dim]
-                 myj = myj + 1
-
-        myj = 0
-
-        for val2 in record.data.values():
-           valsmca = []
-           if type(val2) in [list]:
-              if dim_list[myj] == 1:
-                 for i in range(0, len(val2)):
-                    valsmca.append(val2[i])
-                 tmp_name = self.mnt_grp + "_1D"
-                 if self.shm_id == tmp_name:
-                    valsmca = numpy.array(valsmca)
-                    sps.putdatarow(self.progname, self.shm_id, record.recordno, valsmca)
-           myj = myj + 1
-
-        vals = numpy.array(vals)
-        tmp_name = self.mnt_grp + "_0D"
-        if self.shm_id == tmp_name:
-           sps.putdatarow(self.progname, self.shm_id, record.recordno, vals)
-
-        self.nopts += 1
-        self.putenv('nopts', self.nopts)
-        self.putenv('peak', 111)
-        self.putenv('peakpos', 34)
-        self.putenv('fwhm', 12.3)
-        self.putenv('fwhmpos', 45)
-        self.putenv('com', 23)
-
-
-    def _endRecordList(self, recordlist):
-        if not self.isInitialized(): return
-        self.putenv('ended', time.ctime(recordlist.getEnvironValue('endtime')))
-
-
-def SharedMemoryRecorder(type, **pars):
-    global SPS_AVAILABLE
-    if type == 'sps' and SPS_AVAILABLE:
-        klass = SPSRecorder
+def SharedMemoryRecorder(type, macro, **pars):
+    """Factory to create shared memory recorders based on the type
+    """
+    rec_manager = macro.getMacroServer().recorder_manager
+    if type == 'sps':
+        klass = rec_manager.getRecorderClass('SPSRecorder')
     else:
-        return
-
+        raise Exception('SharedMemory %s is not supported.' % type)
     return klass(**pars)
diff --git a/src/sardana/macroserver/scan/recorder/storage.py b/src/sardana/macroserver/scan/recorder/storage.py
index f4c2dab..f9e3fb7 100644
--- a/src/sardana/macroserver/scan/recorder/storage.py
+++ b/src/sardana/macroserver/scan/recorder/storage.py
@@ -25,7 +25,8 @@
 
 """This is the macro server scan data output recorder module"""
 
-__all__ = ["BaseFileRecorder", "FileRecorder"]
+__all__ = ["AmbiguousRecorderError", "BaseFileRecorder", 
+           "BaseNAPI_FileRecorder", "BaseNEXUS_FileRecorder","FileRecorder"]
 
 __docformat__ = 'restructuredtext'
 
@@ -41,10 +42,15 @@ import PyTango
 from sardana.taurus.core.tango.sardana import PlotType
 from sardana.macroserver.macro import Type
 from sardana.macroserver.scan.recorder.datarecorder import DataRecorder, \
-    DataFormats, SaveModes
+    SaveModes
+from sardana.macroserver.msexception import MacroServerException
 from taurus.core.util.containers import chunks
 
 
+class AmbiguousRecorderError(MacroServerException):
+    pass
+
+
 class BaseFileRecorder(DataRecorder):
     def __init__(self, **pars):
         DataRecorder.__init__(self, **pars)
@@ -61,424 +67,12 @@ class BaseFileRecorder(DataRecorder):
         return '<unknown>'
 
 
-class FIO_FileRecorder(BaseFileRecorder):
-    """ Saves data to a file """
-
-    formats = { DataFormats.fio : '.fio' }
-
-    def __init__(self, filename=None, macro=None, **pars):
-        BaseFileRecorder.__init__(self)
-        self.base_filename = filename
-        if macro:
-            self.macro = macro
-        self.db = PyTango.Database()
-        if filename:
-            self.setFileName(self.base_filename)
-
-    def setFileName(self, filename):
-        if self.fd != None: 
-            self.fd.close()
-   
-        dirname = os.path.dirname(filename)
-        
-        if not os.path.isdir(dirname):
-            try:
-                os.makedirs(dirname)
-            except:
-                self.filename = None
-                return
-        self.currentlist = None
-        #
-        # construct the filename, e.g. : /dir/subdir/etcdir/prefix_00123.fio
-        #
-        tpl = filename.rpartition('.')
-        try: # For avoiding error when calling at __init__
-            serial = self.recordlist.getEnvironValue('serialno')
-            self.filename = "%s_%05d.%s" % (tpl[0], serial, tpl[2])
-            #
-            # in case we have MCAs, prepare the dir name
-            #
-            self.mcaDirName = "%s_%05d" % (tpl[0], serial)
-        except:
-            self.filename = "%s_%s.%s" % (tpl[0], "[ScanId]", tpl[2])
-
-    def getFormat(self):
-        return DataFormats.whatis(DataFormats.fio)
-    
-    def _startRecordList(self, recordlist):
-
-        if self.base_filename is None:
-            return
-
-        self.setFileName(self.base_filename)
-        
-        envRec = recordlist.getEnviron()
-
-        self.sampleTime = envRec['estimatedtime'] / (envRec['total_scan_intervals'] + 1)
-        #datetime object
-        start_time = envRec['starttime']
-        
-        self.motorNames = envRec[ 'ref_moveables']
-        self.mcaNames = []
-        self.ctNames = []
-        for e in envRec['datadesc']:
-            if len( e.shape) == 1:
-                self.mcaNames.append( e.name)
-            else:
-                self.ctNames.append( e.name)
-        #
-        # we need the aliases for the column description
-        #
-        self.mcaAliases = []
-        for mca in self.mcaNames:
-            lst = mca.split("/")
-            self.mcaAliases.append( self.db.get_alias( "/".join( lst[1:])))
-
-        # self.names = [ e.name for e in envRec['datadesc'] ]
-        self.fd = open( self.filename,'w')
-        #
-        # write the comment section of the header
-        #
-        self.fd.write("!\n! Comments\n!\n%%c\n %s\nuser %s Acquisition started at %s\n" % 
-                      (envRec['title'], envRec['user'], start_time.ctime()))
-        self.fd.flush()
-        #
-        # write the parameter section, including the motor positions, if needed
-        #
-        self.fd.write("!\n! Parameter\n!\n%p\n")
-        self.fd.flush()
-        env = self.macro.getAllEnv()
-        if env.has_key( 'FlagFioWriteMotorPositions') and env['FlagFioWriteMotorPositions'] == True:
-            all_motors = self.macro.findObjs('.*', type_class=Type.Motor)
-            all_motors.sort()
-            for mot in all_motors:
-                pos = mot.getPosition()
-                if pos is None:
-                    record = "%s = nan\n" % (mot)
-                else:
-                    record = "%s = %g\n" % (mot, mot.getPosition())
-                    
-                self.fd.write( record)
-            self.fd.flush()
-        #
-        # write the data section starting with the description of the columns
-        #
-        self.fd.write("!\n! Data\n!\n%d\n")
-        self.fd.flush()
-        i = 1
-        for col in envRec[ 'datadesc']:
-            if col.name == 'point_nb':
-                continue
-            if col.name == 'timestamp':
-                continue
-            dType = 'FLOAT'
-            if col.dtype == 'float64':
-                dType = 'DOUBLE'
-            outLine = " Col %d %s %s\n" % ( i, col.label, dType)
-            self.fd.write( outLine)
-            i += 1
-        #
-        # 11.9.2012 timestamp to the end
-        #
-        outLine = " Col %d %s %s\n" % ( i, 'timestamp', 'DOUBLE')
-        self.fd.write( outLine)
-
-        self.fd.flush()
-
-    def _writeRecord(self, record):
-        if self.filename is None:
-            return
-        nan, ctNames, fd = float('nan'), self.ctNames, self.fd
-        outstr = ''
-        for c in ctNames:
-            if c == "timestamp" or c == "point_nb":
-                continue
-            outstr += ' ' + str(record.data.get(c, nan))
-        #
-        # 11.9.2012 timestamp to the end
-        #
-        outstr += ' ' + str(record.data.get('timestamp', nan))
-        outstr += '\n'
-        
-        fd.write( outstr )
-        fd.flush()
-
-        if len( self.mcaNames) > 0:
-            self._writeMcaFile( record)
-
-    def _endRecordList(self, recordlist):
-        if self.filename is None:
-            return
-
-        envRec = recordlist.getEnviron()
-        end_time = envRec['endtime'].ctime()
-        self.fd.write("! Acquisition ended at %s\n" % end_time)
-        self.fd.flush()
-        self.fd.close()
-
-    def _writeMcaFile( self, record):
-        if self.mcaDirName is None:
-            return
-
-        if not os.path.isdir( self.mcaDirName):
-            try:
-                os.makedirs( self.mcaDirName)
-            except:
-                self.mcaDirName = None
-                return
-        currDir = os.getenv( 'PWD')
-        os.chdir( self.mcaDirName)
-
-        serial = self.recordlist.getEnvironValue('serialno')
-        if type(self.recordlist.getEnvironValue('ScanFile')).__name__ == 'list':
-            scanFile = self.recordlist.getEnvironValue('ScanFile')[0]
-        else:
-            scanFile = self.recordlist.getEnvironValue('ScanFile')
-
-        mcaFileName = "%s_%05d_mca_s%d.fio" % (scanFile.split('.')[0], serial, record.data['point_nb'] + 1)
-        fd = open( mcaFileName,'w')
-        fd.write("!\n! Comments\n!\n%%c\n Position %g, Index %d \n" % 
-                      ( record.data[ self.motorNames[0]], record.data[ 'point_nb']))
-        fd.write("!\n! Parameter \n%%p\n Sample_time = %g \n" % ( self.sampleTime))
-        self.fd.flush()
-
-        col = 1
-        fd.write("!\n! Data \n%d \n")
-        for mca in self.mcaAliases:
-            fd.write(" Col %d %s FLOAT \n" % (col, mca))
-            col = col + 1
-
-        if not record.data[ self.mcaNames[0]] is None:
-            #print "+++storage.py, recordno", record.recordno
-            #print "+++storage.py, record.data", record.data
-            #print "+++storage.py, len %d,  %s" % (len( record.data[ self.mcaNames[0]]), self.mcaNames[0])
-            #
-            # the MCA arrays me be of different size. the short ones are extended by zeros.
-            #
-            lMax = len( record.data[ self.mcaNames[0]])
-            for mca in self.mcaNames:
-                if len(record.data[ mca]) > lMax:
-                    lMax = len(record.data[ mca])
-                    
-            for i in range( 0, lMax):
-                line = ""
-                for mca in self.mcaNames:
-                    if i > (len(record.data[mca]) - 1):
-                        line = line + " 0"
-                    else:
-                        line = line + " " + str( record.data[ mca][i])
-                line = line + "\n"
-                fd.write(line)
-            
-            fd.close()
-        else:
-            #print "+++storage.py, recordno", record.recordno, "data None"
-            pass
-            
-        os.chdir( currDir)
-
-class SPEC_FileRecorder(BaseFileRecorder):
-    """ Saves data to a file """
-
-    formats = { DataFormats.Spec : '.spec' }
-    supported_dtypes = ('float32','float64','int8',
-                        'int16','int32','int64','uint8',
-                        'uint16','uint32','uint64')
-
-    def __init__(self, filename=None, macro=None, **pars):
-        BaseFileRecorder.__init__(self)
-        if filename:
-            self.setFileName(filename)
-    
-    def setFileName(self, filename):
-        if self.fd != None:
-            self.fd.close()
-   
-        dirname = os.path.dirname(filename)
-        
-        if not os.path.isdir(dirname):
-            try:
-                os.makedirs(dirname)
-            except:
-                self.filename = None
-                return
-        self.filename    = filename
-        self.currentlist = None
-
-    def getFormat(self):
-        return DataFormats.whatis(DataFormats.Spec)
-    
-    def _startRecordList(self, recordlist):
-        '''Prepares and writes the scan header.'''
-        if self.filename is None:
-            return
-
-        env = recordlist.getEnviron()
-        
-        #datetime object
-        start_time = env['starttime']
-        epoch = time.mktime(start_time.timetuple())
-        serialno = env['serialno']
-        
-        #store names for performance reason
-        labels = []
-        names = []
-        for e in env['datadesc']:
-            dims = len(e.shape)
-            if not dims or (dims == 1 and e.shape[0] == 1):
-                sanitizedlabel = "".join(x for x in e.label.replace(' ', '_') if x.isalnum() or x == '_')  #substitute whitespaces by underscores and remove other non-alphanumeric characters
-                labels.append(sanitizedlabel)
-                names.append(e.name)
-        self.names = names
-        
-        # prepare pre-scan snapshot
-        snapshot_labels, snapshot_values = self._preparePreScanSnapshot(env)
-        # format scan header
-        data = {
-                'serialno':  serialno,
-                'title':     env['title'],
-                'user':      env['user'],
-                'epoch':     epoch,
-                'starttime': start_time.ctime(),
-                'nocols':    len(names),
-                'labels':    '  '.join(labels)
-               }
-        #Compatibility with PyMca
-        if os.path.exists(self.filename):
-            header = '\n'
-        else:
-            header = ''
-        header += '#S %(serialno)s %(title)s\n'
-        header += '#U %(user)s\n'
-        header += '#D %(epoch)s\n'
-        header += '#C Acquisition started at %(starttime)s\n'
-        # add a pre-scan snapshot (sep is two spaces for labels!!)
-        header += self._prepareMultiLines('O', '  ', snapshot_labels)
-        header += self._prepareMultiLines('P', ' ', snapshot_values)
-        header += '#N %(nocols)s\n'
-        header += '#L %(labels)s\n'
-        
-        self.fd = open(self.filename,'a')
-        self.fd.write(header % data )
-        self.fd.flush()
-        
-    def _prepareMultiLines(self, character, sep, items_list):
-        '''Translate list of lists of items into multiple line string
-        
-        :param character (string): each line will start #<character><line_nr>
-        :sep: separator (string): separator to use between items
-        :param items_list (list):list of lists of items
-        
-        :return multi_lines (string): string with all the items'''
-        multi_lines = ''
-        for nr, items in enumerate(items_list):
-            start = '#%s%d ' % (character, nr)
-            items_str = sep.join(map(str, items))
-            end = '\n'
-            line = start + items_str + end
-            multi_lines += line 
-        return multi_lines
-    
-    def _preparePreScanSnapshot(self, env):
-        '''Extract pre-scan snapshot, filters elements of shape different 
-        than scalar and split labels and values into chunks of 8 items.
-        
-        :param: env (dict) scan environment
-        
-        :return: labels, values (tuple<list,list>)
-                 labels - list of chunks with 8 elements containing labels 
-                 values - list of chunks with 8 elements containing values    
-        '''
-        # preScanSnapShot is a list o ColumnDesc objects
-        pre_scan_snapshot = env.get('preScanSnapShot',[])
-        labels = []; values = []
-        for column_desc in pre_scan_snapshot:
-            shape = column_desc.shape # shape is a tuple of dimensions
-            label = column_desc.label
-            dtype = column_desc.dtype
-            pre_scan_value = column_desc.pre_scan_value
-            # skip items with shape different than scalar
-            if  len(shape) > 0:
-                self.info('Pre-scan snapshot of "%s" will not be stored.' + \
-                          ' Reason: value is non-scalar', label)
-                continue
-            if dtype not in self.supported_dtypes:
-                self.info('Pre-scan snapshot of "%s" will not be stored.' + \
-                          ' Reason: type %s not supported', label, dtype)
-                continue
-            labels.append(label)
-            values.append(pre_scan_value)
-        # split labels in chunks o 8 items
-        labels_chunks = list(chunks(labels, 8))
-        values_chunks = list(chunks(values, 8))
-        return labels_chunks, values_chunks
-        
-    def _writeRecord(self, record):
-        if self.filename is None:
-            return
-        nan, names, fd = float('nan'), self.names, self.fd
-        
-        d = []
-        for c in names:
-            data = record.data.get(c)
-            if data is None: data = nan
-            d.append(str(data))
-        outstr  = ' '.join(d)
-        outstr += '\n'
-        
-        fd.write( outstr )
-        fd.flush()
-
-    def _endRecordList(self, recordlist):
-        if self.filename is None:
-            return
-
-        env = recordlist.getEnviron()
-        end_time = env['endtime'].ctime()
-        self.fd.write("#C Acquisition ended at %s\n" % end_time)
-        self.fd.flush()
-        self.fd.close()
-
-                    
-    def _addCustomData(self, value, name, **kwargs):
-        '''
-        The custom data will be added as a comment line in the form:: 
-        
-        #C name : value
-        
-        ..note:: non-scalar values (or name/values containing end-of-line) will not be written
-        '''
-        if self.filename is None:
-            self.info('Custom data "%s" will not be stored in SPEC file. Reason: uninitialized file',name)
-            return
-        if numpy.rank(value) > 0:  #ignore non-scalars
-            self.info('Custom data "%s" will not be stored in SPEC file. Reason: value is non-scalar', name)
-            return
-        v = str(value)
-        if '\n' in v or '\n' in name: #ignore if name or the string representation of the value contains end-of-line
-            self.info('Custom data "%s" will not be stored in SPEC file. Reason: unsupported format',name)
-            return
-        
-        fileWasClosed = self.fd is None or self.fd.closed
-        if fileWasClosed:
-            try:
-                self.fd = open(self.filename,'a')
-            except:
-                self.info('Custom data "%s" will not be stored in SPEC file. Reason: cannot open file',name)
-                return
-        self.fd.write('#C %s : %s\n' % (name, v))
-        self.fd.flush()
-        if fileWasClosed:
-            self.fd.close() #leave the file descriptor as found
-        
-        
-
 class BaseNEXUS_FileRecorder(BaseFileRecorder):
     """Base class for NeXus file recorders"""   
     
-    formats = { DataFormats.w5 : '.h5', 
-                DataFormats.w4 : '.h4', 
-                DataFormats.wx : '.xml' }
+    formats = {'w5': '.h5',
+               'w4': '.h4',
+               'wx': '.xml'}
     supported_dtypes = ('float32','float64','int8',
                         'int16','int32','int64','uint8',
                         'uint16','uint32','uint64') #note that 'char' is not supported yet!
@@ -509,11 +103,11 @@ class BaseNEXUS_FileRecorder(BaseFileRecorder):
         #obtain preferred nexus file mode for writing from the filename extension (defaults to hdf5)
         extension = os.path.splitext(filename)[1]
         inv_formats = dict(itertools.izip(self.formats.itervalues(), self.formats.iterkeys()))
-        self.nxfilemode = inv_formats.get(extension.lower(), DataFormats.w5)
+        self.nxfilemode = inv_formats.get(extension.lower(), 'w5')
         self.currentlist = None
     
     def getFormat(self):
-        return DataFormats.whatis(self.nxfilemode)
+        return self.nxfilemode
     
     def sanitizeName(self, name):
         '''It returns a version of the given name that can be used as a python
@@ -538,7 +132,7 @@ class BaseNEXUS_FileRecorder(BaseFileRecorder):
         if fd is None:
             fd = getattr(src,'nxfile', getattr(dst,'nxfile'))
         if fd is None:
-            raise NeXusError('Cannot get a file handle')
+            raise self.nxs.NeXusError('Cannot get a file handle')
         
         if isinstance(src, self.nxs.NXobject):
             src = src.nxpath
@@ -692,609 +286,26 @@ class BaseNAPI_FileRecorder(BaseNEXUS_FileRecorder):
             except:
                 self.fd.makegroup(g, group_type)
                 self.fd.opengroup(g, group_type)
-                
-
-class NXscan_FileRecorder(BaseNAPI_FileRecorder):
-    """saves data to a nexus file that follows the NXscan application definition
-    
-        """
-
-    def __init__(self, filename=None, macro=None, overwrite=False, **pars):
-        BaseNAPI_FileRecorder.__init__(self, filename=filename, macro=macro, overwrite=overwrite, **pars)
-            
-    def _startRecordList(self, recordlist):
-        nxs = self.nxs
-        nxfilemode = self.getFormat()
-        
-        if self.filename is None:
-            return
-        
-        self.currentlist = recordlist
-        env = self.currentlist.getEnviron()
-        serialno = env["serialno"]
-        self._dataCompressionRank = env.get("DataCompressionRank", self._dataCompressionRank)
-        
-        if not self.overwrite and os.path.exists(self.filename): nxfilemode='rw'
-        self.fd = nxs.open(self.filename, nxfilemode)
-        self.entryname = "entry%d" % serialno
-        try:
-            self.fd.makegroup(self.entryname,"NXentry")
-        except NeXusError:
-            entrynames = self.fd.getentries().keys()
-            
-            #===================================================================
-            ##Warn and abort
-            if self.entryname in entrynames:
-                raise RuntimeError(('"%s" already exists in %s. To prevent data corruption the macro will be aborted.\n'%(self.entryname, self.filename)+
-                                    'This is likely caused by a wrong ScanID\n'+
-                                    'Possible workarounds:\n'+
-                                    '  * first, try re-running this macro (the ScanID may be automatically corrected)\n'
-                                    '  * if not, try changing ScanID with senv, or...\n'+
-                                    '  * change the file name (%s will be in both files containing different data)\n'%self.entryname+
-                                    '\nPlease report this problem.'))
-            else:
-                raise              
-            #===================================================================
-            
-            #===================================================================
-            ## Warn and continue writing to another entry
-            #if self.entryname in entrynames:
-            #    i = 2
-            #    newname = "%s_%i"%(self.entryname,i)
-            #    while(newname in entrynames):
-            #        i +=1
-            #        newname = "%s_%i"%(self.entryname,i)
-            #    self.warning('"%s" already exists. Using "%s" instead. This may indicate a bug in %s',self.entryname, newname, self.macro.name)
-            #    self.macro.warning('"%s" already exists. Using "%s" instead. \nThis may indicate a bug in %s. Please report it.',self.entryname, newname, self.macro.name)
-            #    self.entryname = newname
-            #    self.fd.makegroup(self.entryname,"NXentry")
-            #===================================================================
-            
-        self.fd.opengroup(self.entryname,"NXentry") 
-        
-        
-        #adapt the datadesc to the NeXus requirements
-        self.datadesc = []
-        for dd in env['datadesc']:
-            dd = dd.clone()
-            dd.label = self.sanitizeName(dd.label)
-            if dd.dtype == 'bool':
-                dd.dtype = 'int8'
-                self.debug('%s will be stored with type=%s',dd.name,dd.dtype)
-            if dd.dtype in self.supported_dtypes:
-                self.datadesc.append(dd)
-            else:
-                self.warning('%s will not be stored. Reason: type %s not supported',dd.name,dd.dtype)
-                        
-        #make a dictionary out of env['instrumentlist'] (use fullnames -paths- as keys)
-        self.instrDict = {}
-        for inst in env.get('instrumentlist', []):
-            self.instrDict[inst.getFullName()] = inst
-        if self.instrDict is {}:
-            self.warning("missing information on NEXUS structure. Nexus Tree won't be created")
-        
-        self.debug("starting new recording %d on file %s", env['serialno'], self.filename)
-
-        #populate the entry with some data
-        self._writeData('definition', 'NXscan', 'char') #this is the Application Definition for NeXus Generic Scans
-        import sardana.release
-        program_name = "%s (%s)" % (sardana.release.name, self.__class__.__name__)
-        self._writeData('program_name', program_name, 'char', attrs={'version':sardana.release.version})
-        self._writeData("start_time",env['starttime'].isoformat(),'char') #note: the type should be NX_DATE_TIME, but the nxs python api does not recognize it
-        self.fd.putattr("epoch",time.mktime(env['starttime'].timetuple()))
-        self._writeData("title",env['title'],'char')
-        self._writeData("entry_identifier",str(env['serialno']),'char')
-        self.fd.makegroup("user","NXuser") #user data goes in a separate group following NX convention...
-        self.fd.opengroup("user","NXuser")
-        self._writeData("name",env['user'],'char')
-        self.fd.closegroup()
-        
-        #prepare the "measurement" group
-        self._createBranch("measurement:NXcollection")
-        if self.savemode == SaveModes.Record:
-            #create extensible datasets
-            for dd in self.datadesc:
-                self._makedata(dd.label, dd.dtype, [nxs.UNLIMITED] + list(dd.shape), chunks=[1] + list(dd.shape))  #the first dimension is extensible
-                if hasattr(dd, 'data_units'):
-                    self.fd.opendata(dd.label)
-                    self.fd.putattr('units', dd.data_units)
-                    self.fd.closedata()
-                    
-        else:
-            #leave the creation of the datasets to _writeRecordList (when we actually know the length of the data to write)
-            pass
-        
-        self._createPreScanSnapshot(env)
-            
-        self.fd.flush()
-    
-    def _createPreScanSnapshot(self, env):
-        #write the pre-scan snapshot in the "measurement:NXcollection/pre_scan_snapshot:NXcollection" group
-        self.preScanSnapShot = env.get('preScanSnapShot',[])
-        self._createBranch('measurement:NXcollection/pre_scan_snapshot:NXcollection')
-        links = {}
-        for dd in self.preScanSnapShot: #desc is a ColumnDesc object
-            label = self.sanitizeName(dd.label)
-            dtype = dd.dtype
-            pre_scan_value = dd.pre_scan_value
-            if dd.dtype == 'bool':
-                dtype = 'int8'
-                pre_scan_value = numpy.int8(dd.pre_scan_value)
-                self.debug('Pre-scan snapshot of %s will be stored with type=%s',dd.name, dtype)
-            if dtype in self.supported_dtypes:
-                nid = self._writeData(label, pre_scan_value, dtype, shape=dd.shape or (1,)) #@todo: fallback shape is hardcoded!
-                links[label] = nid
-            else:
-                self.warning('Pre-scan snapshot of %s will not be stored. Reason: type %s not supported',dd.name, dtype)
-                
-        self.fd.closegroup() #we are back at the measurement group
-        
-        measurement_entries = self.fd.getentries()
-        for label,nid in links.items():
-            if label not in measurement_entries:
-                self.fd.makelink(nid)
-         
-    def _writeRecord(self, record):
-        if self.filename is None:
-            return
-        # most used variables in the loop
-        fd, debug, warning = self.fd, self.debug, self.warning
-        nparray, npshape = numpy.array, numpy.shape
-        rec_data, rec_nb = record.data, record.recordno
-        
-        for dd in self.datadesc:
-            if record.data.has_key( dd.name ):
-                data = rec_data[dd.name]
-                fd.opendata(dd.label)
-                
-                if data is None:
-                    data = numpy.zeros(dd.shape, dtype=dd.dtype)
-                if not hasattr(data, 'shape'):
-                    data = nparray([data], dtype=dd.dtype)
-                elif dd.dtype != data.dtype.name:
-                    debug('%s casted to %s (was %s)', dd.label, dd.dtype,
-                                                      data.dtype.name)
-                    data = data.astype(dd.dtype)
-
-                slab_offset = [rec_nb] + [0] * len(dd.shape)
-                shape = [1] + list(npshape(data))
-                try:
-                    fd.putslab(data, slab_offset, shape)
-                except:
-                    warning("Could not write <%s> with shape %s", data, shape)
-                    raise
-                    
-                ###Note: the following 3 lines of code were substituted by the one above.
-                ###      (now we trust the datadesc info instead of asking the nxs file each time)
-                #shape,dtype=self.fd.getinfo()
-                #shape[0]=1 #the shape of the record is of just 1 slab in the extensible dimension (first dim)
-                #self.fd.putslab(record.data[lbl],[record.recordno]+[0]*(len(shape)-1),shape)
-                fd.closedata()
-            else:
-                debug("missing data for label '%s'", dd.label)
-        fd.flush()
-
-    def _endRecordList(self, recordlist):
-
-        if self.filename is None:
-            return
-        
-        self._populateInstrumentInfo()
-        self._createNXData()
-
-        env = self.currentlist.getEnviron()
-        self.fd.openpath("/%s:NXentry" % self.entryname)
-        self._writeData("end_time",env['endtime'].isoformat(),'char')
-        self.fd.flush()
-        self.debug("Finishing recording %d on file %s:", env['serialno'], self.filename)
-        #self.fd.show('.') #prints nexus file summary on stdout (only the current entry)
-        self.fd.close()
-        self.currentlist = None
-
-    def writeRecordList(self, recordlist):
-        """Called when in BLOCK writing mode"""
-        self._startRecordList( recordlist )
-        for dd in self.datadesc:
-            self._makedata(dd.label, dd.dtype, [len(recordlist.records)]+list(dd.shape), chunks=[1]+list(dd.shape))
-            self.fd.opendata(dd.label)
-            try:
-                #try creating a single block to write it at once
-                block=numpy.array([r.data[dd.label] for r in recordlist.records],dtype=dd.dtype)
-                #if dd.dtype !='char': block=numpy.array(block,dtype=dtype) #char not supported anyway
-                self.fd.putdata(block)
-            except KeyError:
-                #if not all the records contain this field, we cannot write it as a block.. so do it record by record (but only this field!)
-                for record in recordlist.records:
-                    if record.data.has_key( dd.label ):
-                        self.fd.putslab(record.data[dd.label],[record.recordno]+[0]*len(dd.shape),[1]+list(dd.shape)) 
-                    else:
-                        self.debug("missing data for label '%s' in record %i", dd.label, record.recordno)
-            self.fd.closedata()
-        self._endRecordList( recordlist )
-
-    def _populateInstrumentInfo(self):
-        measurementpath = "/%s:NXentry/measurement:NXcollection" % self.entryname
-        #create a link for each
-        for dd in self.datadesc:
-            if getattr(dd, 'instrument', None):  #we don't link if it is None or it is empty
-                try:
-                    datapath = "%s/%s" % (measurementpath, dd.label)
-                    self.fd.openpath(datapath)
-                    nid = self.fd.getdataID()
-                    self._createBranch(dd.instrument)
-                    self.fd.makelink(nid)
-                except Exception,e:
-                    self.warning("Could not create link to '%s' in '%s'. Reason: %s",datapath, dd.instrument, repr(e))
-                    
-        for dd in self.preScanSnapShot:
-            if getattr(dd,'instrument', None):
-                try:
-                    label = self.sanitizeName(dd.label)
-                    datapath = "%s/pre_scan_snapshot:NXcollection/%s" % (measurementpath, label)
-                    self.fd.openpath(datapath)
-                    nid = self.fd.getdataID()
-                    self._createBranch(dd.instrument)
-                    self.fd.makelink(nid)
-                except Exception,e:
-                    self.warning("Could not create link to '%s' in '%s'. Reason: %s",datapath, dd.instrument, repr(e))
-                
-    def _createNXData(self):
-        '''Creates groups of type NXdata by making links to the corresponding datasets 
-        '''        
-        #classify by type of plot:
-        plots1d = {}
-        plots1d_names = {}
-        i = 1
-        for dd in self.datadesc:
-            ptype = getattr(dd, 'plot_type', PlotType.No)
-            if ptype == PlotType.No:
-                continue
-            elif ptype == PlotType.Spectrum:
-                axes = ":".join(dd.plot_axes) #converting the list into a colon-separated string
-                if axes in plots1d:
-                    plots1d[axes].append(dd)
-                else:
-                    plots1d[axes] = [dd]
-                    plots1d_names[axes] = 'plot_%i' % i  #Note that datatesc ordering determines group name indexing
-                    i += 1
-            else:
-                continue  #@todo: implement support for images and other
-        
-        #write the 1D NXdata group
-        for axes, v in plots1d.items():
-            self.fd.openpath("/%s:NXentry" % (self.entryname))
-            groupname = plots1d_names[axes]
-            self.fd.makegroup(groupname,'NXdata')
-            #write the signals
-            for i, dd in enumerate(v):
-                src = "/%s:NXentry/measurement:NXcollection/%s" % (self.entryname, dd.label)
-                dst = "/%s:NXentry/%s:NXdata" % (self.entryname, groupname)
-                self._nxln(src, dst)
-                self.fd.opendata(dd.label)
-                self.fd.putattr('signal', min(i + 1, 2))
-                self.fd.putattr('axes', axes)
-                self.fd.putattr('interpretation', 'spectrum')
-            #write the axes
-            for axis in axes.split(':'):
-                src = "/%s:NXentry/measurement:NXcollection/%s" % (self.entryname, axis)
-                dst = "/%s:NXentry/%s:NXdata" % (self.entryname, groupname)
-                try:
-                    self._nxln(src, dst)
-                except:
-                    self.warning("cannot create link for '%s'. Skipping",axis)
-                    
-    def _addCustomData(self, value, name, nxpath=None, dtype=None, **kwargs):
-        '''
-        apart from value and name, this recorder can use the following optional parameters:
-        
-        :param nxpath: (str) a nexus path (optionally using name:nxclass notation for
-                       the group names). See the rules for automatic nxclass
-                       resolution used by
-                       :meth:`NXscan_FileRecorder._createBranch`.
-                       If None given, it defaults to 
-                       nxpath='custom_data:NXcollection'
-                       
-        :param dtype: name of data type (it is inferred from value if not given)
-                       
-        '''           
-        if nxpath is None:
-            nxpath = 'custom_data:NXcollection'
-        if dtype is None:
-            if numpy.isscalar(value):
-                dtype = numpy.dtype(type(value)).name
-                if numpy.issubdtype(dtype, str):
-                    dtype = 'char'
-                if dtype == 'bool':
-                    value, dtype = int(value), 'int8' 
-            else:
-                value = numpy.array(value)
-                dtype = value.dtype.name
-            
-        if dtype not in self.supported_dtypes and dtype != 'char':
-            self.warning("cannot write '%s'. Reason: unsupported data type",name)
-            return
-        #open the file if necessary 
-        fileWasClosed = self.fd is None or not self.fd.isopen
-        if fileWasClosed:
-            if not self.overwrite and os.path.exists(self.filename): nxfilemode = 'rw'
-            import nxs
-            self.fd = nxs.open(self.filename, nxfilemode)
-        #write the data
-        self._createBranch(nxpath)
-        try:
-            self._writeData(name, value, dtype)
-        except ValueError, e:
-            msg = "Error writing %s. Reason: %s" % (name, str(e))
-            self.warning(msg)
-            self.macro.warning(msg)
-        #leave the file as it was
-        if fileWasClosed:
-            self.fd.close()
-        
-            
-class NXxas_FileRecorder(BaseNEXUS_FileRecorder):
-    """saves data to a nexus file that follows the NXsas application definition
-    
-        """
-        
-    def __init__(self, filename=None, macro=None, overwrite=False, **pars):
-        BaseNEXUS_FileRecorder.__init__(self, filename=filename, macro=macro, overwrite=overwrite, **pars)
-        
-        
-    def _startRecordList(self, recordlist):
-        nxs = self.nxs
-        if self.filename is None:
-            return
-        
-        #get the recordlist environment
-        self.currentlist = recordlist
-        env = self.currentlist.getEnviron()
-        
-        #adapt the datadesc to the NeXus requirements
-        self.datadesc = []
-        for dd in env['datadesc']:
-            dd = dd.clone()
-            dd.label = self.sanitizeName(dd.label)
-            if dd.dtype == 'bool':
-                dd.dtype = 'int8'
-                self.debug('%s will be stored with type=%s',dd.name,dd.dtype)
-            if dd.dtype in self.supported_dtypes:
-                self.datadesc.append(dd)
-            else:
-                self.warning('%s will not be stored. Reason: type %s not supported',dd.name,dd.dtype)
-        
-        
-        serialno = env["serialno"]
-        nxfilemode = self.getFormat()
-        if not self.overwrite and os.path.exists(self.filename): nxfilemode='rw'               
-        
-        self.debug("starting new recording %d on file %s", serialno, self.filename)
-        
-        #create an nxentry and write it to file
-        self.nxentry = nxs.NXentry(name= "entry%d" % serialno)
-        self.nxentry.save(self.filename, format=nxfilemode)
-
-        #add fields to nxentry
-        import sardana.release
-        program_name = "%s (%s)" % (sardana.release.name, self.__class__.__name__)
-        self.nxentry.insert(nxs.NXfield(name='start_time', value=env['starttime'].isoformat()))
-        self.nxentry.insert(nxs.NXfield(name='title', value=env['title']))
-        self.nxentry.insert(nxs.NXfield(name='definition', value='NXxas'))
-        self.nxentry.insert(nxs.NXfield(name='epoch', value=time.mktime(env['starttime'].timetuple())))
-        self.nxentry.insert(nxs.NXfield(name='program_name', value=program_name, attrs={'version':sardana.release.version}))
-        self.nxentry.insert(nxs.NXfield(name='entry_identifier', value=env['serialno']))
-                
-        #add the "measurement" group (a NXcollection containing all counters from the mntgrp for convenience) 
-        measurement = nxs.NXcollection(name='measurement')
-        self.ddfieldsDict = {}
-        for dd in self.datadesc:
-            field = NXfield_comp(name=dd.label,
-                                 dtype=dd.dtype,
-                                 shape=[nxs.UNLIMITED] + list(dd.shape),
-                                 nxslab_dims=[1] + list(dd.shape)
-                                 )
-            if hasattr(dd,'data_units'):
-                field.attrs['units'] = dd.data_units
-            measurement.insert(field)
-            #create a dict of fields in the datadesc for easier access later on
-            self.ddfieldsDict[dd.label] = field
-        
-        self.nxentry.insert(measurement)
-        
-        #user group
-        nxuser = nxs.NXuser()
-        self.nxentry.insert(nxuser)
-        nxuser['name'] = env['user']
-
-        #sample group
-        nxsample = nxs.NXsample()
-        self.nxentry.insert(nxsample)
-        nxsample['name'] = env['SampleInfo'].get('name','Unknown')
-        
-        #monitor group
-        scan_acq_time = env.get('integ_time')
-        scan_monitor_mode = scan_acq_time>1 and 'timer' or 'monitor'
-        nxmonitor = nxs.NXmonitor(mode=scan_monitor_mode,
-                        preset=scan_acq_time)
-        self.nxentry.insert(nxmonitor)
-        monitor_data = self.ddfieldsDict[self.sanitizeName(env['monitor'])] #to be linked later on
-        
-        #instrument group
-        nxinstrument = nxs.NXinstrument()
-        self.nxentry.insert(nxinstrument)
-        
-        #monochromator  group
-        nxmonochromator = nxs.NXmonochromator()
-        nxinstrument.insert(nxmonochromator)
-        energy_data = self.ddfieldsDict[self.sanitizeName(env['monochromator'])] #to be linked later on
-        
-        #incoming_beam  group
-        nxincoming_beam = nxs.NXdetector(name='incoming_beam')
-        nxinstrument.insert(nxincoming_beam)
-        incbeam_data = self.ddfieldsDict[self.sanitizeName(env['incbeam'])] #to be linked later on
-        
-        #absorbed_beam  group
-        nxabsorbed_beam = nxs.NXdetector(name='absorbed_beam')
-        nxinstrument.insert(nxabsorbed_beam)
-        absbeam_data = self.ddfieldsDict[self.sanitizeName(env['absbeam'])] #to be linked later on
-        absbeam_data.attrs['signal'] = '1'
-        absbeam_data.attrs['axes'] = 'energy'
-        
-        #source group
-        nxsource = nxs.NXsource()
-        nxinstrument.insert(nxsource) 
-        nxinstrument['source']['name'] = env.get('SourceInfo',{}).get('name','Unknown')
-        nxinstrument['source']['type'] = env.get('SourceInfo',{}).get('type','Unknown')
-        nxinstrument['source']['probe'] = env.get('SourceInfo',{}).get('x-ray','Unknown')
-        
-        #data group
-        nxdata = nxs.NXdata()
-        self.nxentry.insert(nxdata)
-        
-        
-        #@todo create the PreScanSnapshot
-        #self._createPreScanSnapshot(env)   
-        
-        #write everything to file
-        self.nxentry.write() 
-        
-        #@todo: do this with the PyTree api instead(how to do named links with the PyTree API????)
-        self._nxln(monitor_data, nxmonitor, name='data')
-        self._nxln(incbeam_data, nxincoming_beam, name='data')
-        self._nxln(absbeam_data, nxabsorbed_beam, name='data')
-        self._nxln(energy_data, nxmonochromator, name='energy')
-        self._nxln(energy_data, nxdata, name='energy')
-        self._nxln(absbeam_data, nxdata, name='absorbed_beam')
-                
-        self.nxentry.nxfile.flush()
-        
-    
-    def _writeRecord(self, record):
-        # most used variables in the loop
-        fd, debug, warning = self.nxentry.nxfile, self.debug, self.warning
-        nparray, npshape = numpy.array, numpy.shape
-        rec_data, rec_nb = record.data, record.recordno
-                
-        for dd in self.datadesc:
-            if record.data.has_key( dd.name ):
-                data = rec_data[dd.name]
-                field = self.ddfieldsDict[dd.label]
-                
-                if data is None:
-                    data = numpy.zeros(dd.shape, dtype=dd.dtype)
-                if not hasattr(data, 'shape'):
-                    data = nparray([data], dtype=dd.dtype)
-                elif dd.dtype != data.dtype.name:
-                    debug('%s casted to %s (was %s)', dd.label, dd.dtype,
-                                                      data.dtype.name)
-                    data = data.astype(dd.dtype)
-
-                slab_offset = [rec_nb] + [0] * len(dd.shape)
-                shape = [1] + list(npshape(data))
-                try:
-                    field.put(data, slab_offset, shape)
-                    field.write()
-                except:
-                    warning("Could not write <%s> with shape %s", data, shape)
-                    raise
-            else:
-                debug("missing data for label '%s'", dd.label)
-        self.nxentry.nxfile.flush()
-
-
-    def _endRecordList(self, recordlist):
-        env=self.currentlist.getEnviron()
-        self.nxentry.insert(nxs.NXfield(name='end_time', value=env['endtime'].isoformat()))
-        #self._populateInstrumentInfo()
-        #self._createNXData()
-        self.nxentry.write()
-        self.nxentry.nxfile.flush()
-        self.debug("Finishing recording %d on file %s:", env['serialno'], self.filename)
-        return
-        
-
-
-#===============================================================================
-# BEGIN: THIS BLOCK SHOULD BE REMOVED IF NEXUS ACCEPTS THE PATCH TO NXfield
-#===============================================================================
-try:
-    from nxs import NXfield #needs Nexus v>=4.3
-    from nxs import napi, NeXusError
-    
-    class NXfield_comp(NXfield):
-        
-        #NOTE: THE CONSTRUCTOR IS OPTIONAL. IF NOT IMPLEMENTED, WE CAN STILL USE THE nxslab_dims PROPERTY
-        def __init__(self, value=None, name='field', dtype=None, shape=(), group=None,
-                     attrs={}, nxslab_dims=None, **attr):
-            NXfield.__init__(self, value=value, name=name, dtype=dtype, shape=shape, group=group,
-                     attrs=attrs, **attr)
-            self._slab_dims = nxslab_dims
-            
-        def write(self):
-            """
-            Write the NXfield, including attributes, to the NeXus file.
-            """
-            if self.nxfile:
-                if self.nxfile.mode == napi.ACC_READ:
-                    raise NeXusError("NeXus file is readonly")
-                if not self.infile:
-                    shape = self.shape
-                    if shape == (): shape = (1,)
-                    with self.nxgroup as path:
-                        if self.nxslab_dims is not None:
-                        #compress
-                            path.compmakedata(self.nxname, self.dtype, shape, 'lzw', 
-                                              self.nxslab_dims)
-                        else:
-                        # Don't use compression
-                            path.makedata(self.nxname, self.dtype, shape)
-                    self._infile = True
-                if not self.saved:            
-                    with self as path:
-                        path._writeattrs(self.attrs)
-                        value = self.nxdata
-                        if value is not None:
-                            path.putdata(value)
-                    self._saved = True
-            else:
-                raise IOError("Data is not attached to a file")
-        
-        def _getnxslabdims(self):
-            try:
-                return self._nxslab_dims
-            except:
-                slab_dims = None
-            #even if slab_dims have not been set, check if the dataset is large 
-            shape = self.shape or (1,)
-            if numpy.prod(shape) > 10000:
-                slab_dims = numpy.ones(len(shape),'i')
-                slab_dims[-1] = min(shape[-1], 100000)
-            return slab_dims
-        
-        def _setnxslabdims(self, slab_dims):
-            self._nxslab_dims = slab_dims
-        
-        nxslab_dims = property(_getnxslabdims,_setnxslabdims,doc="Slab (a.k.a. chunk) dimensions for compression")
-except:
-    pass #NXxas_FileRecorder won't be usable
-
-
-#===============================================================================
-# END: THE ABOVE BLOCK SHOULD BE REMOVED IF NEXUS ACCEPTS THE PATCH TO NXfield
-#===============================================================================
 
 
 def FileRecorder(filename, macro, **pars):
     ext = os.path.splitext(filename)[1].lower() or '.spec'
-    
-    hintedklass = globals().get(getattr(macro,'hints',{}).get('FileRecorder',None))
-    
-    if hintedklass is not None and issubclass(hintedklass, BaseFileRecorder): 
-        klass = hintedklass
-    elif ext in NXscan_FileRecorder.formats.values():
-        klass = NXscan_FileRecorder
-    elif ext in FIO_FileRecorder.formats.values():
-        klass = FIO_FileRecorder
+    rec_manager = macro.getMacroServer().recorder_manager
+
+    hinted_recorder = getattr(macro, 'hints', {}).get('FileRecorder', None)
+    if hinted_recorder is not None:
+        macro.deprecated("FileRecorder macro hints are deprecated. "
+                         "Use ScanRecorder variable instead.")
+        klass = rec_manager.getRecorderClass(hinted_recorder)
     else:
-        klass = SPEC_FileRecorder
+        klasses = rec_manager.getRecorderClasses(
+            filter=BaseFileRecorder, extension=ext)
+        len_klasses = len(klasses)
+        if len_klasses == 0:
+            klass = rec_manager.getRecorderClass('SPEC_FileRecorder')
+        elif len_klasses == 1:
+            klass = klasses.values()[0]
+        else:
+            raise AmbiguousRecorderError('Choice of recorder for %s '
+                                         'extension is ambiguous' % ext)
     return klass(filename=filename, macro=macro, **pars)
diff --git a/src/sardana/macroserver/scan/recorder/__init__.py b/src/sardana/macroserver/test/__init__.py
old mode 100644
new mode 100755
similarity index 83%
copy from src/sardana/macroserver/scan/recorder/__init__.py
copy to src/sardana/macroserver/test/__init__.py
index 8f71c3f..37597d5
--- a/src/sardana/macroserver/scan/recorder/__init__.py
+++ b/src/sardana/macroserver/test/__init__.py
@@ -22,12 +22,3 @@
 ## along with Sardana.  If not, see <http://www.gnu.org/licenses/>.
 ##
 ##############################################################################
-
-"""This is the macro server scan recorder module"""
-
-__docformat__ = 'restructuredtext'
-
-from .datarecorder import *
-from .output import *
-from .sharedmemory import *
-from .storage import *
\ No newline at end of file
diff --git a/src/sardana/macroserver/scan/recorder/__init__.py b/src/sardana/macroserver/test/res/__init__.py
old mode 100644
new mode 100755
similarity index 83%
copy from src/sardana/macroserver/scan/recorder/__init__.py
copy to src/sardana/macroserver/test/res/__init__.py
index 8f71c3f..37597d5
--- a/src/sardana/macroserver/scan/recorder/__init__.py
+++ b/src/sardana/macroserver/test/res/__init__.py
@@ -22,12 +22,3 @@
 ## along with Sardana.  If not, see <http://www.gnu.org/licenses/>.
 ##
 ##############################################################################
-
-"""This is the macro server scan recorder module"""
-
-__docformat__ = 'restructuredtext'
-
-from .datarecorder import *
-from .output import *
-from .sharedmemory import *
-from .storage import *
\ No newline at end of file
diff --git a/src/sardana/macroserver/scan/recorder/__init__.py b/src/sardana/macroserver/test/res/fakerecorders.py
old mode 100644
new mode 100755
similarity index 83%
copy from src/sardana/macroserver/scan/recorder/__init__.py
copy to src/sardana/macroserver/test/res/fakerecorders.py
index 8f71c3f..329326e
--- a/src/sardana/macroserver/scan/recorder/__init__.py
+++ b/src/sardana/macroserver/test/res/fakerecorders.py
@@ -23,11 +23,8 @@
 ##
 ##############################################################################
 
-"""This is the macro server scan recorder module"""
+from sardana.macroserver.scan.recorder.storage import BaseFileRecorder
 
-__docformat__ = 'restructuredtext'
-
-from .datarecorder import *
-from .output import *
-from .sharedmemory import *
-from .storage import *
\ No newline at end of file
+class FakeScanRecorder(BaseFileRecorder):
+    """ Fake ScanRecorder class for test purpose"""
+    pass
diff --git a/src/sardana/macroserver/scan/recorder/__init__.py b/src/sardana/macroserver/test/res/recorders/path1/fakerecorders.py
old mode 100644
new mode 100755
similarity index 76%
copy from src/sardana/macroserver/scan/recorder/__init__.py
copy to src/sardana/macroserver/test/res/recorders/path1/fakerecorders.py
index 8f71c3f..05b09d8
--- a/src/sardana/macroserver/scan/recorder/__init__.py
+++ b/src/sardana/macroserver/test/res/recorders/path1/fakerecorders.py
@@ -23,11 +23,12 @@
 ##
 ##############################################################################
 
-"""This is the macro server scan recorder module"""
+from sardana.macroserver.scan.recorder.storage import BaseFileRecorder
 
-__docformat__ = 'restructuredtext'
+class FakeScanRecorder(BaseFileRecorder):
+    """ Fake ScanRecorder class for test purpose"""
+    pass
 
-from .datarecorder import *
-from .output import *
-from .sharedmemory import *
-from .storage import *
\ No newline at end of file
+class FakeScanRecorderFormat(BaseFileRecorder):
+    """ Fake ScanRecorder class for test purpose"""
+    formats = {'Spec': '.spec'}
diff --git a/src/sardana/macroserver/scan/recorder/__init__.py b/src/sardana/macroserver/test/res/recorders/path2/fakerecorders.py
old mode 100644
new mode 100755
similarity index 76%
copy from src/sardana/macroserver/scan/recorder/__init__.py
copy to src/sardana/macroserver/test/res/recorders/path2/fakerecorders.py
index 8f71c3f..05b09d8
--- a/src/sardana/macroserver/scan/recorder/__init__.py
+++ b/src/sardana/macroserver/test/res/recorders/path2/fakerecorders.py
@@ -23,11 +23,12 @@
 ##
 ##############################################################################
 
-"""This is the macro server scan recorder module"""
+from sardana.macroserver.scan.recorder.storage import BaseFileRecorder
 
-__docformat__ = 'restructuredtext'
+class FakeScanRecorder(BaseFileRecorder):
+    """ Fake ScanRecorder class for test purpose"""
+    pass
 
-from .datarecorder import *
-from .output import *
-from .sharedmemory import *
-from .storage import *
\ No newline at end of file
+class FakeScanRecorderFormat(BaseFileRecorder):
+    """ Fake ScanRecorder class for test purpose"""
+    formats = {'Spec': '.spec'}
diff --git a/src/sardana/macroserver/scan/recorder/__init__.py b/src/sardana/macroserver/test/res/recorders/path3/fakerecorders.py
old mode 100644
new mode 100755
similarity index 76%
copy from src/sardana/macroserver/scan/recorder/__init__.py
copy to src/sardana/macroserver/test/res/recorders/path3/fakerecorders.py
index 8f71c3f..05b09d8
--- a/src/sardana/macroserver/scan/recorder/__init__.py
+++ b/src/sardana/macroserver/test/res/recorders/path3/fakerecorders.py
@@ -23,11 +23,12 @@
 ##
 ##############################################################################
 
-"""This is the macro server scan recorder module"""
+from sardana.macroserver.scan.recorder.storage import BaseFileRecorder
 
-__docformat__ = 'restructuredtext'
+class FakeScanRecorder(BaseFileRecorder):
+    """ Fake ScanRecorder class for test purpose"""
+    pass
 
-from .datarecorder import *
-from .output import *
-from .sharedmemory import *
-from .storage import *
\ No newline at end of file
+class FakeScanRecorderFormat(BaseFileRecorder):
+    """ Fake ScanRecorder class for test purpose"""
+    formats = {'Spec': '.spec'}
diff --git a/src/sardana/macroserver/scan/recorder/__init__.py b/src/sardana/macroserver/test/res/recorders/pathexternal/specrecorder.py
similarity index 84%
copy from src/sardana/macroserver/scan/recorder/__init__.py
copy to src/sardana/macroserver/test/res/recorders/pathexternal/specrecorder.py
index 8f71c3f..9715326 100644
--- a/src/sardana/macroserver/scan/recorder/__init__.py
+++ b/src/sardana/macroserver/test/res/recorders/pathexternal/specrecorder.py
@@ -23,11 +23,9 @@
 ##
 ##############################################################################
 
-"""This is the macro server scan recorder module"""
+from sardana.macroserver.scan.recorder import BaseFileRecorder
 
-__docformat__ = 'restructuredtext'
+class SPEC_FileRecorder(BaseFileRecorder):
+    """ Saves data to a file """
 
-from .datarecorder import *
-from .output import *
-from .sharedmemory import *
-from .storage import *
\ No newline at end of file
+    formats = {'Spec': '.spec'}
diff --git a/src/sardana/macroserver/test/test_msparameter.py b/src/sardana/macroserver/test/test_msparameter.py
new file mode 100644
index 0000000..2252e64
--- /dev/null
+++ b/src/sardana/macroserver/test/test_msparameter.py
@@ -0,0 +1,125 @@
+from taurus.external import unittest
+from taurus.test import insertTest
+
+from sardana.macroserver.macro import Type
+from sardana.macroserver.msparameter import ParamDecoder, WrongParamType
+
+from sardana.macroserver.mstypemanager import TypeManager
+
+class FakeMacroServer(object):
+    name = "FakeMacroServer"
+
+macro_server = FakeMacroServer()
+TYPE_MANAGER = TypeManager(macro_server)
+
+params_def1 = [
+    {
+        "name": "param1",
+        "type": Type.String,
+        "default_value": None,
+        "description": "param1 description"
+    }
+]
+
+params_def2 = [
+    {
+        "name": "param1",
+        "type": Type.Integer,
+        "default_value": None,
+        "description": "param1 description"
+    }
+]
+
+params_def3 = [
+    {
+        "name": "param1",
+        "type": [
+            {
+                "name": "subparam1",
+                "type": Type.Integer,
+                "default_value": None,
+                "description": "subparam1 description",
+            }
+        ],
+        "default_value": None,
+        "description": "param1 description",
+        "min": 0,
+        "max": None
+    }
+]
+
+params_raw1 = ["value1"]
+params_raw2 = [[1]]
+params_raw3 = []
+expected_params1 = ["value1"]
+expected_params2 = [[1]]
+expected_params3 = []
+
+doc1 = "Decode macro with one single parameter with correct value"
+doc2 = "Decode macro with one single parameter with wrong value"
+doc3 = "Decode macro with one simple repeat parameter with correct value"
+doc4 = "Decode macro with one simple repeat parameter with min=0 and no values"
+
+ at insertTest(helper_name="decode", test_method_name="test_decode1",
+            test_method_doc=doc1, params_def=params_def1,
+            params_raw=params_raw1, expected_params=expected_params1)
+ at insertTest(helper_name="decode", test_method_name="test_decode2",
+            test_method_doc=doc2, params_def=params_def2,
+            params_raw=params_raw1, expected_exception=WrongParamType)
+ at insertTest(helper_name="decode", test_method_name="test_decode3",
+            test_method_doc=doc3, params_def=params_def3,
+            params_raw=params_raw1, expected_exception=WrongParamType)
+ at insertTest(helper_name="decode", test_method_name="test_decode4",
+            test_method_doc=doc4, params_def=params_def3,
+            params_raw=params_raw3, expected_params=expected_params3)
+class TestParamDecoder(unittest.TestCase):
+
+    def setUp(self):
+        unittest.TestCase.setUp(self)
+        self.type_manager = TYPE_MANAGER
+        # use this code to parameter types of sardana interfaces e.g. Motor
+#         type_motor = self.type_manager.getTypeObj(Type.Motor)
+#         type_motor.getObj = lambda name:name
+
+    def decode(self, params_def, params_raw, expected_params=None,
+               expected_exception=None):
+        """Test decoding of macro parameters using ParamDecoder class by
+        comparing the result with either of the expected_params or the
+        expcted_exception result, not both of them.
+
+        :param params_def: list(dict) where each dict represents one parameter
+            definition (required keys: name, type, default_value, description
+            and min & max in case of the repeat parameters)
+        :param params_raw: list of parameter values or XML strucutre with
+            parameter values
+        :param expected_params: list of the expected parameter values after the
+            decoding process
+        :param expected_exception: expected exception class
+        """
+        if expected_params is None and expected_exception is None:
+            raise ValueError("missing expected_params or expected_exception")
+        if not expected_params is None and not expected_exception is None:
+            raise ValueError("too many expected expected values")
+        exception = None
+        try:
+            param_decoder = ParamDecoder(self.type_manager, params_def,
+                                         params_raw)
+        except Exception, e:
+            exception = e
+        if expected_params:
+            exception_message = getattr(exception, "message", None)
+            msg = "unexpected exception: %s (%s)" % (exception,
+                                                     exception_message)
+            self.assertIsNone(exception, msg)
+            params = param_decoder.getParamList()
+            msg = ("decoding result (%s) does not match with the expected"
+                   " result (%s)" % (params, expected_params))
+            self.assertListEqual(params, expected_params, msg)
+        if expected_exception:
+            msg = ("decoding exception type (%s) does not match with the"
+                   " expected exception type (%s)" % (type(exception),
+                                                     expected_exception))
+            self.assertIsInstance(exception, expected_exception, msg)
+
+    def tearDown(self):
+        unittest.TestCase.tearDown(self)
\ No newline at end of file
diff --git a/src/sardana/macroserver/test/test_msrecordermanager.py b/src/sardana/macroserver/test/test_msrecordermanager.py
new file mode 100755
index 0000000..bd4f476
--- /dev/null
+++ b/src/sardana/macroserver/test/test_msrecordermanager.py
@@ -0,0 +1,171 @@
+#!/usr/bin/env python
+
+##############################################################################
+##
+## 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/>.
+##
+##############################################################################
+
+import os
+
+from taurus.external import unittest
+from taurus.test import insertTest
+
+from sardana.macroserver.macroserver import MacroServer
+from sardana.macroserver.scan.recorder import DataRecorder
+from sardana.macroserver.scan.recorder.storage import FileRecorder,\
+    BaseFileRecorder
+
+
+_TEST_DIR = os.path.dirname(os.path.abspath(__file__))
+_FAKE_RECORDER_DIR = os.path.join(_TEST_DIR, 'res')
+
+
+ at insertTest(helper_name='getRecorderClass', klass_name="JsonRecorder")
+ at insertTest(helper_name='getRecorderClass', klass_name="FIO_FileRecorder")
+ at insertTest(helper_name='getRecorderClass', klass_name="FakeScanRecorder",
+            extra_paths=[_FAKE_RECORDER_DIR])
+ at insertTest(helper_name='getRecorderClasses', filter=DataRecorder,
+            extra_paths=[_FAKE_RECORDER_DIR], extra_recorders=1)
+ at insertTest(helper_name='getRecorderClasses', extra_paths=[_FAKE_RECORDER_DIR],
+            extra_recorders=1)
+ at insertTest(helper_name='getRecorderPath',
+            recorder_path=["/tmp/foo", "#/tmp/foo2"], expected_num_path=2)
+ at insertTest(helper_name='getRecorderPath', recorder_path=["/tmp/foo:/tmp/foo2"],
+            expected_num_path=3)
+ at insertTest(helper_name='getRecorderPath', recorder_path=["/tmp/foo"],
+            expected_num_path=2)
+ at insertTest(helper_name='getRecorderPath')
+class RecorderManagerTest(unittest.TestCase):
+    # Just an hardcode fullname for create an instance of MacroServer.
+    # This macroserver does not need to be defined.
+    ms_fullname = "macroserver/demo1/1"
+
+    def setUp(self):
+        name = self.ms_fullname.split("/")[1]
+        self._macro_server = MacroServer(self.ms_fullname, name, macro_path=[],
+                                    recorder_path=[])
+        self.manager = self._macro_server.recorder_manager
+
+    def tearDown(self):
+        pass
+
+    def _updateRecorderManager(self, recorder_path):
+        """Helper for update the sardana recorder manager
+        """
+        self.manager.setRecorderPath(recorder_path)
+
+    def getRecorderPath(self, recorder_path=[], expected_num_path=1):
+        """Helper for test the number of reading recorder paths.
+        The number of reading path sould be len(recorder_path) + 1
+        """
+        if recorder_path is not []:
+            self._updateRecorderManager(recorder_path)
+        # Get the list of recorder path(s)
+        paths = self.manager.getRecorderPath()
+        num_paths = len(paths)
+        msg = "The number of paths do not concur, read %d, expected %d" %\
+              (num_paths, expected_num_path)
+        self.assertEqual(num_paths, expected_num_path, msg)
+
+    def getRecorderClasses(self, filter=None, extra_paths=None,
+                           extra_recorders=0):
+        """Helper for test getRecorderClasses method of the record Manager.
+        """
+        if filter is None:
+            filter = DataRecorder
+        # Use default recorders paths
+        self.manager.setRecorderPath([])
+        default_recorder_klass = self.manager.getRecorderClasses(filter)
+        # Add extra recorders paths
+        if extra_paths is not None:
+            self.manager.setRecorderPath(extra_paths)
+        recorder_klass = self.manager.getRecorderClasses(filter)
+        n_default_recorders = len(default_recorder_klass)
+        n_recorders = len(recorder_klass)
+        total_recorders = n_default_recorders + extra_recorders
+        msg = "Number of recorder classes do not concur, expected %d, get %d" %\
+              (total_recorders, n_recorders)
+        self.assertEqual(total_recorders, n_recorders, msg)
+
+    def getRecorderClass(self, klass_name, extra_paths=[]):
+        """Helper for test getRecorderClass method of the record Manager.
+        """
+        self.manager.setRecorderPath(extra_paths)
+        klass = self.manager.getRecorderClass(klass_name)
+        msg = "Recoder manager does not found the class %s" %(klass_name)
+        self.assertNotEqual(klass, None, msg)
+        _name = klass.__name__
+        msg = "The class %s is not subclass of DataRecorder" %(_name)
+        self.assertTrue(issubclass(klass, DataRecorder), msg)
+        msg = "The class name giveb by the recorder manager is different." +\
+              "Expected %s, get %s" %(klass_name, _name)
+        self.assertEqual(_name, klass_name, msg)
+
+    def test_SameClassNames(self):
+        """Test whether ordered path precedence is maintained in case of
+        different recorder classes with the same name located in different
+        paths.
+        """
+        path1 = os.path.join(_TEST_DIR, 'res', 'recorders', 'path1')
+        path2 = os.path.join(_TEST_DIR, 'res', 'recorders', 'path2')
+        path3 = os.path.join(_TEST_DIR, 'res', 'recorders', 'path3')
+        # set three paths containing recorders with the same class names
+        recorder_path = [path3, path1, path2]
+        self._updateRecorderManager(recorder_path)
+        klass = self.manager.getRecorderMetaClass('FakeScanRecorder')
+        # retrieve path to the recorder library
+        path = os.sep.join(klass.lib.full_name.split(os.sep)[:-1])
+        msg = 'Ordered path precedence is not maintained by RecorderManager'
+        self.assertEqual(path3, path, msg)
+
+    def test_SameFormats(self):
+        """Test whether ordered path precedence is maintained in case of
+        different recorder classes supporting the same format located in
+        different paths.
+        """
+        path1 = os.path.join(_TEST_DIR, 'res', 'recorders', 'path1')
+        path2 = os.path.join(_TEST_DIR, 'res', 'recorders', 'path2')
+        path3 = os.path.join(_TEST_DIR, 'res', 'recorders', 'path3')
+        recorder_path = [path3, path1, path2]
+        # set three paths containing recorders of the same format
+        self._updateRecorderManager(recorder_path)
+        klasses = self.manager.getRecorderMetaClasses(filter=BaseFileRecorder,
+                                                  extension='.spec')
+        klass = klasses.values()[0]
+        # retrieve path to the recorder library
+        path = os.sep.join(klass.lib.full_name.split(os.sep)[:-1])
+        msg = 'Ordered path precedence is not maintained by RecorderManager'
+        self.assertEqual(path3, path, msg)
+
+    def test_ExternalVsBuiltinPrecedence(self):
+        """Test if external recorders are of higher priority than the built-in)
+        """
+        external_path = os.path.join(_TEST_DIR, 'res', 'recorders', 
+                                     'pathexternal')
+
+        # set three paths containing recorders with the same class names
+        recorder_path = [external_path]
+        self._updateRecorderManager(recorder_path)
+        klass = self.manager.getRecorderMetaClass('SPEC_FileRecorder')
+        # retrieve path to the recorder library
+        path = os.sep.join(klass.lib.full_name.split(os.sep)[:-1])
+        msg = 'Wrong precedence of recorder paths'
+        self.assertEqual(path, external_path, msg)
diff --git a/src/sardana/pool/pool.py b/src/sardana/pool/pool.py
index d5f5a83..9ef4f35 100644
--- a/src/sardana/pool/pool.py
+++ b/src/sardana/pool/pool.py
@@ -532,6 +532,11 @@ class Pool(PoolContainer, PoolObject, SardanaElementManager, SardanaIDManager):
         self.fire_event(EventType("ElementCreated"), elem)
         return ret
 
+    def rename_element(self, old_name, new_name):
+        PoolContainer.rename_element(self, old_name, new_name)
+        elem = self.get_element_by_name(new_name)
+        self.fire_event(EventType("ElementChanged"), elem)
+
     def delete_element(self, name):
         try:
             elem = self.get_element(name=name)
diff --git a/src/sardana/pool/poolcontrollermanager.py b/src/sardana/pool/poolcontrollermanager.py
index 6d6f6a9..3e0cafd 100644
--- a/src/sardana/pool/poolcontrollermanager.py
+++ b/src/sardana/pool/poolcontrollermanager.py
@@ -37,6 +37,7 @@ import copy
 import types
 import inspect
 
+from taurus.external.ordereddict import OrderedDict
 from taurus.core import ManagerState
 from taurus.core.util.log import Logger
 from taurus.core.util.singleton import Singleton
@@ -158,9 +159,11 @@ class ControllerManager(Singleton, Logger):
 
         controller_file_names = self._findControllerLibNames()
 
-        for controller_file_name in controller_file_names:
+        for mod_name, file_name in controller_file_names.iteritems():
+            dir_name = os.path.dirname(file_name)
+            path = [dir_name]
             try:
-                self.reloadControllerLib(controller_file_name, reload=reload)
+                self.reloadControllerLib(mod_name, path, reload=reload)
             except Exception:
                 pass
 
@@ -175,15 +178,15 @@ class ControllerManager(Singleton, Logger):
     def _findControllerLibNames(self, path=None):
         """internal method"""
         path = path or self.getControllerPath()
-        ret = []
-        for p in path:
+        ret = OrderedDict()
+        for p in reversed(path):
             try:
                 for f in os.listdir(p):
                     name, ext = os.path.splitext(f)
                     if not name[0].isalpha():
                         continue
                     if ext.endswith('py'):
-                        ret.append(name)
+                        ret[name] = os.path.abspath(os.path.join(p, f))
             except:
                 self.debug("'%s' is not a valid path" % p)
         return ret
diff --git a/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py b/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py
new file mode 100644
index 0000000..0fd2162
--- /dev/null
+++ b/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py
@@ -0,0 +1,1421 @@
+# -*- coding: utf-8 -*-
+
+##############################################################################
+#
+# This file is part of Sardana
+#
+# http://www.tango-controls.org/static/sardana/latest/doc/html/index.html
+#
+# Copyright: 2013 PetraIII,
+#            2013 Synchrotron-Soleil
+#                 L'Orme des Merisiers Saint-Aubin
+#                 BP 48 91192 GIF-sur-YVETTE CEDEX
+#
+# 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 the definition of an Hkl pseudo motor controller
+for the Sardana Device Pool"""
+
+__author__ = ("Maria-Teresa Nunez-Pardo-de-Verra <tnunez at mail.desy.de>, "
+              "Picca Frédéric-Emmanuel <picca at synchrotron-soleil.fr>")
+__copyright__ = ("Copyright (c) 2013 DESY "
+                 "Copyright (c) 2013 Synchrotron-Soleil "
+                 "L'Orme des Merisiers Saint-Aubin "
+                 "BP 48 91192 GIF-sur-YVETTE CEDEX")
+__license__ = 'LGPL-3+'
+
+__all__ = ["Diffrac6C", "DiffracE6C", "DiffracE4C", "Diffractometer"]
+
+__docformat__ = 'restructuredtext'
+
+import os
+import time
+
+import PyTango
+
+from itertools import chain
+
+from gi.repository import GLib
+from gi.repository import Hkl
+
+from taurus.core.util.codecs import CodecFactory
+
+from sardana import DataAccess
+from sardana.pool.controller import PseudoMotorController
+from sardana.pool.controller import Type, Access, Description
+from sardana.pool.controller import Memorize, Memorized, MemorizedNoInit, NotMemorized  # noqa
+
+ReadOnly = DataAccess.ReadOnly
+ReadWrite = DataAccess.ReadWrite
+USER = Hkl.UnitEnum.USER
+DEFAULT_CRYSTAL = "default_crystal"
+
+
+from taurus.core.util.log import Logger
+
+logger = Logger.getLogger("ControllerManager")
+
+logger.info("The diffractometer controller is at early stage. Controller attributes and commands can slightly change.")
+
+
+class AxisPar(object):
+    def __init__(self, engine, name):
+        self.engine = engine
+        self.name = name
+        self._modes = None
+
+    @property
+    def mode(self):
+        return self.engine.current_mode_get()
+
+    @mode.setter
+    def mode(self, value):
+        self.engine.current_mode_set(value)
+
+    @property
+    def modes(self):
+        if self._modes is None:
+            self._modes = self.engine.modes_names_get()
+        return self._modes
+
+    @property
+    def modeparameters(self):
+        return self.engine.parameters_names_get()
+
+    @property
+    def modeparametersvalues(self):
+         return [p.value_get(USER)
+                 for p in [self.engine.parameter_get(n)
+                           for n in self.modeparameters]]
+
+    @modeparametersvalues.setter
+    def modeparametersvalues(self, value):
+        for parameter, v in zip(self.modeparameters,
+                                value):
+            p = self.engine.parameter_get(parameter)
+            p.value_set(v, USER)
+            self.engine.parameter_set(parameter, p)
+
+# TODO: all the fit attributes (AFit or GammaFit)should change its type to bool 
+class DiffracBasis(PseudoMotorController):
+
+    """ The PseudoMotor controller for the diffractometer"""
+
+    class_prop = {'DiffractometerType': {Type: str,
+                                         Description: 'Type of the diffractometer, e.g. E6C'},  # noqa
+    }
+
+    ctrl_attributes = {'Crystal': {Type: str,
+                                   Memorize: MemorizedNoInit,  # noqa If Crystal is changed to memorized. The changes commented with "memcrystal"
+                                   Access: ReadWrite},
+                       'AffineCrystal': {Type: int,
+                                         Memorize: MemorizedNoInit,
+                                         Description: "Fine tunning of lattice parameters and UB matrix based on current crystal reflections. Reflections with affinement set to 0 are not used. A new crystal with the post fix (affine) is created and set as current crystal",  # noqa
+                                         Access: ReadWrite},
+                       'Wavelength': {Type: float,
+                                      Memorize: MemorizedNoInit,
+                                      Access: ReadWrite},
+                       'EngineMode': {Type: str,  # TODO delete
+                                      Memorize: MemorizedNoInit,
+                                      Access: ReadWrite},
+                       'EngineModeList': {Type: (str,), Access: ReadOnly},
+                       'HKLModeList': {Type: (str,), Access: ReadOnly},  # TODO delete
+                       'UBMatrix': {Type: ((float,), ),
+                                    Description: "The reflection matrix",
+                                    Access: ReadOnly},
+                       'Ux': {Type: float, Access: ReadWrite},
+                       'Uy': {Type: float, Access: ReadWrite},
+                       'Uz': {Type: float, Access: ReadWrite},
+                       'ComputeUB': {Type: (int,),
+                                    Description: "Compute reflection matrix using two given reflections",  # noqa
+                                    Access: ReadWrite},
+                       'LatticeReciprocal': {Type: (float,),
+                                             Description: "The reciprocal lattice parameters of the sample",  # noqa
+                                             Access: ReadOnly},
+                       'A': {Type: float,
+                             Description: "a parameter of the lattice",
+                             Memorize: MemorizedNoInit,
+                             Access: ReadWrite},
+                       'B': {Type: float,
+                             Description: "b parameter of the lattice",
+                             Memorize: MemorizedNoInit,
+                             Access: ReadWrite},
+                       'C': {Type: float,
+                             Description: "c parameter of the lattice",
+                             Memorize: MemorizedNoInit,
+                             Access: ReadWrite},
+                       'Alpha': {Type: float,
+                                 Description: "alpha parameter of the lattice",
+                                 Memorize: MemorizedNoInit,
+                                 Access: ReadWrite},
+                       'Beta': {Type: float,
+                                Description: "beta parameter of the lattice",
+                                Memorize: MemorizedNoInit,
+                                Access: ReadWrite},
+                       'Gamma': {Type: float,
+                                 Description: "gamma parameter of the lattice",
+                                 Memorize: MemorizedNoInit,
+                                 Access: ReadWrite},
+                       'AFit': {Type: int,
+                                Description: "Fit value of the a parameter of the lattice",  # noqa
+                                Memorize: MemorizedNoInit,
+                                Access: ReadWrite},
+                       'BFit': {Type: int,
+                                Description: "Fit value of the b parameter of the lattice",  # noqa
+                                Memorize: MemorizedNoInit,
+                                Access: ReadWrite},
+                       'CFit': {Type: int,
+                                Description: "Fit value of the c parameter of the lattice",  # noqa
+                                Memorize: MemorizedNoInit,
+                                Access: ReadWrite},
+                       'AlphaFit': {Type: int,
+                                    Description: "Fit value of the alpha parameter of the lattice",  # noqa
+                                    Memorize: MemorizedNoInit,
+                                    Access: ReadWrite},
+                       'BetaFit': {Type: int,
+                                   Description: "Fit value of the beta parameter of the lattice",  # noqa
+                                   Memorize: MemorizedNoInit,
+                                   Access: ReadWrite},
+                       'GammaFit': {Type: int,
+                                    Description: "Fit value of the gamma parameter of the lattice",  # noqa
+                                    Memorize: MemorizedNoInit,
+                                    Access: ReadWrite},
+                       'TrajectoryList': {Type: ((float,), (float,)),
+                                          Description: "List of trajectories for hklSim",  # noqa
+                                          Access: ReadOnly},
+                       'SelectedTrajectory': {Type: int,
+                                              Description: "Index of the trajectory you want to take for the given hkl values. 0 (by default) if first.",  # noqa
+                                              Access: ReadWrite},
+                       'ComputeTrajectoriesSim': {Type: (float,),
+                                                  Description: "Pseudo motor values to compute the list of trajectories (1, 2 or 3 args)",  # noqa
+                                                  Access: ReadWrite},
+                       'Engine': {Type: str,
+                                  Memorize: MemorizedNoInit,
+                                  Access: ReadWrite},
+                       'EngineList': {Type: (str,), Access: ReadOnly},
+                       'EnginesConf': {Type: str, Access: ReadOnly},
+                       'CrystalList': {Type: (str,), Access: ReadOnly},
+                       'AddCrystal': {Type: str,
+                                      Memorize: MemorizedNoInit,
+                                      Access: ReadWrite},
+                       'DeleteCrystal': {Type: str,
+                                         Memorize: MemorizedNoInit,
+                                         Access: ReadWrite},
+                       'AddReflection': {Type: (float,),
+                                         Description: "Add reflection to current sample",  # noqa
+                                         Access: ReadWrite},
+                       'SubstituteReflection': {Type: (float,),
+                                                  Description: "Substitute reflexion with the given index by the given one",  # noqa
+                                                  Access: ReadWrite},
+                       'SwapReflections01': {Type: int,
+                                             Description: "Swap primary and secondary reflections",  # noqa
+                                             Access: ReadWrite},
+                       'ReflectionList': {Type: ((float,), (float,)),
+                                          Description: "List of reflections for current sample",  # noqa
+                                          Access: ReadOnly},
+                       'RemoveReflection': {Type: int,
+                                            Memorize: MemorizedNoInit,
+                                            Description: "Remove reflection with given index",  # noqa
+                                            Access: ReadWrite},
+                       'LoadReflections': {Type: str,
+                                           Description: "Load the reflections from the file given as argument",  # noqa
+                                           Memorize: MemorizedNoInit,
+                                           Access: ReadWrite},
+                       'SaveReflections': {Type: str,
+                                           Description: "Save current reflections to file",  # noqa
+                                           Memorize: MemorizedNoInit,
+                                           Access: ReadWrite},
+                       'LoadCrystal':     {Type: str,
+                                           Description: "Load the lattice parameters and reflections from the file corresponding to the given crystal",  # noqa
+                                           Memorize: MemorizedNoInit,
+                                           Access: ReadWrite},
+                       'SaveCrystal': {Type: int,
+                                       Description: "Save current crystal parameters and reflections",  # noqa
+                                       Memorize: NotMemorized,
+                                       Access: ReadWrite},
+                       'SaveDirectory': {Type: str,
+                                         Description: "Directory to save the crystal files to",  # noqa
+                                         Memorize: Memorized,
+                                         Access: ReadWrite},
+                       'AdjustAnglesToReflection': {Type: (float,),
+                                                    Description: "Set the given angles to the reflection with given index",  # noqa
+                                                    Access: ReadWrite},
+                       'ReflectionAngles': {Type: ((float,), (float,)),
+                                            Description: "Angles between reflections",  # noqa
+                                            Access: ReadOnly},
+                       'ModeParametersNames': {Type: (str,), # TODO delete
+                                               Description: "Name of the parameters of the current mode (if any)",  # noqa
+                                               Access: ReadOnly},
+                       'ModeParametersValues': {Type: (float,),  # TODO delete
+                                                Description: "Value of the parameters of the current mode (if any)",  # noqa
+                                                Access: ReadWrite},
+                       'PsiRefH': {Type: float,
+                                   Description: "x coordinate of the psi reference vector (-999 if not applicable)",  # noqa
+                                   Memorize: MemorizedNoInit,
+                                   Access: ReadWrite},
+                       'PsiRefK': {Type: float,
+                                   Description: "y coordinate of the psi reference vector (-999 if not applicable)",  # noqa
+                                   Memorize: MemorizedNoInit,
+                                   Access: ReadWrite},
+                       'PsiRefL': {Type: float,
+                                   Description: "z coordinate of the psi reference vector (-999 if not applicable)",  # noqa
+                                   Memorize: MemorizedNoInit,
+                                   Access: ReadWrite},
+                       'MotorList': {Type: (str,),
+                                     Description: "Name of the real motors",
+                                     Access: ReadOnly},
+                       'HKLPseudoMotorList': {Type: (str,),
+                                              Description: "Name of the hkl pseudo motors",  # noqa
+                                              Access: ReadOnly},
+                       'MotorRoles': {Type: (str,),
+                                          Description: "Name of the motor roles",  # noqa
+                                          Access: ReadOnly},
+                       'EnergyDevice': {Type: str,
+                                        Description: "Name of the energy device to read the energy from",
+                                        Memorize: Memorized,
+                                        Access: ReadWrite},
+                       'AutoEnergyUpdate': {Type: int,
+                                            Description: "If 1 wavelength is read from EnergyDevice",
+                                            Memorize: Memorized,
+                                            Access: ReadWrite},
+                       'ComputeHKL': {Type: (float,),
+                                            Description: "Compute hkl for given angles",
+                                            Memorize: NotMemorized,
+                                            Access: ReadWrite},
+    }
+
+    axis_attributes = {
+        'Mode': {
+            Type: str,
+            Memorize: MemorizedNoInit,
+            Access: ReadWrite
+        },
+        'Modes': {
+            Type: (str,),
+            Access: ReadOnly
+        },
+        'ModeParameters': {
+            Type: (str,),
+            Description: "Name of the parameters of the current mode (if any)",  # noqa
+            Access: ReadOnly
+        },
+        'ModeParametersValues': {
+            Type: (float,),
+            Description: "Value of the parameters of the current mode (if any)",  # noqa
+            Access: ReadWrite
+        },
+    }
+
+    def GetAxisExtraPar(self, axis, name):
+        return getattr(self.axispar[axis - 1], name.lower())
+
+    def SetAxisExtraPar(self, axis, name, value):
+         setattr(self.axispar[axis - 1], name.lower(), value)
+
+    MaxDevice = 1
+
+    def __init__(self, inst, props, *args, **kwargs):
+        """ Do the default init plus the specific diffractometer
+        staff.
+        @param properties of the controller
+        """
+        PseudoMotorController.__init__(self, inst, props, *args, **kwargs)
+
+        # Comment out if memorized crystal (memcrystal)
+        # self.first_crystal_set = 1
+        self.samples = {}
+        self.samples[DEFAULT_CRYSTAL] = self.sample = Hkl.Sample.new(DEFAULT_CRYSTAL)  # noqa
+
+        lattice = self.sample.lattice_get()
+        self._a = self._b = self._c = 1.5
+        self._alpha = self._beta = self._gamma = 90.
+        lattice.set(self._a, self._b, self._c,
+                    self._alpha, self._beta, self._gamma, USER)
+        self.sample.lattice_set(lattice)
+
+        self.detector = Hkl.Detector.factory_new(Hkl.DetectorType(0))
+
+        for key, factory in Hkl.factories().iteritems():
+            if key == self.DiffractometerType:
+                self.geometry = factory.create_new_geometry()
+                self.engines = factory.create_new_engine_list()
+
+        self.nb_ph_axes = len(self.geometry.axis_names_get())
+
+        self.engines.init(self.geometry, self.detector, self.sample)
+
+        # Engine hkl -> it has not effect, it will be set the last value set
+        # (stored in DB)
+        self.engine = self.engines.engine_get_by_name("hkl")
+
+        self.engine_list = []
+        self.axispar = []
+        for engine in self.engines.engines_get():
+            self.engine_list.append(engine.name_get())
+            for pseudo in engine.pseudo_axis_names_get():
+                self.axispar.append(AxisPar(engine, pseudo))
+
+        self.engine_list = tuple(self.engine_list)
+        self.engines_conf = None  # defered because it does not work in the __init__
+
+        self.trajectorylist = []
+
+        # simulation part
+        self.lastpseudopos = [0] * 3
+        self.selected_trajectory = 0
+
+        # Put to -1 the attributes that are actually commands
+        self._affinecrystal = -1
+        self._removereflection = -1
+        self._deletecrystal = 'nothing'
+        self._addcrystal = 'nothing'
+        self._addreflection = [-1.]
+        self._substitutereflection = [-1.]
+        self._swapreflections01 = -1
+        self._loadreflections = " "  # Only to create the member, the
+                                     # value will be overwritten by
+                                     # the one in stored in the
+                                     # database
+        self._savereflections = " "  # Only to create the member, the
+                                     # value will be overwritten by
+                                     # the one in stored in the
+                                     # database
+        self._loadcrystal = " "  # Only to create the member, the
+                                 # value will be overwritten by the
+                                 # one in stored in the database
+        self._savecrystal = -1
+        self._savedirectory = " "  # Only to create the member, the
+                                   # value will be overwritten by the
+                                   # one in stored in the database
+        self._energydevice = " "  # Only to create the member, the
+                                   # value will be overwritten by the
+                                   # one in stored in the database
+        self._autoenergyupdate = 0  # Only to create the member, the
+                                   # value will be overwritten by the
+                                   # one in stored in the database
+        self._computehkl = []
+
+        self.energy_device = None
+        self.lambda_to_e = 12398.424 # Amstrong * eV
+
+    def _solutions(self, values, curr_physical_position):
+        # set all the motor min and max to restrain the solutions
+        # with only valid positions.
+        for role, current in zip(self.motor_roles, curr_physical_position):
+            motor = self.GetMotor(role)
+            axis = self.geometry.axis_get(role)
+            axis.value_set(current, USER)
+            try:
+                config = PyTango.AttributeProxy(motor.get_full_name() + '/position').get_config()  # noqa
+                mini, maxi = float(config.min_value), float(config.max_value)
+                axis.min_max_set(mini, maxi, USER)
+            except ValueError:
+                pass
+            self.geometry.axis_set(role, axis)
+
+        # computation and select the expected solution
+        return self.engine.pseudo_axis_values_set(values, USER)
+
+    def CalcPhysical(self, axis, pseudo_pos, curr_physical_pos):
+        return self.CalcAllPhysical(pseudo_pos, curr_physical_pos)[axis - 1]
+
+    def CalcPseudo(self, axis, physical_pos, curr_pseudo_pos):
+        return self.CalcAllPseudo(physical_pos, curr_pseudo_pos)[axis - 1]
+
+    def CalcAllPhysical(self, pseudo_pos, curr_physical_pos):
+        # TODO it should work with all the kind of engine ? or only
+        # with the hkl engine ? What I understand from this is that
+        # the pseudos values contain all the values from all the
+        # engines. This is not generic at all. If I add new engines
+        # this code should be fixed. worse the engine names is
+        # hardcoded. for now I will just rewrite the code with the
+        # same logic but with the new hkl API.
+
+        engine_name = self.engine.name_get()
+        values = None
+        if engine_name == "hkl":
+            values = [pseudo_pos[0], pseudo_pos[1], pseudo_pos[2]]
+        elif self.nb_ph_axes == 4:  # noqa "E4CV", "E4CH", "SOLEIL MARS": hkl(3), psi(1), q(1); "K4CV": hkl(3), psi(1), q(1), eulerians(3); "SOLEIL SIXS MED1+2": hkl(3), q2(2), qper_qpar(2)
+            if engine_name == "psi":
+                values = [pseudo_pos[3]]
+            elif engine_name == "q":
+                values = [pseudo_pos[4]]
+            elif engine_name == "eulerians":
+                values = [pseudo_pos[5], pseudo_pos[6], pseudo_pos[7]]
+            elif engine_name == "q2":
+                values = [pseudo_pos[3], pseudo_pos[4]]
+            elif engine_name == "qper_qpar":
+                values = [pseudo_pos[5], pseudo_pos[6]]
+        elif self.nb_ph_axes == 6:  # noqa "E6C", "SOLEIL SIXS MED2+2": hkl(3), psi(1), q2(2), qper_qpar(2); "K6C":  hkl(3), psi(1), q2(2), qper_qpar(2), eulerians(3); "PETRA3 P09 EH2": hkl(3)
+            if engine_name == "psi":
+                values = [pseudo_pos[3]]
+            elif engine_name == "q2":
+                values = [pseudo_pos[4], pseudo_pos[5]]
+            elif engine_name == "qper_qpar":
+                values = [pseudo_pos[6], pseudo_pos[7]]
+            elif engine_name == "eulerians":
+                values = [pseudo_pos[8], pseudo_pos[9], pseudo_pos[10]]
+
+        self.getWavelength()
+
+        solutions = self._solutions(values, curr_physical_pos)
+        for i, item in enumerate(solutions.items()):
+            if i == self.selected_trajectory:
+                angles = item.geometry_get().axis_values_get(USER)
+
+        # TODO why replace this by a tuple ?
+        return tuple(angles)
+
+    def CalcAllPseudo(self, physical_pos, curr_pseudo_pos):
+        # TODO howto avoid this nb_ph_axes, does the length of the
+        # physical values are not equal to the expected len of the
+        # geometry axes.
+        
+        self.getWavelength()
+            
+
+        # write the physical motor into the geometry
+        self.geometry.axis_values_set(physical_pos[:self.nb_ph_axes], USER)
+        self.engines.get()
+
+        # extract all the pseudo axes values
+        values = []
+        for engine in self.engines.engines_get():
+            values = values + engine.pseudo_axis_values_get(USER)
+
+        return tuple(values)
+
+    def getCrystal(self):
+        return self.sample.name_get()
+
+    def setCrystal(self, value):
+        self.sample = self.samples[value]
+        # Used this part and comment the above one out if memorized crystal (memcrystal)
+        #if self.first_crystal_set == 0:
+        #    if value in self.samples:
+        #        self.sample = self.samples[value]
+        #else:
+        #    self.samples[value] = Hkl.Sample.new(
+        #        value)  # By default a crystal is created with lattice parameters 1.54, 1.54, 1.54, 90., 90., 90.
+        #    self.sample = self.samples[value]
+        #    lattice = self.sample.lattice_get()
+        #    lattice.set(self._a, self._b, self._c, self._alpha, self._beta, self._gamma, USER)
+        #    self.sample.lattice_set(lattice)
+        #    # For getting the UB matrix changing
+        #    self.sample.lattice_set(lattice)
+
+        #   self.first_crystal_set = 0
+        self.engines.init(self.geometry, self.detector, self.sample)
+
+    def setAffineCrystal(self, value):
+        new_sample_name = self.sample.name_get() + " (affine)"
+        if new_sample_name not in self.samples:
+            sample = self.sample.copy()
+            sample.name_set(new_sample_name)
+            sample.affine()
+            self.sample = self.samples[new_sample_name] = sample
+
+    def getWavelength(self):
+        if self._energydevice != " " and self._autoenergyupdate:
+            try:
+                if self.energy_device == None:
+                    self.energy_device = PyTango.DeviceProxy(self._energydevice)
+                energy = self.energy_device.Position
+                wavelength = self.lambda_to_e/energy
+                self.setWavelength(wavelength)
+            except:
+                self._log.warning("Not able to get energy from energy device")
+        return self.geometry.wavelength_get(USER)
+
+    def setWavelength(self, value):
+        self.geometry.wavelength_set(value, USER)
+
+    def getEngineMode(self):
+        return self.engine.current_mode_get()
+
+    def setEngineMode(self, value):
+        # TODO why not throw the exception when the mode name is
+        # wrong. The hkl library return an usefull Exception for this.
+        for mode in self.engine.modes_names_get():
+            if value == mode:
+                self.engine.current_mode_set(mode)
+
+    def getEngineModeList(self):
+        return self.engine.modes_names_get()
+
+    def getHKLModeList(self):
+        # TODO seems to me complicate... why this Hkl specific part.
+        # It seems that this controleur mix all the engines and does
+        # something special with the hkl one ???   neverthless I would
+        # be possible to create a self.engines instead of recomputing
+        # it all the time.
+        for key, factory in Hkl.factories().iteritems():
+            if key == self.DiffractometerType:
+                new_engines = factory.create_new_engine_list()
+        new_engines.init(self.geometry, self.detector, self.sample)
+        hkl_engine = new_engines.engine_get_by_name("hkl")
+        return hkl_engine.modes_names_get()
+    def getUBMatrix(self):
+        UB = self.sample.UB_get()
+        return [[UB.get(i, j) for j in range(3)] for i in range(3)]
+
+    def getUx(self):
+        return self.sample.ux_get().value_get(USER)
+
+    def setUx(self, value):
+        ux = self.sample.ux_get()
+        ux.value_set(value, USER)
+        self.sample.ux_set(ux)
+
+    def getUy(self):
+        return self.sample.uy_get().value_get(USER)
+
+    def setUy(self, value):
+        uy = self.sample.uy_get()
+        uy.value_set(value, USER)
+        self.sample.uy_set(uy)
+
+    def getUz(self):
+        return self.sample.uz_get().value_get(USER)
+
+    def setUz(self, value):
+        uz = self.sample.uz_get()
+        uz.value_set(value, USER)
+        self.sample.uz_set(uz)
+
+    def setComputeUB(self, value):
+        if len(value) < 2:
+            return
+        nb_reflections = len(self.sample.reflections_get())
+        if value[0] >= nb_reflections or value[1] >= nb_reflections:
+            return
+        i = 0
+        for ref in self.sample.reflections_get():
+            if value[0] == i:
+                ref1 = ref
+            if value[1] == i:
+                ref2 = ref
+            i = i + 1
+        self.sample.compute_UB_busing_levy(ref1, ref2)
+
+    def getLatticeReciprocal(self):
+        lattice = self.sample.lattice_get()
+        reciprocal = lattice.copy()
+        lattice.reciprocal(reciprocal)
+        return reciprocal.get(USER)
+
+    def getA(self):
+        lattice = self.sample.lattice_get()
+        a, _b, _c, _alpha, _beta, _gamma = lattice.get(USER)
+        return a
+
+    def setA(self, value):
+        lattice = self.sample.lattice_get()
+        a, b, c, alpha, beta, gamma = lattice.get(USER)
+        lattice.set(value, b, c, alpha, beta, gamma, USER)
+        self.sample.lattice_set(lattice)
+        self._a = value
+
+    def getB(self):
+        lattice = self.sample.lattice_get()
+        _a, b, _c, _alpha, _beta, _gamma = lattice.get(USER)
+        return b
+
+    def setB(self, value):
+        lattice = self.sample.lattice_get()
+        a, b, c, alpha, beta, gamma = lattice.get(USER)
+        lattice.set(a, value, c, alpha, beta, gamma, USER)
+        self.sample.lattice_set(lattice)
+        self._b = value
+
+    def getC(self):
+        lattice = self.sample.lattice_get()
+        _a, _b, c, _alpha, _beta, _gamma = lattice.get(USER)
+        return c
+
+    def setC(self, value):
+        lattice = self.sample.lattice_get()
+        a, b, c, alpha, beta, gamma = lattice.get(USER)
+        lattice.set(a, b, value, alpha, beta, gamma, USER)
+        self.sample.lattice_set(lattice)
+        self._c = value
+
+    def getAlpha(self):
+        lattice = self.sample.lattice_get()
+        _a, _b, _c, alpha, _beta, _gamma = lattice.get(USER)
+        return alpha
+
+    def setAlpha(self, value):
+        lattice = self.sample.lattice_get()
+        a, b, c, alpha, beta, gamma = lattice.get(USER)
+        lattice.set(a, b, c, value, beta, gamma, USER)
+        self.sample.lattice_set(lattice)
+        self._alpha = value
+
+    def getBeta(self):
+        lattice = self.sample.lattice_get()
+        _a, _b, _c, _alpha, beta, _gamma = lattice.get(USER)
+        return beta
+
+    def setBeta(self, value):
+        lattice = self.sample.lattice_get()
+        a, b, c, alpha, beta, gamma = lattice.get(USER)
+        lattice.set(a, b, c, alpha, value, gamma, USER)
+        self.sample.lattice_set(lattice)
+        self._beta = value
+
+    def getGamma(self):
+        lattice = self.sample.lattice_get()
+        _a, _b, _c, _alpha, _beta, gamma = lattice.get(USER)
+        return gamma
+
+    def setGamma(self, value):
+        lattice = self.sample.lattice_get()
+        a, b, c, alpha, beta, gamma = lattice.get(USER)
+        lattice.set(a, b, c, alpha, beta, value, USER)
+        self.sample.lattice_set(lattice)
+        self._gamma = value
+
+    def getAFit(self):
+        apar = self.sample.lattice_get().a_get()
+        return apar.fit_get()
+
+    def setAFit(self, value):
+        apar = self.sample.lattice_get().a_get()
+        apar.fit_set(value)
+
+    def getBFit(self):
+        bpar = self.sample.lattice_get().b_get()
+        return bpar.fit_get()
+
+    def setBFit(self, value):
+        bpar = self.sample.lattice_get().b_get()
+        bpar.fit_set(value)
+
+    def getCFit(self):
+        cpar = self.sample.lattice_get().c_get()
+        return cpar.fit_get()
+
+    def setCFit(self, value):
+        cpar = self.sample.lattice_get().c_get()
+        cpar.fit_set(value)
+
+    def getAlphaFit(self):
+        alphapar = self.sample.lattice_get().alpha_get()
+        return alphapar.fit_get()
+
+    def setAlphaFit(self, value):
+        alphapar = self.sample.lattice_get().alpha_get()
+        alphapar.fit_set(value)
+
+    def getBetaFit(self):
+        betapar = self.sample.lattice_get().beta_get()
+        return betapar.fit_get()
+
+    def setBetaFit(self, value):
+        betapar = self.sample.lattice_get().beta_get()
+        betapar.fit_set(value)
+
+    def getGammaFit(self):
+        gammapar = self.sample.lattice_get().gamma_get()
+        return gammapar.fit_get()
+
+    def setGammaFit(self, value):
+        gammapar = self.sample.lattice_get().gamma_get()
+        gammapar.fit_set(value)
+
+    def getComputeTrajectoriesSim(self):
+        return self.lastpseudopos
+
+    def setComputeTrajectoriesSim(self, values):
+        # TODO the hkl library should return these informations
+        # assert (len(values) == len(self.engine.pseudo_axis_names_get()),
+        #         "Not the right number of parameters given (%d, %d expected)"
+        #         " for the \"%s\" engine" %
+        #         (len(values),
+        #          len(self.engine.pseudo_axis_names_get()),
+        #          self.engine.name_get())
+        curr_physical_pos = self.geometry.axis_values_get(USER)
+        solutions = self._solutions(values, curr_physical_pos)
+        self.trajectorylist = [item.geometry_get().axis_values_get(USER)
+                               for item in solutions.items()]
+        self.lastpseudos = tuple(values)
+
+    def getTrajectoryList(self):
+        return self.trajectorylist
+
+    def getSelectedTrajectory(self):
+        return self.selected_trajectory
+
+    def setSelectedTrajectory(self, value):
+        self.selected_trajectory = value
+
+    def getEngine(self):
+        return self.engine.name_get()
+
+    def setEngine(self, value):
+        self.engine = self.engines.engine_get_by_name(value)
+
+    def getEngineList(self):
+        return self.engine_list
+
+    def getEnginesConf(self):
+        if self.engines_conf is None:
+            elements = []
+            for engine in self.engines.engines_get():
+                name = engine.name_get()
+                motors = tuple([self.GetPseudoMotor(pseudo)
+                                for pseudo in engine.pseudo_axis_names_get()])
+                elements.append((name, motors))
+            json_codec = CodecFactory().getCodec(format)
+            f, conf = json_codec.encode(('', tuple(elements)))[1]
+            self.engines_conf = conf
+
+    def setAddCrystal(self, value):
+        if value not in self.samples:
+            self.samples[value] = Hkl.Sample.new(value)
+            # value returned when the attribute is read
+            self._addcrystal = value
+
+    def getCrystalList(self):
+        return list(self.samples)
+
+    def setDeleteCrystal(self, value):
+        if value in self.samples:
+            self.samples.pop(value)
+            # value returned when the attribute is read
+            self._deletecrystal = value
+
+    def setAddReflection(self, value):
+        # Parameters: h, k, l, [affinement], angles are the current ones
+        # Read current motor positions
+        motor_position = []
+        for i in range(0, self.nb_ph_axes):
+            motor = self.GetMotor(i)
+            motor_position.append(motor.position.value)
+        self.geometry.axis_values_set(motor_position, USER)
+        newref = self.sample.add_reflection(
+            self.geometry, self.detector, value[0], value[1], value[2])
+        # Set affinement if given (by default reflections are created with
+        # affinement = 1)
+        if len(value) > 3:
+            newref.flag_set(value[3])
+
+    def setSubstituteReflection(self, value):
+        # Parameters: index, h, k, l, [affinement], angles are the current ones
+        # Read current reflections
+        old_reflections = self.sample.reflections_get()
+        nb_old_ref = len(old_reflections)
+        hkla = []
+        ihkla = 0
+        angles = []
+        for ref in old_reflections:
+            hkla.append([])
+            hkla[ihkla].append(ref.hkl_get()[0])
+            hkla[ihkla].append(ref.hkl_get()[1])
+            hkla[ihkla].append(ref.hkl_get()[2])
+            hkla[ihkla].append(ref.flag_get())
+            angles.append([])
+            angles[ihkla].append(ihkla)
+            for angle in ref.geometry_get().axis_values_get(USER):
+                angles[ihkla].append(angle)
+            ihkla = ihkla + 1
+
+        # Remove reflections with index bigger than the inserted one
+        if value[0] < nb_old_ref:
+            for j in range(int(value[0]), nb_old_ref):
+                # The index are shifted so we have to remove always de
+                # first index
+                self.setRemoveReflection(int(value[0]))
+
+        # Check if the index is bigger than existing ones
+        if value[0] < nb_old_ref:
+            for i in range(0, nb_old_ref):
+                if i < value[0]:
+                    pass
+                elif i == value[0]:
+                    # add new reflection
+                    self.setAddReflection(value[1:])
+                elif i > value[0]:
+                    self.setAddReflection(hkla[i])
+                    self.setAdjustAnglesToReflection(angles[i])
+        else:  # add the new one
+            self.setAddReflection(value[1:])
+
+    def setSwapReflections01(self, value):
+        # Read current reflections
+        reflections = self.sample.reflections_get()
+        nb_ref = len(reflections)
+
+        if nb_ref < 2:
+            self._log.warning("Only %d reflection(s) defined. Swap not possible" % (nb_ref,))
+            return
+
+        hkla = []
+        ihkla = 0
+        angles = []
+        for ref in reflections:
+            if ihkla < 2:
+                hkla.append([])
+                if ihkla == 1:
+                    hkla[ihkla].append(0)
+                else:
+                    hkla[ihkla].append(1)
+                hkla[ihkla].append(ref.hkl_get()[0])
+                hkla[ihkla].append(ref.hkl_get()[1])
+                hkla[ihkla].append(ref.hkl_get()[2])
+                hkla[ihkla].append(ref.flag_get())
+                angles.append([])
+                # swap the index
+                if ihkla == 1:
+                    angles[ihkla].append(0)
+                else:
+                    angles[ihkla].append(1)
+                for angle in ref.geometry_get().axis_values_get(USER):
+                    angles[ihkla].append(angle)
+            ihkla = ihkla + 1
+
+        # Remove reflection 0
+        self.setRemoveReflection(0)
+
+        # Insert old ref 1 to 0
+
+        self.setSubstituteReflection(hkla[1])
+        self.setAdjustAnglesToReflection(angles[1])
+
+        # Remove reflection 1
+
+        self.setRemoveReflection(1)
+
+        # Insert old ref 0 to 1
+
+        self.setSubstituteReflection(hkla[0])
+        self.setAdjustAnglesToReflection(angles[0])
+
+
+    def getReflectionList(self):
+        reflectionslist = []
+        i = 0
+        for ref in self.sample.reflections_get():
+            reflectionslist.append([])
+            reflectionslist[i].append(i)  # Reflection index
+            reflectionslist[i].append(ref.hkl_get()[0])
+            reflectionslist[i].append(ref.hkl_get()[1])
+            reflectionslist[i].append(ref.hkl_get()[2])
+            reflectionslist[i].append(0)  # Relevance
+            reflectionslist[i].append(ref.flag_get())  # Affinement
+            for value in ref.geometry_get().axis_values_get(USER):
+                reflectionslist[i].append(value)
+            i = i + 1
+        return reflectionslist
+
+    def setRemoveReflection(self, value):  # value: reflexion index
+        i = 0
+        for ref in self.sample.reflections_get():
+            if i == value:
+                self.sample.del_reflection(ref)
+            i = i + 1
+
+    def setLoadReflections(self, value):  # value: complete path of the file with the reflections to set
+        # Read the file
+        with open(value, 'r') as f:
+            self._loadreflections = value
+
+            # Remove all reflections
+            for reflection in self.sample.reflections_get():
+                self.sample.del_reflection(reflection)
+
+            # add one reflection per line (no check for now)
+            # TODO it seems that the wavelength is missing in the file.
+            for line in f:
+                # the reflection line is structured like this
+                # index 0 -> reflec. index;
+                # index 1, 2, 3 -> hkl;
+                # index 4 -> relevance
+                # index 5 -> affinement;
+                # last ones (2, 4 or 6) -> geometry axes values
+                values = [float(v) for v in line.split(' ')]
+
+                # create the reflection
+                reflection = self.sample.add_reflection(self.geometry, self.detector,
+                                                        values[1], values[2], values[3])
+                # set the affinement
+                reflection.flag_set(values[5])
+
+                # set the axes values
+                geometry = reflection.geometry_get()
+                geometry.axis_values_set(values[6:], USER)
+                reflection.geometry_set(geometry)
+
+    def setLoadCrystal(self, value):  # value: complete path of the file with the crystal to set
+        # Read the file
+        
+        with open(value, 'r') as crystal_file:
+            self._loadcrystal = value
+
+            nb_ref = 0
+    
+            for line in crystal_file:
+                line = line.replace("\n","")
+                if line.find("Crystal") != -1:
+                    # Add crystal
+                    line = line.replace(" ", "")
+                    crystal = line.split("Crystal",1)[1]
+                    self.setAddCrystal(crystal)
+                    # Set crystal
+                    self.sample = self.samples[crystal]
+                    self.engines.init(self.geometry, self.detector, self.sample)
+                    # Remove all reflections from crystal (there should not be any ... but just in case)
+                    for ref in self.sample.reflections_get():
+                        self.sample.del_reflection(ref)
+                elif line.find("Wavelength") != -1:
+                    line = line.replace(" ", "")
+                    wavelength = float(line.split("Wavelength",1)[1]) # The value will be set after creating the new geometry with the reflections
+                    self.geometry.wavelength_set(wavelength, USER)
+                elif line.find("A") != -1 and line.find("B") != -1 and line.find("C") != -1:
+                    par_line = line.split(" ")
+                    avalue = float(par_line[1])
+                    bvalue = float(par_line[3])
+                    cvalue = float(par_line[5])
+                    lattice = self.sample.lattice_get()
+                    a, b, c, alpha, beta, gamma = lattice.get(USER)
+                    lattice.set(avalue,bvalue,cvalue, alpha, beta, gamma, USER)
+                    # For getting the UB matrix changing
+                    self.sample.lattice_set(lattice)
+                    self._a = avalue
+                    self._b = bvalue
+                    self._c = cvalue
+                elif line.find("Alpha") != -1 and line.find("Beta") != -1 and line.find("Gamma") != -1:
+                    par_line = line.split(" ")
+                    alphavalue = float(par_line[1])
+                    betavalue = float(par_line[3])
+                    gammavalue = float(par_line[5])
+                    lattice = self.sample.lattice_get()
+                    a, b, c, alpha, beta, gamma = lattice.get(USER)
+                    lattice.set(a,b,c, alphavalue, betavalue, gammavalue, USER)
+                    # For getting the UB matrix changing
+                    self.sample.lattice_set(lattice)
+                    self._alpha = alphavalue
+                    self._beta = betavalue
+                    self._gamma = gammavalue
+                elif line.find("Engine") != -1:
+                    line = line.split(" ")
+                    engine = line[1]
+                    self.setEngine(engine)
+                elif line.find("Mode") != -1:
+                    line = line.split(" ")
+                    mode = line[1]
+                    self.setEngineMode(mode)
+                elif line.find("PsiRef") != -1:
+                    if line.find("PsiRef not available") == -1:
+                        psiref_line = line.split(" ")
+                        psirefh = float(psiref_line[1])
+                        psirefk = float(psiref_line[2])
+                        psirefl = float(psiref_line[3])
+                        try:
+                            self.setPsiRefH(psirefh)
+                        except:
+                            self._log.warning("PsiRefH not set. Psi not available in current mode")
+                        try:
+                            self.setPsiRefK(psirefk)
+                        except:
+                            self._log.warning("PsiRefK not set. Psi not available in current mode")
+                        try:
+                            self.setPsiRefL(psirefl)
+                        except:
+                            self._log.warning("PsiRefL not set. Psi not available in current mode")
+                elif line.find("R0") != -1 or line.find("R1") != -1:
+                    if line.find("R0") != -1:
+                        line = line.split("R0 ")[1]
+                    else:
+                        line = line.split("R1 ")[1]
+                    # Set reflections
+                    ref_values = []
+                    for value in line.split(' '):
+                        try:
+                            ref_values.append(float(value))  # index 0 -> reflec. index; index 1,2, 3 hkl; 4 relevance; 5 affinement; last ones (2, 4 or 6) angles
+                        except:
+                            pass
+                    # Set hkl values to the reflection
+                    newref = self.sample.add_reflection(
+                        self.geometry, self.detector, ref_values[1], ref_values[2], ref_values[3])
+                    # Set affinement
+                    newref.flag_set(ref_values[5])
+                    # Adjust angles
+                    new_angles = []
+                    for i in range(6, len(ref_values)):
+                        new_angles.append(ref_values[i])
+                    geometry = newref.geometry_get()
+                    geometry.axis_values_set(new_angles, USER)
+                    newref.geometry_set(geometry)
+                    nb_ref = nb_ref + 1
+                elif line.find("SaveDirectory") != -1:
+                    line = line.split(" ")
+                    if self._savedirectory == " " or self._savedirectory == "":
+                        self._savedirectory = line[1]
+                elif line.find("AutoEnergyUpdate") != -1:
+                    line = line.split(" ")
+                    self._autoenergyupdate = int(line[1])
+
+        if nb_ref > 1:
+            values = [0,1]
+            self.setComputeUB(values)
+
+
+    def setSaveReflections(self, value):  # value: directory, the file would be given by the name of the sample
+        complete_file_name = value + str(self.sample.name_get()) + ".ref"
+        complete_file_name = complete_file_name.replace(' ', '')
+        complete_file_name = complete_file_name.replace('(', '_')
+        complete_file_name = complete_file_name.replace(')', '_')
+        try:
+            open(complete_file_name)
+            new_file_name = complete_file_name + "_" + str(time.time())
+            cmd = "mv " + str(complete_file_name) + " " + str(new_file_name)
+            os.system(cmd)
+        except:
+            pass
+        with open(complete_file_name, 'w') as ref_file:
+            reflections = self.getReflectionList()
+            for ref in reflections:
+                ref_str = ""
+                for val in ref:
+                    ref_str = ref_str + str(val) + " "
+                ref_str = ref_str[:-1]
+                ref_str = ref_str + '\n'
+                ref_file.write(ref_str)
+
+    def setSaveCrystal(self, value):  # value: not used
+        default_file_name = self._savedirectory + "/defaultcrystal.txt"
+        crystal_file_name = self._savedirectory + "/" + self.sample.name_get() + ".txt"
+
+        with open(default_file_name, 'w') as crys_file:
+            # date
+    
+            date_str = "Created at " + time.strftime("%Y-%m-%d %H:%M") + "\n\n"
+            crys_file.write(date_str)
+    
+            # write crystal name
+    
+            crystal_name = self.sample.name_get()
+            crys_str = "Crystal    " +   crystal_name + "\n\n"
+            crys_file.write(crys_str)
+    
+            # write wavelength
+    
+            wavelength = self.geometry.wavelength_get(USER)
+            wl_str = "Wavelength " + str(wavelength) + "\n\n"
+            crys_file.write(wl_str)
+    
+    
+            # write lattice parameters
+            apar = self.sample.lattice_get().a_get()
+            a = apar.value_get(USER)
+            bpar = self.sample.lattice_get().b_get()
+            b = bpar.value_get(USER)
+            cpar = self.sample.lattice_get().c_get()
+            c = cpar.value_get(USER)
+            alphapar = self.sample.lattice_get().alpha_get()
+            alpha = alphapar.value_get(USER)
+            betapar = self.sample.lattice_get().beta_get()
+            beta = betapar.value_get(USER)
+            gammapar = self.sample.lattice_get().gamma_get()
+            gamma = gammapar.value_get(USER)
+            par_str = "A " + str(a) + " B " + str(b) + " C " + str(c) + "\n"
+            crys_file.write(par_str)
+            par_str = "Alpha " + str(alpha) + " Beta " + str(beta) + " Gamma " + str(gamma) + "\n\n"
+            crys_file.write(par_str)
+    
+            # write reflections
+            reflections = self.getReflectionList()
+            ref_in = 0
+            for ref in reflections:
+                ref_str = ""
+                for val in ref:
+                    ref_str = ref_str + str(val) + " "
+                ref_str = ref_str[:-1]
+                ref_str = "R" + str(ref_in) + " " + ref_str + '\n'
+                if ref_in < 2:
+                    crys_file.write(ref_str)
+                ref_in = ref_in + 1
+            if ref_in == 0:
+                ref_str = "No reflections\n"
+                crys_file.write(ref_str)
+            crys_file.write("\n")
+    
+            # write engine
+            engine_str = "Engine " + self.engine.name_get() +  "\n\n"
+            crys_file.write(engine_str)
+    
+            # write mode
+            mode_str = "Mode " + self.engine.current_mode_get() +  "\n\n"
+            crys_file.write(mode_str)
+    
+            # write psiref (if available in mode)
+    
+            psirefh = self.getPsiRefH()
+            psirefk = self.getPsiRefK()
+            psirefl = self.getPsiRefL()
+            if psirefh != -999 or psirefk != -999 or psirefl != -999:
+                psi_str = "PsiRef " + str(psirefh) + " " + str(psirefk) + " " + str(psirefl) + "\n\n"
+            else:
+                psi_str = "PsiRef not available in current engine mode\n\n"
+    
+            crys_file.write(psi_str)
+    
+            # write autoenergyupdate value
+     
+            autoenergyupdate_str = "AutoEnergyUpdate " + str(self._autoenergyupdate) + "\n\n" 
+            crys_file.write(autoenergyupdate_str)
+    
+            # Only for info in the file but not for loading:
+    
+            # write ub matrix
+            for i in range(0,3):
+                ub_str = ""
+                for j in range(0,3):
+                    #ub_str = ub_str + "U" + str(i) + str(j) + " " + str(self.sample.UB_get().get(i, j)) + " "
+                    ub_str = ub_str + "U" + str(i) + str(j) + " %.3f" % self.sample.UB_get().get(i, j) + " "
+                ub_str = ub_str + "\n"
+                crys_file.write(ub_str)
+            crys_file.write("\n")
+    
+            # write u vector
+            u_str = "Ux " + str(self.sample.ux_get().value_get(USER)) + " Uy " + str(self.sample.uy_get().value_get(USER)) + " Uz " + str(self.sample.uz_get().value_get(USER)) + "\n\n"
+            crys_file.write(u_str)
+    
+            # write directory where the file is saved
+    
+            dir_str = "SaveDirectory " + self._savedirectory + "\n"
+            crys_file.write(dir_str)
+
+        cmd = "cp " + str(default_file_name) + " " + str(crystal_file_name)
+        os.system(cmd)
+
+    def setSaveDirectory(self, value):
+        self._savedirectory = value
+
+    def setAdjustAnglesToReflection(self, value):  # value: reflexion index + new angles
+        ref_index = value[0]
+        new_angles = value[1:]
+        i = 0
+        for ref in self.sample.reflections_get():
+            if i == ref_index:
+                geometry = ref.geometry_get()
+                geometry.axis_values_set(new_angles, USER)
+                ref.geometry_set(geometry)
+            i = i + 1
+
+    def getReflectionAngles(self):
+        reflectionsangles = []
+        i = -1
+        for ref1 in self.sample.reflections_get():
+            i = i + 1
+            j = -1
+            reflectionsangles.append([])
+            for ref2 in self.sample.reflections_get():
+                j = j + 1
+                angle = 0
+                if i < j:
+                    angle = self.sample.get_reflection_measured_angle(
+                        ref1, ref2)
+                elif j < i:
+                    angle = self.sample.get_reflection_theoretical_angle(
+                        ref1, ref2)
+                reflectionsangles[i].append(angle)
+        return reflectionsangles
+
+    def getModeParametersNames(self):
+        return self.engine.parameters_names_get()
+    
+
+    def getModeParametersValues(self):
+        return [p.value_get(USER)
+                for p in [self.engine.parameter_get(n)
+                          for n in self.engine.parameters_names_get()]]
+
+    def setModeParametersValues(self, value):
+        for parameter, v in zip(self.engine.parameters_names_get(), value):
+            p = self.engine.parameter_get(parameter)
+            p.value_set(v, USER)
+            self.engine.parameter_set(parameter, p)
+
+    def _getPsiRef(self, parameters):
+        # TODO I do not understand this method. check that the
+        # parameters names are ok. I changed one.
+        value = -999
+        for parameter in self.engine.parameters_names_get():
+            if parameter in parameters:
+                value = self.engine.parameter_get(parameter).value_get(USER)
+        return value
+
+    def getPsiRefH(self):
+        return self._getPsiRef(["h1", "h2", "x"])
+
+    def getPsiRefK(self):
+        return self._getPsiRef(["k1", "k2", "y"])
+
+    def getPsiRefL(self):
+        return self._getPsiRef(["l1", "l2", "z"])
+
+    def _setPsiRef(self, parameters, value):
+        # TODO idem here :)
+        value_set = False
+        for parameter in self.engine.parameters_names_get():
+            if parameter in parameters:
+                p = self.engine.parameter_get(parameter)
+                p.value_set(value, USER)
+                self.engine.parameter_set(parameter, p)
+                value_set = True
+        if not value_set:
+            raise Exception("psiref not available in this mode")
+
+    def setPsiRefH(self, value):
+        self._setPsiRef(["h1", "h2", "x"], value)
+
+    def setPsiRefK(self, value):
+        self._setPsiRef(["k1", "k2", "y"], value)
+
+    def setPsiRefL(self, value):
+        self._setPsiRef(["l1", "l2", "z"], value)
+
+    def getMotorList(self):
+        motor_names = []
+        for i in range(0, self.nb_ph_axes):
+            motor = self.GetMotor(i)
+            mot_info = motor.name + "  (" + motor.full_name + ")"
+            motor_names.append(mot_info)
+        return motor_names
+
+    def getHKLPseudoMotorList(self):
+        hkl_pm_names = []
+        for i in range(0, 3):
+            motor = self.GetPseudoMotor(i)
+            mot_info = motor.name + "  (" + motor.full_name + ")"
+            hkl_pm_names.append(mot_info)
+        return hkl_pm_names
+
+    def getMotorRoles(self):
+        roles_names = tuple(self.geometry.axis_names_get())
+        return roles_names
+
+    def setEnergyDevice(self, value):
+        self._energydevice = value
+        try:
+            self.energy_device = PyTango.DeviceProxy(self._energydevice)
+        except:
+            self.energy_device = None
+            self._log.warning("Not able to create proxy to energydevice")
+
+    def setAutoEnergyUpdate(self, value):
+        self._autoenergyupdate = value
+
+    def setComputeHKL(self, value):
+              
+        self.getWavelength()
+            
+
+        # write the physical motor into the geometry
+        if len(value) >=  self.nb_ph_axes:
+            self.geometry.axis_values_set(value[:self.nb_ph_axes], USER)
+            self.engines.get()
+        else:            
+            raise Exception("Not enough arguments. %d are need " % (self.nb_ph_axes))
+
+        # extract hkl  values
+        values = []
+        engine = self.engines.engine_get_by_name("hkl")
+        values = engine.pseudo_axis_values_get(USER)
+    
+        self._computehkl = values
+
+        
+
+
+# 6C Diffractometers ####################
+
+
+class Diffrac6C(DiffracBasis):  # DiffractometerType: "PETRA3 P09 EH2"
+
+    """ The PseudoMotor controller for the diffractometer"""
+
+    pseudo_motor_roles = "h", "k", "l"
+    motor_roles = "mu", "omega", "chi", "phi", "delta", "gamma"
+
+    def __init__(self, inst, props, *args, **kwargs):
+        """ Do the default init plus the specific diffractometer
+        staff.
+        @param properties of the controller
+        """
+
+        DiffracBasis.__init__(self, inst, props, *args, **kwargs)
+
+
+class DiffracE6C(DiffracBasis):
+
+    """ The PseudoMotor controller for the diffractometer"""
+
+    pseudo_motor_roles = "h", "k", "l", "psi", "q", "alpha", "qper", "qpar"
+    motor_roles = "mu", "omega", "chi", "phi", "gamma", "delta"
+
+    def __init__(self, inst, props, *args, **kwargs):
+        """ Do the default init plus the specific diffractometer
+        staff.
+        @param properties of the controller
+        """
+
+        DiffracBasis.__init__(self, inst, props, *args, **kwargs)
+
+# 4C Diffractometers ####################
+
+
+class DiffracE4C(DiffracBasis):
+
+    """ The PseudoMotor controller for the diffractometer"""
+
+    pseudo_motor_roles = "h", "k", "l", "psi", "q"
+    motor_roles = "omega", "chi", "phi", "tth"
+
+    def __init__(self, inst, props, *args, **kwargs):
+        """ Do the default init plus the specific diffractometer
+        staff.
+        @param properties of the controller
+        """
+
+        DiffracBasis.__init__(self, inst, props, *args, **kwargs)
+
+
+# wip Generic diffractometer
+
+class Diffractometer(DiffracBasis):
+    """ The PseudoMotor controller for the diffractometer"""
+
+    def __init__(self, inst, props, *args, **kwargs):
+        """ Do the default init plus the specific diffractometer
+        staff.
+        @param properties of the controller
+        """
+        DiffracBasis.__init__(self, inst, props, *args, **kwargs)
+
+        factory = Hkl.factories()[self.DiffractometerType]
+        self.geometry = factory.create_new_geometry()
+        self.engines = factory.create_new_engine_list()
+
+        # dynamically set the roles using the hkl library
+        Diffractometer.motor_roles = tuple(self.geometry.axis_names_get())
+        Diffractometer.pseudo_motor_roles = tuple(
+            chain.from_iterable(
+                [engine.pseudo_axis_names_get()
+                 for engine in self.engines.engines_get()]))
diff --git a/src/sardana/release.py b/src/sardana/release.py
index 25b36a5..29fdf54 100644
--- a/src/sardana/release.py
+++ b/src/sardana/release.py
@@ -49,7 +49,7 @@ name = 'sardana'
 #: the new substring. We have to avoid using either dashes or underscores,
 #: because bdist_rpm does not accept dashes (an RPM) convention, and
 #: bdist_deb does not accept underscores (a Debian convention).
-version_info = (1, 6, 1, 'rc', 0)
+version_info = (2, 0, 0, 'rc', 0)
 version = '.'.join(map(str, version_info[:3]))
 revision = str(version_info[4])
 
diff --git a/src/sardana/sardanabase.py b/src/sardana/sardanabase.py
index c8f5c43..2cae07c 100644
--- a/src/sardana/sardanabase.py
+++ b/src/sardana/sardanabase.py
@@ -74,6 +74,14 @@ class SardanaBaseObject(EventGenerator, EventReceiver, Logger):
         :return: this sardana object name
         :rtype: str"""
         return self._name
+    
+    def set_name(self, name):
+        """Sets sardana object name
+        
+        :param: sardana object name
+        :type: str"""
+        self._name = name
+
 
     def get_full_name(self):
         """Returns this sardana object full name
@@ -178,7 +186,7 @@ class SardanaBaseObject(EventGenerator, EventReceiver, Logger):
 
     manager = property(get_manager,
                        doc="reference to the :class:`sardana.Manager`")
-    name = property(get_name, doc="object name")
+    name = property(get_name, set_name, doc="object name")
     full_name = property(get_full_name, doc="object full name")
     frontend = property(get_frontend, doc="the object frontend")
 
diff --git a/src/sardana/sardanacontainer.py b/src/sardana/sardanacontainer.py
index 0cf84e5..58edbeb 100644
--- a/src/sardana/sardanacontainer.py
+++ b/src/sardana/sardanacontainer.py
@@ -217,14 +217,18 @@ class SardanaContainer(object):
         return [ elem.get_name() for elem in self.get_elements_by_type(t) ]
 
     def rename_element(self, old_name, new_name):
-        """Rename a pool object
+        """Rename an object
            
-           :param old_name: old pool object name
+           :param old_name: old object name
            :type old_name: str
-           :param new_name: new pool object name
+           :param new_name: new object name
            :type new_name: str
         """
-        raise NotImplementedError
+        element = self._element_names.pop(old_name, None)
+        if element is None:
+            raise KeyError('There is no element with name %s' % old_name)
+        element.name = new_name
+        self._element_names[new_name] = element
 
     def check_element(self, name, full_name):
         raise_element_name = True
diff --git a/src/sardana/sardanacustomsettings.py b/src/sardana/sardanacustomsettings.py
index a940419..6017b75 100644
--- a/src/sardana/sardanacustomsettings.py
+++ b/src/sardana/sardanacustomsettings.py
@@ -46,3 +46,10 @@ LOG_BCK_COUNT = 5
 #: - "CLI": Input via spock command line. This is the default.
 #: - "Qt": Input via Qt dialogs
 SPOCK_INPUT_HANDLER = "CLI"
+
+#: Use this map in order to avoid ambiguity with scan recorders (file) if
+#: extension is intended to be the recorder selector.
+#: dict<str, str>
+#: key   - scan file extension
+#: value - recorder name
+SCAN_RECORDER_MAP = None 
diff --git a/src/sardana/sardanadefs.py b/src/sardana/sardanadefs.py
index 88eeb01..1c2a6ca 100644
--- a/src/sardana/sardanadefs.py
+++ b/src/sardana/sardanadefs.py
@@ -287,6 +287,8 @@ ElementType = Enumeration("ElementType", (\
     "Instrument",
     "ControllerClass",
     "ControllerLibrary",
+    "RecorderClass",
+    "RecorderLibrary",
     "MacroServer",
     "Door",
     "MacroClass",
diff --git a/src/sardana/sardanamodulemanager.py b/src/sardana/sardanamodulemanager.py
old mode 100644
new mode 100755
index 0abb729..923cf13
--- a/src/sardana/sardanamodulemanager.py
+++ b/src/sardana/sardanamodulemanager.py
@@ -183,8 +183,31 @@ class ModuleManager(Singleton, Logger):
                 mfile.close()
         return pathname
 
+    def isValidModule(self, module_name, path=None):
+        """ Method to verify is a module is loadable.
+        """
+        m, mfile = None, None
+        fake_name = "_" + module_name + "_"
+        try:
+            mfile, pathname, desc = imp.find_module(module_name, path)
+            self.info("(re)loading module %s...", module_name)
+            m = imp.load_module(fake_name, mfile, pathname, desc)
+        except:
+            self.error("Invalid module %s", module_name)
+            self.debug("Details:", exc_info=1)
+            return False, sys.exc_info()
+        finally:
+            if mfile is not None:
+                mfile.close()
+
+        del sys.modules[fake_name]
+        return True, None
+
     def reloadModule(self, module_name, path=None, reload=True):
         """Loads/reloads the given module name"""
+        valid, _ = self.isValidModule(module_name, path)
+        if not valid:
+            return None
 
         if not reload:
             return self.loadModule(module_name, path=path)
@@ -205,7 +228,7 @@ class ModuleManager(Singleton, Logger):
                 mfile.close()
 
         if m is None:
-            return
+            return None
 
         self._modules[module_name] = m
 
@@ -250,7 +273,7 @@ class ModuleManager(Singleton, Logger):
         with PathContext(path):
             self.info("loading module %s...", module_name)
             try:
-                module = __import__(module_name, globals(), locals(), [], -1)
+                module = __import__(module_name, globals(), locals(), -1)
             except:
                 self.error("Error loading module %s", module_name)
                 self.debug("Details:", exc_info=1)
diff --git a/src/sardana/spock/colors.py b/src/sardana/spock/colors.py
index 26105a7..dd68759 100644
--- a/src/sardana/spock/colors.py
+++ b/src/sardana/spock/colors.py
@@ -26,85 +26,8 @@
 
 """This package provides the sardana library"""
 
-__all__ = ['make_color_table', 'TermColors', 'HTMLColors', 'TermTangoColors',
-           'TermTauSWDevStateColors', 'HTMLTangoColors']
+__all__ = ['make_color_table', 'TermColors', 'HTMLColors']
 
 __docformat__ = 'restructuredtext'
 
-def make_color_table(in_class,use_name=False):
-    """Build a set of color attributes in a class.
-
-    Helper function for building the *TermColors classes."""
-    
-    color_templates = (
-        ("Black"       , "0;30"),
-        ("Red"         , "0;31"),
-        ("Green"       , "0;32"),
-        ("Brown"       , "0;33"),
-        ("Blue"        , "0;34"),
-        ("Purple"      , "0;35"),
-        ("Cyan"        , "0;36"),
-        ("LightGray"   , "0;37"),
-        ("DarkGray"    , "1;30"),
-        ("LightRed"    , "1;31"),
-        ("LightGreen"  , "1;32"),
-        ("Yellow"      , "1;33"),
-        ("LightBlue"   , "1;34"),
-        ("LightPurple" , "1;35"),
-        ("LightCyan"   , "1;36"),
-        ("White"       , "1;37"),  )
-
-    for name,value in color_templates:
-        if use_name:
-            setattr(in_class,name,in_class._base % name)
-        else:
-            setattr(in_class,name,in_class._base % value)
-
-class TermColors:
-    """Color escape sequences.
-
-    This class defines the escape sequences for all the standard (ANSI?) 
-    colors in terminals. Also defines a NoColor escape which is just the null
-    string, suitable for defining 'dummy' color schemes in terminals which get
-    confused by color escapes.
-
-    This class should be used as a mixin for building color schemes.
-    
-    Basicaly this class is just a copy of IPython.ColorANSI.TermColors class"""
-    
-    NoColor = ''  # for color schemes in color-less terminals.
-    Normal = '\033[0m'   # Reset normal coloring
-    _base  = '\033[%sm'  # Template for all other colors
-
-class HTMLColors:
-    
-    NoColor = ''
-    Normal = '</font>'
-    _base = '<font color=%s>'
-
-# Build the actual color table as a set of class attributes:
-make_color_table(TermColors)
-make_color_table(HTMLColors,True)
-
-import PyTango
-
-TermTangoColors = { PyTango.DevState.ON      : TermColors.Green,
-                    PyTango.DevState.ALARM   : TermColors.Brown,
-                    PyTango.DevState.FAULT   : TermColors.Red,
-                    PyTango.DevState.UNKNOWN : TermColors.LightGray,
-                    None                     : TermColors.DarkGray }
-
-from taurus.core import TaurusSWDevState
-
-TermTauSWDevStateColors = {
-    TaurusSWDevState.Uninitialized       : TermColors.LightGray,
-    TaurusSWDevState.Running             : TermColors.Green, 
-    TaurusSWDevState.Shutdown            : TermColors.DarkGray, 
-    TaurusSWDevState.Crash               : TermColors.Red,
-    TaurusSWDevState.EventSystemShutdown : TermColors.Brown }
-    
-HTMLTangoColors = { PyTango.DevState.ON      : HTMLColors.Green,
-                    PyTango.DevState.ALARM   : HTMLColors.Brown,
-                    PyTango.DevState.FAULT   : HTMLColors.Red,
-                    PyTango.DevState.UNKNOWN : HTMLColors.LightGray,
-                    None                     : HTMLColors.DarkGray }
\ No newline at end of file
+from taurus.core.util.console import make_color_table, TermColors, HTMLColors
\ No newline at end of file
diff --git a/src/sardana/spock/config.py b/src/sardana/spock/config.py
index 63328e1..2a3e221 100644
--- a/src/sardana/spock/config.py
+++ b/src/sardana/spock/config.py
@@ -28,8 +28,15 @@
 
 __all__ = ['Spock']
 
-from IPython.config.configurable import Configurable
-from IPython.utils.traitlets import Unicode, Bool
+
+try:
+    # IPython 4.x
+    from traitlets.config.configurable import Configurable
+    from traitlets import Unicode, Bool
+except ImportError:
+    # IPython < 4.x
+    from IPython.config.configurable import Configurable
+    from IPython.utils.traitlets import Unicode, Bool
 
 class Spock(Configurable):
     macro_server_name = Unicode(config=True)
diff --git a/src/sardana/spock/ipython_01_00/genutils.py b/src/sardana/spock/ipython_01_00/genutils.py
index 1add778..0ea510f 100644
--- a/src/sardana/spock/ipython_01_00/genutils.py
+++ b/src/sardana/spock/ipython_01_00/genutils.py
@@ -61,12 +61,20 @@ from IPython.core.profiledir import ProfileDirError, ProfileDir
 from IPython.core.application import BaseIPythonApplication
 from IPython.core.interactiveshell import InteractiveShell
 from IPython.utils.io import ask_yes_no as _ask_yes_no
-from IPython.utils.path import get_ipython_dir
 from IPython.utils.process import arg_split
 from IPython.utils.coloransi import TermColors
-from IPython.config.application import Application
 from IPython.terminal.ipapp import TerminalIPythonApp, launch_new_instance
 
+try:
+    # IPython 4.x
+    from traitlets.config.application import Application
+    from IPython.paths import get_ipython_dir
+except:
+    # IPython <4.x
+   from IPython.config.application import Application
+   from IPython.utils.path import get_ipython_dir
+
+
 import taurus
 #from taurus.core import Release as TCRelease
 
@@ -793,6 +801,7 @@ def load_ipython_extension(ipython):
     init_taurus()
 
     config = ipython.config
+
     user_ns = ipython.user_ns
     user_ns['MACRO_SERVER_NAME'] = config.Spock.macro_server_name
     user_ns['MACRO_SERVER_ALIAS'] = config.Spock.macro_server_alias
@@ -945,12 +954,6 @@ object?   -> Details about 'object'. ?object also works, ?? prints more.
     completer.greedy = False
 
     # ------------------------------------
-    # InteractiveShellApp
-    # ------------------------------------
-    i_shell_app = config.InteractiveShellApp
-    i_shell_app.ignore_old_config = True
-
-    # ------------------------------------
     # TerminalIPythonApp: options for the IPython terminal (and not Qt Console)
     # ------------------------------------
     term_app = config.TerminalIPythonApp
@@ -1124,10 +1127,19 @@ def prepare_cmdline(argv=None):
 
 
 def run():
-
     try:
-        from IPython.utils.traitlets import Unicode
-        from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
+        try:
+            # IPython 4.x
+            from traitlets import Unicode
+            from qtconsole.rich_jupyter_widget import RichIPythonWidget
+            from qtconsole.qtconsoleapp import IPythonQtConsoleApp
+            # TODO: check if we can/should set IPythonQtConsoleApp.version
+        except:
+            # IPython <4.x
+            from IPython.utils.traitlets import Unicode
+            from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
+            from IPython.qt.console.qtconsoleapp import IPythonQtConsoleApp
+            IPythonQtConsoleApp.version.default_value = release.version
         
         class SpockConsole(RichIPythonWidget):
 
@@ -1137,10 +1149,8 @@ def run():
                 config = get_config()
                 return config.FrontendWidget.banner
 
-        import IPython.qt.console.qtconsoleapp
-        IPythonQtConsoleApp = IPython.qt.console.qtconsoleapp.IPythonQtConsoleApp
         IPythonQtConsoleApp.widget_factory = SpockConsole
-        IPythonQtConsoleApp.version.default_value = release.version
+
     except ImportError:
         pass
 
diff --git a/src/sardana/spock/release.py b/src/sardana/spock/release.py
index 314a6dc..0888980 100644
--- a/src/sardana/spock/release.py
+++ b/src/sardana/spock/release.py
@@ -39,7 +39,7 @@ name = 'spock'
 revision = '1'
 
 #version = '0.8.1.svn.r' + revision.rstrip('M')
-version = '1.6.1'
+version = '2.0.0'
 
 description = "An enhanced interactive Macro Server shell."
 
diff --git a/src/sardana/tango/macroserver/Door.py b/src/sardana/tango/macroserver/Door.py
index c21f643..f16f070 100644
--- a/src/sardana/tango/macroserver/Door.py
+++ b/src/sardana/tango/macroserver/Door.py
@@ -291,7 +291,20 @@ class Door(SardanaDevice):
     
     def always_executed_hook(self):
         pass
-    
+
+    def dev_status(self):
+        self._status = SardanaDevice.dev_status(self)
+        self._status += '\n Macro stack ([state] macro):'
+        macro = self.getRunningMacro()
+        mstack = ''
+        while macro is not None:
+            mstate = macro.getMacroStatus()['state']
+            mstack = '\n    -[%s]\t%s' % (mstate, macro.getCommand()) + mstack
+            macro = macro.getParentMacro()
+        self._status += mstack
+        return self._status
+
+
     def read_attr_hardware(self,data):
         pass
     
@@ -392,7 +405,8 @@ class Door(SardanaDevice):
         return [etree.tostring(xml_seq, pretty_print=False)]
 
     def is_RunMacro_allowed(self):
-        return self.get_state() in [Macro.Finished, Macro.Abort]
+        return self.get_state() in [Macro.Finished, Macro.Abort,
+                                    Macro.Exception]
 
     def SimulateMacro(self, par_str_list):
         raise Exception("Not implemented yet")
@@ -410,7 +424,8 @@ class Door(SardanaDevice):
         return ret
     
     def is_GetMacroEnv_allowed(self):
-        return self.get_state() in [Macro.Finished, Macro.Abort]
+        return self.get_state() in [Macro.Finished, Macro.Abort,
+                                    Macro.Exception]
 
 
 class DoorClass(SardanaDeviceClass):
diff --git a/src/sardana/tango/macroserver/MacroServer.py b/src/sardana/tango/macroserver/MacroServer.py
index 12c2017..9ba516f 100644
--- a/src/sardana/tango/macroserver/MacroServer.py
+++ b/src/sardana/tango/macroserver/MacroServer.py
@@ -26,6 +26,7 @@
 """The MacroServer tango module"""
 
 import os.path
+import sys
 
 from PyTango import Util, Except, DevVoid, DevLong, DevString, DevState, \
     DevEncoded, DevVarStringArray, READ, READ_WRITE, SCALAR, SPECTRUM, DebugIt
@@ -109,7 +110,8 @@ class MacroServer(SardanaDevice):
             self.error("Failed to setup log report to %s",
                        self.LogReportFilename)
             self.debug("Details:", exc_info=1)
-        
+
+        macro_server.set_recorder_path(self.RecorderPath)
         macro_server.set_macro_path(self.MacroPath)
         macro_server.set_pool_names(self.PoolNames)
         
@@ -251,9 +253,12 @@ class MacroServer(SardanaDevice):
         """
         macro_server = self.macro_server
         codec = CodecFactory().getCodec('json')
-        ret = [ codec.encode(('', macro.serialize()))[1]
-            for macro in macro_server.get_macros()
-                if macro.name in macro_names ]
+        ret = [ ]
+       
+        for _, macro in macro_server.get_macros().items():
+            if macro.name in macro_names:
+                ret.append(codec.encode(('', macro.serialize()))[1])
+                
         return ret
     
     def ReloadMacro(self, macro_names):
@@ -336,6 +341,11 @@ class MacroServerClass(SardanaDeviceClass):
             "list of directories to search for macros (path separators "
             "can be '\n' or ':')",
             [] ],
+        'RecorderPath':
+            [DevVarStringArray,
+            "list of directories to search for recorders (path separators "
+            "can be '\n' or ':')",
+            [] ],
         'PythonPath':
             [DevVarStringArray,
             "list of directories to be appended to sys.path at startup (path "
diff --git a/src/sardana/tango/pool/Pool.py b/src/sardana/tango/pool/Pool.py
index e10a887..2adda0c 100644
--- a/src/sardana/tango/pool/Pool.py
+++ b/src/sardana/tango/pool/Pool.py
@@ -457,6 +457,16 @@ class Pool(PyTango.Device_4Impl, Logger):
         for kwargs in kwargs_seq:
             self._create_single_element(kwargs)
 
+    def RenameElement(self, argin):
+        old_name = argin[0]
+        new_name = argin[1]
+        self.pool.rename_element(old_name, new_name)
+        # obtain normal name (without database) and apply new alias
+        element = self.pool.get_element_by_name(new_name)
+        db = PyTango.Util.instance().get_database()
+        normal_name = '/'.join(element.full_name.split('/')[-3:])
+        db.put_device_alias(normal_name, new_name)
+
     def _create_single_element(self, kwargs):
         elem_type_str = kwargs['type']
         ctrl_name = kwargs['ctrl_name']
@@ -509,9 +519,6 @@ class Pool(PyTango.Device_4Impl, Logger):
                 if elem_type == ElementType.Motor:
                     data["position"] = { "abs_change" : "1.0"}
                     data["dialposition"] = { "abs_change" : "5.0"}
-                    if get_tango_version_number() < 80000:
-                        print 20 * "AH! "
-                        data["limit_switches"] = { "abs_change" : "1.0"}
                 elif elem_type == ElementType.CTExpChannel:
                     data["value"] = { "abs_change" : "1.0"}
                 elif elem_type == ElementType.PseudoMotor:
@@ -689,7 +696,7 @@ class Pool(PyTango.Device_4Impl, Logger):
 
         evt_name = evt_type.name.lower()
 
-        if evt_name in ("elementcreated", "elementdeleted"):
+        if evt_name in ("elementcreated", "elementdeleted", "elementchanged"):
             elem = evt_value
             elem_type = elem.get_type()
             td = TYPE_MAP_OBJ[elem_type]
@@ -706,10 +713,12 @@ class Pool(PyTango.Device_4Impl, Logger):
             self.ElementsCache = None
 
             value = { }
-            if "created" in evt_name:
+            if evt_name == "elementcreated":
                 key = 'new'
-            else:
+            elif evt_name == "elementdeleted":
                 key = 'del'
+            else:
+                key = 'change'
             json_elem = elem.serialize(pool=self.pool.full_name)
             value[key] = json_elem,
             value = CodecFactory().getCodec('json').encode(('', value))
@@ -905,7 +914,7 @@ class Pool(PyTango.Device_4Impl, Logger):
             ctrl_class = ctrl_classes[name]
             data = None
             if ctrl_class is not None:
-                data = ctrl_class.toDict()
+                data = ctrl_class.serialize()
             ret.append(data)
         return json.dumps(ret)
 
@@ -1191,6 +1200,23 @@ where the class is described).
     {1}
 """.format(RELOAD_CONTROLLER_CLASS_PAR_IN_DOC, RELOAD_CONTROLLER_CLASS_PAR_OUT_DOC)
 
+RENAME_ELEMENT_PAR_IN_DOC = """
+Two elements sequence of strings: <old element name>, <new element name>
+"""
+
+RENAME_ELEMENT_PAR_OUT_DOC = "None"
+
+RENAME_ELEMENT_CLASS_INFO_DOC = """\
+Tango command to rename the element (rename Pool element and put new alias in
+the Tango Database).
+
+:param argin:
+    {0}
+:type argin: list<str>
+:return:
+    {1}
+""".format(RENAME_ELEMENT_PAR_IN_DOC, RENAME_ELEMENT_PAR_OUT_DOC)
+
 STOP_PAR_IN_DOC = "None"
 STOP_PAR_OUT_DOC = "None"
 
@@ -1241,6 +1267,7 @@ Pool.DeleteElement.__func__.__doc__ = DELETE_ELEMENT_DOC
 Pool.GetControllerClassInfo.__func__.__doc__ = GET_CONTROLLER_CLASS_INFO_DOC
 Pool.ReloadControllerLib.__func__.__doc__ = RELOAD_CONTROLLER_LIB_INFO_DOC
 Pool.ReloadControllerClass.__func__.__doc__ = RELOAD_CONTROLLER_CLASS_INFO_DOC
+Pool.RenameElement.__func__.__doc__ = RENAME_ELEMENT_CLASS_INFO_DOC
 Pool.Stop.__func__.__doc__ = STOP_DOC
 Pool.Abort.__func__.__doc__ = ABORT_DOC
 
@@ -1328,6 +1355,9 @@ class PoolClass(PyTango.DeviceClass):
         'ReloadControllerClass':
             [[PyTango.DevString, RELOAD_CONTROLLER_CLASS_PAR_IN_DOC],
              [PyTango.DevVoid, RELOAD_CONTROLLER_CLASS_PAR_OUT_DOC]],
+        'RenameElement':
+            [[PyTango.DevVarStringArray, RENAME_ELEMENT_PAR_IN_DOC],
+             [PyTango.DevVoid, RENAME_ELEMENT_PAR_OUT_DOC]],
         'GetControllerCode':
             [[PyTango.DevVarStringArray, "<Controller library name> [, <Controller class name>]"],
             [PyTango.DevVarStringArray, "result is a sequence of 3 strings:\n"
diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py
index ee94238..babd346 100644
--- a/src/sardana/taurus/core/tango/sardana/macro.py
+++ b/src/sardana/taurus/core/tango/sardana/macro.py
@@ -466,6 +466,7 @@ class ParamNode(BaseNode):
 
 class SingleParamNode(ParamNode):
     """Single parameter class."""
+
     def __init__(self, parent=None, param=None):
         ParamNode.__init__(self, parent, param)
         if param is None: return
@@ -478,6 +479,11 @@ class SingleParamNode(ParamNode):
     def __len__(self):
         return 0
 
+    def __repr__(self):
+        if self._value is None:
+            return "None"
+        return self._value
+
     def value(self):
         return self._value
 
@@ -488,6 +494,8 @@ class SingleParamNode(ParamNode):
         return self._defValue
 
     def setDefValue(self, defValue):
+        if defValue == "None":
+            defValue = None
         self._defValue = defValue
 
     def type(self):
@@ -497,8 +505,10 @@ class SingleParamNode(ParamNode):
         self._type = type
 
     def toXml(self):
-        paramElement = etree.Element("param", name=self.name(),
-                                              value=self.value())
+        value = self.value()
+        paramElement = etree.Element("param", name=self.name())
+        if not value is None:
+            paramElement.set("value", value)
         return paramElement
 
     def fromXml(self, xmlElement):
@@ -521,6 +531,9 @@ class SingleParamNode(ParamNode):
             return ([val], alert)
         return ([val], "")
 
+    def toList(self):
+        return self._value
+
 class RepeatParamNode(ParamNode, BranchNode):
     """Repeat parameter class."""
 
@@ -531,6 +544,9 @@ class RepeatParamNode(ParamNode, BranchNode):
             return
         self.setParamsInfo(copy.deepcopy(param.get('type')))
 
+    def __repr__(self):
+        return repr(self.children())
+
     def arrangeIndexes(self):
         for i, child in enumerate(self.children()):
             child.setIndex(i + 1)
@@ -552,6 +568,7 @@ class RepeatParamNode(ParamNode, BranchNode):
         self.insertChild(repeat)
         for repeatParam in self.paramsInfo():
             repeat.addParam(repeatParam)
+        return repeat
 
     def isReachedMin(self):
         if self.min() is None:
@@ -623,7 +640,7 @@ class RepeatParamNode(ParamNode, BranchNode):
 
     def fromXml(self, xmlElement):
         self.setName(xmlElement.get("name"))
-        for repeatElement in xmlElement.getiterator("repeat"):
+        for repeatElement in xmlElement:
             repeat = RepeatNode(self)
             repeat.fromXml(repeatElement)
             self.insertChild(repeat)
@@ -634,6 +651,9 @@ class RepeatParamNode(ParamNode, BranchNode):
             motors += child.allMotors()
         return motors
 
+    def toList(self):
+        return [child.toList() for child in self.children()]
+
 #    def isAllowedMoveUp(self):
 #        return self is not self.parent().child(0)
 #
@@ -649,6 +669,9 @@ class RepeatNode(BranchNode):
         if parent is None: return
         self.setIndex(len(self.parent()) + 1)
 
+    def __repr__(self):
+        return repr(self.children())
+
     def index(self):
         return self._index
 
@@ -698,6 +721,12 @@ class RepeatNode(BranchNode):
     def isAllowedMoveDown(self):
         return self is not self.parent().child(len(self.parent()) - 1)
 
+    def toList(self):
+        if len(self.children()) == 1:
+            return self.child(0).toList()
+        else:
+            return [child.toList() for child in self.children()]
+
 class MacroNode(BranchNode):
     """Class to represent macro element."""
     count = 0
@@ -871,6 +900,7 @@ class MacroNode(BranchNode):
 
     def toSpockCommand(self):
         values, alerts = self.toRun()
+        values = map(str, values)
         return "%s %s" % (self.name(), str.join(' ', values))
 
 
@@ -883,7 +913,7 @@ class MacroNode(BranchNode):
         else:
             valueString = ''
             for value in values:
-                valueString += (value + ', ')
+                valueString += (str(value) + ', ')
             return '[%s]' % valueString[:-2]
 
 #    def allMotors(self):
@@ -1000,7 +1030,10 @@ class MacroNode(BranchNode):
         """
 
         macroElement = etree.Element("macro", name=self.name())
-        if withId: macroElement.set("id", str(self.id()))
+        if withId: 
+            id_ = self.id()
+            if not id_ is None:
+                macroElement.set("id", str(self.id()))
         for hookPlace in self.hookPlaces():
             hookElement = etree.SubElement(macroElement, "hookPlace")
             hookElement.text = hookPlace
@@ -1051,6 +1084,15 @@ class MacroNode(BranchNode):
             param.setValue(words[index])
             self.addParam(param)
 
+    def toList(self):
+        """Convert to list representation in format:
+        [macro_name, *parameter_values] where complex repeat parameters are
+        encapsulated in lists e.g. ['mv', [['mot01', 0], ['mot02', 0]]]
+        """
+        list_ = [param.toList() for param in self.params()]
+        list_.insert(0, self.name())
+        return list_
+
 
 class SequenceNode(BranchNode):
     """Class to represent sequence element."""
@@ -1124,3 +1166,69 @@ def ParamFactory(paramInfo):
         param = SingleParamNode(param=paramInfo)
     return param
 
+def createMacroNode(macro_name, params_def, macro_params):
+    """The best effort creation of the macro XML object. It tries to
+    convert flat list of string parameter values to the correct macro XML
+    object. The cases that can not be converted are:
+        * repeat parameter containing repeat parameters
+        * two repeat parameters
+        * repeat parameter that is not the last parameter
+
+    :param macro_name: (str) macro name
+    :param params_def: (list<dict>) list of param definitions
+    :param macro_params: (sequence[str]) list of parameter values
+
+    :return (lxml.etree._Element) macro XML element
+
+    .. todo:: This method implements exactly the same logic as :meth:
+    `sardana.taurus.core.tango.sardana.macroserver.BaseDoor._createMacroXmlFromStr`
+    unify them and place in some common location.
+    """
+    macro_node = MacroNode(name=macro_name)
+    # create parameter nodes (it is recursive for repeat parameters containing
+    # repeat parameters)
+    for param_info in params_def:
+        macro_node.addParam(ParamFactory(param_info))
+    # obtain just the first level parameters
+    param_nodes = macro_node.params()
+    # validate if creating XML is possible (two last cases) 
+    contain_param_repeat = False
+    len_param_nodes = len(param_nodes)
+    for i, param_node in enumerate(param_nodes):
+        if isinstance(param_node, RepeatParamNode):
+            if contain_param_repeat:
+                msg = "Only one repeat parameter is allowed"
+                raise Exception(msg)
+            if i < len_param_nodes - 1:
+                msg = "Repeat parameter must be the last one"
+                raise Exception(msg)
+            contain_param_repeat = True
+    # this ignores raw_parameters which exceeds the param_def  
+    for param_node, param_raw in zip(param_nodes, macro_params):
+        if isinstance(param_node, SingleParamNode):
+            param_node.setValue(param_raw)
+        # the rest of the values are interpreted as repeat parameter
+        elif isinstance(param_node, RepeatParamNode):
+            params_info = param_node.paramsInfo()
+            params_info_len = len(params_info)
+            rep = 0; mem = 0
+            rest_raw = macro_params[i:]
+            for member_raw in rest_raw:
+                repeat_node = param_node.child(rep)
+                # add a new repeat node (this is needed when the raw values
+                # fill more repeat nodes that the minimum number of 
+                # repetitions e.g. min=0
+                if repeat_node is None:
+                    repeat_node = param_node.addRepeat()
+                member_node = repeat_node.child(mem)
+                if isinstance(member_node, RepeatParamNode):
+                    msg = ("Nested repeat parameters are not allowed")
+                    raise Exception(msg)
+                member_node.setValue(member_raw)
+                mem += 1
+                mem %= params_info_len
+                if mem == 0:
+                    rep += 1
+            break
+    return macro_node
+
diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py
index d514533..d31459c 100644
--- a/src/sardana/taurus/core/tango/sardana/macroserver.py
+++ b/src/sardana/taurus/core/tango/sardana/macroserver.py
@@ -51,13 +51,12 @@ from taurus.core.util.containers import CaselessDict
 from taurus.core.util.codecs import CodecFactory
 from taurus.core.util.event import EventGenerator, AttributeEventWait
 from taurus.core.tango import TangoDevice
-
-
 from .macro import MacroInfo, Macro, \
     MacroNode, ParamFactory, RepeatNode, RepeatParamNode, SingleParamNode, \
     ParamNode
 from .sardana import BaseSardanaElementContainer, BaseSardanaElement
 from .pool import getChannelConfigs
+from .macro import createMacroNode
 
 CHANGE_EVT_TYPES = TaurusEventType.Change, TaurusEventType.Periodic
 
@@ -459,6 +458,24 @@ class BaseDoor(MacroServerDevice):
         self._user_xml = None
         self._block_lines = 0
 
+    def _createMacroXml(self, macro_name, macro_params):
+        """The best effort creation of the macro XML object. It tries to
+        convert flat list of string parameter values to the correct macro XML
+        object. The cases that can not be converted are:
+            * repeat parameter containing repeat parameters
+            * two repeat parameters
+            * repeat parameter that is not the last parameter
+
+        :param macro_name: (str) macro name
+        :param macro_params: (sequence[str]) list of parameter values
+
+        :return (lxml.etree._Element) macro XML element
+        """
+        macro_info = self.macro_server.getMacroInfoObj(macro_name)
+        params_def = macro_info.parameters
+        macro_node = createMacroNode(macro_name, params_def, macro_params)
+        return macro_node.toXml()
+
     def preRunMacro(self, obj, parameters):
         self._clearRunMacro()
 
@@ -478,11 +495,11 @@ class BaseDoor(MacroServerDevice):
                     macros.append((obj, parameters))
                 xml_root = xml_seq = etree.Element('sequence')
                 for m in macros:
-                    xml_macro = etree.SubElement(xml_seq, 'macro')
-                    xml_macro.set('name', m[0])
+                    macro_name = m[0]
+                    macro_params = m[1]
+                    xml_macro = self._createMacroXml(macro_name, macro_params)
                     xml_macro.set('id', str(uuid.uuid1()))
-                    for p in m[1]:
-                        etree.SubElement(xml_macro, 'param', value=p)
+                    xml_seq.append(xml_macro)
         elif etree.iselement(obj):
             xml_root = obj
         else:
@@ -845,8 +862,8 @@ class BaseMacroServer(MacroServerDevice):
         return element
 
     def _removeElement(self, element_data):
-        name = element_data['name']
-        element = self.getElementInfo(name)
+        full_name = element_data['full_name']
+        element = self.getElementInfo(full_name)
         self.getElementsInfo().removeElement(element)
         return element
 
diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py
index e68eb34..1194245 100644
--- a/src/sardana/taurus/core/tango/sardana/pool.py
+++ b/src/sardana/taurus/core/tango/sardana/pool.py
@@ -1237,6 +1237,9 @@ class MGConfiguration(object):
                 except:
                     pass
 
+    def getChannels(self):
+        return self.channel_list
+
     def getChannelInfo(self, channel_name):
         try:
             return self.tango_channels_info[channel_name]
@@ -1395,7 +1398,7 @@ class MeasurementGroup(PoolElement):
         self.write_attribute("configuration", json.dumps(cfg))
 
     def getChannels(self):
-        return self.getConfiguration().channel_list
+        return self.getConfiguration().getChannels()
 
     def getCounters(self):
         cfg = self.getConfiguration()
@@ -1443,6 +1446,42 @@ class MeasurementGroup(PoolElement):
         self._last_integ_time = ctime
         self.getIntegrationTimeObj().write(ctime)
 
+    def enableChannels(self, channels):
+        '''Enable acquisition of the indicated channels.
+
+        :param channels: (seq<str>) a sequence of strings indicating
+                         channel names
+        '''
+        self._enableChannels(channels, True)
+
+    def disableChannels(self, channels):
+        '''Disable acquisition of the indicated channels.
+
+        :param channels: (seq<str>) a sequence of strings indicating
+                         channel names
+        '''
+        self._enableChannels(channels, False)
+
+    def _enableChannels(self, channels, state):
+        found = {}
+        for channel in channels:
+            found[channel] = False
+        cfg = self.getConfiguration()
+        for channel in cfg.getChannels():
+            name = channel['name']
+            if name in channels:
+                channel['enabled'] = state
+                found[name] = True
+        wrong_channels = []
+        for ch, f in found.items():
+            if f is False:
+                wrong_channels.append(ch)
+        if len(wrong_channels) > 0:
+            msg = 'channels: %s are not present in measurement group' % \
+                                                                wrong_channels
+            raise Exception(msg)
+        self.setConfiguration(cfg.raw_data)
+
     def _start(self, *args, **kwargs):
         self.Start()
 
@@ -1583,14 +1622,28 @@ class Pool(TangoDevice, MoveableSource):
             element = BaseSardanaElement(**element_data)
             elements.addElement(element)
         for element_data in elems.get('del', ()):
-            element = self.getElementInfo(element_data['name'])
+            element = self.getElementInfo(element_data['full_name'])
             try:
                 elements.removeElement(element)
             except:
                 self.warning("Failed to remove %s", element_data)
-
+        for element_data in elems.get('change', ()):
+            element = self._removeElement(element_data)
+            element = self._addElement(element_data)
         return elems
 
+    def _addElement(self, element_data):
+        element_data['manager'] = self
+        element = BaseSardanaElement(**element_data)
+        self.getElementsInfo().addElement(element)
+        return element
+
+    def _removeElement(self, element_data):
+        name = element_data['full_name']
+        element = self.getElementInfo(name)
+        self.getElementsInfo().removeElement(element)
+        return element
+
     def getElementsInfo(self):
         return self._elements
 
@@ -1743,6 +1796,14 @@ class Pool(TangoDevice, MoveableSource):
         elements_info = self.getElementsInfo()
         return self._wait_for_element_in_container(elements_info, name)
 
+    def renameElement(self, old_name, new_name):
+        self.debug('trying to rename element: %s to: %s', old_name, new_name)
+        self.command_inout('RenameElement', [old_name, new_name])
+        elements_info = self.getElementsInfo()
+        return self._wait_for_element_in_container(elements_info, new_name,
+                                                   contains=True)
+
+
     def deleteElement(self, name):
         self.debug('trying to delete element: %s', name)
         self.command_inout('DeleteElement', name)
diff --git a/src/sardana/macroserver/scan/recorder/__init__.py b/src/sardana/taurus/core/tango/sardana/test/__init__.py
old mode 100644
new mode 100755
similarity index 81%
copy from src/sardana/macroserver/scan/recorder/__init__.py
copy to src/sardana/taurus/core/tango/sardana/test/__init__.py
index 8f71c3f..7413de5
--- a/src/sardana/macroserver/scan/recorder/__init__.py
+++ b/src/sardana/taurus/core/tango/sardana/test/__init__.py
@@ -7,27 +7,18 @@
 ## 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 is the macro server scan recorder module"""
-
-__docformat__ = 'restructuredtext'
-
-from .datarecorder import *
-from .output import *
-from .sharedmemory import *
-from .storage import *
\ No newline at end of file
+##############################################################################
\ No newline at end of file
diff --git a/src/sardana/taurus/core/tango/sardana/test/test_macro.py b/src/sardana/taurus/core/tango/sardana/test/test_macro.py
new file mode 100755
index 0000000..99bf6d8
--- /dev/null
+++ b/src/sardana/taurus/core/tango/sardana/test/test_macro.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+
+##############################################################################
+##
+## 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/>.
+##
+##############################################################################
+
+"""Module with tests for macro related utils."""
+
+from lxml import etree
+
+from taurus.external import unittest
+from taurus.test import insertTest
+from sardana.taurus.core.tango.sardana.macro import createMacroNode
+from sardana.macroserver.macro import Type
+
+# TODO: Use unittest.mock instead of this fake class.
+from sardana.macroserver.mstypemanager import TypeManager
+class FakeMacroServer(object):
+    name = "FakeMacroServer"
+
+macro_server = FakeMacroServer()
+tm = TypeManager(macro_server)
+
+
+pt8_params_def = [
+    {
+        "name" : "m_p_pair",
+        "type": [
+            {
+                "name": "motor",
+                "type": Type.Motor,
+                "default_value": None,
+                "description": "motor",
+            },
+            {
+                "name": "pos",
+                "type": Type.Integer,
+                "default_value": None,
+                "description": "position",
+            }
+        ],
+        "description": "pair of motor and position",
+        "min": 1,
+        "max": None
+    }
+]
+pt8_params_value = ["mot73", "5.0", "mot74", "8.0"]
+
+# 
+pt8_xml = \
+'''<macro name="pt8">
+  <paramrepeat name="m_p_pair">
+    <repeat nr="1">
+      <param name="motor" value="mot73"/>
+      <param name="pos" value="5.0"/>
+    </repeat>
+    <repeat nr="2">
+      <param name="motor" value="mot74"/>
+      <param name="pos" value="8.0"/>
+    </repeat>
+  </paramrepeat>
+</macro>'''
+
+ at insertTest(helper_name='verifyXML', macro_name="pt8", param_def=pt8_params_def,
+            param_value=pt8_params_value, expected_xml_rep=pt8_xml)
+class MacroNodeTestCase(unittest.TestCase):
+
+    def _validateXML(self, macronode_xml, expected_xml):
+        '''
+        :param macronode_xml: macronode lxml.etree
+        :param expected_xml:  expected lxml.etree
+        '''
+        expected_str = etree.tostring(expected_xml)
+        macronode_str = etree.tostring(macronode_xml, pretty_print=True)
+        msg = "XML encodings are not equal"
+        # TODO: check why macronode_str has an extra whitespace charactger 
+        # at the end. strips should not be necessary
+        self.assertEquals(expected_str.strip(), macronode_str.strip(), msg)
+
+    def verifyXML(self, macro_name, param_def, param_value, expected_xml_rep):
+        """
+        Helper to verify the generated XML of a macroNode
+        :param macro_name: (str) name of the macro
+        :param param_def:   (list<dict>) macro parameters definition
+        :param param_value: (list<str>) list of strins representing macro
+            parameters values
+        :param expected_xml_rep: "pretty print" string representation of a XML
+            macroNode
+        """
+        # Create the MacroNide with the inputs
+        macronode = createMacroNode(macro_name, param_def, param_value)
+        # Get the MacroNode equivalent XML tree
+        macronode_xml = macronode.toXml()
+        # Create a XML tree
+        expected_xml = etree.fromstring(expected_xml_rep)
+        # Validate the XML tree
+        self._validateXML(macronode_xml, expected_xml)
diff --git a/src/sardana/macroserver/scan/recorder/__init__.py b/src/sardana/taurus/qt/qtgui/extra_hkl/__init__.py
similarity index 82%
copy from src/sardana/macroserver/scan/recorder/__init__.py
copy to src/sardana/taurus/qt/qtgui/extra_hkl/__init__.py
index 8f71c3f..051b029 100644
--- a/src/sardana/macroserver/scan/recorder/__init__.py
+++ b/src/sardana/taurus/qt/qtgui/extra_hkl/__init__.py
@@ -7,27 +7,26 @@
 ## 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 is the macro server scan recorder module"""
-
-__docformat__ = 'restructuredtext'
+"""
+__init__.py: 
+"""
 
-from .datarecorder import *
-from .output import *
-from .sharedmemory import *
-from .storage import *
\ No newline at end of file
+from hklscan import HKLScan
+from ubmatrix import UBMatrixBase
+from diffractometeralignment import DiffractometerAlignment
diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/computeu.py b/src/sardana/taurus/qt/qtgui/extra_hkl/computeu.py
new file mode 100644
index 0000000..20d13e5
--- /dev/null
+++ b/src/sardana/taurus/qt/qtgui/extra_hkl/computeu.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+
+##############################################################################
+##
+## 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/>.
+##
+##############################################################################
+
+
+__docformat__ = 'restructuredtext'
+
+import sys
+from taurus.external.qt import Qt
+import taurus.core
+from taurus.qt.qtgui.container import TaurusWidget
+
+from taurus.qt.qtgui.util.ui import UILoadable
+
+
+ at UILoadable(with_ui="_ui")
+class ComputeU(TaurusWidget):
+
+    def __init__(self, parent=None, designMode=False):
+        TaurusWidget.__init__(self, parent, designMode=designMode)
+
+        self.loadUi(filename="computeu.ui")
+
+        self.connect(self._ui.ComputeButton, Qt.SIGNAL(
+            "clicked()"), self.compute_u)
+
+    @classmethod
+    def getQtDesignerPluginInfo(cls):
+        ret = TaurusWidget.getQtDesignerPluginInfo()
+        ret['module'] = 'computeu'
+        ret['group'] = 'Taurus Containers'
+        ret['container'] = ':/designer/frame.png'
+        ret['container'] = False
+        return ret
+
+    def setModel(self, model):
+        if model != None:
+            self.device = taurus.Device(model)
+
+    def compute_u(self):
+        index = []
+        index.append(int(self._ui.indexreflection1lineEdit.text()))
+        index.append(int(self._ui.indexreflection2lineEdit.text()))
+
+        self.device.write_attribute("computeub", index)
+
+
+def main():
+    app = Qt.QApplication(sys.argv)
+    w = ComputeU()
+    w.show()
+    sys.exit(app.exec_())
+
+if __name__ == "__main__":
+    main()
diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py b/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py
new file mode 100644
index 0000000..733721f
--- /dev/null
+++ b/src/sardana/taurus/qt/qtgui/extra_hkl/diffractometeralignment.py
@@ -0,0 +1,395 @@
+#!/usr/bin/env python
+
+##############################################################################
+##
+## 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/>.
+##
+##############################################################################
+
+
+__docformat__ = 'restructuredtext'
+
+import sys
+import time
+from taurus.external.qt import Qt
+# TODO: avoid using of PyTango - use Taurus instead
+import PyTango
+import sardana
+
+from taurus.qt.qtgui.container import TaurusWidget
+from taurus.qt.qtgui.display import TaurusLabel
+from taurus.qt.qtgui.base import TaurusBaseWidget
+
+from taurus.external.qt import QtCore, QtGui
+
+from taurus.qt.qtcore.communication import SharedDataManager
+from taurus.qt.qtgui.input import TaurusValueLineEdit
+
+
+import taurus.core.util.argparse
+import taurus.qt.qtgui.application
+from taurus.qt.qtgui.util.ui import UILoadable
+
+from taurus.qt.qtgui.extra_macroexecutor import TaurusMacroConfigurationDialog
+
+
+from selectsignal import SelectSignal
+
+
+class EngineModesComboBox(Qt.QComboBox, TaurusBaseWidget):
+    """ComboBox representing list of engine modes"""
+
+    def __init__(self, parent=None):
+        name = self.__class__.__name__
+        self.call__init__wo_kw(Qt.QComboBox, parent)
+        self.call__init__(TaurusBaseWidget, name)
+        self.setSizeAdjustPolicy(Qt.QComboBox.AdjustToContentsOnFirstShow)
+        self.setToolTip("Choose a engine mode ...")
+        QtCore.QMetaObject.connectSlotsByName(self)
+
+    def loadEngineModeNames(self, enginemodes):
+        self.clear()
+        self.addItems(enginemodes)
+
+
+ at UILoadable(with_ui="_ui")
+class DiffractometerAlignment(TaurusWidget):
+
+    def __init__(self, parent=None, designMode=False):
+        TaurusWidget.__init__(self, parent, designMode=designMode)
+
+        self.loadUi(filename="diffractometeralignment.ui")
+
+        self.selectsignal = SelectSignal()
+
+        self.connect(self._ui.AlignmentStopButton,
+                     Qt.SIGNAL("clicked()"), self.stop_movements)
+        self.connect(self._ui.AlignmentStoreReflectionButton,
+                     Qt.SIGNAL("clicked()"), self.store_reflection)
+
+        self.connect(self._ui.MacroServerConnectionButton, Qt.SIGNAL(
+            "clicked()"), self.open_macroserver_connection_panel)
+
+        self.connect(self._ui.SelectSignalButton, Qt.SIGNAL(
+            "clicked()"), self.open_selectsignal_panel)
+
+        # Create a global SharedDataManager
+        Qt.qApp.SDM = SharedDataManager(self)
+
+    @classmethod
+    def getQtDesignerPluginInfo(cls):
+        ret = TaurusWidget.getQtDesignerPluginInfo()
+        ret['module'] = 'diffractometeralignment'
+        ret['group'] = 'Taurus Containers'
+        ret['container'] = ':/designer/frame.png'
+        ret['container'] = False
+        return ret
+
+    def setModel(self, model):
+        if model != None:
+            self.device = taurus.Device(model)
+
+        self.pseudo_motor_names = []
+        for motor in self.device.hklpseudomotorlist:
+            self.pseudo_motor_names.append(motor.split(' ')[0])
+
+        self.h_device_name = self.pseudo_motor_names[0]
+        self.h_device = taurus.Device(self.h_device_name)
+        self.k_device_name = self.pseudo_motor_names[1]
+        self.k_device = taurus.Device(self.k_device_name)
+        self.l_device_name = self.pseudo_motor_names[2]
+        self.l_device = taurus.Device(self.l_device_name)
+
+        # Set model to hkl components
+
+        hmodel = self.h_device_name + "/Position"
+        self._ui.taurusValueLineH.setModel(hmodel)
+        self._ui.taurusLabelValueH.setModel(hmodel)
+        kmodel = self.k_device_name + "/Position"
+        self._ui.taurusValueLineK.setModel(kmodel)
+        self._ui.taurusLabelValueK.setModel(kmodel)
+        lmodel = self.l_device_name + "/Position"
+        self._ui.taurusValueLineL.setModel(lmodel)
+        self._ui.taurusLabelValueL.setModel(lmodel)
+
+        # Add dynamically the angle widgets
+
+        motor_list = self.device.motorlist
+        self.motor_names = []
+        self.motor_devices = []
+
+        for motor in self.device.motorlist:
+            self.motor_names.append(motor.split(' ')[0])
+            self.motor_devices.append(taurus.Device(
+                self.motor_names[len(self.motor_names) - 1]))
+
+        self.nb_motors = len(motor_list)
+
+        angles_labels = []
+        self.angles_names = []
+        angles_taurus_label = []
+        angles_taurus_input = []
+
+        gap_x = 650 / self.nb_motors
+
+        try:
+            self.angles_names = self.device.motorroles
+        except:  # Only for compatibility
+            if self.nb_motors == 4:
+                self.angles_names.append("omega")
+                self.angles_names.append("chi")
+                self.angles_names.append("phi")
+                self.angles_names.append("theta")
+            elif self.nb_motors == 6:
+                self.angles_names.append("mu")
+                self.angles_names.append("th")
+                self.angles_names.append("chi")
+                self.angles_names.append("phi")
+                self.angles_names.append("gamma")
+                self.angles_names.append("delta")
+
+        for i in range(0, self.nb_motors):
+            angles_labels.append(QtGui.QLabel(self))
+            angles_labels[i].setGeometry(
+                QtCore.QRect(150 + gap_x * i, 40, 71, 17))
+            angles_labels[i].setLayoutDirection(QtCore.Qt.RightToLeft)
+            alname = "angleslabel" + str(i)
+            angles_labels[i].setObjectName(alname)
+            angles_labels[i].setText(QtGui.QApplication.translate(
+                "HKLScan", self.angles_names[i], None,
+                QtGui.QApplication.UnicodeUTF8))
+
+            angles_taurus_label.append(TaurusLabel(self))
+            angles_taurus_label[i].setGeometry(
+                QtCore.QRect(150 + gap_x * i, 70, 81, 19))
+            atlname = "anglestauruslabel" + str(i)
+            angles_taurus_label[i].setObjectName(atlname)
+            angles_taurus_label[i].setModel(self.motor_names[i] + "/Position")
+
+            angles_taurus_input.append(TaurusValueLineEdit(self))
+            angles_taurus_input[i].setGeometry(
+                QtCore.QRect(145 + gap_x * i, 100, 91, 27))
+            atlname = "anglestaurusinput" + str(i)
+            angles_taurus_input[i].setObjectName(atlname)
+            angles_taurus_input[i].setModel(self.motor_names[i] + "/Position")
+
+        # Set model to engine and modes
+
+        enginemodel = model + '/engine'
+        self._ui.taurusLabelEngine.setModel(enginemodel)
+        enginemodemodel = model + '/enginemode'
+        self._ui.taurusLabelEngineMode.setModel(enginemodemodel)
+
+        self.enginemodescombobox = EngineModesComboBox(self)
+        self.enginemodescombobox.setGeometry(QtCore.QRect(150, 315, 221, 27))
+        self.enginemodescombobox.setObjectName("enginemodeslist")
+
+        self.enginemodescombobox.loadEngineModeNames(self.device.hklmodelist)
+
+        self.connect(self.enginemodescombobox, Qt.SIGNAL(
+            "currentIndexChanged(QString)"), self.onModeChanged)
+
+        # Add dynamically the scan buttons, range inputs and 'to max' buttons
+
+        scan_buttons = []
+        self.range_inputs = []
+        self.tomax_buttons = []  # The text will be change when the max. is computed
+
+        exec_functions = [self.exec_scan1, self.exec_scan2, self.exec_scan3,
+                          self.exec_scan4, self.exec_scan5, self.exec_scan6]
+
+        tomax_functions = [self.tomax_scan1, self.tomax_scan2, self.tomax_scan3,
+                           self.tomax_scan4, self.tomax_scan5, self.tomax_scan6]
+
+        gap_x = 650 / self.nb_motors
+
+        for i in range(0, self.nb_motors):
+            scan_buttons.append(QtGui.QPushButton(self))
+            scan_buttons[i].setGeometry(
+                QtCore.QRect(150 + gap_x * i, 405, 100, 26))
+            wname = "scanbutton" + str(i)
+            scan_buttons[i].setObjectName(wname)
+            scan_buttons[i].setText(QtGui.QApplication.translate(
+                "DiffractometerAlignment", self.angles_names[i], None,
+                QtGui.QApplication.UnicodeUTF8))
+            self.connect(scan_buttons[i], Qt.SIGNAL(
+                "clicked()"), exec_functions[i])
+
+            self.range_inputs.append(QtGui.QLineEdit(self))
+            self.range_inputs[i].setGeometry(
+                QtCore.QRect(150 + gap_x * i, 440, 100, 26))
+            self.range_inputs[i].setLayoutDirection(QtCore.Qt.RightToLeft)
+            wname = "rangeinput" + str(i)
+            self.range_inputs[i].setObjectName(wname)
+
+            self.tomax_buttons.append(QtGui.QPushButton(self))
+            self.tomax_buttons[i].setGeometry(
+                QtCore.QRect(150 + gap_x * i, 475, 100, 26))
+            wname = "tomaxbutton" + str(i)
+            self.tomax_buttons[i].setObjectName(wname)
+            self.tomax_buttons[i].setText(QtGui.QApplication.translate(
+                "DiffractometerAlignment", 'n.n.', None,
+                QtGui.QApplication.UnicodeUTF8))
+            self.connect(self.tomax_buttons[i], Qt.SIGNAL(
+                "clicked()"), tomax_functions[i])
+
+    def exec_scan1(self):
+        self.exec_scan(0)
+
+    def exec_scan2(self):
+        self.exec_scan(1)
+
+    def exec_scan3(self):
+        self.exec_scan(2)
+
+    def exec_scan4(self):
+        self.exec_scan(3)
+
+    def exec_scan5(self):
+        self.exec_scan(4)
+
+    def exec_scan6(self):
+        self.exec_scan(5)
+
+    def exec_scan(self, imot):
+        macro_command = []
+
+        macro_command.append("_diff_scan")
+        macro_command.append(str(self.motor_names[imot]))
+        current_pos = self.motor_devices[imot].Position
+        range_scan = float(self.range_inputs[imot].text())
+        macro_command.append(str(current_pos - range_scan))
+        macro_command.append(str(current_pos + range_scan))
+        macro_command.append(str(self._ui.NbPointslineEdit.text()))
+        macro_command.append(
+            str(self.selectsignal._ui.SampleTimelineEdit.text()))
+        macro_command.append(str(self.selectsignal._ui.SignallineEdit.text()))
+
+        self.door_device.RunMacro(macro_command)
+        while(self.door_device.State()) == PyTango.DevState.RUNNING:
+            time.sleep(0.01)
+        # TODO: the string parsing should be eliminated and the sardana
+        # generic "goto_peak" feature should be used instead - when available
+        output_values = self.door_device.read_attribute("Output").value
+        if output_values != None:
+            for i in range(len(output_values)):
+                if output_values[i] == "Position to move":
+                    self.tomax_buttons[imot].setText(QtGui.QApplication.translate(
+                        "DiffractometerAlignment", str(output_values[i + 1]),
+                        None, QtGui.QApplication.UnicodeUTF8))
+
+    def tomax_scan1(self):
+        self.tomax_scan(0)
+
+    def tomax_scan2(self):
+        self.tomax_scan(1)
+
+    def tomax_scan3(self):
+        self.tomax_scan(2)
+
+    def tomax_scan4(self):
+        self.tomax_scan(3)
+
+    def tomax_scan5(self):
+        self.tomax_scan(4)
+
+    def tomax_scan6(self):
+        self.tomax_scan(5)
+
+    def tomax_scan(self, imot):
+        motor = str(self.motor_names[imot])
+        position = str(self.tomax_buttons[imot].text())
+        macro_command = ["mv", motor, position]
+        self.door_device.RunMacro(macro_command)
+
+    def onModeChanged(self, modename):
+        if self.device.engine != "hkl":
+            self.device.write_attribute("engine", "hkl")
+        self.device.write_attribute("enginemode", str(modename))
+
+    def open_macroserver_connection_panel(self):
+        w = TaurusMacroConfigurationDialog(self)
+        Qt.qApp.SDM.connectReader("macroserverName", w.selectMacroServer)
+        Qt.qApp.SDM.connectReader("doorName", w.selectDoor)
+        Qt.qApp.SDM.connectReader("doorName", self.onDoorChanged)
+        Qt.qApp.SDM.connectWriter(
+            "macroserverName", w, 'macroserverNameChanged')
+        Qt.qApp.SDM.connectWriter("doorName", w, 'doorNameChanged')
+
+        w.show()
+
+    def onDoorChanged(self, doorName):
+        if doorName != self.door_device_name:
+            self.door_device_name = doorName
+            self.door_device = taurus.Device(doorName)
+
+    def stop_movements(self):
+        self.door_device.StopMacro()
+
+    def store_reflection(self):
+        hklref = []
+        hklref.append(self.h_device.Position)
+        hklref.append(self.k_device.Position)
+        hklref.append(self.l_device.Position)
+
+        self.device.write_attribute("addreflection", hklref)
+
+    def open_selectsignal_panel(self):
+
+        self.selectsignal.update_signals(self.door_device_name)
+        self.selectsignal.show()
+
+
+def main():
+
+    parser = taurus.core.util.argparse.get_taurus_parser()
+    parser.usage = "%prog <model> [door_name]"
+    desc = ("a taurus application for diffractometer alignment: h, k, l " +
+            "movements and scans, go to maximum, ...")
+    parser.set_description(desc)
+
+    app = taurus.qt.qtgui.application.TaurusApplication(cmd_line_parser=parser,
+            app_version=sardana.Release.version)
+    app.setApplicationName("diffractometeralignment")
+    args = app.get_command_line_args()
+    if len(args) < 1:
+        msg = "model not set (requires diffractometer controller)"
+        parser.error(msg)
+
+    w = DiffractometerAlignment()
+    w.model = args[0]
+    w.setModel(w.model)
+
+    w.door_device = None
+    w.door_device_name = None
+    if len(args) > 1:
+        w.onDoorChanged(args[1])
+    else:
+        msg = ("No door name supplied. Connection to MacroServer/Door will " +
+               "not not automatically done.")
+        app.warning(msg)
+
+    w.show()
+
+    sys.exit(app.exec_())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/src/sardana/test/testsuite.py b/src/sardana/taurus/qt/qtgui/extra_hkl/displayscanangles.py
similarity index 54%
copy from src/sardana/test/testsuite.py
copy to src/sardana/taurus/qt/qtgui/extra_hkl/displayscanangles.py
index 96d195b..860f7e6 100644
--- a/src/sardana/test/testsuite.py
+++ b/src/sardana/taurus/qt/qtgui/extra_hkl/displayscanangles.py
@@ -23,40 +23,39 @@
 ##
 ##############################################################################
 
-"""
-This module defines the test suite for the whole Sardana package
-Usage::
+__docformat__ = 'restructuredtext'
 
-  from sardana.test import testsuite
-  testsuite.run()
+import sys
+from taurus.external.qt import Qt
+from taurus.qt.qtgui.container import TaurusWidget
 
-"""
+from taurus.qt.qtgui.util.ui import UILoadable
 
-__docformat__ = 'restructuredtext'
 
-import sys, os
-from taurus.external import unittest
-import sardana
-
-
-def run():
-    '''Runs all tests for the taurus package
-
-    :returns: the test runner result
-    :rtype: unittest.result.TestResult
-    '''
-    # discover all tests within the sardana/src directory
-    loader = unittest.defaultTestLoader
-    suite = loader.discover(os.path.dirname(sardana.__file__))
-    # use the basic text test runner that outputs to sys.stderr
-    runner = unittest.TextTestRunner(descriptions=True, verbosity=2)
-    # run the test suite
-    result = runner.run(suite)
-    return result
-
-if __name__ == '__main__':
-    result = run()
-    exit_code = 0
-    if not result.wasSuccessful():
-        exit_code = 1
-    sys.exit(exit_code)
\ No newline at end of file
+ at UILoadable(with_ui="_ui")
+class DisplayScanAngles(TaurusWidget):
+
+    def __init__(self, parent=None, designMode=False):
+        TaurusWidget.__init__(self, parent, designMode=designMode)
+
+        self.loadUi(filename="displayscanangles.ui")
+        # self._ui.setupUi(self)
+
+    @classmethod
+    def getQtDesignerPluginInfo(cls):
+        ret = TaurusWidget.getQtDesignerPluginInfo()
+        ret['module'] = 'displayscanangles'
+        ret['group'] = 'Taurus Containers'
+        ret['container'] = 'y'
+        ret['container'] = False
+        return ret
+
+
+def main():
+    app = Qt.QApplication(sys.argv)
+    w = DisplayScanAngles()
+    w.show()
+    sys.exit(app.exec_())
+
+if __name__ == "__main__":
+    main()
diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py b/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py
new file mode 100644
index 0000000..e347c07
--- /dev/null
+++ b/src/sardana/taurus/qt/qtgui/extra_hkl/hklscan.py
@@ -0,0 +1,397 @@
+#!/usr/bin/env python
+
+##############################################################################
+##
+## 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/>.
+##
+##############################################################################
+
+__docformat__ = 'restructuredtext'
+
+import sys
+
+import sardana
+from taurus.external.qt import Qt
+from taurus.qt.qtgui.container import TaurusWidget
+from taurus.qt.qtgui.display import TaurusLabel
+from taurus.qt.qtgui.base import TaurusBaseWidget
+
+from taurus.external.qt import QtCore, QtGui
+
+import taurus.core
+from taurus.qt.qtcore.communication import SharedDataManager
+from taurus.qt.qtgui.input import TaurusValueLineEdit
+
+from displayscanangles import DisplayScanAngles
+
+import taurus.core.util.argparse
+import taurus.qt.qtgui.application
+from taurus.qt.qtgui.util.ui import UILoadable
+
+from PyTango import *
+from taurus.qt.qtgui.extra_macroexecutor import TaurusMacroExecutorWidget, TaurusSequencerWidget, \
+    TaurusMacroConfigurationDialog, \
+    TaurusMacroDescriptionViewer, DoorOutput, DoorDebug, DoorResult
+
+
+class EngineModesComboBox(Qt.QComboBox, TaurusBaseWidget):
+    """ComboBox representing list of engine modes"""
+
+    def __init__(self, parent=None):
+        name = self.__class__.__name__
+        self.call__init__wo_kw(Qt.QComboBox, parent)
+        self.call__init__(TaurusBaseWidget, name)
+        self.setSizeAdjustPolicy(Qt.QComboBox.AdjustToContentsOnFirstShow)
+        self.setToolTip("Choose a engine mode ...")
+        QtCore.QMetaObject.connectSlotsByName(self)
+
+    def loadEngineModeNames(self, enginemodes):
+        self.clear()
+        self.addItems(enginemodes)
+
+
+ at UILoadable(with_ui="_ui")
+class HKLScan(TaurusWidget):
+
+    def __init__(self, parent=None, designMode=False):
+        TaurusWidget.__init__(self, parent, designMode=designMode)
+
+        self.loadUi(filename="hklscan.ui")
+
+        self.connect(self._ui.hklStartScanButton,
+                     Qt.SIGNAL("clicked()"), self.start_hklscan)
+        self.connect(self._ui.hklStopScanButton,
+                     Qt.SIGNAL("clicked()"), self.stop_hklscan)
+        self.connect(self._ui.hklDisplayAnglesButton,
+                     Qt.SIGNAL("clicked()"), self.display_angles)
+        self.connect(self._ui.MacroServerConnectionButton, Qt.SIGNAL(
+            "clicked()"), self.open_macroserver_connection_panel)
+
+        # Create a global SharedDataManager
+        Qt.qApp.SDM = SharedDataManager(self)
+
+    @classmethod
+    def getQtDesignerPluginInfo(cls):
+        ret = TaurusWidget.getQtDesignerPluginInfo()
+        ret['module'] = 'hklscan'
+        ret['group'] = 'Taurus Containers'
+        ret['container'] = ':/designer/frame.png'
+        ret['container'] = True
+        return ret
+
+    def setModel(self, model):
+        if model != None:
+            self.device = taurus.Device(model)
+
+        self.pseudo_motor_names = []
+        for motor in self.device.hklpseudomotorlist:
+            self.pseudo_motor_names.append(motor.split(' ')[0])
+
+        self.h_device_name = self.pseudo_motor_names[0]
+        self.h_device = taurus.Device(self.h_device_name)
+        self.k_device_name = self.pseudo_motor_names[1]
+        self.k_device = taurus.Device(self.k_device_name)
+        self.l_device_name = self.pseudo_motor_names[2]
+        self.l_device = taurus.Device(self.l_device_name)
+
+        # Add dynamically the angle widgets
+
+        motor_list = self.device.motorlist
+        motor_names = []
+        for motor in self.device.motorlist:
+            motor_names.append(motor.split(' ')[0])
+
+        self.nb_motors = len(motor_list)
+
+        angles_labels = []
+        angles_names = []
+        angles_taurus_label = []
+
+        gap_x = 800 / self.nb_motors
+
+        try:
+            angles_names = self.device.motorroles
+        except:  # Only for compatibility
+            if self.nb_motors == 4:
+                angles_names.append("omega")
+                angles_names.append("chi")
+                angles_names.append("phi")
+                angles_names.append("theta")
+            elif self.nb_motors == 6:
+                angles_names.append("mu")
+                angles_names.append("th")
+                angles_names.append("chi")
+                angles_names.append("phi")
+                angles_names.append("gamma")
+                angles_names.append("delta")
+
+        for i in range(0, self.nb_motors):
+            angles_labels.append(QtGui.QLabel(self))
+            angles_labels[i].setGeometry(
+                QtCore.QRect(50 + gap_x * i, 290, 51, 17))
+            alname = "angleslabel" + str(i)
+            angles_labels[i].setObjectName(alname)
+            angles_labels[i].setText(QtGui.QApplication.translate(
+                "HKLScan", angles_names[i], None, QtGui.QApplication.UnicodeUTF8))
+            angles_taurus_label.append(TaurusLabel(self))
+            angles_taurus_label[i].setGeometry(
+                QtCore.QRect(50 + gap_x * i, 320, 81, 19))
+            atlname = "anglestauruslabel" + str(i)
+            angles_taurus_label[i].setObjectName(atlname)
+            angles_taurus_label[i].setModel(motor_names[i] + "/Position")
+
+        # Set model to hkl display
+
+        hmodel = self.h_device_name + "/Position"
+        self._ui.taurusValueLineH.setModel(hmodel)
+        self._ui.taurusLabelValueH.setModel(hmodel)
+        kmodel = self.k_device_name + "/Position"
+        self._ui.taurusValueLineK.setModel(kmodel)
+        self._ui.taurusLabelValueK.setModel(kmodel)
+        lmodel = self.l_device_name + "/Position"
+        self._ui.taurusValueLineL.setModel(lmodel)
+        self._ui.taurusLabelValueL.setModel(lmodel)
+
+        # Set model to engine and modes
+
+        enginemodel = model + '/engine'
+        self._ui.taurusLabelEngine.setModel(enginemodel)
+        enginemodemodel = model + '/enginemode'
+        self._ui.taurusLabelEngineMode.setModel(enginemodemodel)
+
+        self.enginemodescombobox = EngineModesComboBox(self)
+        self.enginemodescombobox.setGeometry(QtCore.QRect(150, 445, 221, 27))
+        self.enginemodescombobox.setObjectName("enginemodeslist")
+
+        self.enginemodescombobox.loadEngineModeNames(self.device.hklmodelist)
+
+        self.connect(self.enginemodescombobox, Qt.SIGNAL(
+            "currentIndexChanged(QString)"), self.onModeChanged)
+
+    def onModeChanged(self, modename):
+        if self.device.engine != "hkl":
+            self.device.write_attribute("engine", "hkl")
+        self.device.write_attribute("enginemode", str(modename))
+
+    def start_hklscan(self):
+        start_hkl = []
+        stop_hkl = []
+        start_hkl.append(float(self._ui.lineEditStartH.text()))
+        start_hkl.append(float(self._ui.lineEditStartK.text()))
+        start_hkl.append(float(self._ui.lineEditStartL.text()))
+        stop_hkl.append(float(self._ui.lineEditStopH.text()))
+        stop_hkl.append(float(self._ui.lineEditStopK.text()))
+        stop_hkl.append(float(self._ui.lineEditStopL.text()))
+        nb_points = int(self._ui.LineEditNbpoints.text())
+        sample_time = float(self._ui.LineEditSampleTime.text())
+        dim = 0
+        macro_name = ["ascan", "a2scan", "a3scan"]
+        macro_command = []
+        index_to_scan = []
+        if self.door_device != None:
+            for i in range(0, 3):
+                if start_hkl[i] != stop_hkl[i]:
+                    dim = dim + 1
+                    index_to_scan.append(i)
+            if dim > 0:
+                macro_command.append(macro_name[dim - 1])
+                for i in range(len(index_to_scan)):
+                    macro_command.append(
+                        str(self.pseudo_motor_names[index_to_scan[i]]))
+                    macro_command.append(str(start_hkl[index_to_scan[i]]))
+                    macro_command.append(str(stop_hkl[index_to_scan[i]]))
+                macro_command.append(str(nb_points))
+                macro_command.append(str(sample_time))
+                self.door_device.RunMacro(macro_command)
+
+    def stop_hklscan(self):
+        self.door_device.StopMacro()
+
+    def display_angles(self):
+
+        xangle = []
+        for i in range(0, 6):
+            xangle.append(40 + i * 100)
+
+        yhkl = 50
+
+        tr = self.device.selectedtrajectory
+
+        w = DisplayScanAngles()
+
+        angles_labels = []
+        angles_names = []
+
+        if self.nb_motors == 4:
+            angles_names.append("omega")
+            angles_names.append("chi")
+            angles_names.append("phi")
+            angles_names.append("theta")
+        elif self.nb_motors == 6:
+            angles_names.append("mu")
+            angles_names.append("th")
+            angles_names.append("chi")
+            angles_names.append("phi")
+            angles_names.append("gamma")
+            angles_names.append("delta")
+
+        dsa_label = []
+        for i in range(0, self.nb_motors):
+            dsa_label.append(QtGui.QLabel(w))
+            dsa_label[i].setGeometry(QtCore.QRect(xangle[i], yhkl, 51, 20))
+            label_name = "dsa_label_" + str(i)
+            dsa_label[i].setObjectName(label_name)
+            dsa_label[i].setText(QtGui.QApplication.translate(
+                "Form", angles_names[i], None, QtGui.QApplication.UnicodeUTF8))
+
+        start_hkl = []
+        stop_hkl = []
+        missed_values = 0
+        # TODO: This code will raise exception if one of the line edits is empty.
+        # But not all dimensions (H & K & L) are obligatory. One could try
+        # to display angles of just 1 or 2 dimensional scan.
+        try:
+            start_hkl.append(float(self._ui.lineEditStartH.text()))
+            start_hkl.append(float(self._ui.lineEditStartK.text()))
+            start_hkl.append(float(self._ui.lineEditStartL.text()))
+            stop_hkl.append(float(self._ui.lineEditStopH.text()))
+            stop_hkl.append(float(self._ui.lineEditStopK.text()))
+            stop_hkl.append(float(self._ui.lineEditStopL.text()))
+            nb_points = int(self._ui.LineEditNbpoints.text())
+        except:
+            nb_points = -1
+            missed_values = 1
+
+        increment_hkl = []
+
+        if nb_points > 0:
+            for i in range(0, 3):
+                increment_hkl.append((stop_hkl[i] - start_hkl[i]) / nb_points)
+
+        taurusValueAngle = []
+
+        for i in range(0, nb_points + 1):
+            hkl_temp = []
+            for j in range(0, 3):
+                hkl_temp.append(start_hkl[j] + i * increment_hkl[j])
+
+            no_trajectories = 0
+            try:
+                self.device.write_attribute("computetrajectoriessim", hkl_temp)
+            except:
+                no_trajectories = 1
+
+            if not no_trajectories:
+
+                angles_list = self.device.trajectorylist[tr]
+
+                taurusValueAngle.append([])
+
+                for iangle in range(0, self.nb_motors):
+                    taurusValueAngle[i].append(TaurusValueLineEdit(w))
+                    taurusValueAngle[i][iangle].setGeometry(
+                        QtCore.QRect(xangle[iangle], yhkl + 30 * (i + 1), 80, 27))
+                    taurusValueAngle[i][iangle].setReadOnly(True)
+                    tva_name = "taurusValueAngle" + str(i) + "_" + str(iangle)
+                    taurusValueAngle[i][iangle].setObjectName(tva_name)
+                    taurusValueAngle[i][iangle].setValue(
+                        "%10.4f" % angles_list[iangle])
+            else:
+                taurusValueAngle.append(TaurusValueLineEdit(w))
+                taurusValueAngle[i].setGeometry(QtCore.QRect(
+                    xangle[0], yhkl + 30 * (i + 1), self.nb_motors * 120, 27))
+                taurusValueAngle[i].setReadOnly(True)
+                tva_name = "taurusValueAngle" + str(i)
+                taurusValueAngle[i].setObjectName(tva_name)
+                taurusValueAngle[i].setValue(
+                    "...                             No angle solution for hkl values                             ...")
+        # TODO: not all dimensions (H & K & L) are obligatory. One could try 
+        # to display angles of just 1 or 2 dimensional scan.
+        if nb_points == -1:
+            nb_points = 0
+            taurusValueAngle.append(TaurusValueLineEdit(w))
+            taurusValueAngle[0].setGeometry(QtCore.QRect(
+                xangle[0], yhkl + 30, self.nb_motors * 120, 27))
+            taurusValueAngle[0].setReadOnly(True)
+            tva_name = "taurusValueAngle"
+            taurusValueAngle[0].setObjectName(tva_name)
+            taurusValueAngle[0].setValue(
+                "...          No scan parameters filled. Fill them in the main window         ...")
+
+        w.resize(self.nb_motors * 140, 120 + nb_points * 40)
+
+        w.show()
+        w.show()
+
+    def open_macroserver_connection_panel(self):
+        w = TaurusMacroConfigurationDialog(self)
+        Qt.qApp.SDM.connectReader("macroserverName", w.selectMacroServer)
+        Qt.qApp.SDM.connectReader("doorName", w.selectDoor)
+        Qt.qApp.SDM.connectReader("doorName", self.onDoorChanged)
+        Qt.qApp.SDM.connectWriter(
+            "macroserverName", w, 'macroserverNameChanged')
+        Qt.qApp.SDM.connectWriter("doorName", w, 'doorNameChanged')
+
+        w.show()
+
+    def onDoorChanged(self, doorName):
+        if doorName != self.door_device_name:
+            self.door_device_name = doorName
+            self.door_device = taurus.Device(doorName)
+
+
+def main():
+
+    parser = taurus.core.util.argparse.get_taurus_parser()
+    parser.usage = "%prog  <model> [door_name]"
+    parser.set_description("a taurus application for performing hkl scans")
+
+    app = taurus.qt.qtgui.application.TaurusApplication(cmd_line_parser=parser,
+            app_version=sardana.Release.version)
+    app.setApplicationName("hklscan")
+    args = app.get_command_line_args()
+    if len(args) < 1:
+        msg = "model not set (requires diffractometer controller)"
+        parser.error(msg)
+
+    w = HKLScan()
+    w.model = args[0]
+    w.setModel(w.model)
+
+    w.door_device = None
+    w.door_device_name = None
+    if len(args) > 1:
+        w.onDoorChanged(args[1])
+    else:
+        print "WARNING: Not door name supplied. Connection to MacroServer/Door not automatically done"
+    w.show()
+
+    sys.exit(app.exec_())
+
+ #   if len(sys.argv)>1: model=sys.argv[1]
+ #   else: model = None
+ #   app = Qt.QApplication(sys.argv)
+ #   w = HKLScan()
+ #   w.setModel(model)
+ #   w.show()
+ #   sys.exit(app.exec_())
+
+if __name__ == "__main__":
+    main()
diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/reflectionseditor.py b/src/sardana/taurus/qt/qtgui/extra_hkl/reflectionseditor.py
new file mode 100644
index 0000000..cbfa37d
--- /dev/null
+++ b/src/sardana/taurus/qt/qtgui/extra_hkl/reflectionseditor.py
@@ -0,0 +1,255 @@
+#!/usr/bin/env python
+
+##############################################################################
+##
+## 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/>.
+##
+##############################################################################
+
+__docformat__ = 'restructuredtext'
+
+import sys
+from taurus.external.qt import Qt
+import taurus.core
+from taurus.qt.qtgui.container import TaurusWidget
+from taurus.qt.qtgui.input import TaurusValueLineEdit
+
+from taurus.external.qt import QtCore, QtGui
+
+from taurus.qt.qtgui.util.ui import UILoadable
+
+
+ at UILoadable(with_ui="_ui")
+class ReflectionsEditor(TaurusWidget):
+
+    def __init__(self, parent=None, designMode=False):
+        TaurusWidget.__init__(self, parent, designMode=designMode)
+
+        self.loadUi(filename="reflectionseditor.ui")
+
+        self.connect(self._ui.ApplyButton, Qt.SIGNAL("clicked()"), self.apply)
+        self.connect(self._ui.ClearButton, Qt.SIGNAL("clicked()"), self.clear)
+
+    @classmethod
+    def getQtDesignerPluginInfo(cls):
+        ret = TaurusWidget.getQtDesignerPluginInfo()
+        ret['module'] = 'reflectionseditor'
+        ret['group'] = 'Taurus Containers'
+        ret['container'] = ':/designer/frame.png'
+        ret['container'] = False
+        return ret
+
+    def setModel(self, model):
+        if model != None:
+            self.device = taurus.Device(model)
+
+        xhkl = []
+        xangles = []
+        xlabels = []
+        for i in range(0, 6):
+            xhkl.append(70 + 80 * i)
+            xangles.append(310 + 80 * i)
+            xlabels.append(340 + 90 * i)
+        ybasis = 20
+
+        reflections = self.device.reflectionlist
+
+        angle_names = []
+        self.angle_labels = []
+        self.hkl_values = []
+        self.angle_values = []
+        self.index_values = []
+
+        # Find number of real motors
+        self.nb_angles = len(self.device.motorlist)
+
+        if reflections != None:
+            self.nb_reflections = len(reflections)
+        else:
+            self.nb_reflections = 0
+
+        try:
+            angle_names = self.device.motorroles
+        except:  # Only for compatibility
+            if self.nb_angles == 4:
+                angle_names.append("omega")
+                angle_names.append("chi")
+                angle_names.append("phi")
+                angle_names.append("tth")
+            elif self.nb_angles == 6:
+                angle_names.append("mu")
+                angle_names.append("th")
+                angle_names.append("chi")
+                angle_names.append("phi")
+                angle_names.append("gamma")
+                angle_names.append("delta")
+
+        for jref in range(0, 10):
+            self.index_values.append(QtGui.QLineEdit(self))
+            self.index_values[jref].setLayoutDirection(QtCore.Qt.RightToLeft)
+            self.index_values[jref].setGeometry(
+                QtCore.QRect(20, ybasis + 30 * (jref + 1), 51, 27))
+            object_name = "indexvalue" + str(jref)
+            self.index_values[jref].setObjectName(object_name)
+
+        for i in range(0, 3):
+            self.hkl_values.append([])
+            for jref in range(0, 10):
+                self.hkl_values[i].append(QtGui.QLineEdit(self))
+                self.hkl_values[i][jref].setLayoutDirection(
+                    QtCore.Qt.RightToLeft)
+                self.hkl_values[i][jref].setGeometry(
+                    QtCore.QRect(xhkl[i], ybasis + 30 * (jref + 1), 81, 27))
+                object_name = "hklvalue" + str(i) + "_" + str(jref)
+                self.hkl_values[i][jref].setObjectName(object_name)
+
+        for i in range(0, self.nb_angles):
+            self.angle_labels.append(QtGui.QLabel(self))
+            self.angle_labels[i].setGeometry(
+                QtCore.QRect(xangles[i], ybasis, 70, 20))
+            self.angle_labels[i].setLayoutDirection(QtCore.Qt.RightToLeft)
+            object_name = "anglelabel" + str(i)
+            self.angle_labels[i].setObjectName(object_name)
+            self.angle_labels[i].setText(QtGui.QApplication.translate(
+                "Form", angle_names[i], None, QtGui.QApplication.UnicodeUTF8))
+            self.angle_values.append([])
+            for jref in range(0, 10):
+                self.angle_values[i].append(QtGui.QLineEdit(self))
+                self.angle_values[i][jref].setLayoutDirection(
+                    QtCore.Qt.RightToLeft)
+                self.angle_values[i][jref].setGeometry(QtCore.QRect(
+                    xangles[i], ybasis + 30 * (jref + 1), 81, 27))
+                object_name = "anglevalue" + str(i) + "_" + str(jref)
+                self.angle_values[i][jref].setObjectName(object_name)
+
+        for jref in range(0, self.nb_reflections):
+            ref = reflections[jref]
+            # Fill index
+            self.index_values[jref].setText(str(jref))
+            # Fill hkl values
+            for i in range(0, 3):
+                self.hkl_values[i][jref].setText(
+                    ("%12.4f" % ref[i + 1]).strip())
+            # Fill the angle values
+            for i in range(0, self.nb_angles):
+                self.angle_values[i][jref].setText(
+                    ("%12.4f" % ref[i + 6]).strip())
+
+    def apply(self):
+        # Get the values for the new reflections
+        hklnew = []
+        anglesnew = []
+        indexnew = []
+        iref_hkl = []
+        iref_angles = []
+        for jref in range(0, 10):
+            hklnew.append([])
+            anglesnew.append([])
+            try:
+                indexnew.append(int(self.index_values[jref].text()))
+            except:
+                indexnew.append(-1)
+            icount = 0
+            for ihkl in range(0, 3):
+                try:
+                    hklnew[jref].append(
+                        float(self.hkl_values[ihkl][jref].text()))
+                    icount = icount + 1
+                except:
+                    hklnew[jref].append('')
+            iref_hkl.append(icount)
+            icount = 0
+            for iangle in range(0, self.nb_angles):
+                try:
+                    anglesnew[jref].append(
+                        float(self.angle_values[iangle][jref].text()))
+                    icount = icount + 1
+                except:
+                    anglesnew[jref].append('')
+            iref_angles.append(icount)
+
+        # Remove all reflections
+        if self.device.reflectionlist != None:
+            self.nb_reflections = len(self.device.reflectionlist)
+        else:
+            self.nb_reflections = 0
+        for i in range(0, self.nb_reflections):
+            # The index is reset after removing, so we remove always the first
+            # one
+            self.device.write_attribute("removereflection", 0)
+
+        # Create reflections (always with current angles attached)
+        for jref in range(0, 10):
+            for iref in range(0, 10):
+                if indexnew[jref] == iref:
+                    self.index_values[jref].setText(str(jref))
+                    if iref_hkl[iref] == 3:
+                        self.device.write_attribute(
+                            "addreflection", hklnew[iref])
+                # Set the angles if given
+                    if iref_angles[iref] == self.nb_angles:
+                        cmd = []
+                        cmd.append(iref)
+                        for i in range(0, self.nb_angles):
+                            cmd.append(anglesnew[iref][i])
+                        self.device.write_attribute(
+                            "adjustanglestoreflection", cmd)
+
+        self.clear()
+
+    def clear(self):
+        reflections = self.device.reflectionlist
+
+        # Clean the values
+        for jref in range(0, 10):
+            # Fill hkl values
+            for i in range(0, 3):
+                self.hkl_values[i][jref].setText('')
+            # Fill the angle values
+            for i in range(0, self.nb_angles):
+                self.angle_values[i][jref].setText('')
+
+        # Add the reflections
+        if reflections != None:
+            self.nb_reflections = len(reflections)
+            for jref in range(0, len(reflections)):
+                # Fill the index
+                self.index_values[jref].setText(str(jref))
+                ref = reflections[jref]
+                # Fill hkl values
+                for i in range(0, 3):
+                    self.hkl_values[i][jref].setText(
+                        ("%12.4f" % ref[i + 1]).strip())
+                # Fill the angle values
+                for i in range(0, self.nb_angles):
+                    self.angle_values[i][jref].setText(
+                        ("%12.4f" % ref[i + 6]).strip())
+        else:
+            self.nb_reflections = 0
+
+
+def main():
+    app = Qt.QApplication(sys.argv)
+    w = ReflectionsEditor()
+    w.show()
+    sys.exit(app.exec_())
+
+if __name__ == "__main__":
+    main()
diff --git a/src/sardana/test/testsuite.py b/src/sardana/taurus/qt/qtgui/extra_hkl/reflectionslist.py
similarity index 54%
copy from src/sardana/test/testsuite.py
copy to src/sardana/taurus/qt/qtgui/extra_hkl/reflectionslist.py
index 96d195b..8463e7d 100644
--- a/src/sardana/test/testsuite.py
+++ b/src/sardana/taurus/qt/qtgui/extra_hkl/reflectionslist.py
@@ -23,40 +23,37 @@
 ##
 ##############################################################################
 
-"""
-This module defines the test suite for the whole Sardana package
-Usage::
+__docformat__ = 'restructuredtext'
 
-  from sardana.test import testsuite
-  testsuite.run()
+import sys
+from taurus.external.qt import Qt
+from taurus.qt.qtgui.container import TaurusWidget
+from taurus.qt.qtgui.util.ui import UILoadable
 
-"""
 
-__docformat__ = 'restructuredtext'
+ at UILoadable(with_ui="_ui")
+class ReflectionsList(TaurusWidget):
+
+    def __init__(self, parent=None, designMode=False):
+        TaurusWidget.__init__(self, parent, designMode=designMode)
+
+        self.loadUi(filename="reflectionslist.ui")
+
+    @classmethod
+    def getQtDesignerPluginInfo(cls):
+        ret = TaurusWidget.getQtDesignerPluginInfo()
+        ret['module'] = 'reflectionslist'
+        ret['group'] = 'Taurus Containers'
+        ret['container'] = ':/designer/frame.png'
+        ret['container'] = True
+        return ret
+
+
+def main():
+    app = Qt.QApplication(sys.argv)
+    w = ReflectionsList()
+    w.show()
+    sys.exit(app.exec_())
 
-import sys, os
-from taurus.external import unittest
-import sardana
-
-
-def run():
-    '''Runs all tests for the taurus package
-
-    :returns: the test runner result
-    :rtype: unittest.result.TestResult
-    '''
-    # discover all tests within the sardana/src directory
-    loader = unittest.defaultTestLoader
-    suite = loader.discover(os.path.dirname(sardana.__file__))
-    # use the basic text test runner that outputs to sys.stderr
-    runner = unittest.TextTestRunner(descriptions=True, verbosity=2)
-    # run the test suite
-    result = runner.run(suite)
-    return result
-
-if __name__ == '__main__':
-    result = run()
-    exit_code = 0
-    if not result.wasSuccessful():
-        exit_code = 1
-    sys.exit(exit_code)
\ No newline at end of file
+if __name__ == "__main__":
+    main()
diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/selectsignal.py b/src/sardana/taurus/qt/qtgui/extra_hkl/selectsignal.py
new file mode 100644
index 0000000..76cb978
--- /dev/null
+++ b/src/sardana/taurus/qt/qtgui/extra_hkl/selectsignal.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+
+##############################################################################
+##
+## 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/>.
+##
+##############################################################################
+
+__docformat__ = 'restructuredtext'
+
+import sys
+from taurus.external.qt import Qt
+from taurus.qt.qtgui.container import TaurusWidget
+from taurus.qt.qtgui.base import TaurusBaseWidget
+import time
+import PyTango
+
+import taurus.core
+
+from taurus.external.qt import QtCore, QtGui
+
+from taurus.qt.qtgui.util.ui import UILoadable
+
+from sardana.taurus.core.tango.sardana.macroserver import registerExtensions
+
+class SignalComboBox(Qt.QComboBox, TaurusBaseWidget):
+    """ComboBox representing list of possible signals"""
+
+    def __init__(self, parent=None):
+        name = self.__class__.__name__
+        self.call__init__wo_kw(Qt.QComboBox, parent)
+        self.call__init__(TaurusBaseWidget, name)
+        self.setSizeAdjustPolicy(Qt.QComboBox.AdjustToContentsOnFirstShow)
+        self.setToolTip("Choose a signal ...")
+        QtCore.QMetaObject.connectSlotsByName(self)
+
+    def loadSignals(self, signals):
+        self.clear()
+        self.addItems(signals)
+
+
+ at UILoadable(with_ui="_ui")
+class SelectSignal(TaurusWidget):
+
+    def __init__(self, parent=None, designMode=False):
+        TaurusWidget.__init__(self, parent, designMode=designMode)
+
+        self.loadUi(filename="selectsignal.ui")
+
+        self.signalComboBox = SignalComboBox(self)
+        self.signalComboBox.setGeometry(QtCore.QRect(70, 50, 161, 27))
+        self.signalComboBox.setObjectName("SignalcomboBox")
+
+        self.connect(self.signalComboBox, Qt.SIGNAL(
+            "currentIndexChanged(QString)"), self.onSignalChanged)
+
+        self.doorName = None
+        self.door_device = None
+        
+        registerExtensions()
+
+    @classmethod
+    def getQtDesignerPluginInfo(cls):
+        ret = TaurusWidget.getQtDesignerPluginInfo()
+        ret['module'] = 'selectsignal'
+        ret['group'] = 'Taurus Containers'
+        ret['container'] = ':/designer/frame.png'
+        ret['container'] = False
+        return ret
+
+    def update_signals(self, doorname):
+        if self.doorName != doorname:
+            self.doorName = doorname
+            self.door_device = taurus.Device(self.doorName)
+            print "Create door_device with name " + str(self.doorName)
+
+        if self.doorName != None:
+            signals = []
+            conf = self.door_device.getExperimentConfiguration()
+            mg_name = conf['ActiveMntGrp']
+            mg = taurus.Device(mg_name)
+            signals = mg.ElementList
+
+            self.signalComboBox.loadSignals(signals)
+
+    def onSignalChanged(self, signalname):
+        self._ui.SignallineEdit.setText(signalname)
+
+
+def main():
+    app = Qt.QApplication(sys.argv)
+    w = SelectSignal()
+    w.show()
+    sys.exit(app.exec_())
+
+if __name__ == "__main__":
+    main()
diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py b/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py
new file mode 100644
index 0000000..08e6e85
--- /dev/null
+++ b/src/sardana/taurus/qt/qtgui/extra_hkl/ubmatrix.py
@@ -0,0 +1,487 @@
+#!/usr/bin/env python
+
+##############################################################################
+##
+## 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/>.
+##
+##############################################################################
+
+__docformat__ = 'restructuredtext'
+
+import sys
+
+import sardana
+from taurus.external.qt import Qt
+from taurus.qt.qtgui.container import TaurusWidget
+from reflectionslist import ReflectionsList
+from reflectionseditor import ReflectionsEditor
+from taurus.qt.qtgui.base import TaurusBaseWidget
+
+from taurus.external.qt import QtCore, QtGui
+
+from taurus.qt.qtgui.container import TaurusWidget
+from taurus.qt.qtgui.input import TaurusValueLineEdit
+import taurus.core
+
+import taurus.core.util.argparse
+import taurus.qt.qtgui.application
+from taurus.qt.qtgui.util.ui import UILoadable
+
+global flag_update
+flag_update = 0
+
+class PrivateComboBox(Qt.QComboBox, TaurusBaseWidget):
+    """ComboBox"""
+
+    def __init__(self, parent=None):
+        name = self.__class__.__name__
+        self.call__init__wo_kw(Qt.QComboBox, parent)
+        self.call__init__(TaurusBaseWidget, name)
+        self.setSizeAdjustPolicy(Qt.QComboBox.AdjustToContentsOnFirstShow)
+        self.setToolTip("Choose an item ...")
+        QtCore.QMetaObject.connectSlotsByName(self)
+
+    def loadItems(self, items):
+        all_items = [self.itemText(i) for i in range(self.count())]
+        for crys in items:
+            if crys not in all_items:
+                self.addItem(crys)
+
+ at UILoadable(with_ui="_ui")
+class UBMatrixBase(TaurusWidget):
+
+    def __init__(self, parent=None, designMode=False):
+        TaurusWidget.__init__(self, parent, designMode=designMode)
+
+        self.loadUi(filename="ubmatrix.ui")
+
+        self.connect(self._ui.UpdateButton, Qt.SIGNAL(
+            "clicked()"), self.update_values)
+        self.connect(self._ui.ComputeUButton,
+                     Qt.SIGNAL("clicked()"), self.compute_ub)
+        self.connect(self._ui.ReflectionsListButton, Qt.SIGNAL(
+            "clicked()"), self.reflections_list_window)
+        self.connect(self._ui.EditReflectionsButton, Qt.SIGNAL(
+            "clicked()"), self.edit_reflections_window)
+        self.connect(self._ui.AffineButton,
+                     Qt.SIGNAL("clicked()"), self.affine)
+        self.connect(self._ui.AddCrystalButton, Qt.SIGNAL(
+            "clicked()"), self.add_select_crystal)
+#        self.connect(self._ui.alattice_value, Qt.SIGNAL("textEdited()"), self.on_alattice_value_textEdited)
+#       Funciona con puro QEditValue pero no con TaurusQEdit ...
+
+    @classmethod
+    def getQtDesignerPluginInfo(cls):
+        ret = TaurusWidget.getQtDesignerPluginInfo()
+        ret['module'] = 'ubmatrix'
+        ret['group'] = 'Taurus Containers'
+        ret['container'] = ':/designer/frame.png'
+        ret['container'] = False
+        return ret
+
+    def setModel(self, model):
+
+        self.model = model
+
+        if model != None:
+            self.device = taurus.Device(model)
+
+        self.update_values()
+        
+        uxmodel = model + "/ux"
+        self._ui.taurusuxvalue.setModel(uxmodel)
+        self._ui.taurusuxeditvalue.setModel(uxmodel)
+        uymodel = model + "/uy"
+        self._ui.taurusuyvalue.setModel(uymodel)
+        self._ui.taurusuyeditvalue.setModel(uymodel)
+        uzmodel = model + "/uz"
+        self._ui.taurusuzvalue.setModel(uzmodel)
+        self._ui.taurusuzeditvalue.setModel(uzmodel)
+        amodel = model + "/a"
+        self._ui.taurusalatticevalue.setModel(amodel)
+        self._ui.taurusalatticeeditvalue.setModel(amodel)
+        bmodel = model + "/b"
+        self._ui.taurusblatticevalue.setModel(bmodel)
+        self._ui.taurusblatticeeditvalue.setModel(bmodel)
+        cmodel = model + "/c"
+        self._ui.taurusclatticevalue.setModel(cmodel)
+        self._ui.taurusclatticeeditvalue.setModel(cmodel)
+        alphamodel = model + "/alpha"
+        self._ui.taurusalphalatticevalue.setModel(alphamodel)
+        self._ui.taurusalphalatticeeditvalue.setModel(alphamodel)
+        betamodel = model + "/beta"
+        self._ui.taurusbetalatticevalue.setModel(betamodel)
+        self._ui.taurusbetalatticeeditvalue.setModel(betamodel)
+        gammamodel = model + "/gamma"
+        self._ui.taurusgammalatticevalue.setModel(gammamodel)
+        self._ui.taurusgammalatticeeditvalue.setModel(gammamodel)
+        psirefhmodel = model + "/psirefh"
+        self._ui.taurusvalue_psirefh.setModel(psirefhmodel)
+        self._ui.tauruseditvalue_psirefh.setModel(psirefhmodel)
+        psirefkmodel = model + "/psirefk"
+        self._ui.taurusvalue_psirefk.setModel(psirefkmodel)
+        self._ui.tauruseditvalue_psirefk.setModel(psirefkmodel)
+        psireflmodel = model + "/psirefl"
+        self._ui.taurusvalue_psirefl.setModel(psireflmodel)
+        self._ui.tauruseditvalue_psirefl.setModel(psireflmodel)
+        wavelengthmodel = model + "/wavelength"
+        self._ui.tauruswavelengthvalue.setModel(wavelengthmodel)
+
+        # Set model to engine and modes
+
+        enginemodel = model + '/engine'
+        self._ui.taurusLabelEngine.setModel(enginemodel)
+
+        self.enginescombobox = PrivateComboBox(self)
+        self.enginescombobox.setGeometry(QtCore.QRect(130, 460, 221, 27))
+        self.enginescombobox.setObjectName("engineslist")
+
+        self.enginescombobox.loadItems(self.device.enginelist)
+
+        self.connect(self.enginescombobox, Qt.SIGNAL(
+            "currentIndexChanged(QString)"), self.onEngineChanged)
+
+        enginemodemodel = model + '/enginemode'
+        self._ui.taurusLabelEngineMode.setModel(enginemodemodel)
+
+        self.enginemodescombobox = PrivateComboBox(self)
+        self.enginemodescombobox.setGeometry(QtCore.QRect(130, 500, 221, 27))
+        self.enginemodescombobox.setObjectName("enginemodeslist")
+
+        self.enginemodescombobox.loadItems(self.device.enginemodelist)
+
+        self.connect(self.enginemodescombobox, Qt.SIGNAL(
+            "currentIndexChanged(QString)"), self.onModeChanged)
+
+        # Set model to crystal
+
+        crystalmodel = model + '/crystal'
+        self._ui.taurusLabelCrystal.setModel(crystalmodel)
+
+        self.crystalscombobox = PrivateComboBox(self)
+        self.crystalscombobox.setGeometry(QtCore.QRect(130, 540, 221, 27))
+        self.crystalscombobox.setObjectName("crystallist")
+
+        self.crystalscombobox.loadItems(self.device.crystallist)
+
+        self.connect(self.crystalscombobox, Qt.SIGNAL(
+            "currentIndexChanged(QString)"), self.onCrystalChanged)
+        
+        
+    def onEngineChanged(self, enginename):
+        self.device.write_attribute("engine", str(enginename))
+
+    def onModeChanged(self, modename):
+        self.device.write_attribute("enginemode", str(modename))
+
+    def onCrystalChanged(self, crystalname):
+        if str(crystalname) != "":
+            self.device.write_attribute("crystal", str(crystalname))
+
+
+    def update_values(self):
+        ub_values = self.device.ubmatrix
+        self._ui.taurusub11value.setValue(ub_values[0][0])
+        self._ui.taurusub12value.setValue(ub_values[0][1])
+        self._ui.taurusub13value.setValue(ub_values[0][2])
+        self._ui.taurusub21value.setValue(ub_values[1][0])
+        self._ui.taurusub22value.setValue(ub_values[1][1])
+        self._ui.taurusub23value.setValue(ub_values[1][2])
+        self._ui.taurusub31value.setValue(ub_values[2][0])
+        self._ui.taurusub32value.setValue(ub_values[2][1])
+        self._ui.taurusub33value.setValue(ub_values[2][2])
+
+        global flag_update
+        if flag_update:
+            all_items = [self.crystalscombobox.itemText(i) for i in range(self.crystalscombobox.count())]
+            for crys in self.device.crystallist:
+                if crys not in all_items:
+                    self.crystalscombobox.addItem(crys)
+            for i in range(self.crystalscombobox.count()):
+                if self.crystalscombobox.itemText(i) not in self.device.crystallist:
+                    self.crystalscombobox.removeItem(i)
+        flag_update = 1
+            
+    def compute_ub(self):
+        index = [0, 1]
+
+        self.device.write_attribute("computeub", index)
+        self.update_values()
+        
+    def reflections_list_window(self):
+
+        reflections = self.device.reflectionlist
+
+        nb_ref = 0
+        xindex = 20
+        xh = 70
+        xk = 150
+        xl = 230
+        xrelevance = 330
+        xaffinement = 380
+        xangle1 = 430
+        xangle2 = 510
+        xangle3 = 590
+        xangle4 = 670
+        xangle5 = 750
+        xangle6 = 830
+        yhkl = 100
+        w = ReflectionsList()
+
+        self.taurusValueIndex = []
+        self.taurusValueH = []
+        self.taurusValueK = []
+        self.taurusValueL = []
+        self.taurusValueRelevance = []
+        self.taurusValueAffinement = []
+        self.taurusValueAngle1 = []
+        self.taurusValueAngle2 = []
+        self.taurusValueAngle3 = []
+        self.taurusValueAngle4 = []
+        self.taurusValueAngle5 = []
+        self.taurusValueAngle6 = []
+
+        if reflections != None:
+            for ref in reflections:
+                if nb_ref == 0:
+                    self.rl_label1_7 = QtGui.QLabel(w)
+                    self.rl_label1_7.setGeometry(
+                        QtCore.QRect(xangle1 + 20, 70, 51, 20))
+                    self.rl_label1_7.setObjectName("rl_label1_7")
+                #               self.testlabel.setLayoutDirection(QtCore.Qt.RightToLeft)
+                    self.rl_label1_8 = QtGui.QLabel(w)
+                    self.rl_label1_8.setGeometry(
+                        QtCore.QRect(xangle2 + 20, 70, 71, 20))
+                    self.rl_label1_8.setObjectName("rl_label1_8")
+                #               self.testlabel.setLayoutDirection(QtCore.Qt.RightToLeft)
+                    self.rl_label1_9 = QtGui.QLabel(w)
+                    self.rl_label1_9.setGeometry(
+                        QtCore.QRect(xangle3 + 20, 70, 41, 20))
+                    self.rl_label1_9.setObjectName("rl_label1_9")
+                    self.rl_label1_10 = QtGui.QLabel(w)
+                    self.rl_label1_10.setGeometry(
+                        QtCore.QRect(xangle4 + 20, 70, 41, 20))
+                    self.rl_label1_10.setObjectName("rl_label1_10")
+                    try:
+                        self.angle_names = self.device.motorroles
+                    except:  # Only for compatibility
+                        if self.nb_motors == 4:
+                            self.angles_names.append("omega")
+                            self.angles_names.append("chi")
+                            self.angles_names.append("phi")
+                            self.angles_names.append("theta")
+                        elif self.nb_motors == 6:
+                            self.angles_names.append("mu")
+                            self.angles_names.append("th")
+                            self.angles_names.append("chi")
+                            self.angles_names.append("phi")
+                            self.angles_names.append("gamma")
+                            self.angles_names.append("delta")
+                # 4circles diffractometer
+                    if len(ref) == 10:
+                        self.rl_label1_7.setText(QtGui.QApplication.translate(
+                            "Form", self.angle_names[0], None, QtGui.QApplication.UnicodeUTF8))
+                        self.rl_label1_8.setText(QtGui.QApplication.translate(
+                            "Form", self.angle_names[1], None, QtGui.QApplication.UnicodeUTF8))
+                        self.rl_label1_9.setText(QtGui.QApplication.translate(
+                            "Form", self.angle_names[2], None, QtGui.QApplication.UnicodeUTF8))
+                        self.rl_label1_10.setText(QtGui.QApplication.translate(
+                            "Form", self.angle_names[3], None, QtGui.QApplication.UnicodeUTF8))
+                # 6 circles diffractometer
+                    elif len(ref) == 12:
+                        self.rl_label1_11 = QtGui.QLabel(w)
+                        self.rl_label1_11.setGeometry(
+                            QtCore.QRect(xangle5 + 20, 70, 71, 20))
+                        self.rl_label1_11.setObjectName("rl_label1_11")
+                        self.rl_label1_12 = QtGui.QLabel(w)
+                        self.rl_label1_12.setGeometry(
+                            QtCore.QRect(xangle6 + 20, 70, 41, 20))
+                        self.rl_label1_12.setObjectName("rl_label1_12")
+                        self.rl_label1_7.setText(QtGui.QApplication.translate(
+                            "Form", self.angle_names[0], None, QtGui.QApplication.UnicodeUTF8))
+                        self.rl_label1_8.setText(QtGui.QApplication.translate(
+                            "Form", self.angle_names[1], None, QtGui.QApplication.UnicodeUTF8))
+                        self.rl_label1_9.setText(QtGui.QApplication.translate(
+                            "Form", self.angle_names[2], None, QtGui.QApplication.UnicodeUTF8))
+                        self.rl_label1_10.setText(QtGui.QApplication.translate(
+                            "Form", self.angle_names[3], None, QtGui.QApplication.UnicodeUTF8))
+                        self.rl_label1_11.setText(QtGui.QApplication.translate(
+                            "Form", self.angle_names[4], None, QtGui.QApplication.UnicodeUTF8))
+                        self.rl_label1_12.setText(QtGui.QApplication.translate(
+                            "Form", self.angle_names[5], None, QtGui.QApplication.UnicodeUTF8))
+
+                self.taurusValueIndex.append(TaurusValueLineEdit(w))
+                self.taurusValueIndex[nb_ref].setGeometry(
+                    QtCore.QRect(xindex, 100 + 30 * (nb_ref), 41, 27))
+                self.taurusValueIndex[nb_ref].setReadOnly(True)
+                indexname = "taurusValueIndex" + str(nb_ref + 2)
+                self.taurusValueIndex[nb_ref].setObjectName(indexname)
+                self.taurusValueIndex[nb_ref].setValue(int(ref[0]))
+
+                self.taurusValueH.append(TaurusValueLineEdit(w))
+                self.taurusValueH[nb_ref].setGeometry(
+                    QtCore.QRect(xh, 100 + 30 * (nb_ref), 81, 27))
+                self.taurusValueH[nb_ref].setReadOnly(True)
+                hname = "taurusValueH" + str(nb_ref + 2)
+                self.taurusValueH[nb_ref].setObjectName(hname)
+                self.taurusValueH[nb_ref].setValue("%10.4f" % ref[1])
+
+                self.taurusValueK.append(TaurusValueLineEdit(w))
+                self.taurusValueK[nb_ref].setGeometry(
+                    QtCore.QRect(xk, 100 + 30 * (nb_ref), 81, 27))
+                self.taurusValueK[nb_ref].setReadOnly(True)
+                kname = "taurusValueK" + str(nb_ref + 2)
+                self.taurusValueK[nb_ref].setObjectName(kname)
+                self.taurusValueK[nb_ref].setValue("%10.4f" % ref[2])
+
+                self.taurusValueL.append(TaurusValueLineEdit(w))
+                self.taurusValueL[nb_ref].setGeometry(
+                    QtCore.QRect(xl, 100 + 30 * (nb_ref), 81, 27))
+                self.taurusValueL[nb_ref].setReadOnly(True)
+                lname = "taurusValueL" + str(nb_ref + 2)
+                self.taurusValueL[nb_ref].setObjectName(lname)
+                self.taurusValueL[nb_ref].setValue("%10.4f" % ref[3])
+
+                self.taurusValueRelevance.append(TaurusValueLineEdit(w))
+                self.taurusValueRelevance[nb_ref].setGeometry(
+                    QtCore.QRect(xrelevance, 100 + 30 * (nb_ref), 41, 27))
+                self.taurusValueRelevance[nb_ref].setReadOnly(True)
+                relevancename = "taurusValueRelevance" + str(nb_ref + 2)
+                self.taurusValueRelevance[nb_ref].setObjectName(relevancename)
+                self.taurusValueRelevance[nb_ref].setValue(int(ref[4]))
+
+                self.taurusValueAffinement.append(TaurusValueLineEdit(w))
+                self.taurusValueAffinement[nb_ref].setGeometry(
+                    QtCore.QRect(xaffinement, 100 + 30 * (nb_ref), 41, 27))
+                self.taurusValueAffinement[nb_ref].setReadOnly(True)
+                affinementname = "taurusValueAffinement" + str(nb_ref + 2)
+                self.taurusValueAffinement[
+                    nb_ref].setObjectName(affinementname)
+                self.taurusValueAffinement[nb_ref].setValue(int(ref[5]))
+
+                self.taurusValueAngle1.append(TaurusValueLineEdit(w))
+                self.taurusValueAngle1[nb_ref].setGeometry(
+                    QtCore.QRect(xangle1, 100 + 30 * (nb_ref), 81, 27))
+                self.taurusValueAngle1[nb_ref].setReadOnly(True)
+                angle1name = "taurusValueAngle1" + str(nb_ref + 2)
+                self.taurusValueAngle1[nb_ref].setObjectName(angle1name)
+                self.taurusValueAngle1[nb_ref].setValue("%10.4f" % ref[6])
+
+                self.taurusValueAngle2.append(TaurusValueLineEdit(w))
+                self.taurusValueAngle2[nb_ref].setGeometry(
+                    QtCore.QRect(xangle2, 100 + 30 * (nb_ref), 81, 27))
+                self.taurusValueAngle2[nb_ref].setReadOnly(True)
+                angle2name = "taurusValueAngle2" + str(nb_ref + 2)
+                self.taurusValueAngle2[nb_ref].setObjectName(angle2name)
+                self.taurusValueAngle2[nb_ref].setValue("%10.4f" % ref[7])
+
+                self.taurusValueAngle3.append(TaurusValueLineEdit(w))
+                self.taurusValueAngle3[nb_ref].setGeometry(
+                    QtCore.QRect(xangle3, 100 + 30 * (nb_ref), 81, 27))
+                self.taurusValueAngle3[nb_ref].setReadOnly(True)
+                angle3name = "taurusValueAngle3" + str(nb_ref + 2)
+                self.taurusValueAngle3[nb_ref].setObjectName(angle3name)
+                self.taurusValueAngle3[nb_ref].setValue("%10.4f" % ref[8])
+
+                self.taurusValueAngle4.append(TaurusValueLineEdit(w))
+                self.taurusValueAngle4[nb_ref].setGeometry(
+                    QtCore.QRect(xangle4, 100 + 30 * (nb_ref), 81, 27))
+                self.taurusValueAngle4[nb_ref].setReadOnly(True)
+                angle4name = "taurusValueAngle4" + str(nb_ref + 2)
+                self.taurusValueAngle4[nb_ref].setObjectName(angle4name)
+                self.taurusValueAngle4[nb_ref].setValue("%10.4f" % ref[9])
+
+                if len(ref) == 12:
+                    self.taurusValueAngle5.append(TaurusValueLineEdit(w))
+                    self.taurusValueAngle5[nb_ref].setGeometry(
+                        QtCore.QRect(xangle5, 100 + 30 * (nb_ref), 81, 27))
+                    self.taurusValueAngle5[nb_ref].setReadOnly(True)
+                    angle5name = "taurusValueAngle5" + str(nb_ref + 2)
+                    self.taurusValueAngle5[nb_ref].setObjectName(angle5name)
+                    self.taurusValueAngle5[nb_ref].setValue("%10.4f" % ref[10])
+
+                    self.taurusValueAngle6.append(TaurusValueLineEdit(w))
+                    self.taurusValueAngle6[nb_ref].setGeometry(
+                        QtCore.QRect(xangle6, 100 + 30 * (nb_ref), 81, 27))
+                    self.taurusValueAngle6[nb_ref].setReadOnly(True)
+                    angle6name = "taurusValueAngle6" + str(nb_ref + 2)
+                    self.taurusValueAngle6[nb_ref].setObjectName(angle6name)
+                    self.taurusValueAngle6[nb_ref].setValue("%10.4f" % ref[11])
+
+                nb_ref = nb_ref + 1
+
+            w.resize(930, 100 + nb_ref * 50)
+
+        else:
+            self.rl_label_nor = QtGui.QLabel(w)
+            self.rl_label_nor.setGeometry(
+                QtCore.QRect(xangle1 - 50, 110, 300, 20))
+            font = QtGui.QFont()
+            font.setPointSize(12)
+            font.setWeight(75)
+            font.setBold(True)
+            self.rl_label_nor.setFont(font)
+            self.rl_label_nor.setObjectName("rl_label_nor")
+            self.rl_label_nor.setText(QtGui.QApplication.translate(
+                "Form", "NO REFLECTIONS", None, QtGui.QApplication.UnicodeUTF8))
+
+        w.show()
+        w.show()
+
+    def edit_reflections_window(self):
+
+        w = ReflectionsEditor()
+        w.setModel(self.model)
+
+        w.show()
+
+    def add_select_crystal(self):
+        new_crystal = str(self._ui.NewCrystalLineEdit.text())
+        self.device.write_attribute("AddCrystal", new_crystal)
+        self.crystalscombobox.loadItems(self.device.crystallist)
+        self.device.write_attribute("Crystal", new_crystal)
+
+    def affine(self):
+        self.device.write_attribute("affinecrystal", 1)
+
+def main():
+
+    parser = taurus.core.util.argparse.get_taurus_parser()
+    parser.usage = "%prog  <model>"
+    parser.set_description(
+        "a taurus application for setting diffractometer parameters: ubmatrix, lattice, reflections, ...")
+
+    app = taurus.qt.qtgui.application.TaurusApplication(cmd_line_parser=parser,
+            app_version=sardana.Release.version)
+    app.setApplicationName("ubmatrix")
+    args = app.get_command_line_args()
+    if len(args) < 1:
+        msg = "model not set (requires diffractometer controller)"
+        parser.error(msg)
+
+    w = UBMatrixBase()
+    w.model = args[0]
+    w.setModel(w.model)
+    w.show()
+
+    sys.exit(app.exec_())
+
+if __name__ == "__main__":
+    main()
diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/ui/computeu.ui b/src/sardana/taurus/qt/qtgui/extra_hkl/ui/computeu.ui
new file mode 100644
index 0000000..e4ca98b
--- /dev/null
+++ b/src/sardana/taurus/qt/qtgui/extra_hkl/ui/computeu.ui
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ComputeU</class>
+ <widget class="TaurusWidget" name="ComputeU">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>157</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QPushButton" name="ComputeButton">
+   <property name="geometry">
+    <rect>
+     <x>140</x>
+     <y>120</y>
+     <width>106</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Compute</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label">
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>20</y>
+     <width>231</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Use reflections (select by index):</string>
+   </property>
+  </widget>
+  <widget class="QLineEdit" name="indexreflection1lineEdit">
+   <property name="geometry">
+    <rect>
+     <x>70</x>
+     <y>60</y>
+     <width>81</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::RightToLeft</enum>
+   </property>
+  </widget>
+  <widget class="QLineEdit" name="indexreflection2lineEdit">
+   <property name="geometry">
+    <rect>
+     <x>240</x>
+     <y>60</y>
+     <width>81</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::RightToLeft</enum>
+   </property>
+  </widget>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>TaurusWidget</class>
+   <extends>QWidget</extends>
+   <header>taurus.qt.qtgui.container</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/ui/diffractometeralignment.ui b/src/sardana/taurus/qt/qtgui/extra_hkl/ui/diffractometeralignment.ui
new file mode 100644
index 0000000..dd1753e
--- /dev/null
+++ b/src/sardana/taurus/qt/qtgui/extra_hkl/ui/diffractometeralignment.ui
@@ -0,0 +1,559 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DiffractometerAlignment</class>
+ <widget class="TaurusWidget" name="DiffractometerAlignment">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>861</width>
+    <height>662</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>diffractometeralignment</string>
+  </property>
+  <widget class="Line" name="line_2">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>190</y>
+     <width>20</width>
+     <height>171</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Vertical</enum>
+   </property>
+  </widget>
+  <widget class="Line" name="line_3">
+   <property name="geometry">
+    <rect>
+     <x>70</x>
+     <y>180</y>
+     <width>761</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="Line" name="line_4">
+   <property name="geometry">
+    <rect>
+     <x>820</x>
+     <y>190</y>
+     <width>20</width>
+     <height>171</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Vertical</enum>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>180</y>
+     <width>31</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>hkl</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_2">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>240</y>
+     <width>61</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Position:</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_3">
+   <property name="geometry">
+    <rect>
+     <x>200</x>
+     <y>210</y>
+     <width>16</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>H</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_4">
+   <property name="geometry">
+    <rect>
+     <x>430</x>
+     <y>210</y>
+     <width>16</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>K</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_5">
+   <property name="geometry">
+    <rect>
+     <x>640</x>
+     <y>210</y>
+     <width>16</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>L</string>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusValueLineH">
+   <property name="geometry">
+    <rect>
+     <x>160</x>
+     <y>270</y>
+     <width>91</width>
+     <height>27</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusValueLineK">
+   <property name="geometry">
+    <rect>
+     <x>380</x>
+     <y>270</y>
+     <width>91</width>
+     <height>27</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusValueLineL">
+   <property name="geometry">
+    <rect>
+     <x>590</x>
+     <y>270</y>
+     <width>91</width>
+     <height>27</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_8">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>20</y>
+     <width>111</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Angles</string>
+   </property>
+  </widget>
+  <widget class="Line" name="line_5">
+   <property name="geometry">
+    <rect>
+     <x>80</x>
+     <y>20</y>
+     <width>761</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="Line" name="line_6">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>30</y>
+     <width>20</width>
+     <height>101</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Vertical</enum>
+   </property>
+  </widget>
+  <widget class="Line" name="line_7">
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>130</y>
+     <width>811</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="Line" name="line_8">
+   <property name="geometry">
+    <rect>
+     <x>820</x>
+     <y>30</y>
+     <width>20</width>
+     <height>91</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Vertical</enum>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_9">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>275</y>
+     <width>61</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Move to:</string>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusLabelValueH">
+   <property name="geometry">
+    <rect>
+     <x>170</x>
+     <y>240</y>
+     <width>81</width>
+     <height>19</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusLabelValueK">
+   <property name="geometry">
+    <rect>
+     <x>390</x>
+     <y>240</y>
+     <width>81</width>
+     <height>19</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusLabelValueL">
+   <property name="geometry">
+    <rect>
+     <x>600</x>
+     <y>240</y>
+     <width>81</width>
+     <height>19</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_10">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>370</y>
+     <width>111</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Scans</string>
+   </property>
+  </widget>
+  <widget class="Line" name="line_9">
+   <property name="geometry">
+    <rect>
+     <x>90</x>
+     <y>370</y>
+     <width>741</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="Line" name="line_10">
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>560</y>
+     <width>811</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="Line" name="line_11">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>380</y>
+     <width>20</width>
+     <height>191</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Vertical</enum>
+   </property>
+  </widget>
+  <widget class="Line" name="line_12">
+   <property name="geometry">
+    <rect>
+     <x>820</x>
+     <y>380</y>
+     <width>20</width>
+     <height>191</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Vertical</enum>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_13">
+   <property name="geometry">
+    <rect>
+     <x>610</x>
+     <y>320</y>
+     <width>41</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Mode:</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_14">
+   <property name="geometry">
+    <rect>
+     <x>430</x>
+     <y>320</y>
+     <width>61</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Engine:</string>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusLabelEngine">
+   <property name="geometry">
+    <rect>
+     <x>490</x>
+     <y>320</y>
+     <width>91</width>
+     <height>19</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusLabelEngineMode">
+   <property name="geometry">
+    <rect>
+     <x>660</x>
+     <y>320</y>
+     <width>161</width>
+     <height>19</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_15">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>320</y>
+     <width>91</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Select mode:</string>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="AlignmentStopButton">
+   <property name="geometry">
+    <rect>
+     <x>50</x>
+     <y>620</y>
+     <width>106</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Stop</string>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="MacroServerConnectionButton">
+   <property name="geometry">
+    <rect>
+     <x>660</x>
+     <y>620</y>
+     <width>191</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Macroserver Connection</string>
+   </property>
+  </widget>
+  <widget class="Line" name="line_13">
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>350</y>
+     <width>811</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_6">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>55</y>
+     <width>61</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Position:</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_16">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>100</y>
+     <width>61</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Move to:</string>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="AlignmentStoreReflectionButton">
+   <property name="geometry">
+    <rect>
+     <x>180</x>
+     <y>620</y>
+     <width>106</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Store Refl.</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_execscan">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>410</y>
+     <width>81</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Exec Scan:</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_range">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>445</y>
+     <width>51</width>
+     <height>16</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Range:</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_tomax">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>480</y>
+     <width>61</width>
+     <height>16</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>To Max.:</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_nbpoints">
+   <property name="geometry">
+    <rect>
+     <x>70</x>
+     <y>530</y>
+     <width>71</width>
+     <height>16</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Nb of intervals:</string>
+   </property>
+  </widget>
+  <widget class="QLineEdit" name="NbPointslineEdit">
+   <property name="geometry">
+    <rect>
+     <x>160</x>
+     <y>525</y>
+     <width>81</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::RightToLeft</enum>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="SelectSignalButton">
+   <property name="geometry">
+    <rect>
+     <x>700</x>
+     <y>530</y>
+     <width>106</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>SelectSignal</string>
+   </property>
+  </widget>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>TaurusWidget</class>
+   <extends>QWidget</extends>
+   <header>taurus.qt.qtgui.container</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>TaurusLabel</class>
+   <extends>QLabel</extends>
+   <header>taurus.qt.qtgui.display</header>
+  </customwidget>
+  <customwidget>
+   <class>TaurusValueLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>taurus.qt.qtgui.input</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/ui/displayscanangles.ui b/src/sardana/taurus/qt/qtgui/extra_hkl/ui/displayscanangles.ui
new file mode 100644
index 0000000..862bb84
--- /dev/null
+++ b/src/sardana/taurus/qt/qtgui/extra_hkl/ui/displayscanangles.ui
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DisplayScanAngles</class>
+ <widget class="TaurusWidget" name="DisplayScanAngles">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>856</width>
+    <height>279</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QLabel" name="label">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>20</y>
+     <width>161</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Angles during the scan</string>
+   </property>
+  </widget>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>TaurusWidget</class>
+   <extends>QWidget</extends>
+   <header>taurus.qt.qtgui.container</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/ui/hklscan.ui b/src/sardana/taurus/qt/qtgui/extra_hkl/ui/hklscan.ui
new file mode 100644
index 0000000..c5011e6
--- /dev/null
+++ b/src/sardana/taurus/qt/qtgui/extra_hkl/ui/hklscan.ui
@@ -0,0 +1,624 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>HKLScan</class>
+ <widget class="TaurusWidget" name="HKLScan">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>848</width>
+    <height>569</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>hklscan</string>
+  </property>
+  <widget class="Line" name="line">
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>220</y>
+     <width>811</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="Line" name="line_2">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>30</y>
+     <width>20</width>
+     <height>191</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Vertical</enum>
+   </property>
+  </widget>
+  <widget class="Line" name="line_3">
+   <property name="geometry">
+    <rect>
+     <x>140</x>
+     <y>20</y>
+     <width>691</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="Line" name="line_4">
+   <property name="geometry">
+    <rect>
+     <x>820</x>
+     <y>30</y>
+     <width>20</width>
+     <height>201</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Vertical</enum>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>20</y>
+     <width>111</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Positions/Limits</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_2">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>80</y>
+     <width>61</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Position:</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_3">
+   <property name="geometry">
+    <rect>
+     <x>200</x>
+     <y>50</y>
+     <width>16</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>H</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_4">
+   <property name="geometry">
+    <rect>
+     <x>430</x>
+     <y>50</y>
+     <width>16</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>K</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_5">
+   <property name="geometry">
+    <rect>
+     <x>640</x>
+     <y>50</y>
+     <width>16</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>L</string>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusValueLineH">
+   <property name="geometry">
+    <rect>
+     <x>160</x>
+     <y>110</y>
+     <width>91</width>
+     <height>27</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusValueLineK">
+   <property name="geometry">
+    <rect>
+     <x>380</x>
+     <y>110</y>
+     <width>91</width>
+     <height>27</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusValueLineL">
+   <property name="geometry">
+    <rect>
+     <x>590</x>
+     <y>110</y>
+     <width>91</width>
+     <height>27</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_6">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>160</y>
+     <width>61</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Start:</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_7">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>200</y>
+     <width>61</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Stop</string>
+   </property>
+  </widget>
+  <widget class="QLineEdit" name="lineEditStartH">
+   <property name="geometry">
+    <rect>
+     <x>160</x>
+     <y>150</y>
+     <width>91</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::RightToLeft</enum>
+   </property>
+  </widget>
+  <widget class="QLineEdit" name="lineEditStartL">
+   <property name="geometry">
+    <rect>
+     <x>590</x>
+     <y>150</y>
+     <width>91</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::RightToLeft</enum>
+   </property>
+  </widget>
+  <widget class="QLineEdit" name="lineEditStartK">
+   <property name="geometry">
+    <rect>
+     <x>380</x>
+     <y>150</y>
+     <width>91</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::RightToLeft</enum>
+   </property>
+  </widget>
+  <widget class="QLineEdit" name="lineEditStopH">
+   <property name="geometry">
+    <rect>
+     <x>160</x>
+     <y>190</y>
+     <width>91</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::RightToLeft</enum>
+   </property>
+  </widget>
+  <widget class="QLineEdit" name="lineEditStopK">
+   <property name="geometry">
+    <rect>
+     <x>380</x>
+     <y>190</y>
+     <width>91</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::RightToLeft</enum>
+   </property>
+  </widget>
+  <widget class="QLineEdit" name="lineEditStopL">
+   <property name="geometry">
+    <rect>
+     <x>590</x>
+     <y>190</y>
+     <width>91</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::RightToLeft</enum>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_8">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>260</y>
+     <width>111</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Angles</string>
+   </property>
+  </widget>
+  <widget class="Line" name="line_5">
+   <property name="geometry">
+    <rect>
+     <x>70</x>
+     <y>260</y>
+     <width>761</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="Line" name="line_6">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>260</y>
+     <width>20</width>
+     <height>101</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Vertical</enum>
+   </property>
+  </widget>
+  <widget class="Line" name="line_7">
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>340</y>
+     <width>811</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="Line" name="line_8">
+   <property name="geometry">
+    <rect>
+     <x>820</x>
+     <y>260</y>
+     <width>20</width>
+     <height>91</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Vertical</enum>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_9">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>120</y>
+     <width>61</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Move to:</string>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusLabelValueH">
+   <property name="geometry">
+    <rect>
+     <x>170</x>
+     <y>80</y>
+     <width>81</width>
+     <height>19</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusLabelValueK">
+   <property name="geometry">
+    <rect>
+     <x>390</x>
+     <y>80</y>
+     <width>81</width>
+     <height>19</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusLabelValueL">
+   <property name="geometry">
+    <rect>
+     <x>600</x>
+     <y>80</y>
+     <width>81</width>
+     <height>19</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_10">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>370</y>
+     <width>111</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Parameters</string>
+   </property>
+  </widget>
+  <widget class="Line" name="line_9">
+   <property name="geometry">
+    <rect>
+     <x>120</x>
+     <y>370</y>
+     <width>711</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="Line" name="line_10">
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>470</y>
+     <width>811</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="Line" name="line_11">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>380</y>
+     <width>20</width>
+     <height>101</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Vertical</enum>
+   </property>
+  </widget>
+  <widget class="Line" name="line_12">
+   <property name="geometry">
+    <rect>
+     <x>820</x>
+     <y>380</y>
+     <width>20</width>
+     <height>101</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Vertical</enum>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_11">
+   <property name="geometry">
+    <rect>
+     <x>180</x>
+     <y>400</y>
+     <width>71</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Nb points:</string>
+   </property>
+  </widget>
+  <widget class="QLineEdit" name="LineEditNbpoints">
+   <property name="geometry">
+    <rect>
+     <x>270</x>
+     <y>396</y>
+     <width>81</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::RightToLeft</enum>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_12">
+   <property name="geometry">
+    <rect>
+     <x>410</x>
+     <y>400</y>
+     <width>101</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Sample time:</string>
+   </property>
+  </widget>
+  <widget class="QLineEdit" name="LineEditSampleTime">
+   <property name="geometry">
+    <rect>
+     <x>520</x>
+     <y>396</y>
+     <width>81</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::RightToLeft</enum>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_13">
+   <property name="geometry">
+    <rect>
+     <x>610</x>
+     <y>450</y>
+     <width>41</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Mode:</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_14">
+   <property name="geometry">
+    <rect>
+     <x>430</x>
+     <y>450</y>
+     <width>61</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Engine:</string>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusLabelEngine">
+   <property name="geometry">
+    <rect>
+     <x>490</x>
+     <y>450</y>
+     <width>91</width>
+     <height>19</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusLabelEngineMode">
+   <property name="geometry">
+    <rect>
+     <x>660</x>
+     <y>450</y>
+     <width>161</width>
+     <height>19</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_15">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>450</y>
+     <width>91</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Select mode:</string>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="hklStartScanButton">
+   <property name="geometry">
+    <rect>
+     <x>90</x>
+     <y>510</y>
+     <width>106</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Start</string>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="hklStopScanButton">
+   <property name="geometry">
+    <rect>
+     <x>220</x>
+     <y>510</y>
+     <width>106</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Stop</string>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="MacroServerConnectionButton">
+   <property name="geometry">
+    <rect>
+     <x>640</x>
+     <y>520</y>
+     <width>191</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Macroserver Connection</string>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="hklDisplayAnglesButton">
+   <property name="geometry">
+    <rect>
+     <x>350</x>
+     <y>510</y>
+     <width>131</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Display Angles</string>
+   </property>
+  </widget>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>TaurusWidget</class>
+   <extends>QWidget</extends>
+   <header>taurus.qt.qtgui.container</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>TaurusLabel</class>
+   <extends>QLabel</extends>
+   <header>taurus.qt.qtgui.display</header>
+  </customwidget>
+  <customwidget>
+   <class>TaurusValueLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>taurus.qt.qtgui.input</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/ui/reflectionseditor.ui b/src/sardana/taurus/qt/qtgui/extra_hkl/ui/reflectionseditor.ui
new file mode 100644
index 0000000..033a524
--- /dev/null
+++ b/src/sardana/taurus/qt/qtgui/extra_hkl/ui/reflectionseditor.ui
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ReflectionsEditor</class>
+ <widget class="TaurusWidget" name="ReflectionsEditor">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>801</width>
+    <height>437</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QLabel" name="rl_label1">
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>20</y>
+     <width>38</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::RightToLeft</enum>
+   </property>
+   <property name="text">
+    <string>Index</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="rl_label1_2">
+   <property name="geometry">
+    <rect>
+     <x>105</x>
+     <y>20</y>
+     <width>36</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::RightToLeft</enum>
+   </property>
+   <property name="text">
+    <string>H</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="rl_label1_3">
+   <property name="geometry">
+    <rect>
+     <x>190</x>
+     <y>20</y>
+     <width>36</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::RightToLeft</enum>
+   </property>
+   <property name="text">
+    <string>K</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="rl_label1_4">
+   <property name="geometry">
+    <rect>
+     <x>260</x>
+     <y>20</y>
+     <width>36</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::RightToLeft</enum>
+   </property>
+   <property name="text">
+    <string>L</string>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="ApplyButton">
+   <property name="geometry">
+    <rect>
+     <x>380</x>
+     <y>390</y>
+     <width>106</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Apply</string>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="ClearButton">
+   <property name="geometry">
+    <rect>
+     <x>250</x>
+     <y>390</y>
+     <width>106</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Clear</string>
+   </property>
+  </widget>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>TaurusWidget</class>
+   <extends>QWidget</extends>
+   <header>taurus.qt.qtgui.container</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/ui/reflectionslist.ui b/src/sardana/taurus/qt/qtgui/extra_hkl/ui/reflectionslist.ui
new file mode 100644
index 0000000..3e8152c
--- /dev/null
+++ b/src/sardana/taurus/qt/qtgui/extra_hkl/ui/reflectionslist.ui
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ReflectionsList</class>
+ <widget class="TaurusWidget" name="ReflectionsList">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>912</width>
+    <height>167</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QLabel" name="ReflectionsListLabel">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>20</y>
+     <width>120</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Reflections List</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="rl_label1">
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>70</y>
+     <width>38</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Index</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="rl_label1_2">
+   <property name="geometry">
+    <rect>
+     <x>120</x>
+     <y>70</y>
+     <width>16</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>H</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="rl_label1_3">
+   <property name="geometry">
+    <rect>
+     <x>195</x>
+     <y>70</y>
+     <width>16</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>K</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="rl_label1_4">
+   <property name="geometry">
+    <rect>
+     <x>275</x>
+     <y>70</y>
+     <width>16</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>L</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="rl_label1_5">
+   <property name="geometry">
+    <rect>
+     <x>330</x>
+     <y>70</y>
+     <width>41</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Relev.</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="rl_label1_6">
+   <property name="geometry">
+    <rect>
+     <x>390</x>
+     <y>70</y>
+     <width>41</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Aff.</string>
+   </property>
+  </widget>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>TaurusWidget</class>
+   <extends>QWidget</extends>
+   <header>taurus.qt.qtgui.container</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/ui/selectsignal.ui b/src/sardana/taurus/qt/qtgui/extra_hkl/ui/selectsignal.ui
new file mode 100644
index 0000000..8f83bea
--- /dev/null
+++ b/src/sardana/taurus/qt/qtgui/extra_hkl/ui/selectsignal.ui
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SelectSignal</class>
+ <widget class="TaurusWidget" name="SelectSignal">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>276</width>
+    <height>201</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QLabel" name="label_signal">
+   <property name="geometry">
+    <rect>
+     <x>50</x>
+     <y>115</y>
+     <width>51</width>
+     <height>16</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Signal:</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_sampletime">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>160</y>
+     <width>101</width>
+     <height>16</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Sample Time:</string>
+   </property>
+  </widget>
+  <widget class="QLineEdit" name="SampleTimelineEdit">
+   <property name="geometry">
+    <rect>
+     <x>150</x>
+     <y>155</y>
+     <width>81</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::RightToLeft</enum>
+   </property>
+  </widget>
+  <widget class="Line" name="line">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>90</y>
+     <width>251</width>
+     <height>16</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_select">
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>20</y>
+     <width>51</width>
+     <height>16</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Select:</string>
+   </property>
+  </widget>
+  <widget class="QLineEdit" name="SignallineEdit">
+   <property name="geometry">
+    <rect>
+     <x>120</x>
+     <y>110</y>
+     <width>111</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::RightToLeft</enum>
+   </property>
+  </widget>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>TaurusWidget</class>
+   <extends>QWidget</extends>
+   <header>taurus.qt.qtgui.container</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/sardana/taurus/qt/qtgui/extra_hkl/ui/ubmatrix.ui b/src/sardana/taurus/qt/qtgui/extra_hkl/ui/ubmatrix.ui
new file mode 100644
index 0000000..0392a03
--- /dev/null
+++ b/src/sardana/taurus/qt/qtgui/extra_hkl/ui/ubmatrix.ui
@@ -0,0 +1,1003 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ubmatrix</class>
+ <widget class="TaurusWidget" name="ubmatrix">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>797</width>
+    <height>880</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>ubmatrix</string>
+  </property>
+  <widget class="QLabel" name="ub11label">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>40</y>
+     <width>41</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>UB11</string>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusub11value">
+   <property name="geometry">
+    <rect>
+     <x>100</x>
+     <y>30</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="readOnly">
+    <bool>true</bool>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="UpdateButton">
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>830</y>
+     <width>106</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Update</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="ub12label">
+   <property name="geometry">
+    <rect>
+     <x>290</x>
+     <y>40</y>
+     <width>41</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>UB12</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="ub13label">
+   <property name="geometry">
+    <rect>
+     <x>510</x>
+     <y>40</y>
+     <width>41</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>UB13</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="ub21label">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>80</y>
+     <width>41</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>UB21</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="ub22label">
+   <property name="geometry">
+    <rect>
+     <x>290</x>
+     <y>80</y>
+     <width>41</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>UB22</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="ub23label">
+   <property name="geometry">
+    <rect>
+     <x>510</x>
+     <y>80</y>
+     <width>41</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>UB23</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="ub31label">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>120</y>
+     <width>41</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>UB31</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="ub32label">
+   <property name="geometry">
+    <rect>
+     <x>290</x>
+     <y>120</y>
+     <width>41</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>UB32</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="ub33label">
+   <property name="geometry">
+    <rect>
+     <x>510</x>
+     <y>120</y>
+     <width>41</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>UB33</string>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusub12value">
+   <property name="geometry">
+    <rect>
+     <x>340</x>
+     <y>30</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="readOnly">
+    <bool>true</bool>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusub13value">
+   <property name="geometry">
+    <rect>
+     <x>570</x>
+     <y>30</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="readOnly">
+    <bool>true</bool>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusub21value">
+   <property name="geometry">
+    <rect>
+     <x>100</x>
+     <y>70</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="readOnly">
+    <bool>true</bool>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusub22value">
+   <property name="geometry">
+    <rect>
+     <x>340</x>
+     <y>70</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="readOnly">
+    <bool>true</bool>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusub23value">
+   <property name="geometry">
+    <rect>
+     <x>570</x>
+     <y>70</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="readOnly">
+    <bool>true</bool>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusub31value">
+   <property name="geometry">
+    <rect>
+     <x>100</x>
+     <y>110</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="readOnly">
+    <bool>true</bool>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusub32value">
+   <property name="geometry">
+    <rect>
+     <x>340</x>
+     <y>110</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="readOnly">
+    <bool>true</bool>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusub33value">
+   <property name="geometry">
+    <rect>
+     <x>570</x>
+     <y>110</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="readOnly">
+    <bool>true</bool>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="ComputeUButton">
+   <property name="geometry">
+    <rect>
+     <x>370</x>
+     <y>830</y>
+     <width>106</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>ComputeUB</string>
+   </property>
+  </widget>
+  <widget class="Line" name="line">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>140</y>
+     <width>791</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusuxeditvalue">
+   <property name="geometry">
+    <rect>
+     <x>100</x>
+     <y>230</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusuyeditvalue">
+   <property name="geometry">
+    <rect>
+     <x>340</x>
+     <y>230</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QLabel" name="uxlabel">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>210</y>
+     <width>41</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Ux</string>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusuzeditvalue">
+   <property name="geometry">
+    <rect>
+     <x>570</x>
+     <y>230</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QLabel" name="uylabel">
+   <property name="geometry">
+    <rect>
+     <x>290</x>
+     <y>210</y>
+     <width>41</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Uy</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="uzlabel">
+   <property name="geometry">
+    <rect>
+     <x>520</x>
+     <y>210</y>
+     <width>41</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Uz</string>
+   </property>
+  </widget>
+  <widget class="Line" name="line_2">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>270</y>
+     <width>781</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="QLabel" name="ublabel">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>10</y>
+     <width>71</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>UB Matrix</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="uvector_label">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>170</y>
+     <width>71</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>U Vector</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="lattice_label">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>290</y>
+     <width>71</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Lattice</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="a_lattice_label">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>330</y>
+     <width>21</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>a</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="b_lattice_label">
+   <property name="geometry">
+    <rect>
+     <x>280</x>
+     <y>330</y>
+     <width>21</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>b</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="c_lattice_label">
+   <property name="geometry">
+    <rect>
+     <x>510</x>
+     <y>330</y>
+     <width>21</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>c</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="alpha_lattice_label">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>390</y>
+     <width>41</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>alpha</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="beta_lattice_label">
+   <property name="geometry">
+    <rect>
+     <x>280</x>
+     <y>390</y>
+     <width>41</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>beta</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="gamma_lattice_label">
+   <property name="geometry">
+    <rect>
+     <x>510</x>
+     <y>390</y>
+     <width>61</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>gamma</string>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusalatticeeditvalue">
+   <property name="geometry">
+    <rect>
+     <x>100</x>
+     <y>340</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusblatticeeditvalue">
+   <property name="geometry">
+    <rect>
+     <x>340</x>
+     <y>340</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusclatticeeditvalue">
+   <property name="geometry">
+    <rect>
+     <x>570</x>
+     <y>340</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusalphalatticeeditvalue">
+   <property name="geometry">
+    <rect>
+     <x>100</x>
+     <y>400</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusbetalatticeeditvalue">
+   <property name="geometry">
+    <rect>
+     <x>340</x>
+     <y>400</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="taurusgammalatticeeditvalue">
+   <property name="geometry">
+    <rect>
+     <x>570</x>
+     <y>400</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="Line" name="line_3">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>430</y>
+     <width>781</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="ReflectionsListButton">
+   <property name="geometry">
+    <rect>
+     <x>500</x>
+     <y>830</y>
+     <width>111</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Reflections List</string>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusuxvalue">
+   <property name="geometry">
+    <rect>
+     <x>100</x>
+     <y>200</y>
+     <width>148</width>
+     <height>20</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusuyvalue">
+   <property name="geometry">
+    <rect>
+     <x>340</x>
+     <y>200</y>
+     <width>148</width>
+     <height>20</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusuzvalue">
+   <property name="geometry">
+    <rect>
+     <x>570</x>
+     <y>200</y>
+     <width>148</width>
+     <height>20</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusalatticevalue">
+   <property name="geometry">
+    <rect>
+     <x>100</x>
+     <y>320</y>
+     <width>148</width>
+     <height>20</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusblatticevalue">
+   <property name="geometry">
+    <rect>
+     <x>340</x>
+     <y>320</y>
+     <width>148</width>
+     <height>20</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusclatticevalue">
+   <property name="geometry">
+    <rect>
+     <x>570</x>
+     <y>320</y>
+     <width>148</width>
+     <height>20</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusalphalatticevalue">
+   <property name="geometry">
+    <rect>
+     <x>100</x>
+     <y>380</y>
+     <width>148</width>
+     <height>20</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusbetalatticevalue">
+   <property name="geometry">
+    <rect>
+     <x>340</x>
+     <y>380</y>
+     <width>148</width>
+     <height>20</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusgammalatticevalue">
+   <property name="geometry">
+    <rect>
+     <x>570</x>
+     <y>380</y>
+     <width>148</width>
+     <height>20</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="EditReflectionsButton">
+   <property name="geometry">
+    <rect>
+     <x>630</x>
+     <y>830</y>
+     <width>111</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Edit Reflections</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="psiref_label">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>650</y>
+     <width>71</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Psi-Ref</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="h_label">
+   <property name="geometry">
+    <rect>
+     <x>50</x>
+     <y>680</y>
+     <width>21</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>H</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="l_label">
+   <property name="geometry">
+    <rect>
+     <x>520</x>
+     <y>680</y>
+     <width>21</width>
+     <height>16</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>L</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="k_label">
+   <property name="geometry">
+    <rect>
+     <x>290</x>
+     <y>680</y>
+     <width>21</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>K</string>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusvalue_psirefh">
+   <property name="geometry">
+    <rect>
+     <x>100</x>
+     <y>660</y>
+     <width>148</width>
+     <height>20</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusvalue_psirefk">
+   <property name="geometry">
+    <rect>
+     <x>340</x>
+     <y>660</y>
+     <width>148</width>
+     <height>20</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusvalue_psirefl">
+   <property name="geometry">
+    <rect>
+     <x>580</x>
+     <y>660</y>
+     <width>148</width>
+     <height>20</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="tauruseditvalue_psirefh">
+   <property name="geometry">
+    <rect>
+     <x>100</x>
+     <y>680</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="tauruseditvalue_psirefk">
+   <property name="geometry">
+    <rect>
+     <x>340</x>
+     <y>680</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusValueLineEdit" name="tauruseditvalue_psirefl">
+   <property name="geometry">
+    <rect>
+     <x>580</x>
+     <y>680</y>
+     <width>148</width>
+     <height>27</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_15">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>500</y>
+     <width>91</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Select mode:</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_13">
+   <property name="geometry">
+    <rect>
+     <x>490</x>
+     <y>500</y>
+     <width>41</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Mode:</string>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusLabelEngine">
+   <property name="geometry">
+    <rect>
+     <x>550</x>
+     <y>460</y>
+     <width>161</width>
+     <height>19</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusLabelEngineMode">
+   <property name="geometry">
+    <rect>
+     <x>550</x>
+     <y>500</y>
+     <width>161</width>
+     <height>19</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_14">
+   <property name="geometry">
+    <rect>
+     <x>490</x>
+     <y>460</y>
+     <width>61</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Engine:</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_16">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>540</y>
+     <width>91</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Select crystal:</string>
+   </property>
+  </widget>
+  <widget class="Line" name="line_4">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>720</y>
+     <width>781</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_17">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>460</y>
+     <width>101</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Select  engine:</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_18">
+   <property name="geometry">
+    <rect>
+     <x>490</x>
+     <y>540</y>
+     <width>51</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Crystal:</string>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="taurusLabelCrystal">
+   <property name="geometry">
+    <rect>
+     <x>550</x>
+     <y>540</y>
+     <width>161</width>
+     <height>19</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="AffineButton">
+   <property name="geometry">
+    <rect>
+     <x>250</x>
+     <y>830</y>
+     <width>106</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Affine</string>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="AddCrystalButton">
+   <property name="geometry">
+    <rect>
+     <x>280</x>
+     <y>590</y>
+     <width>171</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Add/Select new crystal</string>
+   </property>
+  </widget>
+  <widget class="QLineEdit" name="NewCrystalLineEdit">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>590</y>
+     <width>221</width>
+     <height>23</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="Line" name="line_5">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>790</y>
+     <width>781</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+  </widget>
+  <widget class="QLabel" name="wavelength_label">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>750</y>
+     <width>131</width>
+     <height>17</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Wavelength</string>
+   </property>
+  </widget>
+  <widget class="TaurusLabel" name="tauruswavelengthvalue">
+   <property name="geometry">
+    <rect>
+     <x>180</x>
+     <y>750</y>
+     <width>148</width>
+     <height>20</height>
+    </rect>
+   </property>
+  </widget>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>TaurusWidget</class>
+   <extends>QWidget</extends>
+   <header>taurus.qt.qtgui.container</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>TaurusLabel</class>
+   <extends>QLabel</extends>
+   <header>taurus.qt.qtgui.display</header>
+  </customwidget>
+  <customwidget>
+   <class>TaurusValueLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>taurus.qt.qtgui.input</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py
index 4fb32d4..6c5ded9 100644
--- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py
+++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py
@@ -204,6 +204,8 @@ class MacroButton(TaurusWidget):
         :param name: (str) text for the button
         '''
         self.macro_name = str(name)
+        #update tooltip
+        self.setToolTip(self.macro_name + ' ' + ' '.join(self.macro_args))
 
     def updateMacroArgument(self, index, value):
         '''change a given argument
@@ -216,6 +218,8 @@ class MacroButton(TaurusWidget):
             self.macro_args.append('')
         #update the given argument
         self.macro_args[index] = str(value)
+        #update tooltip
+        self.setToolTip(self.macro_name + ' ' + ' '.join(self.macro_args))
     
     def updateMacroArgumentFromSignal(self, index, obj, signal):
         '''deprecated'''
diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py
index e073f0e..5b2e24d 100644
--- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py
+++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py
@@ -39,6 +39,7 @@ from taurus.qt.qtgui.display import TaurusLed
 from taurus.qt.qtgui.dialog import TaurusMessageBox
 from taurus.qt.qtgui.resource import getIcon, getThemeIcon
 
+import sardana
 from sardana.taurus.core.tango.sardana import macro
 from sardana.taurus.core.tango.sardana.macro import MacroRunException
 from sardana.taurus.qt.qtgui.extra_macroexecutor.macroparameterseditor import ParamEditorManager, ParamEditorModel, StandardMacroParametersEditor
@@ -103,17 +104,30 @@ class SpockCommandWidget(Qt.QLineEdit, TaurusBaseContainer):
         self.connect(self._ctrlUpAction, Qt.SIGNAL("triggered()"), self.controlUpAction)
 
     def setCommand(self):
-        command = self._model.toSpockCommand()
-        command = command.replace("None", "").strip()
+        command = self._model.toSpockCommand().strip()
         if not self.disableSpockCommandUpdate:
             self.setText(command)
 
+    def onDataChanged(self, idx):
+        """
+        If data is changed to nothing set it to the default value.
+        Otherwise update the spock command and check the validation.
+        This is a workaround for bug-451 that clear all input parameters when an
+        empty string parameter is deselected.
+        """
+        if idx.data() == "":
+            defaultvalue = self._model.nodeFromIndex(idx).defValue()
+            if defaultvalue != "":
+                self._model.setData(idx, defaultvalue)
+        else:
+            self.setCommand()
+
     def setModel(self, model):
         enable = bool(model)
         self.disableEditMode = not enable
         self.setEnabled(enable)
         self._model = model
-        self.connect(self._model, Qt.SIGNAL("dataChanged(QModelIndex,QModelIndex)"), self.setCommand)
+        self.connect(self._model, Qt.SIGNAL("dataChanged(QModelIndex,QModelIndex)"), self.onDataChanged)
         self.connect(self._model, Qt.SIGNAL("modelReset()"), self.setCommand)
 
     def model(self):
@@ -810,6 +824,7 @@ class TaurusMacroExecutorWidget(TaurusWidget):
         door = Device(self.doorName())
         doorState = door.state()
         if doorState == PyTango.DevState.ON or doorState == PyTango.DevState.ALARM:
+            self.setFocus()
             paramEditorModel = self.paramEditorModel()
             macroNode = paramEditorModel.root()
             id = macroNode.assignId()
@@ -984,7 +999,7 @@ def main():
     from taurus.qt.qtgui.application import TaurusApplication
     import taurus
 
-    app = TaurusApplication(sys.argv, app_version=taurus.Release.version)
+    app = TaurusApplication(sys.argv, app_version=sardana.Release.version)
     args = app.get_command_line_args()
 
     app.setOrganizationName("Taurus")
diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/delegate.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/delegate.py
index bdf357c..66d796f 100644
--- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/delegate.py
+++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/delegate.py
@@ -79,7 +79,7 @@ class ParamEditorDelegate(Qt.QStyledItemDelegate):
     def setEditorData(self, editor, index):
         if index.column() == 1:
             text = Qt.from_qvariant(index.model().data(index, Qt.Qt.DisplayRole), str)
-            if text == "None" or text == "":
+            if text == "None" or text == "" or text is None:
                 Qt.QStyledItemDelegate.setEditorData(self, editor, index)
             else:
                 editor.setValue(text)
diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py
index b023958..12bb659 100644
--- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py
+++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroparameterseditor/model.py
@@ -155,7 +155,7 @@ class ParamEditorModel(Qt.QAbstractItemModel):
             if index.column() == 0:
                 return Qt.QVariant(node.name())
             elif index.column() == 1:
-                return Qt.QVariant(node.value())
+                return Qt.QVariant(str(node.value()))
 
         return Qt.QVariant()
 
diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/delegate.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/delegate.py
index c8cbb7d..9b742f6 100644
--- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/delegate.py
+++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/delegate.py
@@ -104,8 +104,8 @@ class MacroParametersProxyDelegate(Qt.QItemDelegate):
     def setEditorData(self, editor, index):
         if index.column() == 1:
             text = Qt.from_qvariant(index.model().data(index, Qt.Qt.DisplayRole), str)
-            if text == "None" or text == "":
-                pass
+            if text in ["None", "", None]:
+                Qt.QItemDelegate.setEditorData(self, editor, index)
             else:
                 node = index.model().mapToSource(index).internalPointer()
                 paramType = node.type()
diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/model.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/model.py
index 6c1ed69..00ed7e5 100644
--- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/model.py
+++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/model.py
@@ -159,7 +159,7 @@ class MacroSequenceTreeModel(Qt.QAbstractItemModel):
             if index.column() == 0:
                 return Qt.QVariant(node.name())
             elif index.column() == 1:
-                return Qt.QVariant(node.value())
+                return Qt.QVariant(str(node.value()))
             elif index.column() == 2:
                 if isinstance(node, macro.MacroNode):
                     return Qt.QVariant(node.progress())
diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py
index 20700f1..7c2e554 100644
--- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py
+++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/sequenceeditor/sequenceeditor.py
@@ -41,6 +41,7 @@ from taurus.qt.qtgui.display import TaurusLed
 from taurus.qt.qtgui.dialog import TaurusMessageBox
 from taurus.qt.qtgui.resource import getIcon, getThemeIcon
 
+import sardana
 from sardana.taurus.qt.qtgui.extra_macroexecutor.common import MacroExecutionWindow, MacroComboBox, standardPlotablesFilter
 from sardana.taurus.qt.qtgui.extra_macroexecutor.macroparameterseditor import ParamEditorManager, StandardMacroParametersEditor
 from sardana.taurus.qt.qtgui.extra_macroexecutor.macroparameterseditor.delegate import ParamEditorDelegate
@@ -862,7 +863,7 @@ def main():
 #    from rfoo.utils import rconsole
 #    rconsole.spawn_server()
 
-    app = TaurusApplication(sys.argv, app_version=taurus.Release.version)
+    app = TaurusApplication(sys.argv, app_version=sardana.Release.version)
     args = app.get_command_line_args()
 
     app.setOrganizationName(globals.ORGANIZATION_NAME)
diff --git a/src/sardana/taurus/qt/qtgui/extra_pool/motor.py b/src/sardana/taurus/qt/qtgui/extra_pool/motor.py
index c5742cb..2d1e59d 100644
--- a/src/sardana/taurus/qt/qtgui/extra_pool/motor.py
+++ b/src/sardana/taurus/qt/qtgui/extra_pool/motor.py
@@ -273,857 +273,6 @@ class TaurusMotorV2(Qt.QWidget, TaurusBaseWidget):
     showText = Qt.pyqtProperty("bool", getShowText, setShowText, resetShowText)
 
 
-###class TaurusMotorH2(Qt.QGroupBox, TaurusBaseWidget):
-###
-###    def __init__(self, parent = None, designMode = False):
-###        name = "TaurusMotorH2"
-###        self._prefix = ''
-###        self._suffix = ''
-###
-###        self.call__init__wo_kw(Qt.QGroupBox, parent)
-###        self.call__init__(TaurusBaseWidget, name, designMode = designMode)
-###
-###        self.setObjectName(name)
-###        self.defineStyle()
-###
-###
-###        ## I CAN NOT INHERIT FROM TAUGROUPBOX !
-###        ## SO ALL THE STUFFABOVE IS NECESSARY
-###        ##
-###        ## ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-###        ##############################################################################
-###        ##
-###        #class TaurusMotorH2(TaurusGroupBox):
-###        #def __init__(self, parent = None):
-###        #self.call__init__wo_kw(TaurusGroupBox,parent)
-###        #self.call__init__(TaurusGroupBox,str(self.objectName()))
-###        self.setupUi()
-###        self.retranslateUi()
-###        self.connect(self.config,Qt.SIGNAL('clicked()'),self.configureMotor)
-###
-###
-###    def configureMotor(self):
-###        Dialog = Qt.QDialog(self)
-###        Dialog.resize((Qt.QSize(Qt.QRect(0,0,310,309).size()).expandedTo(Dialog.minimumSizeHint())))
-###        motorV2 = TaurusMotorV2(Dialog)
-###        motorV2.setModel(self.model)
-###        motorV2.setGeometry(Qt.QRect(10,10,291,291))
-###        Dialog.show()
-###
-###
-###
-###    def minimumSizeHint(self):
-###        return Qt.QSize(211,80)
-###
-###    def sizeHint(self):
-###        return Qt.QSize(211,80)
-###
-###
-###    def setupUi(self):
-###
-###        self.gridlayout = Qt.QGridLayout(self)
-###        self.gridlayout.setObjectName("gridlayout")
-###
-###        self.vboxlayout = Qt.QVBoxLayout()
-###        self.vboxlayout.setObjectName("vboxlayout")
-###
-###        self.hboxlayout = Qt.QHBoxLayout()
-###        self.hboxlayout.setObjectName("hboxlayout")
-###
-###        self.taurusValueLabel_2 = TaurusValueLabel(self)
-###        self.taurusValueLabel_2.setFrameShape(Qt.QFrame.NoFrame)
-###        self.taurusValueLabel_2.setFrameShadow(Qt.QFrame.Plain)
-###        self.taurusValueLabel_2.setShowQuality(False)
-###        self.taurusValueLabel_2.setUseParentModel(True)
-###        self.taurusValueLabel_2.setObjectName("taurusValueLabel_2")
-###        self.hboxlayout.addWidget(self.taurusValueLabel_2)
-###
-###        self.TaurusStateLed_17 = TaurusStateLed(self)
-###        self.TaurusStateLed_17.setUseParentModel(True)
-###        self.TaurusStateLed_17.setObjectName("TaurusStateLed_17")
-###        self.hboxlayout.addWidget(self.TaurusStateLed_17)
-###
-###        spacerItem = Qt.QSpacerItem(40,20,Qt.QSizePolicy.Expanding,Qt.QSizePolicy.Minimum)
-###        self.hboxlayout.addItem(spacerItem)
-###
-###        self.TaurusLimitSwitch = TaurusLimitSwitch(self)
-###        self.TaurusLimitSwitch.setUseParentModel(True)
-###        self.TaurusLimitSwitch.setBoolIndex(2)
-###        self.TaurusLimitSwitch.setObjectName("TaurusLimitSwitch")
-###        self.hboxlayout.addWidget(self.TaurusLimitSwitch)
-###
-###        self.label_3 = Qt.QLabel(self)
-###        self.label_3.setAlignment(Qt.Qt.AlignCenter)
-###        self.label_3.setObjectName("label_3")
-###        self.hboxlayout.addWidget(self.label_3)
-###
-###        self.TaurusLimitSwitch_2 = TaurusLimitSwitch(self)
-###        self.TaurusLimitSwitch_2.setUseParentModel(True)
-###        self.TaurusLimitSwitch_2.setBoolIndex(1)
-###        self.TaurusLimitSwitch_2.setObjectName("TaurusLimitSwitch_2")
-###        self.hboxlayout.addWidget(self.TaurusLimitSwitch_2)
-###        self.vboxlayout.addLayout(self.hboxlayout)
-###
-###        self.hboxlayout1 = Qt.QHBoxLayout()
-###        self.hboxlayout1.setObjectName("hboxlayout1")
-###
-###        self.TaurusValueLineEdit_21 = TaurusValueLineEdit(self)
-###        self.TaurusValueLineEdit_21.setUseParentModel(True)
-###        self.TaurusValueLineEdit_21.setObjectName("TaurusValueLineEdit_21")
-###        self.hboxlayout1.addWidget(self.TaurusValueLineEdit_21)
-###
-###        self.taurusValueLabel_21 = TaurusValueLabel(self)
-###
-###        sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Expanding,Qt.QSizePolicy.Preferred)
-###        sizePolicy.setHorizontalStretch(0)
-###        sizePolicy.setVerticalStretch(0)
-###        sizePolicy.setHeightForWidth(self.taurusValueLabel_21.sizePolicy().hasHeightForWidth())
-###        self.taurusValueLabel_21.setSizePolicy(sizePolicy)
-###        self.taurusValueLabel_21.setUseParentModel(True)
-###        self.taurusValueLabel_21.setObjectName("taurusValueLabel_21")
-###        self.hboxlayout1.addWidget(self.taurusValueLabel_21)
-###
-###        self.taurusConfigLabel_18 = TaurusConfigLabel(self)
-###        self.taurusConfigLabel_18.setMaximumSize(Qt.QSize(27,22))
-###        self.taurusConfigLabel_18.setUseParentModel(True)
-###        self.taurusConfigLabel_18.setObjectName("taurusConfigLabel_18")
-###        self.hboxlayout1.addWidget(self.taurusConfigLabel_18)
-###
-###        self.config = Qt.QToolButton(self)
-###        self.config.setObjectName("config")
-###        self.hboxlayout1.addWidget(self.config)
-###        self.vboxlayout.addLayout(self.hboxlayout1)
-###        self.gridlayout.addLayout(self.vboxlayout,0,0,1,1)
-###
-###
-###    def retranslateUi(self):
-###        self.taurusValueLabel_2.setModel(Qt.QApplication.translate("Form", "/State", None, Qt.QApplication.UnicodeUTF8))
-###        self.TaurusStateLed_17.setModel(Qt.QApplication.translate("Form", "/State", None, Qt.QApplication.UnicodeUTF8))
-###        self.TaurusLimitSwitch.setModel(Qt.QApplication.translate("Form", "/Limit_switches", None, Qt.QApplication.UnicodeUTF8))
-###        self.label_3.setText(Qt.QApplication.translate("Form", "- lim +", None, Qt.QApplication.UnicodeUTF8))
-###        self.TaurusLimitSwitch_2.setModel(Qt.QApplication.translate("Form", "/Limit_switches", None, Qt.QApplication.UnicodeUTF8))
-###        self.TaurusValueLineEdit_21.setModel(Qt.QApplication.translate("Form", "/Position", None, Qt.QApplication.UnicodeUTF8))
-###        self.taurusValueLabel_21.setModel(Qt.QApplication.translate("Form", "/Position", None, Qt.QApplication.UnicodeUTF8))
-###        self.taurusConfigLabel_18.setModel(Qt.QApplication.translate("Form", "/Position?configuration=unit", None, Qt.QApplication.UnicodeUTF8))
-###        self.config.setText(Qt.QApplication.translate("Form", "cfg", None, Qt.QApplication.UnicodeUTF8))
-###
-###
-###    ##############################################################################
-###    ## VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
-###    ##
-###    ## I CAN NOT INHERIT FROM TAUGROUPBOX !
-###    ## SO ALL THE STUFF BELOW IS NECESSARY
-###
-###
-###
-###    def defineStyle(self):
-###        palette = Qt.QPalette()
-###        self.setPalette(palette)
-###        self.updateStyle()
-###
-###    #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-###    # TaurusBaseWidget over writing
-###    #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-###
-###    def getDisplayValue(self):
-###        return (self._prefix or '') + TaurusBaseWidget.getDisplayValue(self) + (self._suffix or '')
-###
-###    def isReadOnly(self):
-###        return True
-###
-###    def updateStyle(self):
-###        if self.getShowQuality():
-###            self.setAutoFillBackground(True)
-###            #TODO: get quality/state from model and update accordingly
-###        else:
-###            self.setAutoFillBackground(False)
-###            #TODO: restore colors
-###        self.update()
-###
-###    #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-###    # QT properties
-###    #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-###
-###    def getPrefixText(self):
-###        return self._prefix
-###
-###    @Qt.pyqtSignature("setPrefixText(QString)")
-###    def setPrefixText(self,prefix):
-###        self._prefix = prefix
-###        self.fireValueChanged()
-###
-###    def getSuffixText(self):
-###        return self._suffix
-###
-###    @Qt.pyqtSignature("setSuffixText(QString)")
-###    def setSuffixText(self,suffix):
-###        self._suffix = suffix
-###        self.fireValueChanged()
-###
-###    model = Qt.pyqtProperty("QString", TaurusBaseWidget.getModel,
-###                                TaurusBaseWidget.setModel, TaurusBaseWidget.resetModel)
-###    useParentModel = Qt.pyqtProperty("bool", TaurusBaseWidget.getUseParentModel,
-###                                         TaurusBaseWidget.setUseParentModel,
-###                                         TaurusBaseWidget.resetUseParentModel)
-###    showQuality = Qt.pyqtProperty("bool", TaurusBaseWidget.getShowQuality,
-###                                      TaurusBaseWidget.setShowQuality,
-###                                      TaurusBaseWidget.resetShowQuality)
-###    showText = Qt.pyqtProperty("bool", TaurusBaseWidget.getShowText,
-###                                   TaurusBaseWidget.setShowText,
-###                                   TaurusBaseWidget.resetShowText)
-###    prefixText = Qt.pyqtProperty("QString", getPrefixText, setPrefixText,
-###                                     doc="prefix text (optional)")
-###    suffixText = Qt.pyqtProperty("QString", getSuffixText, setSuffixText,
-###                                    doc="suffix text (optional)")
-###
-###
-###
-###
-###class TaurusMotorV(Qt.QGroupBox, TaurusBaseWidget):
-###
-###    def __init__(self, parent = None):
-###        name = "TaurusMotorV"
-###        self._prefix = ''
-###        self._suffix = ''
-###
-###        self.call__init__wo_kw(Qt.QGroupBox, parent)
-###        self.call__init__(TaurusBaseWidget, name)
-###
-###        self.setObjectName(name)
-###        self.defineStyle()
-###
-###
-###        ## I CAN NOT INHERIT FROM TAUGROUPBOX !
-###        ## SO ALL THE STUFFABOVE IS NECESSARY
-###        ##
-###        ## ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-###        ##############################################################################
-###        ##
-###        #class TaurusMotorV(TaurusGroupBox):
-###        #def __init__(self, parent = None):
-###        #self.call__init__wo_kw(TaurusGroupBox,parent)
-###        #self.call__init__(TaurusGroupBox,str(self.objectName()))
-###        self.setupUi()
-###        self.retranslateUi()
-###        self.connect(self.config,Qt.SIGNAL('clicked()'),self.configureMotor)
-###
-###
-###    def configureMotor(self):
-###        Dialog = Qt.QDialog(self)
-###        Dialog.resize((Qt.QSize(Qt.QRect(0,0,310,309).size()).expandedTo(Dialog.minimumSizeHint())))
-###        motorV2 = TaurusMotorV2(Dialog)
-###        motorV2.setModel(self.model)
-###        motorV2.setGeometry(Qt.QRect(10,10,291,291))
-###        Dialog.show()
-###
-###
-###
-###
-###    def minimumSizeHint(self):
-###        return Qt.QSize(150,128)
-###
-###    def sizeHint(self):
-###        return Qt.QSize(150,128)
-###
-###    def setupUi(self):
-###
-###        self.gridlayout = Qt.QGridLayout(self)
-###        self.gridlayout.setObjectName("gridlayout")
-###
-###        self.vboxlayout = Qt.QVBoxLayout()
-###        self.vboxlayout.setObjectName("vboxlayout")
-###
-###        self.hboxlayout = Qt.QHBoxLayout()
-###        self.hboxlayout.setObjectName("hboxlayout")
-###
-###        self.taurusValueLabel_2 = TaurusValueLabel(self)
-###        self.taurusValueLabel_2.setFrameShape(Qt.QFrame.NoFrame)
-###        self.taurusValueLabel_2.setFrameShadow(Qt.QFrame.Plain)
-###        self.taurusValueLabel_2.setShowQuality(False)
-###        self.taurusValueLabel_2.setUseParentModel(True)
-###        self.taurusValueLabel_2.setObjectName("taurusValueLabel_2")
-###        self.hboxlayout.addWidget(self.taurusValueLabel_2)
-###
-###        self.TaurusStateLed_17 = TaurusStateLed(self)
-###        self.TaurusStateLed_17.setUseParentModel(True)
-###        self.TaurusStateLed_17.setObjectName("TaurusStateLed_17")
-###        self.hboxlayout.addWidget(self.TaurusStateLed_17)
-###        self.vboxlayout.addLayout(self.hboxlayout)
-###
-###        self.hboxlayout1 = Qt.QHBoxLayout()
-###        self.hboxlayout1.setObjectName("hboxlayout1")
-###
-###        self.TaurusValueLineEdit_21 = TaurusValueLineEdit(self)
-###        self.TaurusValueLineEdit_21.setUseParentModel(True)
-###        self.TaurusValueLineEdit_21.setObjectName("TaurusValueLineEdit_21")
-###        self.hboxlayout1.addWidget(self.TaurusValueLineEdit_21)
-###
-###        self.config = Qt.QToolButton(self)
-###        self.config.setObjectName("config")
-###        self.hboxlayout1.addWidget(self.config)
-###        self.vboxlayout.addLayout(self.hboxlayout1)
-###
-###        self.hboxlayout2 = Qt.QHBoxLayout()
-###        self.hboxlayout2.setObjectName("hboxlayout2")
-###
-###        self.taurusValueLabel_21 = TaurusValueLabel(self)
-###        self.taurusValueLabel_21.setUseParentModel(True)
-###        self.taurusValueLabel_21.setObjectName("taurusValueLabel_21")
-###        self.hboxlayout2.addWidget(self.taurusValueLabel_21)
-###
-###        self.taurusConfigLabel_18 = TaurusConfigLabel(self)
-###        self.taurusConfigLabel_18.setMaximumSize(Qt.QSize(27,22))
-###        self.taurusConfigLabel_18.setUseParentModel(True)
-###        self.taurusConfigLabel_18.setObjectName("taurusConfigLabel_18")
-###        self.hboxlayout2.addWidget(self.taurusConfigLabel_18)
-###        self.vboxlayout.addLayout(self.hboxlayout2)
-###
-###        self.hboxlayout3 = Qt.QHBoxLayout()
-###        self.hboxlayout3.setObjectName("hboxlayout3")
-###
-###        self.TaurusLimitSwitch = TaurusLimitSwitch(self)
-###        self.TaurusLimitSwitch.setUseParentModel(True)
-###        self.TaurusLimitSwitch.setBoolIndex(2)
-###        self.TaurusLimitSwitch.setObjectName("TaurusLimitSwitch")
-###        self.hboxlayout3.addWidget(self.TaurusLimitSwitch)
-###
-###        self.label_3 = Qt.QLabel(self)
-###        self.label_3.setAlignment(Qt.Qt.AlignCenter)
-###        self.label_3.setObjectName("label_3")
-###        self.hboxlayout3.addWidget(self.label_3)
-###
-###        self.TaurusLimitSwitch_2 = TaurusLimitSwitch(self)
-###        self.TaurusLimitSwitch_2.setUseParentModel(True)
-###        self.TaurusLimitSwitch_2.setBoolIndex(1)
-###        self.TaurusLimitSwitch_2.setObjectName("TaurusLimitSwitch_2")
-###        self.hboxlayout3.addWidget(self.TaurusLimitSwitch_2)
-###        self.vboxlayout.addLayout(self.hboxlayout3)
-###        self.gridlayout.addLayout(self.vboxlayout,0,0,1,1)
-###
-###
-###    def retranslateUi(self):
-###        self.taurusValueLabel_2.setModel(Qt.QApplication.translate("Form", "/State", None, Qt.QApplication.UnicodeUTF8))
-###        self.TaurusStateLed_17.setModel(Qt.QApplication.translate("Form", "/State", None, Qt.QApplication.UnicodeUTF8))
-###        self.TaurusValueLineEdit_21.setModel(Qt.QApplication.translate("Form", "/Position", None, Qt.QApplication.UnicodeUTF8))
-###        self.config.setText(Qt.QApplication.translate("Form", "cfg", None, Qt.QApplication.UnicodeUTF8))
-###        self.taurusValueLabel_21.setModel(Qt.QApplication.translate("Form", "/Position", None, Qt.QApplication.UnicodeUTF8))
-###        self.taurusConfigLabel_18.setModel(Qt.QApplication.translate("Form", "/Position?configuration=unit", None, Qt.QApplication.UnicodeUTF8))
-###        self.TaurusLimitSwitch.setModel(Qt.QApplication.translate("Form", "/Limit_switches", None, Qt.QApplication.UnicodeUTF8))
-###        self.label_3.setText(Qt.QApplication.translate("Form", "- lim +", None, Qt.QApplication.UnicodeUTF8))
-###        self.TaurusLimitSwitch_2.setModel(Qt.QApplication.translate("Form", "/Limit_switches", None, Qt.QApplication.UnicodeUTF8))
-###
-###    ##############################################################################
-###    ## VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
-###    ##
-###    ## I CAN NOT INHERIT FROM TAUGROUPBOX !
-###    ## SO ALL THE STUFF BELOW IS NECESSARY
-###
-###
-###
-###    def defineStyle(self):
-###        palette = Qt.QPalette()
-###        self.setPalette(palette)
-###        self.updateStyle()
-###
-###    #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-###    # TaurusBaseWidget over writing
-###    #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-###
-###    def getDisplayValue(self):
-###        return (self._prefix or '') + TaurusBaseWidget.getDisplayValue(self) + (self._suffix or '')
-###
-###    def isReadOnly(self):
-###        return True
-###
-###    def updateStyle(self):
-###        if self.getShowQuality():
-###            self.setAutoFillBackground(True)
-###            #TODO: get quality/state from model and update accordingly
-###        else:
-###            self.setAutoFillBackground(False)
-###            #TODO: restore colors
-###        self.update()
-###
-###    #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-###    # QT properties
-###    #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-###
-###    def getPrefixText(self):
-###        return self._prefix
-###
-###    @Qt.pyqtSignature("setPrefixText(QString)")
-###    def setPrefixText(self,prefix):
-###        self._prefix = prefix
-###        self.fireValueChanged()
-###
-###    def getSuffixText(self):
-###        return self._suffix
-###
-###    @Qt.pyqtSignature("setSuffixText(QString)")
-###    def setSuffixText(self,suffix):
-###        self._suffix = suffix
-###        self.fireValueChanged()
-###
-###    model = Qt.pyqtProperty("QString", TaurusBaseWidget.getModel,
-###                                TaurusBaseWidget.setModel, TaurusBaseWidget.resetModel)
-###    useParentModel = Qt.pyqtProperty("bool", TaurusBaseWidget.getUseParentModel,
-###                                         TaurusBaseWidget.setUseParentModel,
-###                                         TaurusBaseWidget.resetUseParentModel)
-###    showQuality = Qt.pyqtProperty("bool", TaurusBaseWidget.getShowQuality,
-###                                      TaurusBaseWidget.setShowQuality,
-###                                      TaurusBaseWidget.resetShowQuality)
-###    showText = Qt.pyqtProperty("bool", TaurusBaseWidget.getShowText,
-###                                   TaurusBaseWidget.setShowText,
-###                                   TaurusBaseWidget.resetShowText)
-###    prefixText = Qt.pyqtProperty("QString", getPrefixText, setPrefixText,
-###                                     doc="prefix text (optional)")
-###    suffixText = Qt.pyqtProperty("QString", getSuffixText, setSuffixText,
-###                                    doc="suffix text (optional)")
-###
-###
-###
-###class TaurusMotorV2(Qt.QGroupBox, TaurusBaseWidget):
-###
-###    def __init__(self, parent = None):
-###        name = "TaurusMotorV2"
-###        self._prefix = ''
-###        self._suffix = ''
-###
-###        self.call__init__wo_kw(Qt.QGroupBox, parent)
-###        self.call__init__(TaurusBaseWidget, name)
-###
-###        self.setObjectName(name)
-###        self.defineStyle()
-###
-###
-###        ## I CAN NOT INHERIT FROM TAUGROUPBOX !
-###        ## SO ALL THE STUFFABOVE IS NECESSARY
-###        ##
-###        ## ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-###        ##############################################################################
-###        ##
-###        #class TaurusMotorV2(TaurusGroupBox):
-###        #def __init__(self, parent = None):
-###        #self.call__init__wo_kw(TaurusGroupBox,parent)
-###        #self.call__init__(TaurusGroupBox,str(self.objectName()))
-###        self.setupUi()
-###        self.retranslateUi()
-###
-###
-###    def minimumSizeHint(self):
-###        return Qt.QSize(260,270)
-###
-###    def sizeHint(self):
-###        return Qt.QSize(260,270)
-###
-###
-###    def setupUi(self):
-###
-###        self.gridlayout = Qt.QGridLayout(self)
-###        self.gridlayout.setObjectName("gridlayout")
-###
-###        self.m1StateLed_2 = TaurusStateLed(self)
-###        self.m1StateLed_2.setLedSize(24)
-###        self.m1StateLed_2.setUseParentModel(True)
-###        self.m1StateLed_2.setObjectName("m1StateLed_2")
-###        self.gridlayout.addWidget(self.m1StateLed_2,0,0,1,1)
-###
-###        self.taurusValueLabel = TaurusValueLabel(self)
-###        self.taurusValueLabel.setFrameShape(Qt.QFrame.NoFrame)
-###        self.taurusValueLabel.setFrameShadow(Qt.QFrame.Plain)
-###        self.taurusValueLabel.setShowQuality(False)
-###        self.taurusValueLabel.setUseParentModel(True)
-###        self.taurusValueLabel.setObjectName("taurusValueLabel")
-###        self.gridlayout.addWidget(self.taurusValueLabel,0,1,1,1)
-###
-###        self.TaurusLimitSwitch = TaurusLimitSwitch(self)
-###        self.TaurusLimitSwitch.setUseParentModel(True)
-###        self.TaurusLimitSwitch.setBoolIndex(2)
-###        self.TaurusLimitSwitch.setObjectName("TaurusLimitSwitch")
-###        self.gridlayout.addWidget(self.TaurusLimitSwitch,0,3,1,1)
-###
-###        self.TaurusLimitSwitch_2 = TaurusLimitSwitch(self)
-###        self.TaurusLimitSwitch_2.setUseParentModel(True)
-###        self.TaurusLimitSwitch_2.setBoolIndex(1)
-###        self.TaurusLimitSwitch_2.setObjectName("TaurusLimitSwitch_2")
-###        self.gridlayout.addWidget(self.TaurusLimitSwitch_2,0,4,1,1)
-###
-###        self.m1PositionLabel_2 = TaurusConfigLabel(self)
-###        self.m1PositionLabel_2.setUseParentModel(True)
-###        self.m1PositionLabel_2.setObjectName("m1PositionLabel_2")
-###        self.gridlayout.addWidget(self.m1PositionLabel_2,1,0,1,2)
-###
-###        self.m1PositionEdit_2 = TaurusValueLineEdit(self)
-###
-###        sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed,Qt.QSizePolicy.Fixed)
-###        sizePolicy.setHorizontalStretch(0)
-###        sizePolicy.setVerticalStretch(0)
-###        sizePolicy.setHeightForWidth(self.m1PositionEdit_2.sizePolicy().hasHeightForWidth())
-###        self.m1PositionEdit_2.setSizePolicy(sizePolicy)
-###        self.m1PositionEdit_2.setUseParentModel(True)
-###        self.m1PositionEdit_2.setObjectName("m1PositionEdit_2")
-###        self.gridlayout.addWidget(self.m1PositionEdit_2,1,2,1,1)
-###
-###        self.m1Position_2 = TaurusValueLabel(self)
-###        self.m1Position_2.setUseParentModel(True)
-###        self.m1Position_2.setObjectName("m1Position_2")
-###        self.gridlayout.addWidget(self.m1Position_2,1,3,1,2)
-###
-###        self.m1PositionUnitLabel_2 = TaurusConfigLabel(self)
-###
-###        sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Expanding,Qt.QSizePolicy.Preferred)
-###        sizePolicy.setHorizontalStretch(0)
-###        sizePolicy.setVerticalStretch(0)
-###        sizePolicy.setHeightForWidth(self.m1PositionUnitLabel_2.sizePolicy().hasHeightForWidth())
-###        self.m1PositionUnitLabel_2.setSizePolicy(sizePolicy)
-###        self.m1PositionUnitLabel_2.setUseParentModel(True)
-###        self.m1PositionUnitLabel_2.setObjectName("m1PositionUnitLabel_2")
-###        self.gridlayout.addWidget(self.m1PositionUnitLabel_2,1,5,1,1)
-###
-###        self.m1VelocityLabel_2 = TaurusConfigLabel(self)
-###        self.m1VelocityLabel_2.setUseParentModel(True)
-###        self.m1VelocityLabel_2.setObjectName("m1VelocityLabel_2")
-###        self.gridlayout.addWidget(self.m1VelocityLabel_2,2,0,1,2)
-###
-###        self.m1VelocityEdit_2 = TaurusValueLineEdit(self)
-###
-###        sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed,Qt.QSizePolicy.Fixed)
-###        sizePolicy.setHorizontalStretch(0)
-###        sizePolicy.setVerticalStretch(0)
-###        sizePolicy.setHeightForWidth(self.m1VelocityEdit_2.sizePolicy().hasHeightForWidth())
-###        self.m1VelocityEdit_2.setSizePolicy(sizePolicy)
-###        self.m1VelocityEdit_2.setUseParentModel(True)
-###        self.m1VelocityEdit_2.setObjectName("m1VelocityEdit_2")
-###        self.gridlayout.addWidget(self.m1VelocityEdit_2,2,2,1,1)
-###
-###        self.m1Velocity_2 = TaurusValueLabel(self)
-###        self.m1Velocity_2.setUseParentModel(True)
-###        self.m1Velocity_2.setObjectName("m1Velocity_2")
-###        self.gridlayout.addWidget(self.m1Velocity_2,2,3,1,2)
-###
-###        self.m1VelocityUnitLabel_2 = TaurusConfigLabel(self)
-###
-###        sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Expanding,Qt.QSizePolicy.Preferred)
-###        sizePolicy.setHorizontalStretch(0)
-###        sizePolicy.setVerticalStretch(0)
-###        sizePolicy.setHeightForWidth(self.m1VelocityUnitLabel_2.sizePolicy().hasHeightForWidth())
-###        self.m1VelocityUnitLabel_2.setSizePolicy(sizePolicy)
-###        self.m1VelocityUnitLabel_2.setUseParentModel(True)
-###        self.m1VelocityUnitLabel_2.setObjectName("m1VelocityUnitLabel_2")
-###        self.gridlayout.addWidget(self.m1VelocityUnitLabel_2,2,5,1,1)
-###
-###        self.m1PositionLabel_4 = TaurusConfigLabel(self)
-###        self.m1PositionLabel_4.setUseParentModel(True)
-###        self.m1PositionLabel_4.setObjectName("m1PositionLabel_4")
-###        self.gridlayout.addWidget(self.m1PositionLabel_4,3,0,1,2)
-###
-###        self.m1PositionEdit_4 = TaurusValueLineEdit(self)
-###
-###        sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed,Qt.QSizePolicy.Fixed)
-###        sizePolicy.setHorizontalStretch(0)
-###        sizePolicy.setVerticalStretch(0)
-###        sizePolicy.setHeightForWidth(self.m1PositionEdit_4.sizePolicy().hasHeightForWidth())
-###        self.m1PositionEdit_4.setSizePolicy(sizePolicy)
-###        self.m1PositionEdit_4.setUseParentModel(True)
-###        self.m1PositionEdit_4.setObjectName("m1PositionEdit_4")
-###        self.gridlayout.addWidget(self.m1PositionEdit_4,3,2,1,1)
-###
-###        self.m1Position_4 = TaurusValueLabel(self)
-###        self.m1Position_4.setUseParentModel(True)
-###        self.m1Position_4.setObjectName("m1Position_4")
-###        self.gridlayout.addWidget(self.m1Position_4,3,3,1,2)
-###
-###        self.m1PositionUnitLabel_4 = TaurusConfigLabel(self)
-###
-###        sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Expanding,Qt.QSizePolicy.Preferred)
-###        sizePolicy.setHorizontalStretch(0)
-###        sizePolicy.setVerticalStretch(0)
-###        sizePolicy.setHeightForWidth(self.m1PositionUnitLabel_4.sizePolicy().hasHeightForWidth())
-###        self.m1PositionUnitLabel_4.setSizePolicy(sizePolicy)
-###        self.m1PositionUnitLabel_4.setUseParentModel(True)
-###        self.m1PositionUnitLabel_4.setObjectName("m1PositionUnitLabel_4")
-###        self.gridlayout.addWidget(self.m1PositionUnitLabel_4,3,5,1,1)
-###
-###        self.m1AccelerationLabel_2 = TaurusConfigLabel(self)
-###        self.m1AccelerationLabel_2.setUseParentModel(True)
-###        self.m1AccelerationLabel_2.setObjectName("m1AccelerationLabel_2")
-###        self.gridlayout.addWidget(self.m1AccelerationLabel_2,4,0,1,2)
-###
-###        self.m1AccelerationEdit_2 = TaurusValueLineEdit(self)
-###
-###        sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed,Qt.QSizePolicy.Fixed)
-###        sizePolicy.setHorizontalStretch(0)
-###        sizePolicy.setVerticalStretch(0)
-###        sizePolicy.setHeightForWidth(self.m1AccelerationEdit_2.sizePolicy().hasHeightForWidth())
-###        self.m1AccelerationEdit_2.setSizePolicy(sizePolicy)
-###        self.m1AccelerationEdit_2.setUseParentModel(True)
-###        self.m1AccelerationEdit_2.setObjectName("m1AccelerationEdit_2")
-###        self.gridlayout.addWidget(self.m1AccelerationEdit_2,4,2,1,1)
-###
-###        self.m1Acceleration_2 = TaurusValueLabel(self)
-###        self.m1Acceleration_2.setUseParentModel(True)
-###        self.m1Acceleration_2.setObjectName("m1Acceleration_2")
-###        self.gridlayout.addWidget(self.m1Acceleration_2,4,3,1,2)
-###
-###        self.m1AccelerationUnitLabel_2 = TaurusConfigLabel(self)
-###
-###        sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Expanding,Qt.QSizePolicy.Preferred)
-###        sizePolicy.setHorizontalStretch(0)
-###        sizePolicy.setVerticalStretch(0)
-###        sizePolicy.setHeightForWidth(self.m1AccelerationUnitLabel_2.sizePolicy().hasHeightForWidth())
-###        self.m1AccelerationUnitLabel_2.setSizePolicy(sizePolicy)
-###        self.m1AccelerationUnitLabel_2.setUseParentModel(True)
-###        self.m1AccelerationUnitLabel_2.setObjectName("m1AccelerationUnitLabel_2")
-###        self.gridlayout.addWidget(self.m1AccelerationUnitLabel_2,4,5,1,1)
-###
-###        self.m1PositionLabel_5 = TaurusConfigLabel(self)
-###        self.m1PositionLabel_5.setUseParentModel(True)
-###        self.m1PositionLabel_5.setObjectName("m1PositionLabel_5")
-###        self.gridlayout.addWidget(self.m1PositionLabel_5,5,0,1,2)
-###
-###        self.m1PositionEdit_5 = TaurusValueLineEdit(self)
-###
-###        sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed,Qt.QSizePolicy.Fixed)
-###        sizePolicy.setHorizontalStretch(0)
-###        sizePolicy.setVerticalStretch(0)
-###        sizePolicy.setHeightForWidth(self.m1PositionEdit_5.sizePolicy().hasHeightForWidth())
-###        self.m1PositionEdit_5.setSizePolicy(sizePolicy)
-###        self.m1PositionEdit_5.setUseParentModel(True)
-###        self.m1PositionEdit_5.setObjectName("m1PositionEdit_5")
-###        self.gridlayout.addWidget(self.m1PositionEdit_5,5,2,1,1)
-###
-###        self.m1Position_5 = TaurusValueLabel(self)
-###        self.m1Position_5.setUseParentModel(True)
-###        self.m1Position_5.setObjectName("m1Position_5")
-###        self.gridlayout.addWidget(self.m1Position_5,5,3,1,2)
-###
-###        self.m1PositionUnitLabel_5 = TaurusConfigLabel(self)
-###
-###        sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Expanding,Qt.QSizePolicy.Preferred)
-###        sizePolicy.setHorizontalStretch(0)
-###        sizePolicy.setVerticalStretch(0)
-###        sizePolicy.setHeightForWidth(self.m1PositionUnitLabel_5.sizePolicy().hasHeightForWidth())
-###        self.m1PositionUnitLabel_5.setSizePolicy(sizePolicy)
-###        self.m1PositionUnitLabel_5.setUseParentModel(True)
-###        self.m1PositionUnitLabel_5.setObjectName("m1PositionUnitLabel_5")
-###        self.gridlayout.addWidget(self.m1PositionUnitLabel_5,5,5,1,1)
-###
-###        self.m1DecelerationLabel_2 = TaurusConfigLabel(self)
-###        self.m1DecelerationLabel_2.setUseParentModel(True)
-###        self.m1DecelerationLabel_2.setObjectName("m1DecelerationLabel_2")
-###        self.gridlayout.addWidget(self.m1DecelerationLabel_2,6,0,1,2)
-###
-###        self.m1DecelerationEdit_2 = TaurusValueLineEdit(self)
-###
-###        sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed,Qt.QSizePolicy.Fixed)
-###        sizePolicy.setHorizontalStretch(0)
-###        sizePolicy.setVerticalStretch(0)
-###        sizePolicy.setHeightForWidth(self.m1DecelerationEdit_2.sizePolicy().hasHeightForWidth())
-###        self.m1DecelerationEdit_2.setSizePolicy(sizePolicy)
-###        self.m1DecelerationEdit_2.setUseParentModel(True)
-###        self.m1DecelerationEdit_2.setObjectName("m1DecelerationEdit_2")
-###        self.gridlayout.addWidget(self.m1DecelerationEdit_2,6,2,1,1)
-###
-###        self.m1Deceleration_2 = TaurusValueLabel(self)
-###        self.m1Deceleration_2.setUseParentModel(True)
-###        self.m1Deceleration_2.setObjectName("m1Deceleration_2")
-###        self.gridlayout.addWidget(self.m1Deceleration_2,6,3,1,2)
-###
-###        self.m1DecelerationUnitLabel_2 = TaurusConfigLabel(self)
-###
-###        sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Expanding,Qt.QSizePolicy.Preferred)
-###        sizePolicy.setHorizontalStretch(0)
-###        sizePolicy.setVerticalStretch(0)
-###        sizePolicy.setHeightForWidth(self.m1DecelerationUnitLabel_2.sizePolicy().hasHeightForWidth())
-###        self.m1DecelerationUnitLabel_2.setSizePolicy(sizePolicy)
-###        self.m1DecelerationUnitLabel_2.setUseParentModel(True)
-###        self.m1DecelerationUnitLabel_2.setObjectName("m1DecelerationUnitLabel_2")
-###        self.gridlayout.addWidget(self.m1DecelerationUnitLabel_2,6,5,1,1)
-###
-###        self.m1PositionLabel_6 = TaurusConfigLabel(self)
-###        self.m1PositionLabel_6.setUseParentModel(True)
-###        self.m1PositionLabel_6.setObjectName("m1PositionLabel_6")
-###        self.gridlayout.addWidget(self.m1PositionLabel_6,7,0,1,2)
-###
-###        self.m1PositionEdit_6 = TaurusValueLineEdit(self)
-###
-###        sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed,Qt.QSizePolicy.Fixed)
-###        sizePolicy.setHorizontalStretch(0)
-###        sizePolicy.setVerticalStretch(0)
-###        sizePolicy.setHeightForWidth(self.m1PositionEdit_6.sizePolicy().hasHeightForWidth())
-###        self.m1PositionEdit_6.setSizePolicy(sizePolicy)
-###        self.m1PositionEdit_6.setUseParentModel(True)
-###        self.m1PositionEdit_6.setObjectName("m1PositionEdit_6")
-###        self.gridlayout.addWidget(self.m1PositionEdit_6,7,2,1,1)
-###
-###        self.m1Position_6 = TaurusValueLabel(self)
-###        self.m1Position_6.setUseParentModel(True)
-###        self.m1Position_6.setObjectName("m1Position_6")
-###        self.gridlayout.addWidget(self.m1Position_6,7,3,1,2)
-###
-###        self.m1PositionUnitLabel_6 = TaurusConfigLabel(self)
-###
-###        sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Expanding,Qt.QSizePolicy.Preferred)
-###        sizePolicy.setHorizontalStretch(0)
-###        sizePolicy.setVerticalStretch(0)
-###        sizePolicy.setHeightForWidth(self.m1PositionUnitLabel_6.sizePolicy().hasHeightForWidth())
-###        self.m1PositionUnitLabel_6.setSizePolicy(sizePolicy)
-###        self.m1PositionUnitLabel_6.setUseParentModel(True)
-###        self.m1PositionUnitLabel_6.setObjectName("m1PositionUnitLabel_6")
-###        self.gridlayout.addWidget(self.m1PositionUnitLabel_6,7,5,1,1)
-###
-###        self.m1StepPerUnitLabel_2 = TaurusConfigLabel(self)
-###        self.m1StepPerUnitLabel_2.setUseParentModel(True)
-###        self.m1StepPerUnitLabel_2.setObjectName("m1StepPerUnitLabel_2")
-###        self.gridlayout.addWidget(self.m1StepPerUnitLabel_2,8,0,1,2)
-###
-###        self.m1StepPerUnitEdit_2 = TaurusValueLineEdit(self)
-###
-###        sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed,Qt.QSizePolicy.Fixed)
-###        sizePolicy.setHorizontalStretch(0)
-###        sizePolicy.setVerticalStretch(0)
-###        sizePolicy.setHeightForWidth(self.m1StepPerUnitEdit_2.sizePolicy().hasHeightForWidth())
-###        self.m1StepPerUnitEdit_2.setSizePolicy(sizePolicy)
-###        self.m1StepPerUnitEdit_2.setUseParentModel(True)
-###        self.m1StepPerUnitEdit_2.setObjectName("m1StepPerUnitEdit_2")
-###        self.gridlayout.addWidget(self.m1StepPerUnitEdit_2,8,2,1,1)
-###
-###        self.m1StepPerUnit_2 = TaurusValueLabel(self)
-###        self.m1StepPerUnit_2.setUseParentModel(True)
-###        self.m1StepPerUnit_2.setObjectName("m1StepPerUnit_2")
-###        self.gridlayout.addWidget(self.m1StepPerUnit_2,8,3,1,2)
-###
-###        self.m1StepPerUnitUnitLabel_2 = TaurusConfigLabel(self)
-###
-###        sizePolicy = Qt.QSizePolicy(Qt.QSizePolicy.Expanding,Qt.QSizePolicy.Preferred)
-###        sizePolicy.setHorizontalStretch(0)
-###        sizePolicy.setVerticalStretch(0)
-###        sizePolicy.setHeightForWidth(self.m1StepPerUnitUnitLabel_2.sizePolicy().hasHeightForWidth())
-###        self.m1StepPerUnitUnitLabel_2.setSizePolicy(sizePolicy)
-###        self.m1StepPerUnitUnitLabel_2.setUseParentModel(True)
-###        self.m1StepPerUnitUnitLabel_2.setObjectName("m1StepPerUnitUnitLabel_2")
-###        self.gridlayout.addWidget(self.m1StepPerUnitUnitLabel_2,8,5,1,1)
-###
-###
-###    def retranslateUi(self):
-###        self.m1StateLed_2.setModel(Qt.QApplication.translate("Dialog", "/State", None, Qt.QApplication.UnicodeUTF8))
-###        self.taurusValueLabel.setModel(Qt.QApplication.translate("Dialog", "/State", None, Qt.QApplication.UnicodeUTF8))
-###        self.TaurusLimitSwitch.setLedColor(Qt.QApplication.translate("Dialog", "ORANGE", None, Qt.QApplication.UnicodeUTF8))
-###        self.TaurusLimitSwitch.setModel(Qt.QApplication.translate("Dialog", "/Limit_switches", None, Qt.QApplication.UnicodeUTF8))
-###        self.TaurusLimitSwitch_2.setLedColor(Qt.QApplication.translate("Dialog", "ORANGE", None, Qt.QApplication.UnicodeUTF8))
-###        self.TaurusLimitSwitch_2.setModel(Qt.QApplication.translate("Dialog", "/Limit_switches", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1PositionLabel_2.setSuffixText(Qt.QApplication.translate("Dialog", ":", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1PositionLabel_2.setModel(Qt.QApplication.translate("Dialog", "/Position?configuration=label", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1PositionEdit_2.setModel(Qt.QApplication.translate("Dialog", "/Position", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1Position_2.setModel(Qt.QApplication.translate("Dialog", "/Position", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1PositionUnitLabel_2.setModel(Qt.QApplication.translate("Dialog", "/Position?configuration=unit", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1VelocityLabel_2.setSuffixText(Qt.QApplication.translate("Dialog", ":", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1VelocityLabel_2.setModel(Qt.QApplication.translate("Dialog", "/Velocity?configuration=label", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1VelocityEdit_2.setModel(Qt.QApplication.translate("Dialog", "/Velocity", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1Velocity_2.setModel(Qt.QApplication.translate("Dialog", "/Velocity", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1VelocityUnitLabel_2.setModel(Qt.QApplication.translate("Dialog", "/Velocity?configuration=unit", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1PositionLabel_4.setSuffixText(Qt.QApplication.translate("Dialog", ":", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1PositionLabel_4.setModel(Qt.QApplication.translate("Dialog", "/Acceleration?configuration=label", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1PositionEdit_4.setModel(Qt.QApplication.translate("Dialog", "/Acceleration", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1Position_4.setModel(Qt.QApplication.translate("Dialog", "/Acceleration", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1PositionUnitLabel_4.setModel(Qt.QApplication.translate("Dialog", "/Acceleration?configuration=unit", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1AccelerationLabel_2.setSuffixText(Qt.QApplication.translate("Dialog", ":", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1AccelerationLabel_2.setModel(Qt.QApplication.translate("Dialog", "/Deceleration?configuration=label", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1AccelerationEdit_2.setModel(Qt.QApplication.translate("Dialog", "/Deceleration", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1Acceleration_2.setModel(Qt.QApplication.translate("Dialog", "/Deceleration", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1AccelerationUnitLabel_2.setModel(Qt.QApplication.translate("Dialog", "/Deceleration?configuration=unit", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1PositionLabel_5.setSuffixText(Qt.QApplication.translate("Dialog", ":", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1PositionLabel_5.setModel(Qt.QApplication.translate("Dialog", "/Offset?configuration=label", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1PositionEdit_5.setModel(Qt.QApplication.translate("Dialog", "/Offset", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1Position_5.setModel(Qt.QApplication.translate("Dialog", "/Offset", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1PositionUnitLabel_5.setModel(Qt.QApplication.translate("Dialog", "/Offset?configuration=unit", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1DecelerationLabel_2.setSuffixText(Qt.QApplication.translate("Dialog", ":", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1DecelerationLabel_2.setModel(Qt.QApplication.translate("Dialog", "/Base_rate?configuration=label", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1DecelerationEdit_2.setModel(Qt.QApplication.translate("Dialog", "/Base_rate", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1Deceleration_2.setModel(Qt.QApplication.translate("Dialog", "/Base_rate", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1DecelerationUnitLabel_2.setModel(Qt.QApplication.translate("Dialog", "/Base_rate?configuration=unit", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1PositionLabel_6.setSuffixText(Qt.QApplication.translate("Dialog", ":", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1PositionLabel_6.setModel(Qt.QApplication.translate("Dialog", "/Step_per_unit?configuration=label", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1PositionEdit_6.setModel(Qt.QApplication.translate("Dialog", "/Step_per_unit", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1Position_6.setModel(Qt.QApplication.translate("Dialog", "/Step_per_unit", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1PositionUnitLabel_6.setModel(Qt.QApplication.translate("Dialog", "/Step_per_unit?configuration=unit", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1StepPerUnitLabel_2.setSuffixText(Qt.QApplication.translate("Dialog", ":", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1StepPerUnitLabel_2.setModel(Qt.QApplication.translate("Dialog", "/Backlash?configuration=label", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1StepPerUnitEdit_2.setModel(Qt.QApplication.translate("Dialog", "/Backlash", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1StepPerUnit_2.setModel(Qt.QApplication.translate("Dialog", "/Backlash", None, Qt.QApplication.UnicodeUTF8))
-###        self.m1StepPerUnitUnitLabel_2.setModel(Qt.QApplication.translate("Dialog", "/Backlash?configuration=unit", None, Qt.QApplication.UnicodeUTF8))
-###
-###    ##############################################################################
-###    ## VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
-###    ##
-###    ## I CAN NOT INHERIT FROM TAUGROUPBOX !
-###    ## SO ALL THE STUFF BELOW IS NECESSARY
-###
-###
-###
-###    def defineStyle(self):
-###        palette = Qt.QPalette()
-###        self.setPalette(palette)
-###        self.updateStyle()
-###
-###    #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-###    # TaurusBaseWidget over writing
-###    #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-###
-###    def getDisplayValue(self):
-###        return (self._prefix or '') + TaurusBaseWidget.getDisplayValue(self) + (self._suffix or '')
-###
-###    def isReadOnly(self):
-###        return True
-###
-###    def updateStyle(self):
-###        if self.getShowQuality():
-###            self.setAutoFillBackground(True)
-###            #TODO: get quality/state from model and update accordingly
-###        else:
-###            self.setAutoFillBackground(False)
-###            #TODO: restore colors
-###        self.update()
-###
-###    #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-###    # QT properties
-###    #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
-###
-###    def getPrefixText(self):
-###        return self._prefix
-###
-###    @Qt.pyqtSignature("setPrefixText(QString)")
-###    def setPrefixText(self,prefix):
-###        self._prefix = prefix
-###        self.fireValueChanged()
-###
-###    def getSuffixText(self):
-###        return self._suffix
-###
-###    @Qt.pyqtSignature("setSuffixText(QString)")
-###    def setSuffixText(self,suffix):
-###        self._suffix = suffix
-###        self.fireValueChanged()
-###
-###    model = Qt.pyqtProperty("QString", TaurusBaseWidget.getModel,
-###                                TaurusBaseWidget.setModel, TaurusBaseWidget.resetModel)
-###    useParentModel = Qt.pyqtProperty("bool", TaurusBaseWidget.getUseParentModel,
-###                                         TaurusBaseWidget.setUseParentModel,
-###                                         TaurusBaseWidget.resetUseParentModel)
-###    showQuality = Qt.pyqtProperty("bool", TaurusBaseWidget.getShowQuality,
-###                                      TaurusBaseWidget.setShowQuality,
-###                                      TaurusBaseWidget.resetShowQuality)
-###    showText = Qt.pyqtProperty("bool", TaurusBaseWidget.getShowText,
-###                                   TaurusBaseWidget.setShowText,
-###                                   TaurusBaseWidget.resetShowText)
-###    prefixText = Qt.pyqtProperty("QString", getPrefixText, setPrefixText,
-###                                     doc="prefix text (optional)")
-###    suffixText = Qt.pyqtProperty("QString", getSuffixText, setSuffixText,
-###                                    doc="suffix text (optional)")
-###
-###
-###
-
 if __name__ == "__main__":
 
     import sys
diff --git a/src/sardana/test/testsuite.py b/src/sardana/test/testsuite.py
index 96d195b..873ea89 100644
--- a/src/sardana/test/testsuite.py
+++ b/src/sardana/test/testsuite.py
@@ -24,7 +24,7 @@
 ##############################################################################
 
 """
-This module defines the test suite for the whole Sardana package
+This module defines the test suite for the whole sardana package
 Usage::
 
   from sardana.test import testsuite
@@ -40,14 +40,15 @@ import sardana
 
 
 def run():
-    '''Runs all tests for the taurus package
+    '''Runs all tests for the sardana package
 
     :returns: the test runner result
     :rtype: unittest.result.TestResult
     '''
     # discover all tests within the sardana/src directory
     loader = unittest.defaultTestLoader
-    suite = loader.discover(os.path.dirname(sardana.__file__))
+    start_dir = os.path.dirname(sardana.__file__)
+    suite = loader.discover(start_dir, top_level_dir=os.path.dirname(start_dir))
     # use the basic text test runner that outputs to sys.stderr
     runner = unittest.TextTestRunner(descriptions=True, verbosity=2)
     # run the test suite
diff --git a/src/sardana/tools/config/sardana.py b/src/sardana/tools/config/sardana.py
index 6dc0338..89d2e2f 100644
--- a/src/sardana/tools/config/sardana.py
+++ b/src/sardana/tools/config/sardana.py
@@ -38,7 +38,7 @@ import logging
 import types, operator
 import json
 
-from taurus.core.util.codecs import CodecFactory
+from taurus.core.util import CodecFactory
 
 LOG = logging.getLogger()
 
@@ -821,7 +821,7 @@ class DevicePoolServer(TangoServer):
                             continue
                         self.o(".")
                         #pars = [ [axis], [ aliasName, name ] ]
-                        pars = "Motor", name, axis, aliasName
+                        pars = ["Motor", name, axis, aliasName]
                         if deviceName.count('/') == 2:
                             pars.append(deviceName)
                         pars = map(str.strip, pars)
@@ -856,7 +856,7 @@ class DevicePoolServer(TangoServer):
                             self.o('x')
                             continue
                         self.o(".")
-                        pars = "CTExpChannel", name, axis, aliasName
+                        pars = ["CTExpChannel", name, axis, aliasName]
                         if deviceName.count('/') == 2:
                             pars.append(deviceName)
                         pars = map(str.strip, pars)
@@ -891,7 +891,7 @@ class DevicePoolServer(TangoServer):
                             self.o('x')
                             continue
                         self.o(".")
-                        pars = "ZeroDExpChannel", name, axis, aliasName
+                        pars = ["ZeroDExpChannel", name, axis, aliasName]
                         if deviceName.count('/') == 2:
                             pars.append(deviceName)
                         pars = map(str.strip, pars)
@@ -929,7 +929,7 @@ class DevicePoolServer(TangoServer):
                             self.o('x')
                             continue
                         self.o(".")
-                        pars = "OneDExpChannel", name, axis, aliasName
+                        pars = ["OneDExpChannel", name, axis, aliasName]
                         if deviceName.count('/') == 2:
                             pars.append(deviceName)
                         pars = map(str.strip, pars)
@@ -964,7 +964,7 @@ class DevicePoolServer(TangoServer):
                             self.o('x')
                             continue
                         self.o(".")
-                        pars = "TwoDExpChannel", name, axis, aliasName
+                        pars = ["TwoDExpChannel", name, axis, aliasName]
                         if deviceName.count('/') == 2:
                             pars.append(deviceName)
                         pars = map(str.strip, pars)
@@ -999,7 +999,7 @@ class DevicePoolServer(TangoServer):
                             self.o('x')
                             continue
                         self.o(".")
-                        pars = "IORegister", name, axis, aliasName
+                        pars = ["IORegister", name, axis, aliasName]
                         if deviceName.count('/') == 2:
                             pars.append(deviceName)
                         pars = map(str.strip, pars)
@@ -1661,7 +1661,7 @@ class Sardana:
             self._devicePools[serv.getInstanceName()] = serv
 
     def prepareMSs(self):
-        servNodes = self._sarNode.findall("MacroServer")
+        servNodes = self._sarNode.findall("MacroServerServer")
 
         self._macServs = {}
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/sardana.git



More information about the debian-science-commits mailing list