From 3f9d696e24b5ffaa32d566e51999bb6dedcfc6f2 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Tue, 22 May 2018 11:18:11 +0200
Subject: [PATCH 001/652] Stop MG in case of starting failure
Stop the acquisition if the Start method raises an exception.
---
src/sardana/taurus/core/tango/sardana/pool.py | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py
index 5eed4fc684..613636adf2 100644
--- a/src/sardana/taurus/core/tango/sardana/pool.py
+++ b/src/sardana/taurus/core/tango/sardana/pool.py
@@ -1760,7 +1760,17 @@ def _enableChannels(self, channels, state):
self.setConfiguration(cfg.raw_data)
def _start(self, *args, **kwargs):
- self.Start()
+ try:
+ self.Start()
+ except Exception as e:
+ while True:
+ try:
+ self.stop()
+ break
+ except Exception:
+ pass
+ # TODO: Do more friendly user the exception message.
+ raise e
def go(self, *args, **kwargs):
start_time = time.time()
From ae904b066501690c0d7fec80795d540ad33d304a Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 30 May 2018 15:41:10 +0200
Subject: [PATCH 002/652] Initial draft after some ALBA brainstorming.
---
doc/source/sep/SEP18.md | 62 +++++++++++++++++++++++++++++++++++++++++
doc/source/sep/index.md | 2 ++
2 files changed, 64 insertions(+)
create mode 100644 doc/source/sep/SEP18.md
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
new file mode 100644
index 0000000000..e0d95ee7dd
--- /dev/null
+++ b/doc/source/sep/SEP18.md
@@ -0,0 +1,62 @@
+ Title: Extend acquisition and synchronization concepts for SEP2 needs.
+ SEP: 18
+ State: DRAFT
+ Reason:
+ New acquisition and synchronization concepts are necessary in order to
+ properly integrate 1D and 2D experimental channels in Sardana (SEP2).
+ Date: 2018-05-30
+ Drivers: Zbigniew Reszela
+ URL: https://github.com/reszelaz/sardana/blob/sep18/doc/source/sep/SEP18.md
+ License: http://www.jclark.com/xml/copying.txt
+ Abstract:
+ SEP6 defined some acquisition and synchronization concepts for the needs
+ of the continuous scan. When working on the 1D and 2D experimental
+ channels integration we see that more concepts are necessary. This SEP
+ introduces them.
+
+Terminology
+-----------
+
+* Measurement - a measurement process that may, but not necessarily, involve
+multiple acquisitions e.g. measurement group count, continuous scan, step scan.
+* Acquisition - a single acquisition e.g. over an integration time, which is a
+part of a measurement
+
+Current Limitations
+-------------------
+
+* The Software and Hardware synchronization types terminology is not self
+descriptive. Internal and External terminology describes better these types.
+* Some hardware or even the Lima library allows some synchronization modes
+that are not foreseen in Sardana and are necessary.
+* Some experimental channels could benefit from preparing them for multiple
+acquisitions before the measurement e.g. software synchronized continuous
+scan or step scan.
+
+Objectives
+----------
+
+Overcome the above limitations.
+
+Design
+------
+
+1. Document well that:
+ * Software(Trigger|Gate) are synonyms to Internal (Trigger|Gate).
+ Internal means that Sardana will synchronize the acquisitions.
+ * Hardware(Trigger|Gate) are synonyms to External(Trigger|Gate).
+ External means that an external to Sardana object (could be hardware)
+ will synchronize the acquisitions.
+2. Extend AcqSynch with two new options:
+ * SoftwareStart (which means internal start)
+ * HardwareStart (which means external start)
+3. Extend AcqSynchType with one new option (supported from expconf):
+ * Start
+4. Allow different types of preparation of channels:
+ * Per measurement preparation with repetitions=n e.g. Prepare(One|All)
+ or a controller parameter
+ * Per acquisition preparation with repetitions=1 e.g. Load(One|All)
+6. Modify acquisition actions (and synchronization action if necessary) so
+they support the new concepts added in points 2 and 4.
+5. Extend GSF (step mode) with measurement preparation (repetitions=n) if
+possible i.e. scan macro knows beforehand the number of points.
diff --git a/doc/source/sep/index.md b/doc/source/sep/index.md
index a293b472c5..d888843c95 100644
--- a/doc/source/sep/index.md
+++ b/doc/source/sep/index.md
@@ -26,6 +26,7 @@ Proposals list
[SEP13][] | REJECTED (moved to [TEP13][]) | Unified plugins support in Taurus & Sardana
[SEP14][] | DRAFT | MSENV taurus schema
[SEP15][] | ACCEPTED | Moving Sardana to Github
+ [SEP18][] | DRAFT | Extend acquisition and synchronization concepts for SEP2 needs
@@ -46,6 +47,7 @@ Proposals list
[SEP13]: http://www.sardana-controls.org/sep/?SEP13.md
[SEP14]: http://www.sardana-controls.org/sep/?SEP14.md
[SEP15]: http://www.sardana-controls.org/sep/?SEP15.md
+[SEP18]: https://github.com/reszelaz/sardana/blob/sep18/doc/source/sep/SEP18.md
[TEP3]: http://www.taurus-scada.org/tep/?TEP3.md
From 19a88683bce5c964e4f08ab8bb230fccb9848120 Mon Sep 17 00:00:00 2001
From: schick
Date: Fri, 8 Jun 2018 15:18:06 +0200
Subject: [PATCH 003/652] skeleton newfile macro
---
src/sardana/macroserver/macros/standard.py | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py
index ead912fa3c..b182d119c5 100644
--- a/src/sardana/macroserver/macros/standard.py
+++ b/src/sardana/macroserver/macros/standard.py
@@ -845,3 +845,24 @@ def run(self, nr):
self.__loop()
progress = ((i + 1) / float(nr)) * 100
yield progress
+
+
+class newfile(Macro):
+ """This macro sets a new ScanFile and ScanID values into the env"""
+
+ # hints = { 'allowsHooks': ('body', 'break', 'continue') }
+ env = ('ScanFile', 'ScanID')
+
+ param_def = [
+ ['ScanID', Type.Integer, 1.0, 'Scan ID']
+ ['ScanFile_list',
+ ParamRepeat(['ScanFile', Type.String, None, 'Name of scan file']),
+ None, 'List of scan file names'],
+ ]
+
+ def run(self, ScanID, ScanFile_list):
+ oldScanFile = self.getEnv('ScanFile')
+ oldScanID = self.getEnv('ScanFile')
+
+ self.output(oldScanFile)
+ self.output(oldScanID)
\ No newline at end of file
From 29b980e980c9a14baf3a6ce105ba5291085f463c Mon Sep 17 00:00:00 2001
From: schick
Date: Fri, 8 Jun 2018 15:34:05 +0200
Subject: [PATCH 004/652] first version working version
---
src/sardana/macroserver/macros/standard.py | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py
index b182d119c5..0e699f8f1f 100644
--- a/src/sardana/macroserver/macros/standard.py
+++ b/src/sardana/macroserver/macros/standard.py
@@ -848,21 +848,25 @@ def run(self, nr):
class newfile(Macro):
- """This macro sets a new ScanFile and ScanID values into the env"""
+ """This macro sets a new ScanFile and ScanID values into the env.
+ The ScanID should be set to the value N of the last executed ScanID
+ in order to continue with a ScanID N+1 for the next scan."""
- # hints = { 'allowsHooks': ('body', 'break', 'continue') }
env = ('ScanFile', 'ScanID')
param_def = [
- ['ScanID', Type.Integer, 1.0, 'Scan ID']
+ ['ScanID', Type.Integer, 1, 'Scan ID'],
['ScanFile_list',
ParamRepeat(['ScanFile', Type.String, None, 'Name of scan file']),
None, 'List of scan file names'],
]
- def run(self, ScanID, ScanFile_list):
- oldScanFile = self.getEnv('ScanFile')
- oldScanID = self.getEnv('ScanFile')
+ def run(self, ScanID, ScanFile_list):
+ self.setEnv('ScanFile', ScanFile_list)
+ self.setEnv('ScanID', ScanID)
- self.output(oldScanFile)
- self.output(oldScanID)
\ No newline at end of file
+ self.output('ScanFile set to: ')
+ for ScanFile in ScanFile_list:
+ self.output(' %s', ScanFile)
+ self.output('ScanID set to: %d', ScanID)
+
\ No newline at end of file
From 31ade43bda965b6346bf20b005b4d084f276a0c8 Mon Sep 17 00:00:00 2001
From: schick
Date: Fri, 8 Jun 2018 16:06:56 +0200
Subject: [PATCH 005/652] add newfile to __all__ list
---
src/sardana/macroserver/macros/standard.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py
index 0e699f8f1f..eb6135ccff 100644
--- a/src/sardana/macroserver/macros/standard.py
+++ b/src/sardana/macroserver/macros/standard.py
@@ -25,7 +25,7 @@
__all__ = ["ct", "mstate", "mv", "mvr", "pwa", "pwm", "repeat", "set_lim",
"set_lm", "set_pos", "settimer", "uct", "umv", "umvr", "wa", "wm",
- "tw", "logmacro"]
+ "tw", "logmacro", "newfile"]
__docformat__ = 'restructuredtext'
From 0973d321c0e720f08b4b2eb1ebdc266b17b14d7f Mon Sep 17 00:00:00 2001
From: Daniel Schick
Date: Mon, 11 Jun 2018 10:59:09 +0200
Subject: [PATCH 006/652] remove whitespaces
---
src/sardana/macroserver/macros/standard.py | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py
index eb6135ccff..c0ecc183e9 100644
--- a/src/sardana/macroserver/macros/standard.py
+++ b/src/sardana/macroserver/macros/standard.py
@@ -860,13 +860,12 @@ class newfile(Macro):
ParamRepeat(['ScanFile', Type.String, None, 'Name of scan file']),
None, 'List of scan file names'],
]
-
- def run(self, ScanID, ScanFile_list):
+
+ def run(self, ScanID, ScanFile_list):
self.setEnv('ScanFile', ScanFile_list)
self.setEnv('ScanID', ScanID)
-
+
self.output('ScanFile set to: ')
for ScanFile in ScanFile_list:
self.output(' %s', ScanFile)
- self.output('ScanID set to: %d', ScanID)
-
\ No newline at end of file
+ self.output('ScanID set to: %d', ScanID)
\ No newline at end of file
From 1f3fe40c0cc2d6d6fd2c89c8aec5b2be9dee87f1 Mon Sep 17 00:00:00 2001
From: Daniel Schick
Date: Mon, 11 Jun 2018 11:26:53 +0200
Subject: [PATCH 007/652] more corrections of whitepsaces
---
src/sardana/macroserver/macros/standard.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py
index c0ecc183e9..da180e4e7f 100644
--- a/src/sardana/macroserver/macros/standard.py
+++ b/src/sardana/macroserver/macros/standard.py
@@ -849,7 +849,7 @@ def run(self, nr):
class newfile(Macro):
"""This macro sets a new ScanFile and ScanID values into the env.
- The ScanID should be set to the value N of the last executed ScanID
+ The ScanID should be set to the value N of the last executed ScanID
in order to continue with a ScanID N+1 for the next scan."""
env = ('ScanFile', 'ScanID')
@@ -868,4 +868,4 @@ def run(self, ScanID, ScanFile_list):
self.output('ScanFile set to: ')
for ScanFile in ScanFile_list:
self.output(' %s', ScanFile)
- self.output('ScanID set to: %d', ScanID)
\ No newline at end of file
+ self.output('ScanID set to: %d', ScanID)
From b52315b395e28246e3da38de3ad91cb03f66a35b Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 13 Jun 2018 11:38:51 +0200
Subject: [PATCH 008/652] Relax restrictions in usage of repeat parameters in
spock
Spock syntax allows to use macros with only one repeat parameter which is
defined at the end. Furthermore no nested repeat parameters are allowed.
Remove this validation so it will be possible to extend the spock syntax
for more cases.
---
src/sardana/taurus/core/tango/sardana/macro.py | 11 -----------
1 file changed, 11 deletions(-)
diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py
index 9b4a4ba3f5..f1f31bee22 100755
--- a/src/sardana/taurus/core/tango/sardana/macro.py
+++ b/src/sardana/taurus/core/tango/sardana/macro.py
@@ -1301,18 +1301,7 @@ def createMacroNode(macro_name, params_def, macro_params):
if not new_interface:
param_nodes = macro_node.params()
- 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)
- # If ParamRepeat only one and as last parameter
- # 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)
From b4790a576f456ecf4e6bee354323266847565c35 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 13 Jun 2018 11:47:25 +0200
Subject: [PATCH 009/652] Extend spock syntax repeat parameters usage
Allow usage of macros with repeat parameters located at arbitrary places
when only one repetition is passed by the user. When more repetitions are
passed by the user the advanced spock syntax must be used (using square
brackets to group repeat parameters and repetitions with more than one
member).
---
.../taurus/core/tango/sardana/macro.py | 28 +++++++++++++++++--
1 file changed, 26 insertions(+), 2 deletions(-)
diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py
index f1f31bee22..60e63da859 100755
--- a/src/sardana/taurus/core/tango/sardana/macro.py
+++ b/src/sardana/taurus/core/tango/sardana/macro.py
@@ -1302,11 +1302,34 @@ def createMacroNode(macro_name, params_def, macro_params):
if not new_interface:
param_nodes = macro_node.params()
len_param_nodes = len(param_nodes)
+ param_idx = 0
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):
+ # Repeat parameters that are not at the end.
+ elif (isinstance(param_node, RepeatParamNode) and
+ param_idx < (len_param_nodes - 1)):
+ repeat_node = param_node.child(0)
+ # 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()
+ if len(repeat_node.children()) > 1:
+ msg = ("repeat parameter with more than one member must "
+ "be defined at the end when intended to "
+ "be used with spock syntax")
+ raise Exception(msg)
+ member_node = repeat_node.child(0)
+ if isinstance(member_node, RepeatParamNode):
+ msg = ("nested repeat parameters are not allowed when "
+ "intended to be used with spock syntax")
+ raise Exception(msg)
+ member_node.setValue(param_raw)
+ # The resting values are interpreted as repeat parameter values.
+ # This ignores parameter values which exceeds the parameters
+ # definition.
+ else:
params_info = param_node.paramsInfo()
params_info_len = len(params_info)
rep = 0
@@ -1329,6 +1352,7 @@ def createMacroNode(macro_name, params_def, macro_params):
if mem == 0:
rep += 1
break
+ param_idx += 1
else:
macro_node.fromList(macro_params)
return macro_node
From 2232c10b94a1a7a3dbed521913703e9ea2774f98 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 13 Jun 2018 11:52:05 +0200
Subject: [PATCH 010/652] Correct comments and exception message (minor)
---
src/sardana/taurus/core/tango/sardana/macro.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py
index 60e63da859..5bb4e4ec78 100755
--- a/src/sardana/taurus/core/tango/sardana/macro.py
+++ b/src/sardana/taurus/core/tango/sardana/macro.py
@@ -1337,14 +1337,15 @@ def createMacroNode(macro_name, params_def, macro_params):
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
+ # 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")
+ msg = ("nested repeat parameters are not allowed "
+ "when intended to be used with spock syntax")
raise Exception(msg)
member_node.setValue(member_raw)
mem += 1
From c3e6538e6f33aa1c933c86794dad7890c30d5d3f Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 13 Jun 2018 15:09:37 +0200
Subject: [PATCH 011/652] Fix bug and use proper index of the last parameter
Commit b52315b accidentally remove the i variable. Fix it.
---
src/sardana/taurus/core/tango/sardana/macro.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py
index 5bb4e4ec78..7ca7958675 100755
--- a/src/sardana/taurus/core/tango/sardana/macro.py
+++ b/src/sardana/taurus/core/tango/sardana/macro.py
@@ -1332,9 +1332,10 @@ def createMacroNode(macro_name, params_def, macro_params):
else:
params_info = param_node.paramsInfo()
params_info_len = len(params_info)
+ last_param_idx = len_param_nodes - 1
rep = 0
mem = 0
- rest_raw = macro_params[i:]
+ rest_raw = macro_params[last_param_idx:]
for member_raw in rest_raw:
repeat_node = param_node.child(rep)
# Add a new repeat node. This is needed when the raw
From ba2a5f8ac7b5f88422dc58ec41ed873271f63b86 Mon Sep 17 00:00:00 2001
From: teresa
Date: Fri, 29 Jun 2018 13:31:00 +0200
Subject: [PATCH 012/652] Updating examples according to this extension
---
src/sardana/macroserver/macros/examples/parameters.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/sardana/macroserver/macros/examples/parameters.py b/src/sardana/macroserver/macros/examples/parameters.py
index 486e5201a3..857fe091e8 100644
--- a/src/sardana/macroserver/macros/examples/parameters.py
+++ b/src/sardana/macroserver/macros/examples/parameters.py
@@ -186,6 +186,7 @@ class pt7d1(Macro):
pt7d1 mot1 1 mot2 3
Using default value, ex.:
pt7d1 [[mot1] [mot2 3]] # at any repetition
+ pt7d1 mot1 # if only one repetition
"""
@@ -262,6 +263,7 @@ class pt10(Macro):
parameter may be defined as first one.
Usage from Spock, ex.:
pt10 [1 3] mot1
+ pt10 1 mot1 # if only one repetition
"""
param_def = [
@@ -279,6 +281,7 @@ class pt11(Macro):
parameters.
Usages from Spock, ex.:
pt11 ct1 [1 3] mot1
+ pt11 ct1 1 mot1 # if only one repetition
"""
param_def = [
@@ -296,6 +299,7 @@ class pt12(Macro):
parameters may defined.
Usage from Spock, ex.:
pt12 [1 3 4] [mot1 mot2]
+ pt12 1 mot1 # if only one repetition for each repeat parameter
"""
param_def = [
From 88ef601084874c2b4c39af3d3d133327f36bc48c Mon Sep 17 00:00:00 2001
From: Daniel Schick
Date: Sat, 7 Jul 2018 02:58:15 +0200
Subject: [PATCH 013/652] change order of input and describe functionality
---
src/sardana/macroserver/macros/standard.py | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py
index d7c76fe5a0..5c665fc859 100644
--- a/src/sardana/macroserver/macros/standard.py
+++ b/src/sardana/macroserver/macros/standard.py
@@ -864,13 +864,18 @@ class newfile(Macro):
env = ('ScanFile', 'ScanID')
param_def = [
- ['ScanID', Type.Integer, 1, 'Scan ID'],
['ScanFile_list',
ParamRepeat(['ScanFile', Type.String, None, 'Name of scan file']),
None, 'List of scan file names'],
+ ['ScanID', Type.Integer, 0, 'Scan ID'],
]
- def run(self, ScanID, ScanFile_list):
+ def run(self, ScanFile_list, ScanID):
+ # check if the ScanFile_list contains any folder
+ # first iterate over every element in the ScanFile_list and extract the
+ # possible folder - then check if at least one folder is set and if
+ # multiples - they have to be equal
+ # if now inputs are givin the macro should be interactive
self.setEnv('ScanFile', ScanFile_list)
self.setEnv('ScanID', ScanID)
From 97796c97ede14511d5cc84d753885ab0a227c8f8 Mon Sep 17 00:00:00 2001
From: Daniel Schick
Date: Mon, 9 Jul 2018 10:05:36 +0200
Subject: [PATCH 014/652] include ScanDir handling for newfile macro
allow for scanDir and multiple file
scanID is given for the upcoming scan - default is 1
---
src/sardana/macroserver/macros/standard.py | 83 +++++++++++++++++-----
1 file changed, 65 insertions(+), 18 deletions(-)
diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py
index 5c665fc859..7abf03c9d6 100644
--- a/src/sardana/macroserver/macros/standard.py
+++ b/src/sardana/macroserver/macros/standard.py
@@ -30,6 +30,7 @@
__docformat__ = 'restructuredtext'
import datetime
+import os
import numpy as np
from taurus import Device
@@ -861,25 +862,71 @@ class newfile(Macro):
The ScanID should be set to the value N of the last executed ScanID
in order to continue with a ScanID N+1 for the next scan."""
- env = ('ScanFile', 'ScanID')
+ env = ('ScanFilePath', 'ScanID')
param_def = [
- ['ScanFile_list',
- ParamRepeat(['ScanFile', Type.String, None, 'Name of scan file']),
- None, 'List of scan file names'],
- ['ScanID', Type.Integer, 0, 'Scan ID'],
+ ['ScanFilePath_list',
+ ParamRepeat(['ScanFilePath', Type.String, None,
+ 'full path to scan file']),
+ None, 'List of scan file names and paths'],
+ ['ScanID', Type.Integer, -1, 'Scan ID'],
]
- def run(self, ScanFile_list, ScanID):
- # check if the ScanFile_list contains any folder
- # first iterate over every element in the ScanFile_list and extract the
- # possible folder - then check if at least one folder is set and if
- # multiples - they have to be equal
- # if now inputs are givin the macro should be interactive
- self.setEnv('ScanFile', ScanFile_list)
- self.setEnv('ScanID', ScanID)
-
- self.output('ScanFile set to: ')
- for ScanFile in ScanFile_list:
- self.output(' %s', ScanFile)
- self.output('ScanID set to: %d', ScanID)
+ def run(self, ScanFilePath_list, ScanID):
+ path_list = []
+ fileName_list = []
+ # traverse the repeat parameters for the path+fileNames
+ for i, ScanFilePath in enumerate(ScanFilePath_list):
+ path = os.path.dirname(ScanFilePath)
+ fileName = os.path.basename(ScanFilePath)
+ if not path and i == 0:
+ # first entry and no given path: use pwd
+ path = os.getcwd()
+ elif not path and i > 0:
+ # not first entry and no given path: use path of last iteration
+ path = path_list[i-1]
+ elif not os.path.isabs(path):
+ # relative path
+ path = os.path.normpath(os.path.join(os.getcwd(), path))
+ else:
+ # absolute path
+ path = os.path.normpath(path)
+
+ if i > 0 and path in ((p) for p in path_list):
+ # check if paths are equal
+ self.error("Multiple paths, %s, to the data files are not"
+ "allowed" % path)
+ return
+ elif not os.path.exists(path):
+ # check if folder exists
+ self.error("Path %s does not exists and has to be created in"
+ "advance." % path)
+ return
+ else:
+ self.debug("Path %s appended." % path)
+ path_list.append(path)
+
+ if not fileName:
+ self.error("No filename is given")
+ return
+ elif i > 0 and fileName in ((f) for f in fileName_list):
+ self.error("Duplicate filename %s." % fileName)
+ return
+ else:
+ self.debug("Filename is %s." % fileName)
+ fileName_list.append(fileName)
+
+ if ScanID < 1:
+ ScanID = 0
+ else:
+ ScanID = ScanID-1
+
+ self.setEnv('ScanFile', fileName_list)
+ self.setEnv('ScanDir', path_list[0])
+ self.setEnv('ScanID', ScanID)
+
+ self.output('ScanDir is: %s', path_list[0])
+ self.output('ScanFile set to: ')
+ for ScanFile in fileName_list:
+ self.output(' %s', ScanFile)
+ self.output('Next scan is #%d', ScanID+1)
From 581d735259cd409a8c3d0bc0550cfcd54e417687 Mon Sep 17 00:00:00 2001
From: Daniel Schick
Date: Mon, 9 Jul 2018 23:19:09 +0200
Subject: [PATCH 015/652] remove pwd and relpath functionality
only absoulte paths on the macroServer host are allowed.
If no ScanDir is given, its existens will be checked
---
src/sardana/macroserver/macros/standard.py | 64 +++++++++++++---------
1 file changed, 38 insertions(+), 26 deletions(-)
diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py
index 7abf03c9d6..ac2b9fd366 100644
--- a/src/sardana/macroserver/macros/standard.py
+++ b/src/sardana/macroserver/macros/standard.py
@@ -858,62 +858,74 @@ def run(self, nr):
class newfile(Macro):
- """This macro sets a new ScanFile and ScanID values into the env.
- The ScanID should be set to the value N of the last executed ScanID
- in order to continue with a ScanID N+1 for the next scan."""
+ """ Sets the ScanDir and ScanFile as well as ScanID in the environment.
+ If ScanFilePath is only a file name, the ScanDir must be set externally via
+ senv ScanDir PathToScanFile or using the %expconf. Otherwise, the path in
+ ScanFilePath must be absolute and existing on the MacroServer host.
+ The ScanID should be set to the value of the upcoming scan number. Default
+ value is 1.
+ """
- env = ('ScanFilePath', 'ScanID')
+ env = ('ScanDir', 'ScanFile', 'ScanID')
param_def = [
- ['ScanFilePath_list',
- ParamRepeat(['ScanFilePath', Type.String, None,
- 'full path to scan file']),
- None, 'List of scan file names and paths'],
+ ['ScanFileDir_list',
+ ParamRepeat(['ScanFileDir', Type.String, None,
+ '(ScanDir/)ScanFile']),
+ None, 'List of (ScanDir/)ScanFile'],
['ScanID', Type.Integer, -1, 'Scan ID'],
]
- def run(self, ScanFilePath_list, ScanID):
+ def run(self, ScanFileDir_list, ScanID):
path_list = []
fileName_list = []
- # traverse the repeat parameters for the path+fileNames
- for i, ScanFilePath in enumerate(ScanFilePath_list):
- path = os.path.dirname(ScanFilePath)
- fileName = os.path.basename(ScanFilePath)
+ # traverse the repeat parameters for the ScanFilePath_list
+ for i, ScanFileDir in enumerate(ScanFileDir_list):
+ path = os.path.dirname(ScanFileDir)
+ fileName = os.path.basename(ScanFileDir)
if not path and i == 0:
- # first entry and no given path: use pwd
- path = os.getcwd()
+ # first entry and no given ScanDir: check if ScanDir exists
+ ScanDir = self.getEnv('ScanDir')
+ if not ScanDir:
+ self.warning('Data is not stored until ScanDir is set! '
+ 'Provide ScanDir with newfile macro: '
+ 'newfile [/] or'
+ 'senv ScanDir or with %expconf')
+ else:
+ path = ScanDir
elif not path and i > 0:
# not first entry and no given path: use path of last iteration
path = path_list[i-1]
elif not os.path.isabs(path):
# relative path
- path = os.path.normpath(os.path.join(os.getcwd(), path))
+ self.error('Only absolute path are allowed!')
+ return
else:
# absolute path
path = os.path.normpath(path)
- if i > 0 and path in ((p) for p in path_list):
+ if i > 0 and (path not in path_list):
# check if paths are equal
- self.error("Multiple paths, %s, to the data files are not"
- "allowed" % path)
+ self.error('Multiple paths to the data files are not allowed')
return
elif not os.path.exists(path):
# check if folder exists
- self.error("Path %s does not exists and has to be created in"
- "advance." % path)
+ self.error('Path %s does not exists on the host of the '
+ 'MacroServerand has to be created in '
+ 'advance.' % path)
return
else:
- self.debug("Path %s appended." % path)
+ self.debug('Path %s appended.' % path)
path_list.append(path)
if not fileName:
- self.error("No filename is given")
+ self.error('No filename is given.')
return
- elif i > 0 and fileName in ((f) for f in fileName_list):
- self.error("Duplicate filename %s." % fileName)
+ elif fileName in fileName_list:
+ self.error('Duplicate filename %s is not allowed.' % fileName)
return
else:
- self.debug("Filename is %s." % fileName)
+ self.debug('Filename is %s.' % fileName)
fileName_list.append(fileName)
if ScanID < 1:
From 26dc568fa7fd07f8d952e29110f5b81621b7f8cf Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Wed, 18 Jul 2018 15:06:34 +0200
Subject: [PATCH 016/652] Not send default parameter value
Do not send the default parameter value on the execution of the macro.
The use of the default value is implemented on the server site.
---
src/sardana/taurus/core/tango/sardana/macro.py | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py
index 9b4a4ba3f5..25c4a34af2 100755
--- a/src/sardana/taurus/core/tango/sardana/macro.py
+++ b/src/sardana/taurus/core/tango/sardana/macro.py
@@ -487,10 +487,10 @@ def __init__(self, parent=None, param=None):
if param is None:
return
self.setType(str(param.get('type')))
- self.setDefValue(str(param.get('default_value', '')))
+ self.setDefValue(str(param.get('default_value', None)))
if self.type() == "User":
self.setDefValue(str(USER_NAME))
- self.setValue(self.defValue())
+ self._value = None
def __len__(self):
return 0
@@ -523,7 +523,9 @@ def setType(self, type):
def toXml(self):
value = self.value()
paramElement = etree.Element("param", name=self.name())
- if not value is None:
+ # set value attribute only if it is different than the default value
+ # the server will assign the default value anyway.
+ if value is not None and value != self.defValue():
paramElement.set("value", value)
return paramElement
@@ -542,9 +544,12 @@ def allMotors(self):
def toRun(self):
val = self.value()
- if val is None or val == "None" or val == "":
- alert = "Parameter " + self.name() + " is missing.
"
- return ([val], alert)
+ if val is None or val == "None":
+ if self.defValue() is None:
+ alert = "Parameter " + self.name() + " is missing.
"
+ return ([val], alert)
+ else:
+ val = self.defValue()
return ([val], "")
def toList(self):
From 7cf519f482e40d1749d220a982893d1c4178a9dd Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Wed, 18 Jul 2018 17:52:46 +0200
Subject: [PATCH 017/652] Implement OptionalParamClass
Implement class and validations to allow optional parameters on the
macros.
---
src/sardana/macroserver/macro.py | 5 +--
src/sardana/macroserver/msparameter.py | 45 ++++++++++++++++++++------
2 files changed, 38 insertions(+), 12 deletions(-)
diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py
index 1fd5d99e33..719b58fc63 100644
--- a/src/sardana/macroserver/macro.py
+++ b/src/sardana/macroserver/macro.py
@@ -32,7 +32,7 @@
__all__ = ["OverloadPrint", "PauseEvent", "Hookable", "ExecMacroHook",
"MacroFinder", "Macro", "macro", "iMacro", "imacro",
"MacroFunc", "Type", "ParamRepeat", "Table", "List", "ViewOption",
- "LibraryError"]
+ "LibraryError", "OptionalParam"]
__docformat__ = 'restructuredtext'
@@ -55,7 +55,8 @@
from sardana.sardanadefs import State
from sardana.util.wrap import wraps
-from sardana.macroserver.msparameter import Type, ParamType, ParamRepeat
+from sardana.macroserver.msparameter import Type, ParamType, ParamRepeat, \
+ OptionalParam
from sardana.macroserver.msexception import StopException, AbortException, \
MacroWrongParameterType, UnknownEnv, UnknownMacro, LibraryError
from sardana.macroserver.msoptions import ViewOption
diff --git a/src/sardana/macroserver/msparameter.py b/src/sardana/macroserver/msparameter.py
index f0422c8959..de482ae035 100644
--- a/src/sardana/macroserver/msparameter.py
+++ b/src/sardana/macroserver/msparameter.py
@@ -36,7 +36,7 @@
from copy import deepcopy
from lxml import etree
-
+import json
from taurus.core.util.containers import CaselessDict
from sardana import ElementType, INTERFACES_EXPANDED
@@ -45,6 +45,28 @@
UnknownMacro, UnknownMacroLibrary
+class OptionalParamClass(dict):
+ def __init__(self, obj):
+ super(OptionalParamClass, self).__init__(obj)
+ attributes = dir(self)
+
+ for attr in attributes:
+ if attr in ['__setattr__', '__repr__', 'raise_error', '__class__',
+ '__dict__', '__weakref__']:
+ continue
+ self.__setattr__(attr, self.raise_error)
+ self.__setattr__ = self.raise_error
+
+ def __repr__(self):
+ return 'OptionalParam'
+
+ def raise_error(*args, **kwargs):
+ raise RuntimeError('can not be accessed')
+
+
+OptionalParam = OptionalParamClass({'___optional_parameter__': True})
+
+
class WrongParam(MacroServerException):
def __init__(self, *args):
@@ -397,22 +419,25 @@ def decodeNormal(self, raw_param, param_def):
else:
value = raw_param
# None or [] indicates default value
- if value is None or (isinstance(value, list) and len(value) == 0):
+ if value is None or (isinstance(value, list) and
+ len(value) == 0):
value = param_def['default_value']
if value is None:
- raise MissingParam, "'%s' not specified" % name
+ raise MissingParam("'%s' not specified" % name)
+ elif isinstance(value, OptionalParamClass):
+ param = OptionalParam
else:
# cast to sting to fulfill with ParamType API
- value = str(value)
- param = param_type.getObj(value)
- except ValueError, e:
- raise WrongParamType, e.message
- except UnknownParamObj, e:
- raise WrongParam, e.message
+ param = param_type.getObj(str(value))
+
+ except ValueError as e:
+ raise WrongParamType(e.message)
+ except UnknownParamObj as 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
+ raise WrongParam(msg)
return param
def decodeRepeat(self, raw_param_repeat, param_repeat_def):
From 93ee0dc91926c0a479bcf7ac043f48adac41abff Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Thu, 19 Jul 2018 16:31:49 +0200
Subject: [PATCH 018/652] Adapt macroexecutor to support optional parameters
---
.../taurus/core/tango/sardana/macro.py | 18 ++++++++----
.../extra_macroexecutor/macroexecutor.py | 28 ++++++++++++++-----
2 files changed, 33 insertions(+), 13 deletions(-)
diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py
index 25c4a34af2..45356bdabe 100755
--- a/src/sardana/taurus/core/tango/sardana/macro.py
+++ b/src/sardana/taurus/core/tango/sardana/macro.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-
+from __future__ import absolute_import
##############################################################################
##
# This file is part of Sardana
@@ -41,6 +41,7 @@
from taurus.core.util.user import USER_NAME
from taurus.core.util.codecs import CodecFactory
+from sardana.macroserver.msparameter import OptionalParam
class MacroRunException(Exception):
@@ -487,9 +488,9 @@ def __init__(self, parent=None, param=None):
if param is None:
return
self.setType(str(param.get('type')))
- self.setDefValue(str(param.get('default_value', None)))
+ self.setDefValue(param.get('default_value', None))
if self.type() == "User":
- self.setDefValue(str(USER_NAME))
+ self.setDefValue(USER_NAME)
self._value = None
def __len__(self):
@@ -507,7 +508,9 @@ def setValue(self, value):
self._value = value
def defValue(self):
- return self._defValue
+ if self._defValue is None:
+ return None
+ return str(self._defValue)
def setDefValue(self, defValue):
if defValue == "None":
@@ -525,8 +528,9 @@ def toXml(self):
paramElement = etree.Element("param", name=self.name())
# set value attribute only if it is different than the default value
# the server will assign the default value anyway.
- if value is not None and value != self.defValue():
- paramElement.set("value", value)
+ if value is not None or str(value).lower() != 'none':
+ if value != self.defValue():
+ paramElement.set("value", value)
return paramElement
def fromXml(self, xmlElement):
@@ -548,6 +552,8 @@ def toRun(self):
if self.defValue() is None:
alert = "Parameter " + self.name() + " is missing.
"
return ([val], alert)
+ elif self._defValue == OptionalParam:
+ val = ''
else:
val = self.defValue()
return ([val], "")
diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py
index f3115c55bb..8d6fbbf872 100644
--- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py
+++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py
@@ -47,6 +47,8 @@
from .favouriteseditor import FavouritesMacrosEditor, HistoryMacrosViewer
from .common import MacroComboBox, MacroExecutionWindow, standardPlotablesFilter
+from sardana.macroserver.msparameter import OptionalParam
+
class MacroProgressBar(Qt.QProgressBar):
@@ -256,11 +258,16 @@ def validateAllExpresion(self, secValidation=False):
self.setStyleSheet("")
self.setToolTip('
'.join(problems))
return
-
self.currentIndex = Qt.QModelIndex()
ix = self.getIndex()
self.currentIndex = ix
counter = 1
+
+ # Get the parameters information to check if there are optional
+ # paramters
+ macro_obj = self.getModelObj()
+ macro_params_info = macro_obj.getElementInfo(mlist[0]).parameters
+
while not ix == Qt.QModelIndex():
try:
propValue = mlist[counter]
@@ -274,13 +281,20 @@ def validateAllExpresion(self, secValidation=False):
message = "" + txt + " " + e[0]
problems.append(message)
except IndexError:
- txt = str(Qt.from_qvariant(
- ix.sibling(ix.row(), 0).data(), str))
- problems.append("" + txt + " is missing!")
+ param_info = macro_params_info[counter-1]
+ # Skip validation in case of optional parameters
+ if param_info['default_value'] == OptionalParam:
+ self.model().setData(self.currentIndex,
+ Qt.QVariant(None))
+ else:
+ txt = str(Qt.from_qvariant(
+ ix.sibling(ix.row(), 0).data(), str))
+ problems.append("" + txt + " is missing!")
- data = str(Qt.from_qvariant(ix.data(), str))
- if data != 'None':
- self.model().setData(self.currentIndex, Qt.QVariant('None'))
+ data = str(Qt.from_qvariant(ix.data(), str))
+ if data != 'None':
+ self.model().setData(self.currentIndex,
+ Qt.QVariant('None'))
counter += 1
ix = self.getIndex()
self.currentIndex = ix
From 116e087a9eb7af0b46fb29b73d0f0bdcf64b1bd5 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Fri, 20 Jul 2018 12:26:13 +0200
Subject: [PATCH 019/652] Add OptionalParam documentation
---
.../devel/howto_macros/macros_general.rst | 37 ++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst
index e24f8a29ae..8822c50575 100644
--- a/doc/source/devel/howto_macros/macros_general.rst
+++ b/doc/source/devel/howto_macros/macros_general.rst
@@ -298,7 +298,9 @@ being a composed of four elements:
- parameter name
- parameter type
- - parameter default value (None means no default value)
+ - parameter default value:
+ * None means no default value
+ * :ref:`OptionalParam `
- parameter description
Here is a list of the most common allowed parameter types:
@@ -321,6 +323,39 @@ 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-optional-parameters:
+
+Optional parameters
+~~~~~~~~~~~~~~~~~
+
+A special default value is the *OptionalParam*. It allows to the macro
+identify if the user introduces a value or not to take a decision.
+
+So, here is an example how to define and use a optional parameter::
+
+ from sardana.macroserver.macro import Macro, Type, OptionalParam
+
+ class count(Macro):
+
+ param_def = [
+ ['itime', Type.Float, 1, 'integration time'],
+ ['mntgrp', Type.MeasurementGroup, OptionalParam, 'MntGrp to use']
+ ]
+
+ def run(self, itime, mntgrp):
+ bkp_active_mntgrp = None
+ try:
+ if mntgrp is not OptionalParam:
+ bkp_active_mntgrp = self.getEnv('ActiveMntGrp')
+ self.setEnv('ActiveMntGrp', mntgrp.name)
+ self.info('Use "{0}" measurement '
+ 'group'.format(self.getEnv('ActiveMntGrp')))
+ self.ct(itime)
+ finally:
+ if bkp_active_mntgrp is not None:
+ self.setEnv('ActiveMntGrp', bkp_active_mntgrp)
+
+
.. _sardana-macro-repeat-parameters:
Repeat parameters
From e87a69e91a686c7d6420ba43aefb60714caf7f4b Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 23 Jul 2018 13:47:30 +0200
Subject: [PATCH 020/652] Fix flake8 error
---
src/sardana/macroserver/msparameter.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/sardana/macroserver/msparameter.py b/src/sardana/macroserver/msparameter.py
index de482ae035..9cf820989f 100644
--- a/src/sardana/macroserver/msparameter.py
+++ b/src/sardana/macroserver/msparameter.py
@@ -36,7 +36,6 @@
from copy import deepcopy
from lxml import etree
-import json
from taurus.core.util.containers import CaselessDict
from sardana import ElementType, INTERFACES_EXPANDED
From e2c82cf9c2dbafd42585427a80b4586726bcdb08 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 23 Jul 2018 13:52:10 +0200
Subject: [PATCH 021/652] Fix Sphinx warning
---
doc/source/devel/howto_macros/macros_general.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst
index 8822c50575..b2155540c7 100644
--- a/doc/source/devel/howto_macros/macros_general.rst
+++ b/doc/source/devel/howto_macros/macros_general.rst
@@ -326,7 +326,7 @@ all available sardana interfaces (:obj:`~sardana.sardanadefs.Interface`)
.. _sardana-macro-optional-parameters:
Optional parameters
-~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~
A special default value is the *OptionalParam*. It allows to the macro
identify if the user introduces a value or not to take a decision.
From 1e091ea9513a7d981276169e2aa43a4885be226e Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Fri, 27 Jul 2018 12:05:35 +0200
Subject: [PATCH 022/652] Register Qt signal for Taurus 4 compatibility
---
.../taurus/qt/qtcore/tango/sardana/macroserver.py | 10 +++++++++-
src/sardana/taurus/qt/qtcore/tango/sardana/pool.py | 5 +++++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
index 5907fa046b..95c03baedf 100644
--- a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
+++ b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
@@ -105,6 +105,15 @@ def logReceived(self, log_name, output):
class QMacroServer(BaseMacroServer, Qt.QObject):
+ # TODO: For Taurus 4 compatibility
+ try:
+ typesUpdated = Qt.pyqtSignal()
+ elementsUpdated = Qt.pyqtSignal()
+ elementsChanged = Qt.pyqtSignal()
+ macrosUpdated = Qt.pyqtSignal()
+ environmentChange = Qt.pyqtSignal(list)
+ except AttributeError:
+ pass
def __init__(self, name, qt_parent=None, **kw):
self.call__init__wo_kw(Qt.QObject, qt_parent)
@@ -145,7 +154,6 @@ def on_elements_changed(self, s, t, v):
def on_environment_changed(self, s, t, v):
ret = added, removed, changed = \
BaseMacroServer.on_environment_changed(self, s, t, v)
-
if added or removed or changed:
self.emit(Qt.SIGNAL("environmentChanged"), ret)
return ret
diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/pool.py b/src/sardana/taurus/qt/qtcore/tango/sardana/pool.py
index 9b0ca17fc4..2ad5335966 100644
--- a/src/sardana/taurus/qt/qtcore/tango/sardana/pool.py
+++ b/src/sardana/taurus/qt/qtcore/tango/sardana/pool.py
@@ -46,6 +46,11 @@ def __init__(self, name, qt_parent=None, **kw):
class QMeasurementGroup(Qt.QObject, TangoDevice):
+ # TODO: For Taurus 4 compatibility
+ try:
+ configurationChanged = Qt.pyqtSignal()
+ except AttributeError:
+ pass
def __init__(self, name, qt_parent=None, **kw):
self.call__init__wo_kw(Qt.QObject, qt_parent)
From 5f8d37113553681afc0edb696e8e8d93a4d736bc Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Fri, 27 Jul 2018 15:42:45 +0200
Subject: [PATCH 023/652] Store configuration attribute
Store configuration attribute to receive the change events.
---
src/sardana/taurus/qt/qtcore/tango/sardana/pool.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/pool.py b/src/sardana/taurus/qt/qtcore/tango/sardana/pool.py
index 2ad5335966..5a47d4e56a 100644
--- a/src/sardana/taurus/qt/qtcore/tango/sardana/pool.py
+++ b/src/sardana/taurus/qt/qtcore/tango/sardana/pool.py
@@ -57,8 +57,8 @@ def __init__(self, name, qt_parent=None, **kw):
self.call__init__(TangoDevice, name, **kw)
self._config = None
- configuration = self.getAttribute("Configuration")
- configuration.addListener(self._configurationChanged)
+ self.__configuration = self.getAttribute("Configuration")
+ self.__configuration.addListener(self._configurationChanged)
def __getattr__(self, name):
try:
From fc0d3519b85ff05d84e2fc4d7bf07997647fabd1 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Fri, 27 Jul 2018 18:51:01 +0200
Subject: [PATCH 024/652] Add experimentConfigurationChange signal
Implement the experimentConfigurationChanged Qt.Signal which will be
emitted when:
* There are changes on the MacroServer enviroment.
* Adding or Removing measurement groups.
* Change the Measurment Groups configurations
---
.../qt/qtcore/tango/sardana/macroserver.py | 37 +++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
index 95c03baedf..beed9b30ca 100644
--- a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
+++ b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
@@ -52,12 +52,15 @@ class QDoor(BaseDoor, Qt.QObject):
infoUpdated = Qt.pyqtSignal(object)
outputUpdated = Qt.pyqtSignal(object)
debugUpdated = Qt.pyqtSignal(object)
+ experimentConfigurationChanged = Qt.pyqtSignal()
except AttributeError:
pass
def __init__(self, name, qt_parent=None, **kw):
self.call__init__wo_kw(Qt.QObject, qt_parent)
self.call__init__(BaseDoor, name, **kw)
+ self._mntgrp_connected = []
+ self._use_experimet_configuration = False
def resultReceived(self, log_name, result):
res = BaseDoor.resultReceived(self, log_name, result)
@@ -103,6 +106,40 @@ def logReceived(self, log_name, output):
pass
return res
+ def _prepare_connections(self):
+ if not self._use_experimet_configuration:
+ self.connect(self.macro_server, Qt.SIGNAL("environmentChanged"),
+ self._experimentalConfiguration)
+ self.connect(self.macro_server, Qt.SIGNAL("elementsChanged"),
+ self._elementsChanged)
+
+ def _elementsChanged(self):
+ len_mnt_grps_connected = len(self._mntgrp_connected)
+ elements = self.macro_server.getElementsOfType("MeasurementGroup")
+ for name, mg in elements.items():
+ if name in self._mntgrp_connected:
+ continue
+ else:
+ obj = mg.getObj()
+ self.connect(obj, Qt.SIGNAL("configurationChanged"),
+ self._experimentalConfiguration)
+ self._mntgrp_connected.append(name)
+
+ if len(self._mntgrp_connected) != len_mnt_grps_connected:
+ self.emit(Qt.SIGNAL("experimentConfigurationChanged"))
+
+ def _experimentalConfiguration(self, *args):
+ self.emit(Qt.SIGNAL("experimentConfigurationChanged"))
+
+ def getExperimentConfigurationObj(self):
+ self._prepare_connections()
+ return BaseDoor.getExperimentConfigurationObj(self)
+
+ def getExperimentConfiguration(self):
+ self._prepare_connections()
+ return BaseDoor.getExperimentConfiguration(self)
+
+
class QMacroServer(BaseMacroServer, Qt.QObject):
# TODO: For Taurus 4 compatibility
From dd1452e8c90fd6e5caa89f474439c8c220a1eb83 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Fri, 27 Jul 2018 18:53:36 +0200
Subject: [PATCH 025/652] Find different on ExperimentConfiguration objects.
---
.../qt/qtgui/extra_sardana/expdescription.py | 49 +++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
index bd7195bdca..aba2c4af3b 100644
--- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
+++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
@@ -66,6 +66,55 @@ def filterAcceptsRow(self, sourceRow, sourceParent):
return True
+def find_diff(first, second):
+ """
+ Return a dict of keys that differ with another config object. If a value
+ is not found in one fo the configs, it will be represented by KEYNOTFOUND.
+ @param first: Fist configuration to diff.
+ @param second: Second configuration to diff.
+ @return diff: Dict of Key => (first.val, second.val)
+ """
+
+ KEYNOTFOUNDIN1 = 'KeyNotFoundInRemote'
+ KEYNOTFOUNDIN2 = 'KeyNotFoundInLocal'
+ SKIPKEYS = ['_controller_name']
+ SKIPLIST = ['scanfile']
+ DICT_TYPES = [taurus.core.util.containers.CaselessDict, dict]
+ diff = {}
+ sd1 = set(first)
+ sd2 = set(second)
+ # Keys missing in the second dict
+
+ for key in sd1.difference(sd2):
+ if key in SKIPKEYS:
+ continue
+ diff[key] = (first[key], KEYNOTFOUNDIN2)
+ # Keys missing in the first dict
+ for key in sd2.difference(sd1):
+ if key in SKIPKEYS:
+ continue
+ diff[key] = (KEYNOTFOUNDIN1, second[key])
+ # Check for differences
+ for key in sd1.intersection(sd2):
+ value1 = first[key]
+ value2 = second[key]
+ if type(value1) in DICT_TYPES:
+ idiff = find_diff(value1, value2)
+ if len(idiff) > 0:
+ diff[key] = idiff
+ elif type(value1) == list and key.lower() not in SKIPLIST:
+ ldiff = []
+ for v1, v2 in zip(value1, value2):
+ idiff = find_diff(v1, v2)
+ ldiff.append(idiff)
+ if len(ldiff) > 0:
+ diff[key] = ldiff
+ else:
+ if value1 != value2:
+ diff[key] = (first[key], second[key])
+ return diff
+
+
@UILoadable(with_ui='ui')
class ExpDescriptionEditor(Qt.QWidget, TaurusBaseWidget):
'''
From 849e2f1422815af0c7c3d98aae07eab1a8419ef9 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Fri, 27 Jul 2018 18:56:28 +0200
Subject: [PATCH 026/652] Create internal Qt.Signal
Add createExpConfChangedDialog Signal to create the message box and use
it for futures change events.
---
.../taurus/qt/qtgui/extra_sardana/expdescription.py | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
index aba2c4af3b..248e3fb6ec 100644
--- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
+++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
@@ -147,6 +147,12 @@ def __init__(self, parent=None, door=None, plotsButton=True):
self._dirty = False
self._dirtyMntGrps = set()
+ # Pending event variables
+ self._expConfChangedDialog = None
+ self.createExpConfChangedDialog = Qt.pyqtSignal()
+ self.connect(self, Qt.SIGNAL('createExpConfChangedDialog'),
+ self._createExpConfChangedDialog)
+
self.connect(self.ui.activeMntGrpCB, Qt.SIGNAL(
'activated (QString)'), self.changeActiveMntGrp)
self.connect(self.ui.createMntGrpBT, Qt.SIGNAL(
@@ -229,6 +235,8 @@ def setModel(self, model):
msname = door.macro_server.getFullName()
self.ui.taurusModelTree.setModel(tghost)
self.ui.sardanaElementTree.setModel(msname)
+ self.connect(door, Qt.SIGNAL("experimentConfigurationChanged"),
+ self._experimentalConfigurationChanged)
def _reloadConf(self, force=False):
if not force and self.isDataChanged():
From e7d18b40b465388bfa048d037ca60405dedb2b9f Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Fri, 27 Jul 2018 18:57:32 +0200
Subject: [PATCH 027/652] Implement message dialog.
---
.../qt/qtgui/extra_sardana/expdescription.py | 97 ++++++++++++++++++-
1 file changed, 96 insertions(+), 1 deletion(-)
diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
index 248e3fb6ec..add95f393e 100644
--- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
+++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
@@ -27,7 +27,9 @@
__all__ = ["ExpDescriptionEditor"]
-from taurus.external.qt import Qt
+
+import json
+from taurus.external.qt import Qt, QtCore
import copy
import taurus
import taurus.core
@@ -198,6 +200,99 @@ def __init__(self, parent=None, door=None, plotsButton=True):
# Taurus Configuration properties and delegates
self.registerConfigDelegate(self.ui.channelEditor)
+ def _getResumeText(self):
+ msg_resume = ' Summary of changes:
'
+ mnt_grps = ''
+ envs = ''
+ for key in self._diff:
+ if key == 'MntGrpConfigs':
+ for names in self._diff['MntGrpConfigs']:
+ if mnt_grps != '':
+ mnt_grps += ', '
+ mnt_grps += '{0}'.format(names)
+ else:
+ if envs != '':
+ envs += ', '
+ envs += '{0}'.format(key)
+ values = ''
+ if mnt_grps != '':
+ values += '- Measurement Groups: {0}
'.format(mnt_grps)
+ if envs != '':
+ values += '- Enviroment variables: {0}
'.format(envs)
+
+ msg_resume += values
+ msg_resume += '
'
+ return msg_resume
+
+ def _getDetialsText(self):
+ msg_detials = 'Changes {key: [external, local], ...}\n'
+ msg_detials += json.dumps(self._diff, sort_keys=True)
+ return msg_detials
+
+ def _createExpConfChangedDialog(self):
+ msg_details = self._getDetialsText()
+ msg_info = self._getResumeText()
+ self._expConfChangedDialog = Qt.QMessageBox()
+ self._expConfChangedDialog.setIcon(Qt.QMessageBox.Warning)
+ self._expConfChangedDialog.setWindowTitle('External Changes')
+ # text = '''
+ #
+ # The experiment configuration has been modified externally.
+ # You can either:
Load the new configuration from the
+ # door
+ # (discarding local changes) or Keep your local configuration
+ # (would eventually overwrite the external changes when applying).
+ #
'''
+ text = '''
+ The experiment configuration has been modified externally.
+ You can either:
+
+ - Load the new configuration from the door
+ (discarding local changes)
+ - Keep your local configuration (would eventually
+ overwrite the external changes when applying)
+
+ '''
+ self._expConfChangedDialog.setText(text)
+ self._expConfChangedDialog.setTextFormat(QtCore.Qt.RichText)
+ self._expConfChangedDialog.setInformativeText(msg_info)
+ self._expConfChangedDialog.setDetailedText(msg_details)
+ self._expConfChangedDialog.setStandardButtons(Qt.QMessageBox.Ok |
+ Qt.QMessageBox.Cancel)
+ btn_ok = self._expConfChangedDialog.button(Qt.QMessageBox.Ok)
+ btn_ok.setText('Load')
+ btn_cancel = self._expConfChangedDialog.button(Qt.QMessageBox.Cancel)
+ btn_cancel.setText('Keep')
+ result = self._expConfChangedDialog.exec_()
+ self._expConfChangedDialog = None
+ if result == Qt.QMessageBox.Ok:
+ self._reloadConf(force=True)
+
+ @QtCore.pyqtSlot()
+ def _experimentalConfigurationChanged(self):
+ try:
+ self._diff = self._getDiff()
+ except Exception:
+ return
+
+ if len(self._diff) > 0:
+ if self._expConfChangedDialog is None:
+ self.emit(Qt.SIGNAL('createExpConfChangedDialog'))
+ else:
+ msg_details = self._getDetialsText()
+ msg_info = self._getResumeText()
+ self._expConfChangedDialog.setInformativeText(msg_info)
+ self._expConfChangedDialog.setDetailedText(msg_details)
+
+ def _getDiff(self):
+ door = self.getModelObj()
+ if door is None:
+ return []
+
+ new_conf = door.getExperimentConfiguration()
+ old_conf = self._localConfig
+ return find_diff(new_conf, old_conf)
+
def getModelClass(self):
'''reimplemented from :class:`TaurusBaseWidget`'''
return taurus.core.taurusdevice.TaurusDevice
From e9f012937938950f6efcd20b88555ebe31ff11c1 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 30 Jul 2018 10:17:51 +0200
Subject: [PATCH 028/652] Fix flake8 errors
---
src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
index add95f393e..4ff2ea806b 100644
--- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
+++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
@@ -247,9 +247,9 @@ def _createExpConfChangedDialog(self):
The experiment configuration has been modified externally.
You can either:
- - Load the new configuration from the door
+
- Load the new configuration from the door
(discarding local changes)
- - Keep your local configuration (would eventually
+
- Keep your local configuration (would eventually
overwrite the external changes when applying)
'''
From a640f723064d874695053209331d48a65705e6d2 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 30 Jul 2018 11:55:17 +0200
Subject: [PATCH 029/652] Change reset button label
Change label to Reload which is more in line with its function.
---
src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
index 4ff2ea806b..610e0617dd 100644
--- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
+++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
@@ -133,6 +133,8 @@ def __init__(self, parent=None, door=None, plotsButton=True):
self.loadUi()
self.ui.buttonBox.setStandardButtons(
Qt.QDialogButtonBox.Reset | Qt.QDialogButtonBox.Apply)
+ self.ui.buttonBox.button(Qt.QDialogButtonBox.Reset).setText('Reload')
+
newperspectivesDict = copy.deepcopy(
self.ui.sardanaElementTree.KnownPerspectives)
#newperspectivesDict[self.ui.sardanaElementTree.DftPerspective]['model'] = [SardanaAcquirableProxyModel, SardanaElementPlainModel]
From 0582890aed65e67e026fe5b9024d76087b8a64fd Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 30 Jul 2018 15:21:22 +0200
Subject: [PATCH 030/652] Add Start AcqSyncType
---
src/sardana/pool/pooldefs.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sardana/pool/pooldefs.py b/src/sardana/pool/pooldefs.py
index 740118d72a..e5ac46e667 100644
--- a/src/sardana/pool/pooldefs.py
+++ b/src/sardana/pool/pooldefs.py
@@ -114,7 +114,7 @@ class SynchParam(SynchEnum):
# Trigger = 0
# Gate = 1
-AcqSynchType = Enumeration("AcqSynchType", ["Trigger", "Gate"])
+AcqSynchType = Enumeration("AcqSynchType", ["Trigger", "Gate", "Start"])
# TODO: convert to to python enums, but having in ming problems with
From 11b9cacdb4fcce6eafb63eca03cb3a99ad2bd0c4 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 30 Jul 2018 15:33:00 +0200
Subject: [PATCH 031/652] Add two new AcqSync
Add SoftwareStart and HardwareStart as new acquisition synchronizations.
---
src/sardana/pool/pooldefs.py | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/sardana/pool/pooldefs.py b/src/sardana/pool/pooldefs.py
index e5ac46e667..7ca8be11c6 100644
--- a/src/sardana/pool/pooldefs.py
+++ b/src/sardana/pool/pooldefs.py
@@ -125,6 +125,8 @@ class AcqSynch(Enumeration):
HardwareTrigger = 1
SoftwareGate = 2
HardwareGate = 3
+ SoftwareStart = 4
+ HardwareStart = 5
@classmethod
def from_synch_type(self, software, synch_type):
@@ -141,6 +143,11 @@ def from_synch_type(self, software, synch_type):
return AcqSynch.SoftwareGate
else:
return AcqSynch.HardwareGate
+ elif synch_type is AcqSynchType.Start:
+ if software:
+ return AcqSynch.SoftwareStart
+ else:
+ return AcqSynch.HardwareStart
else:
raise ValueError("Unable to determine AcqSynch from %s" %
synch_type)
From c84c47d4f311c6098329d2587931fd01bc44c7fc Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 30 Jul 2018 15:55:31 +0200
Subject: [PATCH 032/652] Fix flake8 errors
---
src/sardana/pool/pooldefs.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/sardana/pool/pooldefs.py b/src/sardana/pool/pooldefs.py
index 7ca8be11c6..8c995527ed 100644
--- a/src/sardana/pool/pooldefs.py
+++ b/src/sardana/pool/pooldefs.py
@@ -68,7 +68,8 @@ class SynchDomain(SynchEnum):
- Time - describes the synchronization in time domain
- Position - describes the synchronization in position domain
- - Monitor - not used at the moment but foreseen for synchronization on monitor
+ - Monitor - not used at the moment but foreseen for synchronization on
+ monitor
.. note::
The SynchDomain class has been included in Sardana
@@ -81,7 +82,8 @@ class SynchDomain(SynchEnum):
Monitor = 2
# - Default - the controller selects the most appropriate domain:
# for active events the precedence should be first Position and then Time
-# for passive events the precedence should be first Time and then Position
+# for passive events the precedence should be first Time and then
+# Position
# Default = 3
@@ -114,6 +116,7 @@ class SynchParam(SynchEnum):
# Trigger = 0
# Gate = 1
+
AcqSynchType = Enumeration("AcqSynchType", ["Trigger", "Gate", "Start"])
From b4a2bbb1a537db1af96e37eab4f39836897b5986 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Wed, 1 Aug 2018 12:36:32 +0200
Subject: [PATCH 033/652] Add auto-update as optional parameter
---
src/sardana/spock/magic.py | 3 ++-
.../qt/qtgui/extra_sardana/expdescription.py | 24 ++++++++++++++-----
2 files changed, 20 insertions(+), 7 deletions(-)
diff --git a/src/sardana/spock/magic.py b/src/sardana/spock/magic.py
index db0e0bf9b3..73a66b081a 100644
--- a/src/sardana/spock/magic.py
+++ b/src/sardana/spock/magic.py
@@ -62,9 +62,10 @@ def expconf(self, parameter_s=''):
# https://sourceforge.net/p/sardana/tickets/10/
import subprocess
import sys
-
fname = sys.modules[ExpDescriptionEditor.__module__].__file__
args = ['python', fname, doorname]
+ if parameter_s == '--auto-update':
+ args.insert(2, parameter_s)
subprocess.Popen(args)
# ===========================================================================
diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
index 610e0617dd..ad4e176eb9 100644
--- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
+++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
@@ -127,7 +127,8 @@ class ExpDescriptionEditor(Qt.QWidget, TaurusBaseWidget):
using the `ExperimentConfiguration` environmental variable for that Door.
'''
- def __init__(self, parent=None, door=None, plotsButton=True):
+ def __init__(self, parent=None, door=None, plotsButton=True,
+ autoUpdate=False):
Qt.QWidget.__init__(self, parent)
TaurusBaseWidget.__init__(self, 'ExpDescriptionEditor')
self.loadUi()
@@ -150,6 +151,7 @@ def __init__(self, parent=None, door=None, plotsButton=True):
self._originalConfiguration = None
self._dirty = False
self._dirtyMntGrps = set()
+ self._autoUpdate = autoUpdate
# Pending event variables
self._expConfChangedDialog = None
@@ -598,10 +600,10 @@ def onPlotsButtonToggled(self, checked):
self.__plotManager = None
-def demo(model=None):
+def demo(model=None, autoUpdate=False):
"""Experiment configuration"""
#w = main_ChannelEditor()
- w = ExpDescriptionEditor()
+ w = ExpDescriptionEditor(autoUpdate=autoUpdate)
if model is None:
from sardana.taurus.qt.qtgui.extra_macroexecutor import \
TaurusMacroConfigurationDialog
@@ -621,14 +623,23 @@ def main():
app = Application.instance()
owns_app = app is None
-
if owns_app:
+ import taurus.core.util.argparse
+ parser = taurus.core.util.argparse.get_taurus_parser()
+ parser.usage = "%prog [options] "
+ parser.add_option('--auto-update', dest='auto_update',
+ action='store_true',
+ help='Set auto update of experimental configuration')
app = Application(app_name="Exp. Description demo", app_version="1.0",
- org_domain="Sardana", org_name="Tango community")
+ org_domain="Sardana", org_name="Tango community",
+ cmd_line_parser=parser)
args = app.get_command_line_args()
+ opt = app.get_command_line_options()
+
if len(args) == 1:
- w = demo(model=args[0])
+ auto_update = opt.auto_update is not None
+ w = demo(model=args[0], autoUpdate=auto_update)
else:
w = demo()
w.show()
@@ -638,5 +649,6 @@ def main():
else:
return w
+
if __name__ == "__main__":
main()
From f93e173a5200f0ea9553a49d5a9683ecd154ed11 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Wed, 1 Aug 2018 13:28:09 +0200
Subject: [PATCH 034/652] Implement auto-update mechanism
---
.../qt/qtgui/extra_sardana/expdescription.py | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
index ad4e176eb9..4fef4ca554 100644
--- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
+++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
@@ -280,13 +280,16 @@ def _experimentalConfigurationChanged(self):
return
if len(self._diff) > 0:
- if self._expConfChangedDialog is None:
- self.emit(Qt.SIGNAL('createExpConfChangedDialog'))
+ if self._autoUpdate:
+ self._reloadConf(force=True)
else:
- msg_details = self._getDetialsText()
- msg_info = self._getResumeText()
- self._expConfChangedDialog.setInformativeText(msg_info)
- self._expConfChangedDialog.setDetailedText(msg_details)
+ if self._expConfChangedDialog is None:
+ self.emit(Qt.SIGNAL('createExpConfChangedDialog'))
+ else:
+ msg_details = self._getDetialsText()
+ msg_info = self._getResumeText()
+ self._expConfChangedDialog.setInformativeText(msg_info)
+ self._expConfChangedDialog.setDetailedText(msg_details)
def _getDiff(self):
door = self.getModelObj()
From b04c91bd9ab1dd5b569f1c26c210c9a4d172e561 Mon Sep 17 00:00:00 2001
From: Daniel Schick
Date: Wed, 1 Aug 2018 13:58:46 +0200
Subject: [PATCH 035/652] typo
---
src/sardana/macroserver/macros/standard.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py
index ac2b9fd366..27773378f0 100644
--- a/src/sardana/macroserver/macros/standard.py
+++ b/src/sardana/macroserver/macros/standard.py
@@ -911,7 +911,7 @@ def run(self, ScanFileDir_list, ScanID):
elif not os.path.exists(path):
# check if folder exists
self.error('Path %s does not exists on the host of the '
- 'MacroServerand has to be created in '
+ 'MacroServer and has to be created in '
'advance.' % path)
return
else:
From 17411bf5cf33dd70cfc77da00ccd91288e70e3de Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Thu, 2 Aug 2018 17:22:54 +0200
Subject: [PATCH 036/652] Add warning message on auto-update mode
---
.../qt/qtgui/extra_sardana/expdescription.py | 22 ++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
index 4fef4ca554..d68c66d417 100644
--- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
+++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
@@ -29,7 +29,7 @@
import json
-from taurus.external.qt import Qt, QtCore
+from taurus.external.qt import Qt, QtCore, QtGui
import copy
import taurus
import taurus.core
@@ -151,7 +151,12 @@ def __init__(self, parent=None, door=None, plotsButton=True,
self._originalConfiguration = None
self._dirty = False
self._dirtyMntGrps = set()
+
+ # Add warning message to the Widget
self._autoUpdate = autoUpdate
+ if self._autoUpdate:
+ w = self._getWarningWidget()
+ self.ui.verticalLayout_3.insertWidget(0, w)
# Pending event variables
self._expConfChangedDialog = None
@@ -204,6 +209,21 @@ def __init__(self, parent=None, door=None, plotsButton=True,
# Taurus Configuration properties and delegates
self.registerConfigDelegate(self.ui.channelEditor)
+ def _getWarningWidget(self):
+ w = Qt.QWidget()
+ layout = QtGui.QHBoxLayout()
+ w.setLayout(layout)
+ icon = QtGui.QIcon.fromTheme('dialog-warning')
+ pixmap =QtGui.QPixmap(icon.pixmap(QtCore.QSize(32, 32)))
+ label_icon = QtGui.QLabel()
+ label_icon.setPixmap(pixmap)
+ label = QtGui.QLabel('This experiment configuration dialog '
+ 'updates automatically on external changes!')
+ layout.addWidget(label_icon)
+ layout.addWidget(label)
+ layout.addStretch(1)
+ return w
+
def _getResumeText(self):
msg_resume = ' Summary of changes:
'
mnt_grps = ''
From 0f1d9ef42a0cc3e0f309c81ac2fc20675c4041a2 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Thu, 2 Aug 2018 17:47:38 +0200
Subject: [PATCH 037/652] Fix flake8 errors
---
src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
index d68c66d417..a57db3c626 100644
--- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
+++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
@@ -214,7 +214,7 @@ def _getWarningWidget(self):
layout = QtGui.QHBoxLayout()
w.setLayout(layout)
icon = QtGui.QIcon.fromTheme('dialog-warning')
- pixmap =QtGui.QPixmap(icon.pixmap(QtCore.QSize(32, 32)))
+ pixmap = QtGui.QPixmap(icon.pixmap(QtCore.QSize(32, 32)))
label_icon = QtGui.QLabel()
label_icon.setPixmap(pixmap)
label = QtGui.QLabel('This experiment configuration dialog '
From 3405457b2778f69d6262dab86605aaa906c7d2c6 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Thu, 2 Aug 2018 18:17:31 +0200
Subject: [PATCH 038/652] Return a None instead of OptionalParam
---
doc/source/devel/howto_macros/macros_general.rst | 2 +-
src/sardana/macroserver/msparameter.py | 6 ++++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst
index b2155540c7..25897c470a 100644
--- a/doc/source/devel/howto_macros/macros_general.rst
+++ b/doc/source/devel/howto_macros/macros_general.rst
@@ -345,7 +345,7 @@ So, here is an example how to define and use a optional parameter::
def run(self, itime, mntgrp):
bkp_active_mntgrp = None
try:
- if mntgrp is not OptionalParam:
+ if mntgrp is not None:
bkp_active_mntgrp = self.getEnv('ActiveMntGrp')
self.setEnv('ActiveMntGrp', mntgrp.name)
self.info('Use "{0}" measurement '
diff --git a/src/sardana/macroserver/msparameter.py b/src/sardana/macroserver/msparameter.py
index 9cf820989f..a85bdaec1d 100644
--- a/src/sardana/macroserver/msparameter.py
+++ b/src/sardana/macroserver/msparameter.py
@@ -407,6 +407,7 @@ def decodeNormal(self, raw_param, param_def):
"""
param_type = param_def["type"]
name = param_def["name"]
+ optional_param = False
if isinstance(param_type, list):
param = self.decodeRepeat(raw_param, param_def)
else:
@@ -424,7 +425,8 @@ def decodeNormal(self, raw_param, param_def):
if value is None:
raise MissingParam("'%s' not specified" % name)
elif isinstance(value, OptionalParamClass):
- param = OptionalParam
+ param = None
+ optional_param = True
else:
# cast to sting to fulfill with ParamType API
param = param_type.getObj(str(value))
@@ -433,7 +435,7 @@ def decodeNormal(self, raw_param, param_def):
raise WrongParamType(e.message)
except UnknownParamObj as e:
raise WrongParam(e.message)
- if param is None:
+ if param is None and not optional_param:
msg = 'Could not create %s parameter "%s" for "%s"' % \
(param_type.getName(), name, raw_param)
raise WrongParam(msg)
From 1b8b8c009da00bb87197307fcc73c116001421a3 Mon Sep 17 00:00:00 2001
From: Grzegorz Kowalski
Date: Mon, 6 Aug 2018 15:50:49 +0200
Subject: [PATCH 039/652] change logstash to logstash-async
---
src/sardana/tango/core/util.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py
index 91b7af4ba9..20ff712945 100644
--- a/src/sardana/tango/core/util.py
+++ b/src/sardana/tango/core/util.py
@@ -914,7 +914,7 @@ def prepare_logstash(args):
log_messages = []
try:
- import logstash
+ from logstash_async.handler import AsynchronousLogstashHandler
except ImportError:
msg = ("Unable to import logstash. Skipping logstash "
+ "configuration...", )
@@ -953,7 +953,8 @@ def get_logstash_conf(dev_name):
if host is not None:
root = Logger.getRootLog()
- handler = logstash.TCPLogstashHandler(host, port, version=1)
+ handler = AsynchronousLogstashHandler(host, port,
+ database_path="/tmp/sardana-logstash-cache.db")
root.addHandler(handler)
msg = ("Log is being sent to logstash listening on %s:%d",
host, port)
From e6c574d85737e1aeb532b02709e471f3194168e3 Mon Sep 17 00:00:00 2001
From: Grzegorz Kowalski
Date: Mon, 6 Aug 2018 16:47:55 +0200
Subject: [PATCH 040/652] don't use full path as program_name
---
src/sardana/tango/core/util.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py
index 20ff712945..cb0c1f0ceb 100644
--- a/src/sardana/tango/core/util.py
+++ b/src/sardana/tango/core/util.py
@@ -955,6 +955,9 @@ def get_logstash_conf(dev_name):
root = Logger.getRootLog()
handler = AsynchronousLogstashHandler(host, port,
database_path="/tmp/sardana-logstash-cache.db")
+ # don't use full path for program_name
+ handler._create_formatter_if_necessary()
+ _, handler.formatter._program_name = os.path.split(handler.formatter._program_name)
root.addHandler(handler)
msg = ("Log is being sent to logstash listening on %s:%d",
host, port)
From 938ff107d291bd80022bf57dd309d3f2209e3c59 Mon Sep 17 00:00:00 2001
From: Grzegorz Kowalski
Date: Mon, 6 Aug 2018 17:11:00 +0200
Subject: [PATCH 041/652] add configuration endpoints for logstash cache
---
src/sardana/tango/core/util.py | 23 ++++++++++++++------
src/sardana/tango/macroserver/MacroServer.py | 11 +++++++++-
src/sardana/tango/pool/Pool.py | 11 +++++++++-
3 files changed, 36 insertions(+), 9 deletions(-)
diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py
index cb0c1f0ceb..ffb817fce9 100644
--- a/src/sardana/tango/core/util.py
+++ b/src/sardana/tango/core/util.py
@@ -921,7 +921,7 @@ def prepare_logstash(args):
log_messages.append(msg,)
return log_messages
- def get_logstash_conf(dev_name):
+ def get_logstash_conf(dev_name, class_name=None):
try:
props = db.get_device_property(dev_name, "LogstashHost")
host = props["LogstashHost"][0]
@@ -932,7 +932,17 @@ def get_logstash_conf(dev_name):
port = int(props["LogstashPort"][0])
except IndexError:
port = 12345
- return host, port
+ try:
+ props = db.get_device_property(dev_name, "LogstashCacheDbPath")
+ cache_db_path = int(props["LogstashCacheDbPath"][0])
+ except IndexError:
+ if class_name == "Pool":
+ cache_db_path = "/tmp/sardana-pool-logstash-cache.db"
+ elif class_name == "MacroServer":
+ cache_db_path = "/tmp/sardana-ms-logstash-cache.db"
+ else:
+ cache_db_path = "/tmp/sardana-logstash-cache.db"
+ return host, port, cache_db_path
db = Database()
@@ -942,19 +952,18 @@ def get_logstash_conf(dev_name):
if bin_name in ["Pool", "MacroServer"]:
class_name = bin_name
dev_name = get_dev_from_class_server(db, class_name, server_name)[0]
- host, port = get_logstash_conf(dev_name)
+ host, port, cache = get_logstash_conf(dev_name, class_name)
else:
dev_name = get_dev_from_class_server(db, "Pool", server_name)[0]
- host, port = get_logstash_conf(dev_name)
+ host, port, cache = get_logstash_conf(dev_name)
if host is None:
dev_name = get_dev_from_class_server(db, "MacroServer",
server_name)[0]
- host, port = get_logstash_conf(dev_name)
+ host, port, cache = get_logstash_conf(dev_name)
if host is not None:
root = Logger.getRootLog()
- handler = AsynchronousLogstashHandler(host, port,
- database_path="/tmp/sardana-logstash-cache.db")
+ handler = AsynchronousLogstashHandler(host, port, database_path=cache)
# don't use full path for program_name
handler._create_formatter_if_necessary()
_, handler.formatter._program_name = os.path.split(handler.formatter._program_name)
diff --git a/src/sardana/tango/macroserver/MacroServer.py b/src/sardana/tango/macroserver/MacroServer.py
index 13e8c1be51..7a0e73ed3f 100644
--- a/src/sardana/tango/macroserver/MacroServer.py
+++ b/src/sardana/tango/macroserver/MacroServer.py
@@ -397,7 +397,16 @@ class MacroServerClass(SardanaDeviceClass):
"basis. Backwards incompatible changes (up to and including "
"its removal) may occur if deemed necessary by the "
"core developers.",
- None]
+ None],
+ 'LogstashCacheDbPath':
+ [DevString,
+ "Path to the Logstash cache database [default: "
+ "/tmp/sardana-ms-logstash-cache.db]. "
+ "This property has been included in Sardana on a provisional "
+ "basis. Backwards incompatible changes (up to and including "
+ "its removal) may occur if deemed necessary by the "
+ "core developers.",
+ "/tmp/sardana-ms-logstash-cache.db"]
}
# Command definitions
diff --git a/src/sardana/tango/pool/Pool.py b/src/sardana/tango/pool/Pool.py
index 4930368afa..eda5ec3a33 100644
--- a/src/sardana/tango/pool/Pool.py
+++ b/src/sardana/tango/pool/Pool.py
@@ -1380,7 +1380,16 @@ class PoolClass(PyTango.DeviceClass):
"basis. Backwards incompatible changes (up to and including "
"its removal) may occur if deemed necessary by the "
"core developers.",
- None]
+ None],
+ 'LogstashCacheDbPath':
+ [PyTango.DevString,
+ "Path to the Logstash cache database [default: "
+ "/tmp/sardana-pool-logstash-cache.db]. "
+ "This property has been included in Sardana on a provisional "
+ "basis. Backwards incompatible changes (up to and including "
+ "its removal) may occur if deemed necessary by the "
+ "core developers.",
+ "/tmp/sardana-pool-logstash-cache.db"]
}
# Command definitions
From 361b5d64eb1c7d07daff476ad22fcb2f62ced7f8 Mon Sep 17 00:00:00 2001
From: Grzegorz Kowalski
Date: Tue, 7 Aug 2018 12:45:35 +0200
Subject: [PATCH 042/652] fix logstash cache path property
---
src/sardana/tango/core/util.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sardana/tango/core/util.py b/src/sardana/tango/core/util.py
index ffb817fce9..e2631eca6e 100644
--- a/src/sardana/tango/core/util.py
+++ b/src/sardana/tango/core/util.py
@@ -934,7 +934,7 @@ def get_logstash_conf(dev_name, class_name=None):
port = 12345
try:
props = db.get_device_property(dev_name, "LogstashCacheDbPath")
- cache_db_path = int(props["LogstashCacheDbPath"][0])
+ cache_db_path = props["LogstashCacheDbPath"][0]
except IndexError:
if class_name == "Pool":
cache_db_path = "/tmp/sardana-pool-logstash-cache.db"
From 97da009fba7b4bc60e2c1727aae6cf4dffa620c1 Mon Sep 17 00:00:00 2001
From: Grzegorz Kowalski
Date: Fri, 10 Aug 2018 16:52:27 +0200
Subject: [PATCH 043/652] Bump version 2.5.0 to 2.5.1-alpha
---
.bumpversion.cfg | 2 +-
src/sardana/release.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 49c6dfb671..99e68d31ca 100755
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -3,7 +3,7 @@ commit = True
message = Bump version {current_version} to {new_version}
tag = False
tag_name = {new_version}
-current_version = 2.5.0
+current_version = 2.5.1-alpha
parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-(?P[a-z]+))?
serialize =
{major}.{minor}.{patch}-{release}
diff --git a/src/sardana/release.py b/src/sardana/release.py
index df919e5d06..118a97d4de 100644
--- a/src/sardana/release.py
+++ b/src/sardana/release.py
@@ -47,7 +47,7 @@
# we use semantic versioning (http://semver.org/) and we update it using the
# bumpversion script (https://github.com/peritus/bumpversion)
-version = '2.5.0'
+version = '2.5.1-alpha'
# generate version_info and revision (**deprecated** since v 2.1.2--alpha).
if '-' in version:
From c31e4a125744fe0a21013683a51ffcff28e21b80 Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Fri, 10 Aug 2018 18:17:25 +0200
Subject: [PATCH 044/652] Update how_to_release.md
---
doc/how_to_release.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/how_to_release.md b/doc/how_to_release.md
index 62703082dc..cccdbf9064 100644
--- a/doc/how_to_release.md
+++ b/doc/how_to_release.md
@@ -63,7 +63,7 @@ tested.
* Ilustration: sardana or official logo (use png)
* Summary: short summary of the news (do not include the whole changelog here..)
* Categories: Release
- 2. After submitting click on Modify this content text of the area <> and provide detailes of the release e.g. changelog.
+ 2. After submitting click on Modify this content text of the area \<\\> and provide detailes of the release e.g. changelog.
12. Notify mailing lists (sardana-users@lists.sourceforge.net, sardana-devel@lists.sourceforge.net, info@tango-controls.org)
## Manual test checklist
From 91fc6b37766913487f9b54fc4ad9261d92fa9690 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Tue, 21 Aug 2018 09:34:23 +0200
Subject: [PATCH 045/652] Avoid multi connections to the same Qt signals
---
src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
index beed9b30ca..4d280e6746 100644
--- a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
+++ b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
@@ -61,6 +61,7 @@ def __init__(self, name, qt_parent=None, **kw):
self.call__init__(BaseDoor, name, **kw)
self._mntgrp_connected = []
self._use_experimet_configuration = False
+ self._connections_prepared = False
def resultReceived(self, log_name, result):
res = BaseDoor.resultReceived(self, log_name, result)
@@ -107,11 +108,13 @@ def logReceived(self, log_name, output):
return res
def _prepare_connections(self):
- if not self._use_experimet_configuration:
+ if not self._use_experimet_configuration and \
+ not self._connections_prepared:
self.connect(self.macro_server, Qt.SIGNAL("environmentChanged"),
self._experimentalConfiguration)
self.connect(self.macro_server, Qt.SIGNAL("elementsChanged"),
self._elementsChanged)
+ self._connections_prepared = True
def _elementsChanged(self):
len_mnt_grps_connected = len(self._mntgrp_connected)
From 7cbdf0f022b9f6230e82061f7522daaa5a7d0496 Mon Sep 17 00:00:00 2001
From: teresa
Date: Thu, 30 Aug 2018 11:28:56 +0200
Subject: [PATCH 046/652] Adapting for 4c diffrac
---
src/sardana/macroserver/macros/hkl.py | 87 +++++++++++++++------------
1 file changed, 50 insertions(+), 37 deletions(-)
diff --git a/src/sardana/macroserver/macros/hkl.py b/src/sardana/macroserver/macros/hkl.py
index 3073a540b4..5d391fc8e7 100644
--- a/src/sardana/macroserver/macros/hkl.py
+++ b/src/sardana/macroserver/macros/hkl.py
@@ -1,25 +1,3 @@
-##############################################################################
-##
-# 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 .
-##
-#############################################################################
"""
Macro library containning diffractometer related macros for the macros
@@ -27,14 +5,6 @@
"""
-__all__ = ["addreflection", "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"]
-
-
-
# TODO: use taurus instead of PyTango API e.g. read_attribute,
# write_attribute. This module is full of PyTango centric calls.
@@ -43,6 +13,11 @@
# 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
@@ -110,11 +85,17 @@ def prepare(self):
self.angle_names.append("delta")
if self.nb_motors == 4:
- self.labelmotor = {'Omega': "omega",
- 'Chi': "chi", 'Phi': "phi", 'Tth': "tth"}
+ self.labelmotor = {'Omega': self.angle_names[0], 'Chi': self.angle_names[1],
+ 'Phi': self.angle_names[2], 'Tth': self.angle_names[3]}
elif self.nb_motors == 6:
- self.labelmotor = {'Mu': "mu", 'Theta': "omega", 'Chi': "chi",
- 'Phi': "phi", 'Gamma': "gamma", 'Delta': "delta"}
+ self.labelmotor = {'Mu': self.angle_names[0], 'Theta': self.angle_names[1],
+ 'Chi': self.angle_names[2], 'Phi': self.angle_names[3],
+ 'Gamma': self.angle_names[4], 'Delta': self.angle_names[5]}
+ elif self.nb_motors == 7:
+ self.labelmotor = {'Omega_t': self.angle_names[0], 'Mu': self.angle_names[1],
+ 'Omega': self.angle_names[2], 'Chi': self.angle_names[3],
+ 'Phi': self.angle_names[4], 'Gamma': self.angle_names[5],
+ 'Delta': self.angle_names[6]}
prop = self.diffrac.get_property(['DiffractometerType'])
for v in prop['DiffractometerType']:
@@ -363,6 +344,18 @@ def run(self, H, K, L, Trajectory):
str_pos[self.labelmotor["Omega"]],
str_pos[self.labelmotor["Chi"]],
str_pos[self.labelmotor["Phi"]]))
+ elif self.nb_motors == 7:
+ self.output("%10s %11s %12s %11s %10s %11s %11s" %
+ ("Omega_t", "Mu", "Omega", "Chi",
+ "Phi", "Gamma", "Delta"))
+ self.output("%10s %11s %12s %11s %10s %11s %11s" %
+ (str_pos[self.labelmotor["Omega_t"]],
+ str_pos[self.labelmotor["Mu"]],
+ str_pos[self.labelmotor["Omega"]],
+ str_pos[self.labelmotor["Chi"]],
+ str_pos[self.labelmotor["Phui"]],
+ str_pos[self.labelmotor["Gamma"]],
+ str_pos[self.labelmotor["Delta"]]))
@@ -601,7 +594,7 @@ def run(self):
self.output("%s %7.5f" % ("Wavelength = ", self.diffrac.WaveLength))
self.output("")
-
+
if self.nb_motors == 6:
str_pos1 = "%7.5f" % self.getDevice(
self.angle_device_names[self.labelmotor["Delta"]]).Position
@@ -616,7 +609,7 @@ def run(self):
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.labelmotor["Delta"], self.labelmotor["Theta"], self.labelmotor["Chi"], self.labelmotor["Phi"], self.labelmotor["Mu"], self.labelmotor["Gamma"]))
self.output("%10s %11s %12s %11s %10s %11s" %
(str_pos1, str_pos2, str_pos3, str_pos4, str_pos5,
str_pos6))
@@ -630,9 +623,29 @@ def run(self):
str_pos4 = "%7.5f" % self.getDevice(
self.angle_device_names[self.labelmotor["Phi"]]).Position
self.output("%10s %11s %12s %11s" %
- ("Tth", "Omega", "Chi", "Phi"))
+ (self.labelmotor["Tth"], self.labelmotor["Omega"], self.labelmotor["Chi"], self.labelmotor["Phi"]))
self.output("%10s %11s %12s %11s" %
(str_pos1, str_pos2, str_pos3, str_pos4))
+ elif self.nb_motors == 7:
+ str_pos1 = "%7.5f" % self.getDevice(
+ self.angle_device_names[self.labelmotor["Omega_t"]]).Position
+ str_pos2 = "%7.5f" % self.getDevice(
+ self.angle_device_names[self.labelmotor["Mu"]]).Position
+ str_pos3 = "%7.5f" % self.getDevice(
+ self.angle_device_names[self.labelmotor["Omega"]]).Position
+ str_pos4 = "%7.5f" % self.getDevice(
+ self.angle_device_names[self.labelmotor["Chi"]]).Position
+ str_pos5 = "%7.5f" % self.getDevice(
+ self.angle_device_names[self.labelmotor["Phi"]]).Position
+ str_pos6 = "%7.5f" % self.getDevice(
+ self.angle_device_names[self.labelmotor["Gamma"]]).Position
+ str_pos7 = "%7.5f" % self.getDevice(
+ self.angle_device_names[self.labelmotor["Delta"]]).Position
+ self.output("%10s %11s %12s %11s %10s %11s %11s" %
+ (self.labelmotor["Omega_t"], self.labelmotor["Mu"], self.labelmotor["Omega"], self.labelmotor["Chi"], self.labelmotor["Phi"], self.labelmotor["Gamma"], self.labelmotor["Delta"]))
+ self.output("%10s %11s %12s %11s %10s %11s %11s" %
+ (str_pos1, str_pos2, str_pos3, str_pos4, str_pos5,
+ str_pos6, str_pos7))
self.setEnv('Q', [self.h_device.position, self.k_device.position,
self.l_device.position, self.diffrac.WaveLength])
From 3d50f807e233d7366ddda857f6107dedc3880d38 Mon Sep 17 00:00:00 2001
From: teresa
Date: Fri, 31 Aug 2018 09:05:37 +0200
Subject: [PATCH 047/652] Adding diffractometer types
---
.../HklPseudoMotorController.py | 45 ++++++++++++++++++-
1 file changed, 43 insertions(+), 2 deletions(-)
diff --git a/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py b/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py
index 738671660d..785f325b38 100644
--- a/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py
+++ b/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py
@@ -452,7 +452,7 @@ def CalcAllPhysical(self, pseudo_pos, curr_physical_pos):
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)
+ 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); "PETRA3 P23 4C": hkl(3), q2(2), qper_qpar(2), tth2(2), petra3_p23_4c_incidence(1), _petra3_p23_4c_emergence(1)
if engine_name == "psi":
values = [pseudo_pos[3]]
elif engine_name == "q":
@@ -463,7 +463,13 @@ def CalcAllPhysical(self, pseudo_pos, curr_physical_pos):
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)
+ elif engine_name == "tth2":
+ values = [pseudo_pos[7], pseudo_pos[8]]
+ elif engine_name == "petra3_p23_4c_incidence":
+ values = [pseudo_pos[9]]
+ elif engine_name == "petra3_p23_4c_emergence":
+ values = [pseudo_pos[10]]
+ 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), "PETRA3 P23 6C": hkl(3), psi(1), q2(2), qper_qpar(2), tth2(2), petra3_p23_6c_incidence(1), _petra3_p23_6c_emergence(1)
if engine_name == "psi":
values = [pseudo_pos[3]]
elif engine_name == "q2":
@@ -472,6 +478,12 @@ def CalcAllPhysical(self, pseudo_pos, curr_physical_pos):
values = [pseudo_pos[6], pseudo_pos[7]]
elif engine_name == "eulerians":
values = [pseudo_pos[8], pseudo_pos[9], pseudo_pos[10]]
+ elif engine_name == "tth2":
+ values = [pseudo_pos[8], pseudo_pos[9]]
+ elif engine_name == "petra3_p23_6c_incidence":
+ values = [pseudo_pos[10]]
+ elif engine_name == "petra3_p23_6c_emergence":
+ values = [pseudo_pos[11]]
# getWavelength updates wavelength in the library in case automatic
# energy update is set. Needed before computing trajectories.
@@ -1396,6 +1408,21 @@ def setComputeHKL(self, value):
# 6C Diffractometers ####################
+class Diffrac6Cp23(DiffracBasis): # DiffractometerType: "PETRA3 P23 6C"
+
+ """ The PseudoMotor controller for the diffractometer"""
+
+ pseudo_motor_roles = "h", "k", "l", "psi", "q", "alpha", "tth2", "alpha_tth2", "incidence", "emergence"
+ motor_roles = "omega_t", "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)
+
class Diffrac6C(DiffracBasis): # DiffractometerType: "PETRA3 P09 EH2"
""" The PseudoMotor controller for the diffractometer"""
@@ -1429,6 +1456,20 @@ def __init__(self, inst, props, *args, **kwargs):
# 4C Diffractometers ####################
+class Diffrac4Cp23(DiffracBasis): # DiffractometerType: "PETRA3 P23 4C"
+
+ """ The PseudoMotor controller for the diffractometer"""
+
+ pseudo_motor_roles = "h", "k", "l", "q", "alpha", "qper", "qpar", "tth2", "alpha_tth2", "incidence", "emergence"
+ motor_roles = "omega_t", "mu", "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)
class DiffracE4C(DiffracBasis):
From 6cccd93a624fdcb696abdb7c6876cfecfac49446 Mon Sep 17 00:00:00 2001
From: teresa
Date: Fri, 31 Aug 2018 11:40:45 +0200
Subject: [PATCH 048/652] pep8
---
.../pool/poolcontrollers/HklPseudoMotorController.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py b/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py
index 785f325b38..9de372e631 100644
--- a/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py
+++ b/src/sardana/pool/poolcontrollers/HklPseudoMotorController.py
@@ -1412,7 +1412,8 @@ class Diffrac6Cp23(DiffracBasis): # DiffractometerType: "PETRA3 P23 6C"
""" The PseudoMotor controller for the diffractometer"""
- pseudo_motor_roles = "h", "k", "l", "psi", "q", "alpha", "tth2", "alpha_tth2", "incidence", "emergence"
+ pseudo_motor_roles = "h", "k", "l", "psi", "q", "alpha", "tth2",
+ "alpha_tth2", "incidence", "emergence"
motor_roles = "omega_t", "mu", "omega", "chi", "phi", "gamma", "delta"
def __init__(self, inst, props, *args, **kwargs):
@@ -1456,11 +1457,13 @@ def __init__(self, inst, props, *args, **kwargs):
# 4C Diffractometers ####################
+
class Diffrac4Cp23(DiffracBasis): # DiffractometerType: "PETRA3 P23 4C"
""" The PseudoMotor controller for the diffractometer"""
- pseudo_motor_roles = "h", "k", "l", "q", "alpha", "qper", "qpar", "tth2", "alpha_tth2", "incidence", "emergence"
+ pseudo_motor_roles = "h", "k", "l", "q", "alpha", "qper", "qpar",
+ "tth2", "alpha_tth2", "incidence", "emergence"
motor_roles = "omega_t", "mu", "gamma", "delta"
def __init__(self, inst, props, *args, **kwargs):
From 58c421d777ae3b441f1fadea9b38cef7c44f0f1a Mon Sep 17 00:00:00 2001
From: teresa
Date: Fri, 31 Aug 2018 13:16:48 +0200
Subject: [PATCH 049/652] pep8 errors
---
src/sardana/macroserver/macros/hkl.py | 31 ++++++++++++++++++---------
1 file changed, 21 insertions(+), 10 deletions(-)
diff --git a/src/sardana/macroserver/macros/hkl.py b/src/sardana/macroserver/macros/hkl.py
index 5d391fc8e7..ed79a494ee 100644
--- a/src/sardana/macroserver/macros/hkl.py
+++ b/src/sardana/macroserver/macros/hkl.py
@@ -85,16 +85,24 @@ def prepare(self):
self.angle_names.append("delta")
if self.nb_motors == 4:
- self.labelmotor = {'Omega': self.angle_names[0], 'Chi': self.angle_names[1],
- 'Phi': self.angle_names[2], 'Tth': self.angle_names[3]}
+ self.labelmotor = {'Omega': self.angle_names[0],
+ 'Chi': self.angle_names[1],
+ 'Phi': self.angle_names[2],
+ 'Tth': self.angle_names[3]}
elif self.nb_motors == 6:
- self.labelmotor = {'Mu': self.angle_names[0], 'Theta': self.angle_names[1],
- 'Chi': self.angle_names[2], 'Phi': self.angle_names[3],
- 'Gamma': self.angle_names[4], 'Delta': self.angle_names[5]}
+ self.labelmotor = {'Mu': self.angle_names[0],
+ 'Theta': self.angle_names[1],
+ 'Chi': self.angle_names[2],
+ 'Phi': self.angle_names[3],
+ 'Gamma': self.angle_names[4],
+ 'Delta': self.angle_names[5]}
elif self.nb_motors == 7:
- self.labelmotor = {'Omega_t': self.angle_names[0], 'Mu': self.angle_names[1],
- 'Omega': self.angle_names[2], 'Chi': self.angle_names[3],
- 'Phi': self.angle_names[4], 'Gamma': self.angle_names[5],
+ self.labelmotor = {'Omega_t': self.angle_names[0],
+ 'Mu': self.angle_names[1],
+ 'Omega': self.angle_names[2],
+ 'Chi': self.angle_names[3],
+ 'Phi': self.angle_names[4],
+ 'Gamma': self.angle_names[5],
'Delta': self.angle_names[6]}
prop = self.diffrac.get_property(['DiffractometerType'])
@@ -609,7 +617,9 @@ def run(self):
str_pos6 = "%7.5f" % self.getDevice(
self.angle_device_names[self.labelmotor["Gamma"]]).Position
self.output("%10s %11s %12s %11s %10s %11s" %
- (self.labelmotor["Delta"], self.labelmotor["Theta"], self.labelmotor["Chi"], self.labelmotor["Phi"], self.labelmotor["Mu"], self.labelmotor["Gamma"]))
+ (self.labelmotor["Delta"], self.labelmotor["Theta"],
+ self.labelmotor["Chi"], self.labelmotor["Phi"],
+ self.labelmotor["Mu"], self.labelmotor["Gamma"]))
self.output("%10s %11s %12s %11s %10s %11s" %
(str_pos1, str_pos2, str_pos3, str_pos4, str_pos5,
str_pos6))
@@ -623,7 +633,8 @@ def run(self):
str_pos4 = "%7.5f" % self.getDevice(
self.angle_device_names[self.labelmotor["Phi"]]).Position
self.output("%10s %11s %12s %11s" %
- (self.labelmotor["Tth"], self.labelmotor["Omega"], self.labelmotor["Chi"], self.labelmotor["Phi"]))
+ (self.labelmotor["Tth"], self.labelmotor["Omega"],
+ self.labelmotor["Chi"], self.labelmotor["Phi"]))
self.output("%10s %11s %12s %11s" %
(str_pos1, str_pos2, str_pos3, str_pos4))
elif self.nb_motors == 7:
From 7c5825eb79e04a963ec12890d4e37aa3969d5a3b Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Mon, 10 Sep 2018 11:59:22 +0200
Subject: [PATCH 050/652] Update CHANGELOG.md
---
CHANGELOG.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9bbed99897..dd8ece617d 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,10 @@ This file follows the formats and conventions from [keepachangelog.com]
## [Unreleased]
+### Added
+- Support to "PETRA3 P23 6C" and "PETRA3 P23 4C" diffractometers by means
+of new controller classes (#923)
+
## [2.5.0] 2018-08-10
### Added
From 277aeee8b2d7c7d472ebf5b017a8b0c7a786b6f0 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 10 Sep 2018 12:22:06 +0200
Subject: [PATCH 051/652] Fix flake8
---
src/sardana/macroserver/macros/hkl.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/sardana/macroserver/macros/hkl.py b/src/sardana/macroserver/macros/hkl.py
index ed79a494ee..9069e8ca84 100644
--- a/src/sardana/macroserver/macros/hkl.py
+++ b/src/sardana/macroserver/macros/hkl.py
@@ -602,7 +602,7 @@ def run(self):
self.output("%s %7.5f" % ("Wavelength = ", self.diffrac.WaveLength))
self.output("")
-
+
if self.nb_motors == 6:
str_pos1 = "%7.5f" % self.getDevice(
self.angle_device_names[self.labelmotor["Delta"]]).Position
@@ -653,7 +653,10 @@ def run(self):
str_pos7 = "%7.5f" % self.getDevice(
self.angle_device_names[self.labelmotor["Delta"]]).Position
self.output("%10s %11s %12s %11s %10s %11s %11s" %
- (self.labelmotor["Omega_t"], self.labelmotor["Mu"], self.labelmotor["Omega"], self.labelmotor["Chi"], self.labelmotor["Phi"], self.labelmotor["Gamma"], self.labelmotor["Delta"]))
+ (self.labelmotor["Omega_t"], self.labelmotor["Mu"],
+ self.labelmotor["Omega"], self.labelmotor["Chi"],
+ self.labelmotor["Phi"], self.labelmotor["Gamma"],
+ self.labelmotor["Delta"]))
self.output("%10s %11s %12s %11s %10s %11s %11s" %
(str_pos1, str_pos2, str_pos3, str_pos4, str_pos5,
str_pos6, str_pos7))
From 628e3137ba94c26846e146b8f64ec392a8dbcf16 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 10 Sep 2018 12:23:12 +0200
Subject: [PATCH 052/652] Reintroduce licence header
---
src/sardana/macroserver/macros/hkl.py | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/src/sardana/macroserver/macros/hkl.py b/src/sardana/macroserver/macros/hkl.py
index 9069e8ca84..81e66f2d32 100644
--- a/src/sardana/macroserver/macros/hkl.py
+++ b/src/sardana/macroserver/macros/hkl.py
@@ -1,3 +1,25 @@
+##############################################################################
+##
+# 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 .
+##
+##############################################################################
"""
Macro library containning diffractometer related macros for the macros
From bf8d5b16728e83e0a2a63b54e7df9e40a452a778 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 10 Sep 2018 12:55:02 +0200
Subject: [PATCH 053/652] Fix typo in addreflection
---
src/sardana/macroserver/macros/hkl.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sardana/macroserver/macros/hkl.py b/src/sardana/macroserver/macros/hkl.py
index 81e66f2d32..b7ea6b43f2 100644
--- a/src/sardana/macroserver/macros/hkl.py
+++ b/src/sardana/macroserver/macros/hkl.py
@@ -35,7 +35,7 @@
# 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",
+__all__ = ["addreflection", "affine", "br", "ca", "caa", "ci", "computeub",
"freeze", "getmode", "hklscan", "hscan", "kscan", "latticecal",
"loadcrystal", "lscan", "newcrystal", "or0", "or1", "orswap",
"pa", "savecrystal", "setaz", "setlat", "setmode", "setor0",
From b346beb33244212f63383886ff40d9da2336a032 Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Mon, 10 Sep 2018 14:20:38 +0200
Subject: [PATCH 054/652] Update CHANGELOG.md
---
CHANGELOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dd8ece617d..ffd5bf9850 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,7 +7,7 @@ This file follows the formats and conventions from [keepachangelog.com]
### Added
- Support to "PETRA3 P23 6C" and "PETRA3 P23 4C" diffractometers by means
-of new controller classes (#923)
+of new controller classes and necessary adaptation to macros (#923, #921)
## [2.5.0] 2018-08-10
From e46163260a3be6501366002b7388004f38ced014 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 10 Sep 2018 18:15:34 +0200
Subject: [PATCH 055/652] Move pre-scan and post-scan hooks out of scan_loop
pre-scan and post-scan hooks are executed within the scan_loop. Before
the scan loop, for example, are started the scan recorders making
them to start the scan entry. It makes more sense to not start them
until the pre-scan hooks are executed - they may want discard the scan
or simply may not want to mix the scan header output with the pre-scan
hooks output.
This solution allows to avoid the code duplication in all the scan
specific implementation.
---
src/sardana/macroserver/scan/gscan.py | 42 ++++++---------------------
1 file changed, 9 insertions(+), 33 deletions(-)
diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py
index 83862aea0a..600c803db6 100644
--- a/src/sardana/macroserver/scan/gscan.py
+++ b/src/sardana/macroserver/scan/gscan.py
@@ -944,6 +944,10 @@ def scan(self):
pass
def step_scan(self):
+ macro = self.macro
+ if hasattr(macro, 'getHooks'):
+ for hook in macro.getHooks('pre-scan'):
+ hook()
self.start()
try:
for i in self.scan_loop():
@@ -960,7 +964,11 @@ def step_scan(self):
self._env["endstatus"] = endstatus
self.end()
self.do_restore()
- if endstatus != ScanEndStatus.Normal:
+ if endstatus == ScanEndStatus.Normal:
+ if hasattr(macro, 'getHooks'):
+ for hook in macro.getHooks('post-scan'):
+ hook()
+ else:
raise
def scan_loop(self):
@@ -1004,10 +1012,6 @@ def scan_loop(self):
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
@@ -1019,10 +1023,6 @@ def scan_loop(self):
if scream:
yield ((i + 1) / nr_points) * 100.0
- if hasattr(macro, 'getHooks'):
- for hook in macro.getHooks('post-scan'):
- hook()
-
if not scream:
yield 100.0
@@ -1755,10 +1755,6 @@ def scan_loop(self):
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)
@@ -1872,10 +1868,6 @@ def scan_loop(self):
self.motion_end_event.wait()
- if hasattr(macro, 'getHooks'):
- for hook in macro.getHooks('post-scan'):
- hook()
-
env = self._env
env['acqtime'] = sum_integ_time
env['delaytime'] = sum_delay
@@ -2452,16 +2444,8 @@ def scan_loop(self):
# 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
@@ -2668,10 +2652,6 @@ def scan_loop(self):
msg = "Relative timestamp (dt) column contains theoretical values"
self.macro.warning(msg)
- if hasattr(macro, 'getHooks'):
- for hook in macro.getHooks('pre-scan'):
- hook()
-
if hasattr(macro, 'getHooks'):
for hook in macro.getHooks('pre-acq'):
hook()
@@ -2689,10 +2669,6 @@ def scan_loop(self):
for hook in macro.getHooks('post-acq'):
hook()
- if hasattr(macro, 'getHooks'):
- for hook in macro.getHooks('post-scan'):
- hook()
-
def _fill_missing_records(self):
# fill record list with dummy records for the final padding
nr_points = self.macro.nr_points
From 87265a4c1ed584222bda81d86668c155352728be Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Wed, 12 Sep 2018 11:07:33 +0200
Subject: [PATCH 056/652] Update CHANGELOG.md
---
CHANGELOG.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ffd5bf9850..0f1f11ea89 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,7 +7,11 @@ This file follows the formats and conventions from [keepachangelog.com]
### Added
- Support to "PETRA3 P23 6C" and "PETRA3 P23 4C" diffractometers by means
-of new controller classes and necessary adaptation to macros (#923, #921)
+ of new controller classes and necessary adaptation to macros (#923, #921)
+
+### Changed
+- Move pre-scan and post-scan hooks out of `scan_loop` method (#920, #922,
+ #933)
## [2.5.0] 2018-08-10
From 1b5b54f7145be35722eebc5dae1c9498d65e6000 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Thu, 12 Jul 2018 16:47:40 +0200
Subject: [PATCH 057/652] Give more visibility to Accessing macro data
---
doc/source/devel/howto_macros/macros_general.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst
index 963bb83bd1..3fb1676fb1 100644
--- a/doc/source/devel/howto_macros/macros_general.rst
+++ b/doc/source/devel/howto_macros/macros_general.rst
@@ -669,7 +669,7 @@ parameters with different *flavors*:
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
From 154677005470088f36a78633790ec9047514f225 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Thu, 12 Jul 2018 17:29:18 +0200
Subject: [PATCH 058/652] Add Adding hooks support
---
.../devel/howto_macros/macros_general.rst | 28 +++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst
index 3fb1676fb1..568bfd9b5b 100644
--- a/doc/source/devel/howto_macros/macros_general.rst
+++ b/doc/source/devel/howto_macros/macros_general.rst
@@ -825,6 +825,34 @@ of user's interruption you must override the
withing the :meth:`~sardana.macroserver.macro.Macro.on_stop` or
:meth:`~sardana.macroserver.macro.Macro.on_abort`.
+Adding hooks support
+--------------------
+
+Your macros may accept to :ref:`attach an arbitrary `
+code, a simple Python callable or even another macro, that will be executed
+at given places. In Sardana this code are called *hooks*, and the places are
+called *hook places*.
+
+In order to allow attaching hooks to your macro you must :ref:`write you
+macro as a class ` while at the same time
+inheriting from the :class:`~sardana.macroserver.macro.Hookable` class.
+
+The hook places can be defined in the ``hints`` class member dictionary with
+the ``allowedHooks`` key and a tuple of strings with the hook places
+identifiers::
+
+ class loop(Macro, Hookable):
+ """A macro that accepts and executes hooks."""
+
+ hints = {"allowsHooks": ("hook-place", "another-hook-place")}
+
+ def run(self):
+ for hook in self.getHooks("hook-place"):
+ hook()
+ self.info("In between hook places")
+ for hook in self.getHooks("another-hook-place"):
+ hook()
+
Using external python libraries
-------------------------------
From 8ec300a57b9a6d0add5e072f917a60e9da30cb0a Mon Sep 17 00:00:00 2001
From: zreszela
Date: Thu, 12 Jul 2018 17:41:21 +0200
Subject: [PATCH 059/652] Fix some typos
---
doc/source/devel/howto_macros/macros_general.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst
index 568bfd9b5b..ed51a9f1fe 100644
--- a/doc/source/devel/howto_macros/macros_general.rst
+++ b/doc/source/devel/howto_macros/macros_general.rst
@@ -838,10 +838,10 @@ macro as a class ` while at the same time
inheriting from the :class:`~sardana.macroserver.macro.Hookable` class.
The hook places can be defined in the ``hints`` class member dictionary with
-the ``allowedHooks`` key and a tuple of strings with the hook places
+the ``allowsHooks`` key and a tuple of strings with the hook places
identifiers::
- class loop(Macro, Hookable):
+ class hookable_macro(Macro, Hookable):
"""A macro that accepts and executes hooks."""
hints = {"allowsHooks": ("hook-place", "another-hook-place")}
From 8762e6d0293e8f3c961cf85078e5f23ed78c1449 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 12 Sep 2018 12:40:15 +0200
Subject: [PATCH 060/652] Document how to attach hooks programmatically
---
.../devel/howto_macros/macros_general.rst | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst
index ed51a9f1fe..4f268fb17d 100644
--- a/doc/source/devel/howto_macros/macros_general.rst
+++ b/doc/source/devel/howto_macros/macros_general.rst
@@ -853,6 +853,25 @@ identifiers::
for hook in self.getHooks("another-hook-place"):
hook()
+Hooks can be programmatically attached to a macro before its execution either
+using the :attr:`~sardana.macroserver.macro.Hookable.hooks` property or
+using the :meth:`~sardana.macroserver.macro.Hookable.appendHook` method::
+
+ def hook_function():
+ pass
+
+ class wrapping_macro(Macro):
+ """A wrapping macro that attaches hooks to a hookable macro
+ and executes it."""
+
+ def run(self):
+ hookable_macro, _ = self.createMacro("hookable_macro")
+ hook_macro = ExecMacroHook(self, "mv", [["mot01", 1]])
+ hookable_macro.hooks = [(hook_macro, ["hook-place"])]
+ hookable_macro.appendHook((hook_function, ["another-hook-place"]))
+ self.runMacro(hookable_macro)
+
+
Using external python libraries
-------------------------------
From 1fa32b507f2674d48dcf377335f7c25f370f3a30 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 12 Sep 2018 13:00:53 +0200
Subject: [PATCH 061/652] Correct hook references
---
doc/source/devel/howto_macros/macros_general.rst | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst
index 4f268fb17d..d4a9383335 100644
--- a/doc/source/devel/howto_macros/macros_general.rst
+++ b/doc/source/devel/howto_macros/macros_general.rst
@@ -778,7 +778,7 @@ prepare HelloWorld to run only after year 1989:
def run(self):
print "Hello, World!"
-.. _sardana-macro-using-external-libraries:
+.. _sardana-macro-handling-macro-stop-and-abort:
Handling macro stop and abort
-----------------------------
@@ -825,6 +825,8 @@ of user's interruption you must override the
withing the :meth:`~sardana.macroserver.macro.Macro.on_stop` or
:meth:`~sardana.macroserver.macro.Macro.on_abort`.
+.. _sardana-macro-adding-hooks-support:
+
Adding hooks support
--------------------
@@ -872,6 +874,8 @@ using the :meth:`~sardana.macroserver.macro.Hookable.appendHook` method::
self.runMacro(hookable_macro)
+.. _sardana-macro-using-external-libraries:
+
Using external python libraries
-------------------------------
From b78e16a1d9c5e5c4177a5ac62967f77a1436e740 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 12 Sep 2018 13:01:16 +0200
Subject: [PATCH 062/652] Add link to programmatic hooks
---
doc/source/users/macro_hooks.rst | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/doc/source/users/macro_hooks.rst b/doc/source/users/macro_hooks.rst
index 6a5616905f..717840a3a5 100644
--- a/doc/source/users/macro_hooks.rst
+++ b/doc/source/users/macro_hooks.rst
@@ -11,16 +11,18 @@ Macro Hooks
===========
A hook is an extra code that can be run at certain points of a macro execution.
-These points are predefined for each hookable macro and passed via a "hints" mechanism.
-The hint tells the macro how and when to run the attached hook.
-Hooks allow the customization of already existing macros and can be added using
-three different ways:
+These points, called *hook places* are predefined for each *hookable* macro.
+The hook place tells the macro how and when to run the attached hook.
+Hooks allow the customization of already existing macros and can be added
+using three different ways:
-- General Hooks
+- :ref:`General Hooks `
- :ref:`Sequencer Hooks `
-- :ref:`Programmatic Hooks `
+- :ref:`Programmatic Hooks `
All available macros can be used as a hook.
+
+.. _sardana-macros-hooks-general:
General Hooks
-------------
From 0c03e3a767ce0de2e28bae47690ffb7dca0f6da0 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 12 Sep 2018 13:04:31 +0200
Subject: [PATCH 063/652] Minor corrections to general hooks
---
doc/source/users/macro_hooks.rst | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/doc/source/users/macro_hooks.rst b/doc/source/users/macro_hooks.rst
index 717840a3a5..b3f1969204 100644
--- a/doc/source/users/macro_hooks.rst
+++ b/doc/source/users/macro_hooks.rst
@@ -29,14 +29,16 @@ General Hooks
The general hooks were implemented in Sardana after the programmatic hooks.
The motivation for this implementation was to allow the customization
-of the scan macros without having to redefine them.
-The general hooks apply to all hookable macros and allow the definition
-of new hints.
-They can be controlled using dedicated macros: :class:`~sardana.macroserver.macros.env.lsgh`,
-:class:`~sardana.macroserver.macros.env.defgh` and :class:`~sardana.macroserver.macros.env.udefgh`.
-For each hook position, hint, several hooks can be run, they will be run in the
-order they were added. The same hook can be run several times in the same position
-if it's added several times.
+of the scan macros without having to redefine them. The general hooks apply
+to all hookable macros.
+
+They can be controlled using dedicated macros:
+:class:`~sardana.macroserver.macros.env.lsgh`,
+:class:`~sardana.macroserver.macros.env.defgh` and
+:class:`~sardana.macroserver.macros.env.udefgh`.
+For each hook place, several hooks can be attached, they will be run in the
+order they were added. The same hook can be run several times in the same
+place if it was added several times.
Examples:
From a5d54d47cf5d706f293ae4f3c878aace442f8db8 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 12 Sep 2018 16:21:53 +0200
Subject: [PATCH 064/652] Do not fill missing records if timescan is stopped
Add a check point in order to avoid filling missing records when
timescan is aborted by the user.
---
src/sardana/macroserver/scan/gscan.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py
index 600c803db6..a61da2bee4 100644
--- a/src/sardana/macroserver/scan/gscan.py
+++ b/src/sardana/macroserver/scan/gscan.py
@@ -2662,6 +2662,7 @@ def scan_loop(self):
self.debug("Waiting for value buffer events to be processed")
self.wait_value_buffer()
self.join_thread_pool()
+ self.macro.checkPoint()
self._fill_missing_records()
yield 100
From 48833fd4638c05761f3e09afd11d1b1d75038845 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 12 Sep 2018 17:14:53 +0200
Subject: [PATCH 065/652] Add aNscanct class
---
src/sardana/macroserver/macros/scan.py | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py
index 726e80de1d..da74e47e43 100644
--- a/src/sardana/macroserver/macros/scan.py
+++ b/src/sardana/macroserver/macros/scan.py
@@ -1415,6 +1415,19 @@ def do_restore(self):
self._motion.move(self.originalPositions)
+class aNscanct(aNscan):
+ """N-dimensional continuous scan. This is **not** meant to be called by
+ the user, but as a generic base to construct ascanct, a2scanct, a3scanct,
+ ..."""
+
+ hints = {"scan": "aNscanct",
+ "allowsHooks": ("pre-scan", "pre-configuration",
+ "post-configuration", "pre-move",
+ "post-move", "pre-acq", "pre-start",
+ "post-acq", "pre-cleanup", "post-cleanup",
+ "post-scan")}
+
+
class ascanct(aNscan, Macro):
"""Do an absolute continuous scan of the specified motor.
ascanct scans one motor, as specified by motor. The motor starts before the
From e875f09d2e00000e8ee06bd0df6421768a4168fd Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 12 Sep 2018 17:16:02 +0200
Subject: [PATCH 066/652] Use aNscanct instead of aNscan class in continuous
scan macros
---
src/sardana/macroserver/macros/scan.py | 38 ++------------------------
1 file changed, 3 insertions(+), 35 deletions(-)
diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py
index da74e47e43..846d89acc7 100644
--- a/src/sardana/macroserver/macros/scan.py
+++ b/src/sardana/macroserver/macros/scan.py
@@ -1428,21 +1428,13 @@ class aNscanct(aNscan):
"post-scan")}
-class ascanct(aNscan, Macro):
+class ascanct(aNscanct, Macro):
"""Do an absolute continuous scan of the specified motor.
ascanct scans one motor, as specified by motor. The motor starts before the
position given by start_pos in order to reach the constant velocity at the
start_pos and finishes at the position after the final_pos in order to
maintain the constant velocity until the final_pos."""
- hints = {'scan': 'ascanct', 'allowsHooks': ('pre-configuration',
- 'post-configuration',
- 'pre-start',
- 'pre-acq',
- 'post-acq',
- 'pre-cleanup',
- 'post-cleanup')}
-
param_def = [['motor', Type.Moveable, None, 'Moveable name'],
['start_pos', Type.Float, None, 'Scan start position'],
['final_pos', Type.Float, None, 'Scan final position'],
@@ -1457,7 +1449,7 @@ def prepare(self, motor, start_pos, final_pos, nr_interv,
latency_time=latency_time, **opts)
-class a2scanct(aNscan, Macro):
+class a2scanct(aNscanct, Macro):
"""Two-motor continuous scan.
a2scanct scans two motors, as specified by motor1 and motor2. Each motor
starts before the position given by its start_pos in order to reach the
@@ -1465,14 +1457,6 @@ class a2scanct(aNscan, Macro):
its final_pos in order to maintain the constant velocity until its
final_pos."""
- hints = {'scan': 'a2scanct', 'allowsHooks': ('pre-configuration',
- 'post-configuration',
- 'pre-start',
- 'pre-acq',
- 'post-acq',
- 'pre-cleanup',
- 'post-cleanup')}
-
param_def = [
['motor1', Type.Moveable, None, 'Moveable 1 to move'],
['start_pos1', Type.Float, None, 'Scan start position 1'],
@@ -1491,7 +1475,7 @@ def prepare(self, m1, s1, f1, m2, s2, f2, nr_interv,
latency_time=latency_time, **opts)
-class a3scanct(aNscan, Macro):
+class a3scanct(aNscanct, Macro):
"""Three-motor continuous scan.
a2scanct scans three motors, as specified by motor1, motor2 and motor3.
Each motor starts before the position given by its start_pos in order to
@@ -1499,14 +1483,6 @@ class a3scanct(aNscan, Macro):
after its final_pos in order to maintain the constant velocity until its
final_pos."""
- hints = {'scan': 'a2scanct', 'allowsHooks': ('pre-configuration',
- 'post-configuration',
- 'pre-start',
- 'pre-acq',
- 'post-acq',
- 'pre-cleanup',
- 'post-cleanup')}
-
param_def = [
['motor1', Type.Moveable, None, 'Moveable 1 to move'],
['start_pos1', Type.Float, None, 'Scan start position 1'],
@@ -1536,14 +1512,6 @@ class a4scanct(aNscan, Macro):
position after its final_pos in order to maintain the constant velocity
until its final_pos."""
- hints = {'scan': 'a2scanct', 'allowsHooks': ('pre-configuration',
- 'post-configuration',
- 'pre-start',
- 'pre-acq',
- 'post-acq',
- 'pre-cleanup',
- 'post-cleanup')}
-
param_def = [
['motor1', Type.Moveable, None, 'Moveable 1 to move'],
['start_pos1', Type.Float, None, 'Scan start position 1'],
From 614f54a5906ec3b2b95070b2c2c83840d533abe2 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 12 Sep 2018 17:16:21 +0200
Subject: [PATCH 067/652] Add dNscanct class
---
src/sardana/macroserver/macros/scan.py | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py
index 846d89acc7..c5ffa24c8c 100644
--- a/src/sardana/macroserver/macros/scan.py
+++ b/src/sardana/macroserver/macros/scan.py
@@ -1536,6 +1536,19 @@ def prepare(self, m1, s1, f1, m2, s2, f2, m3, s3, f3, m4, s4, f4,
latency_time=latency_time, **opts)
+class dNscanct(dNscan):
+ """N-dimensional continuous scan. This is **not** meant to be called by
+ the user, but as a generic base to construct ascanct, a2scanct, a3scanct,
+ ..."""
+
+ hints = {"scan": "dNscanct",
+ "allowsHooks": ("pre-scan", "pre-configuration",
+ "post-configuration", "pre-move",
+ "post-move", "pre-acq", "pre-start",
+ "post-acq", "pre-cleanup", "post-cleanup",
+ "post-scan")}
+
+
class dscanct(dNscan, Macro):
"""Do an a relative continuous motor scan,
dscanct scans a motor, as specified by motor1.
From 23e9ec8cef3f7af0784b90001e21074dae092097 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 12 Sep 2018 17:17:36 +0200
Subject: [PATCH 068/652] Use dNscanct instead of dNscan in relative continuous
scans
---
src/sardana/macroserver/macros/scan.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py
index c5ffa24c8c..3d6b29d093 100644
--- a/src/sardana/macroserver/macros/scan.py
+++ b/src/sardana/macroserver/macros/scan.py
@@ -1549,7 +1549,7 @@ class dNscanct(dNscan):
"post-scan")}
-class dscanct(dNscan, Macro):
+class dscanct(dNscanct, Macro):
"""Do an a relative continuous motor scan,
dscanct scans a motor, as specified by motor1.
The Motor starts before the position given by its start_pos in order to
@@ -1571,7 +1571,7 @@ def prepare(self, motor, start_pos, final_pos, nr_interv,
latency_time=latency_time, **opts)
-class d2scanct(dNscan, Macro):
+class d2scanct(dNscanct, Macro):
"""continuous two-motor scan relative to the starting positions,
d2scanct scans three motors, as specified by motor1 and motor2.
Each motor starts before the position given by its start_pos in order to
@@ -1595,7 +1595,7 @@ def prepare(self, m1, s1, f1, m2, s2, f2, integ_time, slow_down, **opts):
mode=ContinuousHwTimeMode, **opts)
-class d3scanct(dNscan, Macro):
+class d3scanct(dNscanct, Macro):
"""continuous three-motor scan relative to the starting positions,
d3scanct scans three motors, as specified by motor1, motor2 and motor3.
Each motor starts before the position given by its start_pos in order to
@@ -1623,7 +1623,7 @@ def prepare(self, m1, s1, f1, m2, s2, f2, m3, s3, f3, integ_time,
integ_time, mode=ContinuousHwTimeMode, **opts)
-class d4scanct(dNscan, Macro):
+class d4scanct(dNscanct, Macro):
"""continuous four-motor scan relative to the starting positions,
d4scanct scans three motors, as specified by motor1, motor2, motor3 and
motor4.
From bdbfb7bd1b4b324d7ca7984f14fbb20d1833b0dd Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 12 Sep 2018 17:17:51 +0200
Subject: [PATCH 069/652] Correct meshct hook places
---
src/sardana/macroserver/macros/scan.py | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/sardana/macroserver/macros/scan.py b/src/sardana/macroserver/macros/scan.py
index 3d6b29d093..10d04c8b9c 100644
--- a/src/sardana/macroserver/macros/scan.py
+++ b/src/sardana/macroserver/macros/scan.py
@@ -1666,10 +1666,12 @@ class meshct(Macro, Hookable):
first motor scan is nested within the second motor scan.
"""
- hints = {'scan': 'meshct', 'allowsHooks': ('pre-scan', 'pre-move',
- 'post-move', 'pre-acq',
- 'post-acq', 'post-step',
- 'post-scan')}
+ hints = {"scan": "meshct",
+ "allowsHooks": ("pre-scan", "pre-configuration",
+ "post-configuration", "pre-move",
+ "post-move", "pre-acq", "pre-start",
+ "post-acq", "pre-cleanup", "post-cleanup",
+ "post-scan")}
env = ('ActiveMntGrp',)
param_def = [
From fccfe2ab9e3acbb3d06af2c5132fd3d9502741ce Mon Sep 17 00:00:00 2001
From: zreszela
Date: Thu, 13 Sep 2018 09:25:53 +0200
Subject: [PATCH 070/652] Small corrections in the docs
---
.../devel/howto_macros/macros_general.rst | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst
index 25897c470a..c7131c7167 100644
--- a/doc/source/devel/howto_macros/macros_general.rst
+++ b/doc/source/devel/howto_macros/macros_general.rst
@@ -299,8 +299,10 @@ being a composed of four elements:
- parameter name
- parameter type
- parameter default value:
- * None means no default value
- * :ref:`OptionalParam `
+ - ``None`` means no default value
+ - ``OptionalParam`` means that
+ :ref:`the parameter value is optional `
+
- parameter description
Here is a list of the most common allowed parameter types:
@@ -328,10 +330,10 @@ all available sardana interfaces (:obj:`~sardana.sardanadefs.Interface`)
Optional parameters
~~~~~~~~~~~~~~~~~~~
-A special default value is the *OptionalParam*. It allows to the macro
-identify if the user introduces a value or not to take a decision.
+A special parameter default value is the ``OptionalParam``. It allows to
+execute a macro even the given parameter value is not specified by the user.
-So, here is an example how to define and use a optional parameter::
+So, here is an example how to define and use the optional parameter::
from sardana.macroserver.macro import Macro, Type, OptionalParam
@@ -347,9 +349,9 @@ So, here is an example how to define and use a optional parameter::
try:
if mntgrp is not None:
bkp_active_mntgrp = self.getEnv('ActiveMntGrp')
- self.setEnv('ActiveMntGrp', mntgrp.name)
- self.info('Use "{0}" measurement '
- 'group'.format(self.getEnv('ActiveMntGrp')))
+ mntgrp_name = mntgrp.name
+ self.setEnv('ActiveMntGrp', mntgrp_name)
+ self.info('Use "{0}" measurement group'.format(mntgrp_name))
self.ct(itime)
finally:
if bkp_active_mntgrp is not None:
From a1542ebfb69da17d0e77c5cdf671271e4f960c24 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Thu, 13 Sep 2018 09:46:15 +0200
Subject: [PATCH 071/652] Change comparison of optional param
---
src/sardana/macroserver/msparameter.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sardana/macroserver/msparameter.py b/src/sardana/macroserver/msparameter.py
index a85bdaec1d..3d89eab028 100644
--- a/src/sardana/macroserver/msparameter.py
+++ b/src/sardana/macroserver/msparameter.py
@@ -424,7 +424,7 @@ def decodeNormal(self, raw_param, param_def):
value = param_def['default_value']
if value is None:
raise MissingParam("'%s' not specified" % name)
- elif isinstance(value, OptionalParamClass):
+ elif value is OptionalParam:
param = None
optional_param = True
else:
From 046c3fdae018ead726ae9c7cc8d970dd7a6a8ba9 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Thu, 13 Sep 2018 09:57:56 +0200
Subject: [PATCH 072/652] Rename OptionalParam to Optional
---
doc/source/devel/howto_macros/macros_general.rst | 8 ++++----
src/sardana/macroserver/macro.py | 4 ++--
src/sardana/macroserver/msparameter.py | 6 +++---
src/sardana/taurus/core/tango/sardana/macro.py | 4 ++--
.../taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py | 4 ++--
5 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst
index c7131c7167..ab49a861cc 100644
--- a/doc/source/devel/howto_macros/macros_general.rst
+++ b/doc/source/devel/howto_macros/macros_general.rst
@@ -300,7 +300,7 @@ being a composed of four elements:
- parameter type
- parameter default value:
- ``None`` means no default value
- - ``OptionalParam`` means that
+ - ``Optional`` means that
:ref:`the parameter value is optional `
- parameter description
@@ -330,18 +330,18 @@ all available sardana interfaces (:obj:`~sardana.sardanadefs.Interface`)
Optional parameters
~~~~~~~~~~~~~~~~~~~
-A special parameter default value is the ``OptionalParam``. It allows to
+A special parameter default value is the ``Optional``. It allows to
execute a macro even the given parameter value is not specified by the user.
So, here is an example how to define and use the optional parameter::
- from sardana.macroserver.macro import Macro, Type, OptionalParam
+ from sardana.macroserver.macro import Macro, Type, Optional
class count(Macro):
param_def = [
['itime', Type.Float, 1, 'integration time'],
- ['mntgrp', Type.MeasurementGroup, OptionalParam, 'MntGrp to use']
+ ['mntgrp', Type.MeasurementGroup, Optional, 'MntGrp to use']
]
def run(self, itime, mntgrp):
diff --git a/src/sardana/macroserver/macro.py b/src/sardana/macroserver/macro.py
index 719b58fc63..2e0e88b025 100644
--- a/src/sardana/macroserver/macro.py
+++ b/src/sardana/macroserver/macro.py
@@ -32,7 +32,7 @@
__all__ = ["OverloadPrint", "PauseEvent", "Hookable", "ExecMacroHook",
"MacroFinder", "Macro", "macro", "iMacro", "imacro",
"MacroFunc", "Type", "ParamRepeat", "Table", "List", "ViewOption",
- "LibraryError", "OptionalParam"]
+ "LibraryError", "Optional"]
__docformat__ = 'restructuredtext'
@@ -56,7 +56,7 @@
from sardana.util.wrap import wraps
from sardana.macroserver.msparameter import Type, ParamType, ParamRepeat, \
- OptionalParam
+ Optional
from sardana.macroserver.msexception import StopException, AbortException, \
MacroWrongParameterType, UnknownEnv, UnknownMacro, LibraryError
from sardana.macroserver.msoptions import ViewOption
diff --git a/src/sardana/macroserver/msparameter.py b/src/sardana/macroserver/msparameter.py
index 3d89eab028..0d75e69e1c 100644
--- a/src/sardana/macroserver/msparameter.py
+++ b/src/sardana/macroserver/msparameter.py
@@ -57,13 +57,13 @@ def __init__(self, obj):
self.__setattr__ = self.raise_error
def __repr__(self):
- return 'OptionalParam'
+ return 'Optional'
def raise_error(*args, **kwargs):
raise RuntimeError('can not be accessed')
-OptionalParam = OptionalParamClass({'___optional_parameter__': True})
+Optional = OptionalParamClass({'___optional_parameter__': True})
class WrongParam(MacroServerException):
@@ -424,7 +424,7 @@ def decodeNormal(self, raw_param, param_def):
value = param_def['default_value']
if value is None:
raise MissingParam("'%s' not specified" % name)
- elif value is OptionalParam:
+ elif value is Optional:
param = None
optional_param = True
else:
diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py
index 45356bdabe..48339b48a2 100755
--- a/src/sardana/taurus/core/tango/sardana/macro.py
+++ b/src/sardana/taurus/core/tango/sardana/macro.py
@@ -41,7 +41,7 @@
from taurus.core.util.user import USER_NAME
from taurus.core.util.codecs import CodecFactory
-from sardana.macroserver.msparameter import OptionalParam
+from sardana.macroserver.msparameter import Optional
class MacroRunException(Exception):
@@ -552,7 +552,7 @@ def toRun(self):
if self.defValue() is None:
alert = "Parameter " + self.name() + " is missing.
"
return ([val], alert)
- elif self._defValue == OptionalParam:
+ elif self._defValue == Optional:
val = ''
else:
val = self.defValue()
diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py
index 8d6fbbf872..b2ec9140df 100644
--- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py
+++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py
@@ -47,7 +47,7 @@
from .favouriteseditor import FavouritesMacrosEditor, HistoryMacrosViewer
from .common import MacroComboBox, MacroExecutionWindow, standardPlotablesFilter
-from sardana.macroserver.msparameter import OptionalParam
+from sardana.macroserver.msparameter import Optional
class MacroProgressBar(Qt.QProgressBar):
@@ -283,7 +283,7 @@ def validateAllExpresion(self, secValidation=False):
except IndexError:
param_info = macro_params_info[counter-1]
# Skip validation in case of optional parameters
- if param_info['default_value'] == OptionalParam:
+ if param_info['default_value'] == Optional:
self.model().setData(self.currentIndex,
Qt.QVariant(None))
else:
From 6cd80f5c76042d1750b134d9c32a599226eb0b14 Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Thu, 13 Sep 2018 11:31:09 +0200
Subject: [PATCH 073/652] Update CHANGELOG.md
---
CHANGELOG.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0f1f11ea89..4bb6abcc9e 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,8 @@ This file follows the formats and conventions from [keepachangelog.com]
## [Unreleased]
### Added
+- Possibility to define macros with optional parameters. These must be the last
+ ones in the definition (#285, #876)
- Support to "PETRA3 P23 6C" and "PETRA3 P23 4C" diffractometers by means
of new controller classes and necessary adaptation to macros (#923, #921)
From 613b9ae98cc079187a17ecf79fbc0fbb8f93b365 Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Thu, 13 Sep 2018 12:40:50 +0200
Subject: [PATCH 074/652] Update CHANGELOG.md
---
CHANGELOG.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4bb6abcc9e..559799f271 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,9 @@ This file follows the formats and conventions from [keepachangelog.com]
- Support to "PETRA3 P23 6C" and "PETRA3 P23 4C" diffractometers by means
of new controller classes and necessary adaptation to macros (#923, #921)
+### Fixed
+- Avoid final padding in timescan when it was stopped by user (#869, #935)
+
### Changed
- Move pre-scan and post-scan hooks out of `scan_loop` method (#920, #922,
#933)
From 9bec42ed9401ed2003dbe1b5a38c9480ed34909a Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Thu, 13 Sep 2018 14:21:23 +0200
Subject: [PATCH 075/652] Update how_to_release.md
---
doc/how_to_release.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/how_to_release.md b/doc/how_to_release.md
index cccdbf9064..688b82f718 100644
--- a/doc/how_to_release.md
+++ b/doc/how_to_release.md
@@ -1,4 +1,4 @@
-# How to release (draft)
+# How to
This is a guide for sardana release managers: it details the steps for making
an official release, including a checklist of stuff that should be manually
From 00a7fe6841f225e3ce56e1cd849ef0412bb67eb0 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 17 Sep 2018 10:20:58 +0200
Subject: [PATCH 076/652] Use experimentConfiguration as method names
---
src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py | 6 +++---
.../taurus/qt/qtgui/extra_sardana/expdescription.py | 7 ++++---
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
index 4d280e6746..455c9ecfeb 100644
--- a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
+++ b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
@@ -111,7 +111,7 @@ def _prepare_connections(self):
if not self._use_experimet_configuration and \
not self._connections_prepared:
self.connect(self.macro_server, Qt.SIGNAL("environmentChanged"),
- self._experimentalConfiguration)
+ self._experimentConfiguration)
self.connect(self.macro_server, Qt.SIGNAL("elementsChanged"),
self._elementsChanged)
self._connections_prepared = True
@@ -125,13 +125,13 @@ def _elementsChanged(self):
else:
obj = mg.getObj()
self.connect(obj, Qt.SIGNAL("configurationChanged"),
- self._experimentalConfiguration)
+ self._experimentConfiguration)
self._mntgrp_connected.append(name)
if len(self._mntgrp_connected) != len_mnt_grps_connected:
self.emit(Qt.SIGNAL("experimentConfigurationChanged"))
- def _experimentalConfiguration(self, *args):
+ def _experimentConfiguration(self, *args):
self.emit(Qt.SIGNAL("experimentConfigurationChanged"))
def getExperimentConfigurationObj(self):
diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
index 502a326142..e606b57c63 100644
--- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
+++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
@@ -323,7 +323,8 @@ def _createExpConfChangedDialog(self):
self._reloadConf(force=True)
@QtCore.pyqtSlot()
- def _experimentalConfigurationChanged(self):
+ def _experimentConfigurationChanged(self):
+ self._diff = ''
try:
self._diff = self._getDiff()
except Exception:
@@ -388,7 +389,7 @@ def setModel(self, model):
self.ui.taurusModelTree.setModel(tghost)
self.ui.sardanaElementTree.setModel(msname)
self.connect(door, Qt.SIGNAL("experimentConfigurationChanged"),
- self._experimentalConfigurationChanged)
+ self._experimentConfigurationChanged)
def _reloadConf(self, force=False):
if not force and self.isDataChanged():
@@ -681,7 +682,7 @@ def main():
parser.usage = "%prog [options] "
parser.add_option('--auto-update', dest='auto_update',
action='store_true',
- help='Set auto update of experimental configuration')
+ help='Set auto update of experiment configuration')
app = Application(app_name="Exp. Description demo", app_version="1.0",
org_domain="Sardana", org_name="Tango community",
cmd_line_parser=parser)
From 6dd9699bf12ee939f7d8fc710e2abc9cfd295176 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 17 Sep 2018 11:14:21 +0200
Subject: [PATCH 077/652] Add more keys to skips
Add plot_axes and PreScanSnapshot as keys with list as values.
---
src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
index e606b57c63..efb0867734 100644
--- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
+++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
@@ -110,7 +110,7 @@ def find_diff(first, second):
KEYNOTFOUNDIN1 = 'KeyNotFoundInRemote'
KEYNOTFOUNDIN2 = 'KeyNotFoundInLocal'
SKIPKEYS = ['_controller_name']
- SKIPLIST = ['scanfile']
+ SKIPLIST = ['scanfile', 'plot_axes', 'prescansnapshot']
DICT_TYPES = [taurus.core.util.containers.CaselessDict, dict]
diff = {}
sd1 = set(first)
From 28c2644156f38e0c3c2b835d984758ce58a4d585 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 17 Sep 2018 11:15:31 +0200
Subject: [PATCH 078/652] Add protections
Add protection on the find_diff method to avoid wrong behaviours.
---
.../taurus/qt/qtgui/extra_sardana/expdescription.py | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
index efb0867734..adecc8f3ae 100644
--- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
+++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
@@ -115,8 +115,8 @@ def find_diff(first, second):
diff = {}
sd1 = set(first)
sd2 = set(second)
- # Keys missing in the second dict
+ # Keys missing in the second dict
for key in sd1.difference(sd2):
if key in SKIPKEYS:
continue
@@ -126,18 +126,25 @@ def find_diff(first, second):
if key in SKIPKEYS:
continue
diff[key] = (KEYNOTFOUNDIN1, second[key])
+
# Check for differences
for key in sd1.intersection(sd2):
value1 = first[key]
value2 = second[key]
if type(value1) in DICT_TYPES:
- idiff = find_diff(value1, value2)
+ try:
+ idiff = find_diff(value1, value2)
+ except Exception:
+ idiff = 'Error on processing'
if len(idiff) > 0:
diff[key] = idiff
elif type(value1) == list and key.lower() not in SKIPLIST:
ldiff = []
for v1, v2 in zip(value1, value2):
- idiff = find_diff(v1, v2)
+ try:
+ idiff = find_diff(v1, v2)
+ except Exception:
+ idiff = 'Error on processing'
ldiff.append(idiff)
if len(ldiff) > 0:
diff[key] = ldiff
From 8c431791a9b2e48d4e2f476c0b0f48ec0d15874d Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 17 Sep 2018 11:18:05 +0200
Subject: [PATCH 079/652] Show error in case of exception
Show the exception error in case of find_diff method fails.
---
src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
index adecc8f3ae..63e2578b2d 100644
--- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
+++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
@@ -334,8 +334,8 @@ def _experimentConfigurationChanged(self):
self._diff = ''
try:
self._diff = self._getDiff()
- except Exception:
- return
+ except Exception as e:
+ raise RuntimeError('Error on processing! {0}'.format(e))
if len(self._diff) > 0:
if self._autoUpdate:
From 2647b335ee9e6872f6e56592337a3bf4644b760f Mon Sep 17 00:00:00 2001
From: zreszela
Date: Tue, 18 Sep 2018 11:08:56 +0200
Subject: [PATCH 080/652] Add LICENSE file
---
LICENSE | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 LICENSE
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000..85dc2b3f4e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,34 @@
+Sardana is Free Software by the CELLS / ALBA Synchrotron, Bellaterra, Spain
+
+SECTION 1: GENERAL LICENSE FOR SARDANA SOURCE CODE
+=================================================
+
+The files in Sardana, except for the cases described in SECTION 2
+are distributed under 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.
+See
+
+SECTION 2: EXCEPTIONS
+=====================
+
+Some files (e.g., those authored by 3rd parties or the documentation
+sources) are distributed under Free Software / documentation licenses
+that may differ from the the general one defined in SECTION 1.
+
+The following is a list of these exceptions:
+
+2.1: Explicit copyright info in header/metadata:
+------------------------------------------------
+
+If a file contains an explicit license or other copyright information in
+its header or metadata which differs from the one defined in SECTION 1,
+such license/copyright info mentioned in the header/metadata prevails.
+
+2.2: Documentation:
+-------------------
+
+The .py scripts in the doc directory are treated as per SECTION 1,
+and the rest of its files are distributed under a Creative Commons
+Attribution 3.0 License
+See
From 467ea063216d3108a9902dffc4809acd7115cd1e Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Tue, 18 Sep 2018 11:19:29 +0200
Subject: [PATCH 081/652] Update CHANGELOG.md
---
CHANGELOG.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 559799f271..f2997e0765 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,8 @@ This file follows the formats and conventions from [keepachangelog.com]
### Fixed
- Avoid final padding in timescan when it was stopped by user (#869, #935)
+- Hook places advertised by continuous scans so the `allowHooks` hint and the
+ code are coherent (#936)
### Changed
- Move pre-scan and post-scan hooks out of `scan_loop` method (#920, #922,
From 1ea2e43ca18e09d18f723319a52bf84072cef981 Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Tue, 18 Sep 2018 14:40:06 +0200
Subject: [PATCH 082/652] Update CHANGELOG.md
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f2997e0765..4533dee2c8 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ This file follows the formats and conventions from [keepachangelog.com]
ones in the definition (#285, #876)
- Support to "PETRA3 P23 6C" and "PETRA3 P23 4C" diffractometers by means
of new controller classes and necessary adaptation to macros (#923, #921)
+- Top LICENSE file that applies to the whole project (#938)
### Fixed
- Avoid final padding in timescan when it was stopped by user (#869, #935)
From 686abc6f91ff746b7a3664f25f968e8ca272acdc Mon Sep 17 00:00:00 2001
From: zreszela
Date: Tue, 18 Sep 2018 15:00:32 +0200
Subject: [PATCH 083/652] Rename some internal variables of QMacroServer
* _experimentConfiguration -> _experimentConfigurationChanged
* environmentChange -> environmentChanged
---
src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
index 455c9ecfeb..aaf22d9a9a 100644
--- a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
+++ b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
@@ -111,7 +111,7 @@ def _prepare_connections(self):
if not self._use_experimet_configuration and \
not self._connections_prepared:
self.connect(self.macro_server, Qt.SIGNAL("environmentChanged"),
- self._experimentConfiguration)
+ self._experimentConfigurationChanged)
self.connect(self.macro_server, Qt.SIGNAL("elementsChanged"),
self._elementsChanged)
self._connections_prepared = True
@@ -125,13 +125,13 @@ def _elementsChanged(self):
else:
obj = mg.getObj()
self.connect(obj, Qt.SIGNAL("configurationChanged"),
- self._experimentConfiguration)
+ self._experimentConfigurationChanged)
self._mntgrp_connected.append(name)
if len(self._mntgrp_connected) != len_mnt_grps_connected:
self.emit(Qt.SIGNAL("experimentConfigurationChanged"))
- def _experimentConfiguration(self, *args):
+ def _experimentConfigurationChanged(self, *args):
self.emit(Qt.SIGNAL("experimentConfigurationChanged"))
def getExperimentConfigurationObj(self):
@@ -151,7 +151,7 @@ class QMacroServer(BaseMacroServer, Qt.QObject):
elementsUpdated = Qt.pyqtSignal()
elementsChanged = Qt.pyqtSignal()
macrosUpdated = Qt.pyqtSignal()
- environmentChange = Qt.pyqtSignal(list)
+ environmentChanged = Qt.pyqtSignal(list)
except AttributeError:
pass
From 5cfb41194c8afadbc91f3b88ff654d7c8ac7bb40 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Tue, 18 Sep 2018 15:35:37 +0200
Subject: [PATCH 084/652] Change sphinx style in find_diff docstring
---
src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
index 63e2578b2d..8912b37fd2 100644
--- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
+++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
@@ -102,9 +102,9 @@ def find_diff(first, second):
"""
Return a dict of keys that differ with another config object. If a value
is not found in one fo the configs, it will be represented by KEYNOTFOUND.
- @param first: Fist configuration to diff.
- @param second: Second configuration to diff.
- @return diff: Dict of Key => (first.val, second.val)
+ :param first: Fist configuration to diff.
+ :param second: Second configuration to diff.
+ :return: Dict of Key => (first.val, second.val)
"""
KEYNOTFOUNDIN1 = 'KeyNotFoundInRemote'
From fe2b398fc3c08fe9dd234404d529c87e5ee63f2d Mon Sep 17 00:00:00 2001
From: zreszela
Date: Tue, 18 Sep 2018 16:52:57 +0200
Subject: [PATCH 085/652] Force first connection to measurement group
configurations
---
src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
index aaf22d9a9a..65269b47eb 100644
--- a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
+++ b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
@@ -114,6 +114,7 @@ def _prepare_connections(self):
self._experimentConfigurationChanged)
self.connect(self.macro_server, Qt.SIGNAL("elementsChanged"),
self._elementsChanged)
+ self._elementsChanged()
self._connections_prepared = True
def _elementsChanged(self):
From b2fb2a91218faac2eba5d901b3dd62200793430c Mon Sep 17 00:00:00 2001
From: zreszela
Date: Tue, 18 Sep 2018 16:53:25 +0200
Subject: [PATCH 086/652] Refactor _elementsChanged
---
.../qt/qtcore/tango/sardana/macroserver.py | 20 ++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
index 65269b47eb..cfcd701036 100644
--- a/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
+++ b/src/sardana/taurus/qt/qtcore/tango/sardana/macroserver.py
@@ -59,7 +59,7 @@ class QDoor(BaseDoor, Qt.QObject):
def __init__(self, name, qt_parent=None, **kw):
self.call__init__wo_kw(Qt.QObject, qt_parent)
self.call__init__(BaseDoor, name, **kw)
- self._mntgrp_connected = []
+ self._mntgrps_connected = []
self._use_experimet_configuration = False
self._connections_prepared = False
@@ -118,18 +118,20 @@ def _prepare_connections(self):
self._connections_prepared = True
def _elementsChanged(self):
- len_mnt_grps_connected = len(self._mntgrp_connected)
- elements = self.macro_server.getElementsOfType("MeasurementGroup")
- for name, mg in elements.items():
- if name in self._mntgrp_connected:
- continue
- else:
+ mntgrps = self.macro_server.getElementsOfType("MeasurementGroup")
+ # one or more measurement group was deleted
+ mntgrp_changed = len(self._mntgrps_connected) > len(mntgrps)
+ new_mntgrp_connected = []
+ for name, mg in mntgrps.items():
+ if name not in self._mntgrps_connected:
+ mntgrp_changed = True # this measurement group is new
obj = mg.getObj()
self.connect(obj, Qt.SIGNAL("configurationChanged"),
self._experimentConfigurationChanged)
- self._mntgrp_connected.append(name)
+ new_mntgrp_connected.append(name)
+ self._mntgrp_connected = new_mntgrp_connected
- if len(self._mntgrp_connected) != len_mnt_grps_connected:
+ if mntgrp_changed:
self.emit(Qt.SIGNAL("experimentConfigurationChanged"))
def _experimentConfigurationChanged(self, *args):
From e08fd9978267194be8b0fe9e1c4ac94cb864e664 Mon Sep 17 00:00:00 2001
From: Daniel Roldan
Date: Tue, 18 Sep 2018 17:12:12 +0200
Subject: [PATCH 087/652] ct/uct macro with mntGrp as a Optional Param
---
src/sardana/macroserver/macros/standard.py | 45 +++++++++++++++-------
1 file changed, 31 insertions(+), 14 deletions(-)
diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py
index 64f793d99d..12d9326f32 100644
--- a/src/sardana/macroserver/macros/standard.py
+++ b/src/sardana/macroserver/macros/standard.py
@@ -41,6 +41,8 @@
ViewOption, iMacro, Hookable
from sardana.macroserver.msexception import StopException
from sardana.macroserver.scan.scandata import Record
+from sardana.macroserver.msparameter import Optional
+
##########################################################################
#
# Motion related macros
@@ -648,17 +650,25 @@ class ct(Macro, Hookable):
env = ('ActiveMntGrp',)
hints = {'allowsHooks': ('pre-acq', 'post-acq')}
param_def = [
- ['integ_time', Type.Float, 1.0, 'Integration time']
+ ['integ_time', Type.Float, 1.0, 'Integration time'],
+ ['mnt_grp', Type.MeasurementGroup, Optional, 'MntGrp to use']
+
]
- def prepare(self, integ_time, **opts):
- mnt_grp_name = self.getEnv('ActiveMntGrp')
- self.mnt_grp = self.getObj(
- mnt_grp_name, type_class=Type.MeasurementGroup)
+ def prepare(self, integ_time, mnt_grp, **opts):
+ if mnt_grp is None:
+ self.mnt_grp_name = self.getEnv('ActiveMntGrp')
+ self.mnt_grp = self.getObj(self.mnt_grp_name,
+ type_class=Type.MeasurementGroup)
+ else:
+ self.mnt_grp_name = mnt_grp.name
+ self.mnt_grp = mnt_grp
- def run(self, integ_time):
+
+ def run(self, integ_time, mnt_grp):
if self.mnt_grp is None:
- self.error('ActiveMntGrp is not defined or has invalid value')
+ self.error('The MntGrp {} is not defined or has invalid '
+ 'value'.format(self.mnt_grp_name))
return
# integration time has to be accessible from with in the hooks
# so declare it also instance attribute
@@ -699,16 +709,22 @@ class uct(Macro):
env = ('ActiveMntGrp',)
param_def = [
- ['integ_time', Type.Float, 1.0, 'Integration time']
+ ['integ_time', Type.Float, 1.0, 'Integration time'],
+ ['mnt_grp', Type.MeasurementGroup, Optional, 'MntGrp to use']
+
]
- def prepare(self, integ_time, **opts):
+ def prepare(self, integ_time, mnt_grp, **opts):
self.print_value = False
- mnt_grp_name = self.getEnv('ActiveMntGrp')
- self.mnt_grp = self.getObj(
- mnt_grp_name, type_class=Type.MeasurementGroup)
+ if mnt_grp is None:
+ self.mnt_grp_name = self.getEnv('ActiveMntGrp')
+ self.mnt_grp = self.getObj(self.mnt_grp_name,
+ type_class=Type.MeasurementGroup)
+ else:
+ self.mnt_grp_name = mnt_grp.name
+ self.mnt_grp = mnt_grp
if self.mnt_grp is None:
return
@@ -726,9 +742,10 @@ def prepare(self, integ_time, **opts):
valueObj = channel.getValueObj_()
valueObj.subscribeEvent(self.counterChanged, channel)
- def run(self, integ_time):
+ def run(self, integ_time, mnt_grp):
if self.mnt_grp is None:
- self.error('ActiveMntGrp is not defined or has invalid value')
+ self.error('The MntGrp {} is not defined or has invalid '
+ 'value'.format(self.mnt_grp_name))
return
self.print_value = True
From bd69df674a1b752f153284d2e6b166002e735319 Mon Sep 17 00:00:00 2001
From: Daniel Roldan
Date: Tue, 18 Sep 2018 17:14:36 +0200
Subject: [PATCH 088/652] fix pep8
---
src/sardana/macroserver/macros/standard.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py
index 12d9326f32..6dd138cfca 100644
--- a/src/sardana/macroserver/macros/standard.py
+++ b/src/sardana/macroserver/macros/standard.py
@@ -659,12 +659,11 @@ def prepare(self, integ_time, mnt_grp, **opts):
if mnt_grp is None:
self.mnt_grp_name = self.getEnv('ActiveMntGrp')
self.mnt_grp = self.getObj(self.mnt_grp_name,
- type_class=Type.MeasurementGroup)
+ type_class=Type.MeasurementGroup)
else:
self.mnt_grp_name = mnt_grp.name
self.mnt_grp = mnt_grp
-
def run(self, integ_time, mnt_grp):
if self.mnt_grp is None:
self.error('The MntGrp {} is not defined or has invalid '
@@ -721,7 +720,7 @@ def prepare(self, integ_time, mnt_grp, **opts):
if mnt_grp is None:
self.mnt_grp_name = self.getEnv('ActiveMntGrp')
self.mnt_grp = self.getObj(self.mnt_grp_name,
- type_class=Type.MeasurementGroup)
+ type_class=Type.MeasurementGroup)
else:
self.mnt_grp_name = mnt_grp.name
self.mnt_grp = mnt_grp
@@ -839,6 +838,7 @@ def run(self, offon, mode):
else:
self.setEnv('LogMacro', False)
+
class repeat(Hookable, Macro):
"""This macro executes as many repetitions of a set of macros as
specified by nr parameter. The macros to be repeated can be
From a7901930205573689f8250a0e82a8b9da3a25fc2 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Wed, 19 Sep 2018 10:37:45 +0200
Subject: [PATCH 089/652] Add shape key to skip
---
src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
index 8912b37fd2..59f38fc540 100644
--- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
+++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
@@ -110,7 +110,7 @@ def find_diff(first, second):
KEYNOTFOUNDIN1 = 'KeyNotFoundInRemote'
KEYNOTFOUNDIN2 = 'KeyNotFoundInLocal'
SKIPKEYS = ['_controller_name']
- SKIPLIST = ['scanfile', 'plot_axes', 'prescansnapshot']
+ SKIPLIST = ['scanfile', 'plot_axes', 'prescansnapshot', 'shape']
DICT_TYPES = [taurus.core.util.containers.CaselessDict, dict]
diff = {}
sd1 = set(first)
From 8f98403081bb54cad6d7ecd5d7c57af9e18299fb Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Wed, 19 Sep 2018 11:23:42 +0200
Subject: [PATCH 090/652] Add more keys to skip
Add more keys than the server can change regardless of the values set
by the GUI.
Skip these key in the comparison process.
---
.../taurus/qt/qtgui/extra_sardana/expdescription.py | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
index 59f38fc540..9911a52b94 100644
--- a/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
+++ b/src/sardana/taurus/qt/qtgui/extra_sardana/expdescription.py
@@ -109,8 +109,14 @@ def find_diff(first, second):
KEYNOTFOUNDIN1 = 'KeyNotFoundInRemote'
KEYNOTFOUNDIN2 = 'KeyNotFoundInLocal'
- SKIPKEYS = ['_controller_name']
+
+ # The GUI can not change these keys. They are changed by the server.
+ SKIPKEYS = ['_controller_name', 'description', 'timer', 'monitor', 'ndim',
+ 'source']
+
+ # These keys can have a list as value.
SKIPLIST = ['scanfile', 'plot_axes', 'prescansnapshot', 'shape']
+
DICT_TYPES = [taurus.core.util.containers.CaselessDict, dict]
diff = {}
sd1 = set(first)
@@ -129,6 +135,8 @@ def find_diff(first, second):
# Check for differences
for key in sd1.intersection(sd2):
+ if key in SKIPKEYS:
+ continue
value1 = first[key]
value2 = second[key]
if type(value1) in DICT_TYPES:
From 3f223383adc701558bb6eae8c169a5cb13492a0b Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 19 Sep 2018 12:07:37 +0200
Subject: [PATCH 091/652] Distinguish error reason and execute stop only once
---
src/sardana/taurus/core/tango/sardana/pool.py | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py
index 539785086f..c0978afed0 100644
--- a/src/sardana/taurus/core/tango/sardana/pool.py
+++ b/src/sardana/taurus/core/tango/sardana/pool.py
@@ -1786,16 +1786,16 @@ def _enableChannels(self, channels, state):
def _start(self, *args, **kwargs):
try:
self.Start()
- except Exception as e:
- while True:
- try:
- self.stop()
- break
- except Exception:
- pass
- # TODO: Do more friendly user the exception message.
+ except DevFailed as e:
+ # TODO: Workaround for CORBA timeout on measurement group start
+ # remove it whenever sardana-org/sardana#93 gets implemented
+ if e[-1].reason == "API_DeviceTimedOut":
+ self.error("start timed out, trying to stop")
+ self.stop()
+ self.debug("stopped")
raise e
+
def go(self, *args, **kwargs):
start_time = time.time()
cfg = self.getConfiguration()
From 43eabb45148e4ea0512a146f7b7cb7577bf0b237 Mon Sep 17 00:00:00 2001
From: Daniel Roldan
Date: Wed, 19 Sep 2018 12:26:58 +0200
Subject: [PATCH 092/652] Provide requested changes
---
src/sardana/macroserver/macros/standard.py | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/sardana/macroserver/macros/standard.py b/src/sardana/macroserver/macros/standard.py
index 6dd138cfca..d9463e8570 100644
--- a/src/sardana/macroserver/macros/standard.py
+++ b/src/sardana/macroserver/macros/standard.py
@@ -41,7 +41,7 @@
ViewOption, iMacro, Hookable
from sardana.macroserver.msexception import StopException
from sardana.macroserver.scan.scandata import Record
-from sardana.macroserver.msparameter import Optional
+from sardana.macroserver.macro import Optional
##########################################################################
#
@@ -651,7 +651,8 @@ class ct(Macro, Hookable):
hints = {'allowsHooks': ('pre-acq', 'post-acq')}
param_def = [
['integ_time', Type.Float, 1.0, 'Integration time'],
- ['mnt_grp', Type.MeasurementGroup, Optional, 'MntGrp to use']
+ ['mnt_grp', Type.MeasurementGroup, Optional, 'Measurement Group to '
+ 'use']
]
@@ -709,7 +710,8 @@ class uct(Macro):
param_def = [
['integ_time', Type.Float, 1.0, 'Integration time'],
- ['mnt_grp', Type.MeasurementGroup, Optional, 'MntGrp to use']
+ ['mnt_grp', Type.MeasurementGroup, Optional, 'Measurement Group to '
+ 'use']
]
From ca4d9a47d17310f49e6a1367dddd75ef3c4d4d33 Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Wed, 19 Sep 2018 12:32:32 +0200
Subject: [PATCH 093/652] Update CHANGELOG.md
---
CHANGELOG.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4533dee2c8..10679e85ee 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,8 @@ This file follows the formats and conventions from [keepachangelog.com]
### Added
- Possibility to define macros with optional parameters. These must be the last
ones in the definition (#285, #876)
+- Workaround for API_DeviceTimedOut errors on MeasurementGroup Start. Call Stop
+ in case this error occured (#764).
- Support to "PETRA3 P23 6C" and "PETRA3 P23 4C" diffractometers by means
of new controller classes and necessary adaptation to macros (#923, #921)
- Top LICENSE file that applies to the whole project (#938)
From c9a668c06f575921534fa0e19e93e2a4201f7fbb Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Wed, 19 Sep 2018 12:56:47 +0200
Subject: [PATCH 094/652] Update CHANGELOG.md
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 10679e85ee..06c0de41a0 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ This file follows the formats and conventions from [keepachangelog.com]
ones in the definition (#285, #876)
- Workaround for API_DeviceTimedOut errors on MeasurementGroup Start. Call Stop
in case this error occured (#764).
+- Optional measurement group parameter to `ct` and `uct` macros (#940, #473)
- Support to "PETRA3 P23 6C" and "PETRA3 P23 4C" diffractometers by means
of new controller classes and necessary adaptation to macros (#923, #921)
- Top LICENSE file that applies to the whole project (#938)
From 3c3a5b9f33f3f79aa00cb3b0e01a8ce204d25208 Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Wed, 19 Sep 2018 18:03:11 +0200
Subject: [PATCH 095/652] Update CHANGELOG.md
---
CHANGELOG.md | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 06c0de41a0..1bff647a00 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,11 @@ This file follows the formats and conventions from [keepachangelog.com]
- Top LICENSE file that applies to the whole project (#938)
### Fixed
+- Make `expconf` react on events of environment, measurement groups and their
+ configurations. An event offers an option to reload the whole experiment
+ configuration or keep the local changes. `expconf` started with
+ `--auto-update` option will automatically reload the whole experiment
+ configuration (#806, #882)
- Avoid final padding in timescan when it was stopped by user (#869, #935)
- Hook places advertised by continuous scans so the `allowHooks` hint and the
code are coherent (#936)
From 3d0da0ee67e8aa6c86f55390762232cfed3872be Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Thu, 20 Sep 2018 12:34:38 +0200
Subject: [PATCH 096/652] Add protection on parameter info reading
Avoid exception raising during the macro typing on the macroexecutor
widget.
---
.../taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py
index b2ec9140df..93f1b057a3 100644
--- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py
+++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macroexecutor.py
@@ -265,10 +265,15 @@ def validateAllExpresion(self, secValidation=False):
# Get the parameters information to check if there are optional
# paramters
- macro_obj = self.getModelObj()
- macro_params_info = macro_obj.getElementInfo(mlist[0]).parameters
+ ms_obj = self.getModelObj()
+ macro_obj = ms_obj.getElementInfo(mlist[0])
+ macro_params_info = None
+ if macro_obj is not None:
+ macro_params_info = macro_obj.parameters
while not ix == Qt.QModelIndex():
+ if macro_params_info is None:
+ break
try:
propValue = mlist[counter]
try:
From c64faf0c3fd9affa7ccd23f68677c12297c8c78e Mon Sep 17 00:00:00 2001
From: Daniel Roldan
Date: Fri, 21 Sep 2018 08:05:56 +0200
Subject: [PATCH 097/652] MacroButton Stop macro instead direct Aborting
---
.../qt/qtgui/extra_macroexecutor/macrobutton.py | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py
index 4654aff27a..c3942c08bb 100755
--- a/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py
+++ b/src/sardana/taurus/qt/qtgui/extra_macroexecutor/macrobutton.py
@@ -271,7 +271,7 @@ def _onButtonClicked(self):
if self.ui.button.isChecked():
self.runMacro()
else:
- self.abort()
+ self.stop()
@ProtectTaurusMessageBox(msg='Error while executing the macro.')
def runMacro(self):
@@ -292,8 +292,13 @@ def runMacro(self):
self.ui.button.setChecked(False)
raise e
+ # For backward compatibility
def abort(self):
- '''abort the macro.'''
+ self.warning("abort method is deprecated. Use stop instead")
+ self.stop()
+
+ def stop(self):
+ '''stop the macro.'''
if self.door is None:
return
self.door.PauseMacro()
@@ -301,15 +306,15 @@ def abort(self):
# we provide a warning message that does not make the process too slow
# It may also be useful and 'ABORT' at TaurusApplication level
# (macros+motions+acquisitions)
- title = 'Aborting macro'
+ title = 'Stopping macro'
message = 'The following macro is still running:\n\n'
message += '%s %s\n\n' % (self.macro_name, ' '.join(self.macro_args))
- message += 'Are you sure you want to abort?\n'
+ message += 'Are you sure you want to Stop?\n'
buttons = Qt.QMessageBox.Ok | Qt.QMessageBox.Cancel
ans = Qt.QMessageBox.warning(
self, title, message, buttons, Qt.QMessageBox.Ok)
if ans == Qt.QMessageBox.Ok:
- self.door.abort(synch=True)
+ self.door.stop(synch=True)
else:
self.ui.button.setChecked(True)
self.door.ResumeMacro()
From df6423c8bf1c8d2004c3c785b728c36149459c64 Mon Sep 17 00:00:00 2001
From: cfalcon
Date: Fri, 21 Sep 2018 08:21:39 +0200
Subject: [PATCH 098/652] Fix sar_info macro reports a wrong description
If a macro module does not have '__doc__' defined
'sar_info' macro reports as description "MACRO in error"
that is not the case.
Fix #944
---
src/sardana/sardanameta.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/sardana/sardanameta.py b/src/sardana/sardanameta.py
index 1324bb83d1..1ccda440ed 100644
--- a/src/sardana/sardanameta.py
+++ b/src/sardana/sardanameta.py
@@ -120,8 +120,9 @@ def __init__(self, **kwargs):
name, _ = os.path.splitext(self.file_name)
self.meta_classes = {}
self.meta_functions = {}
- if module is not None and module.__doc__:
- self.description = module.__doc__
+ if module is not None:
+ if module.__doc__ is not None:
+ self.description = module.__doc__
self._code = getsourcelines(module)[0]
else:
self.description = name + " in error!"
From 34f8c58520d596cc394c82ec61437e7e163efc49 Mon Sep 17 00:00:00 2001
From: cfalcon
Date: Fri, 21 Sep 2018 08:29:44 +0200
Subject: [PATCH 099/652] relmaclib macro does not respect the MacroPath
precedence
If there are two modules with the same name relmaclib macro
reloads the wrong module and raises an AttributeError.
Fix it avoiding to reverse the given list of path in the
'MacroManagermsmacro.reloadMacroLib' method.
Fix #927
---
src/sardana/macroserver/msmacromanager.py | 7 -------
1 file changed, 7 deletions(-)
diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py
index 29d88d70a8..2b93c37474 100644
--- a/src/sardana/macroserver/msmacromanager.py
+++ b/src/sardana/macroserver/msmacromanager.py
@@ -484,13 +484,6 @@ def reloadMacroLib(self, module_name, path=None):
means the current MacroPath will be used]
:return: the MacroLibrary object for the reloaded macro library"""
path = path or self.getMacroPath()
- # 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()
-
mod_manager = ModuleManager()
m, exc_info = None, None
valid, exc_info = mod_manager.isValidModule(module_name, path)
From a1104ece5abf1a5abd741861b07c2160398c73c7 Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Fri, 21 Sep 2018 11:04:14 +0200
Subject: [PATCH 100/652] Update CHANGELOG.md
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1bff647a00..fe5cbb284b 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@ This file follows the formats and conventions from [keepachangelog.com]
configuration or keep the local changes. `expconf` started with
`--auto-update` option will automatically reload the whole experiment
configuration (#806, #882)
+- Reload macro library overriding another library (#927, #946)
- Avoid final padding in timescan when it was stopped by user (#869, #935)
- Hook places advertised by continuous scans so the `allowHooks` hint and the
code are coherent (#936)
From 5c859983c97a53e82ba55d33e256d0d952d70e96 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Fri, 21 Sep 2018 13:53:53 +0200
Subject: [PATCH 101/652] Add pt1d example macro
---
.../macroserver/macros/examples/parameters.py | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/src/sardana/macroserver/macros/examples/parameters.py b/src/sardana/macroserver/macros/examples/parameters.py
index 857fe091e8..04e8539a68 100644
--- a/src/sardana/macroserver/macros/examples/parameters.py
+++ b/src/sardana/macroserver/macros/examples/parameters.py
@@ -56,6 +56,19 @@ def run(self, f):
pass
+class pt1d(Macro):
+ """Macro with one float parameter with default value..
+ Usage from Spock, ex.:
+ pt1d 1
+ pt1d
+ """
+
+ param_def = [['value', Type.Float, None, 'some bloody float']]
+
+ def run(self, f):
+ pass
+
+
class pt2(Macro):
"""Macro with one Motor parameter: Each parameter is described in the
param_def sequence as being a sequence of four elements: name, type,
From 9b2cd8589a3259bdbefed4919ae3fe01ed2c3ae1 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Fri, 21 Sep 2018 14:03:40 +0200
Subject: [PATCH 102/652] Add tests for ParamParser with parameters definition
---
src/sardana/spock/test/test_parser.py | 252 ++++++++++++++++++++++++++
1 file changed, 252 insertions(+)
diff --git a/src/sardana/spock/test/test_parser.py b/src/sardana/spock/test/test_parser.py
index 6f2d6449df..808d4b41cc 100644
--- a/src/sardana/spock/test/test_parser.py
+++ b/src/sardana/spock/test/test_parser.py
@@ -61,3 +61,255 @@ def parse(self, params_str, params):
msg = "Parsing failed (result: %r; expected: %r)" %\
(result, params)
self.assertListEqual(result, params, msg)
+
+
+pt0_params_def = []
+
+pt1d_params_def = [
+ {
+ "default_value": 99,
+ "description": "some bloody float",
+ "max": None,
+ "min": 1,
+ "name": "value",
+ "type": "Float"
+ }
+]
+
+pt3_params_def = [
+ {
+ "default_value": None,
+ "description": "List of values",
+ "max": None,
+ "min": 1,
+ "name": "numb_list",
+ "type": [
+ {
+ "default_value": None,
+ "description": "value",
+ "max": None,
+ "min": 1,
+ "name": "position",
+ "type": "Float"
+ }
+ ]
+ }
+]
+
+pt3d_params_def = [
+ {
+ "default_value": None,
+ "description": "List of values",
+ "max": None,
+ "min": 1,
+ "name": "numb_list",
+ "type": [
+ {
+ "default_value": 21,
+ "description": "value",
+ "max": None,
+ "min": 1,
+ "name": "position",
+ "type": "Float"
+ }
+ ]
+ }
+]
+
+pt5_params_def = [
+ {
+ "default_value": None,
+ "description": "Motor to move",
+ "max": None,
+ "min": 1,
+ "name": "motor",
+ "type": "Motor"
+ },
+ {
+ "default_value": None,
+ "description": "List of values",
+ "max": None,
+ "min": 1,
+ "name": "numb_list",
+ "type": [
+ {
+ "default_value": None,
+ "description": "value",
+ "max": None,
+ "min": 1,
+ "name": "position",
+ "type": "Float"
+ }
+ ]
+ }
+]
+
+pt7_params_def = [
+ {
+ "default_value": None,
+ "description": "List of motor/position pairs",
+ "max": None,
+ "min": 1,
+ "name": "m_p_pair",
+ "type": [
+ {
+ "default_value": None,
+ "description": "Motor to move",
+ "max": None,
+ "min": 1,
+ "name": "motor",
+ "type": "Motor"
+ },
+ {
+ "default_value": None,
+ "description": "Position to move to",
+ "max": None,
+ "min": 1,
+ "name": "position",
+ "type": "Float"
+ }
+ ]
+ }
+]
+
+pt10_params_def = [
+ {
+ "default_value": None,
+ "description": "List of values",
+ "max": None,
+ "min": 1,
+ "name": "numb_list",
+ "type": [
+ {
+ "default_value": None,
+ "description": "value",
+ "max": None,
+ "min": 1,
+ "name": "pos",
+ "type": "Float"
+ }
+ ]
+ },
+ {
+ "default_value": None,
+ "description": "Motor to move",
+ "max": None,
+ "min": 1,
+ "name": "motor",
+ "type": "Motor"
+ },
+]
+
+pt13_params_def = [
+ {
+ "default_value": None,
+ "description": "Motor groups",
+ "max": None,
+ "min": 1,
+ "name": "motor_group_list",
+ "type": [
+ {
+ "default_value": None,
+ "description": "List of motors",
+ "max": None,
+ "min": 1,
+ "name": "motor list",
+ "type": [
+ {
+ "default_value": None,
+ "description": "Motor to move",
+ "max": None,
+ "min": 1,
+ "name": "motor",
+ "type": "Motor"
+ }
+ ]
+ }
+ ]
+ }
+]
+
+pt14_params_def = [
+ {
+ "default_value": None,
+ "description": "Motor groups",
+ "max": None,
+ "min": 1,
+ "name": "motor_group_list",
+ "type": [
+ {
+ "default_value": None,
+ "description": "List of motors",
+ "max": None,
+ "min": 1,
+ "name": "motor list",
+ "type": [
+ {
+ "default_value": None,
+ "description": "Motor to move",
+ "max": None,
+ "min": 1,
+ "name": "motor",
+ "type": "Motor"
+ }
+ ]
+ },
+ {
+ "default_value": None,
+ "description": "Number",
+ "max": None,
+ "min": 1,
+ "name": "float",
+ "type": "Float"
+ }
+ ]
+ }
+]
+
+
+@insertTest(helper_name="parse", params_def=pt0_params_def,
+ params_str="", params=[])
+@insertTest(helper_name="parse", params_def=pt1d_params_def,
+ params_str="1", params=["1"])
+@insertTest(helper_name="parse", params_def=pt1d_params_def,
+ params_str="", params=[])
+@insertTest(helper_name="parse", params_def=pt3_params_def,
+ params_str="1 34 15", params=[["1", "34", "15"]])
+@insertTest(helper_name="parse", params_def=pt3_params_def,
+ params_str="[1 34 15]", params=[["1", "34", "15"]])
+@insertTest(helper_name="parse", params_def=pt3d_params_def,
+ params_str="1 34 15", params=[["1", "34", "15"]])
+@insertTest(helper_name="parse", params_def=pt3d_params_def,
+ params_str="[1 34 15]", params=[["1", "34", "15"]])
+@insertTest(helper_name="parse", params_def=pt3d_params_def,
+ params_str="[1 [] 15]", params=[["1", [], "15"]])
+@insertTest(helper_name="parse", params_def=pt5_params_def,
+ params_str="mot1 1 3", params=["mot1", ["1", "3"]])
+@insertTest(helper_name="parse", params_def=pt5_params_def,
+ params_str="mot1 [1 3]", params=["mot1", ["1", "3"]])
+@insertTest(helper_name="parse", params_def=pt7_params_def,
+ params_str="mot1 1 mot2 3",
+ params=[[["mot1", "1"], ["mot2", "3"]]])
+@insertTest(helper_name="parse", params_def=pt7_params_def,
+ params_str="[[mot1 1] [mot2 3]]",
+ params=[[["mot1", "1"], ["mot2", "3"]]])
+@insertTest(helper_name="parse", params_def=pt10_params_def,
+ params_str="[1 3] mot1", params=[["1", "3"], "mot1"])
+@insertTest(helper_name="parse", params_def=pt10_params_def,
+ params_str="1 mot1", params=[["1"], "mot1"])
+@insertTest(helper_name="parse", params_def=pt13_params_def,
+ params_str="[[mot1 mot2] [mot3 mot4]]",
+ params=[[["mot1", "mot2"], ["mot3", "mot4"]]])
+@insertTest(helper_name="parse", params_def=pt14_params_def,
+ params_str="[[[mot1 mot2] 3] [[mot3] 5]]",
+ params=[[[["mot1", "mot2"], "3"], [["mot3"], "5"]]])
+class ParamParserWithDefTestCase(unittest.TestCase):
+ """Unit tests for ParamParser class initialized with parameters
+ definition.
+ """
+ def parse(self, params_def, params_str, params):
+ p = ParamParser(params_def)
+ result = p.parse(params_str)
+ msg = "Parsing failed (result: %r; expected: %r)" % \
+ (result, params)
+ self.assertListEqual(result, params, msg)
From 1adbb8f13b1d3bfbc5293565278587e9582ee940 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Fri, 21 Sep 2018 14:04:22 +0200
Subject: [PATCH 103/652] Add inteligence to ParamParser to use parameters
definition
---
src/sardana/spock/parser.py | 183 ++++++++++++++++++++++++++++++++----
1 file changed, 165 insertions(+), 18 deletions(-)
diff --git a/src/sardana/spock/parser.py b/src/sardana/spock/parser.py
index 7668b6b668..5472b1f5f4 100644
--- a/src/sardana/spock/parser.py
+++ b/src/sardana/spock/parser.py
@@ -58,6 +58,26 @@ def generate_tokens(text):
yield tok
+def is_repeat_param(param_def):
+ return isinstance(param_def["type"], list)
+
+
+def is_repeat_param_single(param_def):
+ return len(param_def) == 1
+
+
+class ParseError(Exception):
+ pass
+
+
+class UnrecognizedParamValue(ParseError):
+ pass
+
+
+class ExcessParamValue(ParseError):
+ pass
+
+
class ParamParser:
"""Implementation of a recursive descent parser. Use the ._accept() method
to test and accept the current lookahead token. Use the ._expect()
@@ -67,12 +87,17 @@ class ParamParser:
Inspired on Python Cookbook 3 (chapter 2.19)
"""
+ def __init__(self, params_def=None):
+ self._params_def = params_def
+
def parse(self, text):
self.tokens = generate_tokens(text)
self.tok = None # Last symbol consumed
self.nexttok = None # Next symbol tokenized
self._advance() # Load first lookahead token
- return self.param()
+ params = self._params()
+ self._end_check()
+ return params
def _advance(self):
"""Advance one token ahead"""
@@ -93,24 +118,146 @@ def _expect(self, toktype):
# Grammar rules follow
- def param(self):
- """Interpret parameters by iterating over generated tokens. Respect
- quotes for string parameters and parenthesis for repeat parameters.
+ def _params(self, params_def=None):
+ """Interpret parameter values by iterating over generated tokens
+ according to parameters definition.
+
+ It is used either at the macro level or a the repeat parameter
+ repetition level.
+
+ :param params_def: parameters definition as used by the
+ :meth:`sardana.macroserver.msmetamacro.Parametrizable.get_parameter`
+ or by the
+ `attr:`sardana.taurus.core.tango.sardana.macro.MacroInfo.parameters`
+ :type params_def: list
+ :param end_check: whether to check if there are parameter values
+ exceeding parameters definition
+ :type end_check: bool
+ :return: parameter values
+ :rtype: list
"""
+ params_def = params_def or self._params_def
+ len_params_def = len(params_def)
params = []
- while True:
- if self._accept("QUOTEDPARAM"):
- # quoted parameters allows using quotes escaped by \\
- string = self.tok.value
- string = string.replace('\\"', '"')
- params.append(string)
- elif self._accept("SINGQUOTEDPARAM"):
- params.append(self.tok.value)
- elif self._accept("PARAM"):
- params.append(self.tok.value)
- elif self._accept("LPAREN"):
- params.append(self.param())
- self._expect("RPAREN")
+ if self.nexttok is not None:
+ for param_idx, param_def in enumerate(params_def):
+ if is_repeat_param(param_def):
+ last_param = False
+ if param_idx == len_params_def - 1:
+ last_param = True
+ repeat_param_def = param_def["type"]
+ param_value = self._repeat_param(repeat_param_def, last_param)
+ else:
+ param_value = self._param()
+ params.append(param_value)
+ return params
+
+ def _param(self):
+ """Interpret normal parameter value. Respect quotes for string
+ parameters.
+
+ :return: parameter value
+ :rtype: str
+ """
+ if self._accept("QUOTEDPARAM"):
+ # quoted parameters allows using quotes escaped by \\
+ string = self.tok.value
+ string = string.replace('\\"', '"')
+ param = string
+ elif self._accept("SINGQUOTEDPARAM"):
+ param = self.tok.value
+ elif self._accept("PARAM"):
+ tok_value = self.tok.value
+ param = tok_value
+ else:
+ msg = "%s is not a valid param value" % self.tok.value
+ raise UnrecognizedParamValue(msg)
+ return param
+
+ def _repeat_param(self, repeat_param_def, last_param):
+ """Interpret repeat parameter.
+
+ Accepts repeat parameters using the following rules:
+ * enclosed in parenthesis
+ * non-enclosed in parenthesis multiple repetitions of the last repeat
+ parameter (can be single or multiple)
+ * non-enclosed in parenthesis one repetition of single repeat
+ parameter at arbitrary position
+
+ :param repeat_param_def: repeat parameter definition
+ :type repeat_param_def: list
+ :param last_param: whether this repeat parameter is the last in the
+ definition
+ :type last_param: bool
+ :return: repeat parameter value
+ :rtype: list
+ """
+ repeats = []
+
+ if self._accept("LPAREN"):
+ while True:
+ repeat = self._repeat(repeat_param_def)
+ if repeat is None:
+ break
+ repeats.append(repeat)
+ self._expect("RPAREN")
+ else:
+ single = is_repeat_param_single(repeat_param_def)
+ if last_param:
+ while True:
+ repeat = []
+ for _ in repeat_param_def:
+ try:
+ param = self._param()
+ except UnrecognizedParamValue:
+ return repeats
+ if single:
+ repeat = param
+ else:
+ repeat.append(param)
+ repeats.append(repeat)
+ elif single:
+ param = self._param()
+ repeats = [param]
+ return repeats
+
+ def _repeat(self, repeat_param_def):
+ """Interpret one repetition of the repeat parameter.
+
+ :param repeat_param_def: repeat parameter definition
+ :type repeat_param_def: list
+ :return: repeat value
+ :rtype: list or None
+ """
+ repeat = None
+ if self._accept("LPAREN"):
+ # empty brackets will be interpreted as a default value
+ if self._accept("RPAREN"):
+ repeat = []
else:
+ repeat = self._params(repeat_param_def)
+ # repetitions of single repeat parameters are not enclosed
+ # in parenthesis so remove it
+ if is_repeat_param_single(repeat_param_def):
+ repeat = repeat[0]
+ self._expect("RPAREN")
+ else:
+ try:
+ repeat = self._param()
+ except UnrecognizedParamValue:
+ # no repeat found - return None
+ pass
+ return repeat
+
+ def _end_check(self):
+ """Check if there are excessive tokens."""
+ excess_tokens = ""
+ if len(self._params_def) == 0 and self.nexttok is not None:
+ excess_tokens += self.nexttok.value
+ while True:
+ self._advance()
+ if self.nexttok is None:
break
- return params
+ excess_tokens += self.nexttok.value
+ if len(excess_tokens) > 0:
+ raise ExcessParamValue("excess tokens are %s" % excess_tokens)
\ No newline at end of file
From 1e61bbdcb1d1536d49af9a83e038cd7d96fa580c Mon Sep 17 00:00:00 2001
From: zreszela
Date: Fri, 21 Sep 2018 14:05:04 +0200
Subject: [PATCH 104/652] Pass parameters definition to ParamParser in spock
---
src/sardana/spock/spockms.py | 27 +++++++++++++++------------
1 file changed, 15 insertions(+), 12 deletions(-)
diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py
index 934563f6d6..cca3035748 100755
--- a/src/sardana/spock/spockms.py
+++ b/src/sardana/spock/spockms.py
@@ -589,16 +589,20 @@ def _addMacro(self, macro_info):
# IPython < 1 magic commands have different API
if genutils.get_ipython_version_list() < [1, 0]:
def macro_fn(shell, parameter_s='', name=macro_name):
- parameters = split_macro_parameters(parameter_s)
door = genutils.get_door()
+ ms = genutils.get_macro_server()
+ params_def = ms.getMacroInfoObj(name).parameters
+ parameters = split_macro_parameters(parameter_s, params_def)
door.runMacro(macro_name, parameters, synch=True)
macro = door.getLastRunningMacro()
if macro is not None: # maybe none if macro was aborted
return macro.getResult()
else:
def macro_fn(parameter_s='', name=macro_name):
- parameters = split_macro_parameters(parameter_s)
door = genutils.get_door()
+ ms = genutils.get_macro_server()
+ params_def = ms.getMacroInfoObj(name).parameters
+ parameters = split_macro_parameters(parameter_s, params_def)
door.runMacro(macro_name, parameters, synch=True)
macro = door.getLastRunningMacro()
if macro is not None: # maybe none if macro was aborted
@@ -620,19 +624,18 @@ def _removeMacro(self, macro_info):
del self._local_magic[macro_name]
-def split_macro_parameters(parameters_s):
+def split_macro_parameters(parameters_s, params_def):
"""Split string with macro parameters into a list with macro parameters.
Whitespaces are the separators between the parameters.
- When the input string contains square brackets it indicates an advanced
- syntax for representing repeat parameters. Repeat parameters are encapsulated
- in square brackets and its internal repetitions, if composed from more than
- one item are also encapsulated in brackets. In this case the output list
- contains lists internally.
+ Repeat parameters are encapsulated in square brackets and its internal
+ repetitions, if composed from more than one item are also encapsulated
+ in brackets. In this case the output list contains lists internally.
- :param parameters_s (string): input string containing parameters
- :returns (list): parameters represented as a list (may contain internal
- lists)
+ :param parameters_s: input string containing parameters
+ :type parameters_s: string
+ :return: parameters represented as a list (may contain internal lists
+ :rtype: list
"""
- parser = ParamParser()
+ parser = ParamParser(params_def)
return parser.parse(parameters_s)
From a2158f028fb13afa357b75780e4a8cc0300af9e4 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Fri, 21 Sep 2018 14:09:24 +0200
Subject: [PATCH 105/652] Create MacroNode relying on correct macro parameters
Now ParamParser knows the parameters definition and always correctly
parses parameters. There is no need to interpret parameter values
based on the old/new interface and the MacroNode can be directly
populated from parameter values list.
---
.../taurus/core/tango/sardana/macro.py | 66 +------------------
1 file changed, 1 insertion(+), 65 deletions(-)
diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py
index 28db7afb4d..4b73b24d68 100755
--- a/src/sardana/taurus/core/tango/sardana/macro.py
+++ b/src/sardana/taurus/core/tango/sardana/macro.py
@@ -1303,69 +1303,5 @@ def createMacroNode(macro_name, params_def, macro_params):
unify them and place in some common location.
"""
macro_node = MacroNode(name=macro_name, params_def=params_def)
- # Check if ParamRepeat used in advanced interface
- new_interface = False
- for param in macro_params:
- if isinstance(param, list):
- new_interface = True
- break
-
- if not new_interface:
- param_nodes = macro_node.params()
- len_param_nodes = len(param_nodes)
- param_idx = 0
- for param_node, param_raw in zip(param_nodes, macro_params):
- if isinstance(param_node, SingleParamNode):
- param_node.setValue(param_raw)
- # Repeat parameters that are not at the end.
- elif (isinstance(param_node, RepeatParamNode) and
- param_idx < (len_param_nodes - 1)):
- repeat_node = param_node.child(0)
- # 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()
- if len(repeat_node.children()) > 1:
- msg = ("repeat parameter with more than one member must "
- "be defined at the end when intended to "
- "be used with spock syntax")
- raise Exception(msg)
- member_node = repeat_node.child(0)
- if isinstance(member_node, RepeatParamNode):
- msg = ("nested repeat parameters are not allowed when "
- "intended to be used with spock syntax")
- raise Exception(msg)
- member_node.setValue(param_raw)
- # The resting values are interpreted as repeat parameter values.
- # This ignores parameter values which exceeds the parameters
- # definition.
- else:
- params_info = param_node.paramsInfo()
- params_info_len = len(params_info)
- last_param_idx = len_param_nodes - 1
- rep = 0
- mem = 0
- rest_raw = macro_params[last_param_idx:]
- 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 "
- "when intended to be used with spock syntax")
- raise Exception(msg)
- member_node.setValue(member_raw)
- mem += 1
- mem %= params_info_len
- if mem == 0:
- rep += 1
- break
- param_idx += 1
- else:
- macro_node.fromList(macro_params)
+ macro_node.fromList(macro_params)
return macro_node
From 7e7f7240a50cd33f51d35e96c0375c49bf3b5185 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Fri, 21 Sep 2018 14:16:18 +0200
Subject: [PATCH 106/652] Comment old ParamParser test
TODO: these should be converted to the new ParamParser (with parameters
definition) tests.
---
src/sardana/spock/test/test_parser.py | 38 +++++++++++++--------------
1 file changed, 19 insertions(+), 19 deletions(-)
diff --git a/src/sardana/spock/test/test_parser.py b/src/sardana/spock/test/test_parser.py
index 808d4b41cc..cbe99b068c 100644
--- a/src/sardana/spock/test/test_parser.py
+++ b/src/sardana/spock/test/test_parser.py
@@ -30,25 +30,25 @@
from sardana.spock.parser import ParamParser
-@insertTest(helper_name="parse",
- params_str='ScanFile "[\\"file.nxs\\", \\"file.dat\\"]"',
- params=["ScanFile", '["file.nxs", "file.dat"]'])
-@insertTest(helper_name="parse", params_str="[1 [] 3]",
- params=[["1", [], "3"]])
-@insertTest(helper_name="parse",
- params_str="2 3 ['Hello world!' 'How are you?']",
- params=["2", "3", ["Hello world!", "How are you?"]])
-@insertTest(helper_name="parse", params_str="ScanFile file.dat",
- params=["ScanFile", "file.dat"])
-@insertTest(helper_name="parse", params_str="'2 3'", params=["2 3"])
-@insertTest(helper_name="parse", params_str='"2 3"', params=["2 3"])
-@insertTest(helper_name="parse", params_str="[[mot01 3][mot02 5]] ct01 999",
- params=[[["mot01", "3"], ["mot02", "5"]], "ct01", "999"])
-@insertTest(helper_name="parse", params_str="[[2 3][4 5]]",
- params=[[["2", "3"], ["4", "5"]]])
-@insertTest(helper_name="parse", params_str="1 [2 3]",
- params=["1", ["2", "3"]])
-@insertTest(helper_name="parse", params_str="2 3", params=["2", "3"])
+# @insertTest(helper_name="parse",
+# params_str='ScanFile "[\\"file.nxs\\", \\"file.dat\\"]"',
+# params=["ScanFile", '["file.nxs", "file.dat"]'])
+# @insertTest(helper_name="parse", params_str="[1 [] 3]",
+# params=[["1", [], "3"]])
+# @insertTest(helper_name="parse",
+# params_str="2 3 ['Hello world!' 'How are you?']",
+# params=["2", "3", ["Hello world!", "How are you?"]])
+# @insertTest(helper_name="parse", params_str="ScanFile file.dat",
+# params=["ScanFile", "file.dat"])
+# @insertTest(helper_name="parse", params_str="'2 3'", params=["2 3"])
+# @insertTest(helper_name="parse", params_str='"2 3"', params=["2 3"])
+# @insertTest(helper_name="parse", params_str="[[mot01 3][mot02 5]] ct01 999",
+# params=[[["mot01", "3"], ["mot02", "5"]], "ct01", "999"])
+# @insertTest(helper_name="parse", params_str="[[2 3][4 5]]",
+# params=[[["2", "3"], ["4", "5"]]])
+# @insertTest(helper_name="parse", params_str="1 [2 3]",
+# params=["1", ["2", "3"]])
+# @insertTest(helper_name="parse", params_str="2 3", params=["2", "3"])
class ParamParserTestCase(unittest.TestCase):
"""Unit tests for ParamParser class."""
From 335fad162ad3d4de582786097e6f5aff9ab1eada Mon Sep 17 00:00:00 2001
From: teresa
Date: Mon, 24 Sep 2018 09:08:13 +0200
Subject: [PATCH 107/652] Type in phi name
---
src/sardana/macroserver/macros/hkl.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sardana/macroserver/macros/hkl.py b/src/sardana/macroserver/macros/hkl.py
index b7ea6b43f2..515848af15 100644
--- a/src/sardana/macroserver/macros/hkl.py
+++ b/src/sardana/macroserver/macros/hkl.py
@@ -383,7 +383,7 @@ def run(self, H, K, L, Trajectory):
str_pos[self.labelmotor["Mu"]],
str_pos[self.labelmotor["Omega"]],
str_pos[self.labelmotor["Chi"]],
- str_pos[self.labelmotor["Phui"]],
+ str_pos[self.labelmotor["Phi"]],
str_pos[self.labelmotor["Gamma"]],
str_pos[self.labelmotor["Delta"]]))
From 9d1e3fceba5d5a9be55278f5cb4a2c405facca40 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Tue, 25 Sep 2018 10:03:11 +0200
Subject: [PATCH 108/652] Fix flake8 errors
---
src/sardana/macroserver/macros/examples/parameters.py | 2 +-
src/sardana/spock/parser.py | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/sardana/macroserver/macros/examples/parameters.py b/src/sardana/macroserver/macros/examples/parameters.py
index 04e8539a68..681aed83b8 100644
--- a/src/sardana/macroserver/macros/examples/parameters.py
+++ b/src/sardana/macroserver/macros/examples/parameters.py
@@ -23,7 +23,7 @@
"""This module contains macros that demonstrate the usage of macro parameters"""
-from sardana.macroserver.macro import *
+from sardana.macroserver.macro import Macro, Type, ParamRepeat
__all__ = ["pt0", "pt1", "pt2", "pt3", "pt3d", "pt4", "pt5", "pt6", "pt7",
"pt7d1", "pt7d2", "pt8", "pt9", "pt10", "pt11", "pt12", "pt13",
diff --git a/src/sardana/spock/parser.py b/src/sardana/spock/parser.py
index 5472b1f5f4..13d1550534 100644
--- a/src/sardana/spock/parser.py
+++ b/src/sardana/spock/parser.py
@@ -146,7 +146,8 @@ def _params(self, params_def=None):
if param_idx == len_params_def - 1:
last_param = True
repeat_param_def = param_def["type"]
- param_value = self._repeat_param(repeat_param_def, last_param)
+ param_value = self._repeat_param(repeat_param_def,
+ last_param)
else:
param_value = self._param()
params.append(param_value)
From 802d1dce2d0ef36796c4bf372d53cbe47b249385 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Tue, 25 Sep 2018 11:28:14 +0200
Subject: [PATCH 109/652] Parse macro params on the server side when necessary
---
src/sardana/macroserver/msmacromanager.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py
index 29d88d70a8..249e331d8a 100644
--- a/src/sardana/macroserver/msmacromanager.py
+++ b/src/sardana/macroserver/msmacromanager.py
@@ -719,9 +719,14 @@ def getMacroInfo(self, macro_names, format='json'):
ret.append(json_codec.encode(('', macro_meta.serialize()))[1])
return ret
- def _createMacroNode(self, macro_name, macro_params):
+ def _createMacroNode(self, macro_name, macro_params_raw):
macro = self.getMacro(macro_name)
params_def = macro.get_parameter()
+ # merge params to a single, space separated, string (spock like)
+ macro_params_str = " ".join(macro_params_raw)
+ param_parser = ParamParser(params_def)
+ # parse string with macro params to the correct list representation
+ macro_params = param_parser.parse(macro_params_str)
return createMacroNode(macro_name, params_def, macro_params)
def decodeMacroParameters(self, door, raw_params):
From da8c5502044b4e252e238256e31bab6068655f8b Mon Sep 17 00:00:00 2001
From: zreszela
Date: Tue, 25 Sep 2018 17:08:34 +0200
Subject: [PATCH 110/652] Break parsing if there are no next tokens
---
src/sardana/spock/parser.py | 27 +++++++++++++++------------
1 file changed, 15 insertions(+), 12 deletions(-)
diff --git a/src/sardana/spock/parser.py b/src/sardana/spock/parser.py
index 13d1550534..b046f4bf40 100644
--- a/src/sardana/spock/parser.py
+++ b/src/sardana/spock/parser.py
@@ -95,6 +95,7 @@ def parse(self, text):
self.tok = None # Last symbol consumed
self.nexttok = None # Next symbol tokenized
self._advance() # Load first lookahead token
+ #import pdb; pdb.set_trace()
params = self._params()
self._end_check()
return params
@@ -139,18 +140,20 @@ def _params(self, params_def=None):
params_def = params_def or self._params_def
len_params_def = len(params_def)
params = []
- if self.nexttok is not None:
- for param_idx, param_def in enumerate(params_def):
- if is_repeat_param(param_def):
- last_param = False
- if param_idx == len_params_def - 1:
- last_param = True
- repeat_param_def = param_def["type"]
- param_value = self._repeat_param(repeat_param_def,
- last_param)
- else:
- param_value = self._param()
- params.append(param_value)
+ for param_idx, param_def in enumerate(params_def):
+ # no next tokens means that the string being parsed had finished
+ if self.nexttok is None:
+ break
+ if is_repeat_param(param_def):
+ last_param = False
+ if param_idx == len_params_def - 1:
+ last_param = True
+ repeat_param_def = param_def["type"]
+ param_value = self._repeat_param(repeat_param_def,
+ last_param)
+ else:
+ param_value = self._param()
+ params.append(param_value)
return params
def _param(self):
From c0066f5e65b72b5eb5ec35521693743a8c99b6d8 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Tue, 25 Sep 2018 18:23:39 +0200
Subject: [PATCH 111/652] Adapt test to new API of createMacroNode
---
src/sardana/taurus/core/tango/sardana/test/test_macro.py | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/sardana/taurus/core/tango/sardana/test/test_macro.py b/src/sardana/taurus/core/tango/sardana/test/test_macro.py
index 65ba237852..8ed8d271f5 100755
--- a/src/sardana/taurus/core/tango/sardana/test/test_macro.py
+++ b/src/sardana/taurus/core/tango/sardana/test/test_macro.py
@@ -41,6 +41,7 @@
pt14d_param_def)
# TODO: Use unittest.mock instead of this fake class.
from sardana.macroserver.mstypemanager import TypeManager
+from sardana.spock.parser import ParamParser
class FakeMacroServer(object):
@@ -72,7 +73,7 @@ class FakeMacroServer(object):
"max": None
}
]
-pt8_params_value = ["mot73", "5.0", "mot74", "8.0"]
+pt8_params_str = "mot73 5.0 mot74 8.0"
#
pt8_xml = \
@@ -91,7 +92,7 @@ class FakeMacroServer(object):
@insertTest(helper_name='verifyXML', macro_name="pt8", param_def=pt8_params_def,
- param_value=pt8_params_value, expected_xml_rep=pt8_xml)
+ param_str=pt8_params_str, expected_xml_rep=pt8_xml)
class MacroNodeTestCase(unittest.TestCase):
def _validateXML(self, macronode_xml, expected_xml):
@@ -106,7 +107,7 @@ def _validateXML(self, macronode_xml, expected_xml):
# 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):
+ def verifyXML(self, macro_name, param_def, param_str, expected_xml_rep):
"""
Helper to verify the generated XML of a macroNode
:param macro_name: (str) name of the macro
@@ -116,6 +117,8 @@ def verifyXML(self, macro_name, param_def, param_value, expected_xml_rep):
:param expected_xml_rep: "pretty print" string representation of a XML
macroNode
"""
+ param_parser = ParamParser(param_def)
+ param_value = param_parser.parse(param_str)
# Create the MacroNide with the inputs
macronode = createMacroNode(macro_name, param_def, param_value)
# Get the MacroNode equivalent XML tree
From 53f04f665bf9340479106b1735af4cf18001ec3a Mon Sep 17 00:00:00 2001
From: zreszela
Date: Tue, 25 Sep 2018 18:27:26 +0200
Subject: [PATCH 112/652] Fix flake8
---
src/sardana/macroserver/msmacromanager.py | 6 +++---
src/sardana/spock/parser.py | 3 +--
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py
index 249e331d8a..992d0a03f8 100644
--- a/src/sardana/macroserver/msmacromanager.py
+++ b/src/sardana/macroserver/msmacromanager.py
@@ -733,9 +733,9 @@ 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
+ :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")
diff --git a/src/sardana/spock/parser.py b/src/sardana/spock/parser.py
index b046f4bf40..b01e57c6aa 100644
--- a/src/sardana/spock/parser.py
+++ b/src/sardana/spock/parser.py
@@ -95,7 +95,6 @@ def parse(self, text):
self.tok = None # Last symbol consumed
self.nexttok = None # Next symbol tokenized
self._advance() # Load first lookahead token
- #import pdb; pdb.set_trace()
params = self._params()
self._end_check()
return params
@@ -264,4 +263,4 @@ def _end_check(self):
break
excess_tokens += self.nexttok.value
if len(excess_tokens) > 0:
- raise ExcessParamValue("excess tokens are %s" % excess_tokens)
\ No newline at end of file
+ raise ExcessParamValue("excess tokens are %s" % excess_tokens)
From f3b576896e996929885c6450b1699f3ab9aa52df Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Wed, 26 Sep 2018 10:53:02 +0200
Subject: [PATCH 113/652] Update CHANGELOG.md
---
CHANGELOG.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fe5cbb284b..c0ca91c008 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,8 @@ This file follows the formats and conventions from [keepachangelog.com]
- Avoid final padding in timescan when it was stopped by user (#869, #935)
- Hook places advertised by continuous scans so the `allowHooks` hint and the
code are coherent (#936)
+- Macro/controller module description when module does not have a docstring
+ (#945)
### Changed
- Move pre-scan and post-scan hooks out of `scan_loop` method (#920, #922,
From fd88ef493f49925d17f7a018dadceb12e689b544 Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Wed, 26 Sep 2018 10:58:08 +0200
Subject: [PATCH 114/652] Update CHANGELOG.md
---
CHANGELOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c0ca91c008..6614b25863 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,7 +7,7 @@ This file follows the formats and conventions from [keepachangelog.com]
### Added
- Possibility to define macros with optional parameters. These must be the last
- ones in the definition (#285, #876)
+ ones in the definition (#285, #876, #943, #941)
- Workaround for API_DeviceTimedOut errors on MeasurementGroup Start. Call Stop
in case this error occured (#764).
- Optional measurement group parameter to `ct` and `uct` macros (#940, #473)
From 03abe97504b70538d76287f0ed31de023d3a6c0f Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Fri, 28 Sep 2018 08:25:03 +0200
Subject: [PATCH 115/652] Implement PoolMGSynchronization class
---
src/sardana/pool/poolmeasurementgroup.py | 42 ++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 0ad982b431..0d0462bb1b 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -122,6 +122,48 @@ def _to_fqdn(name, logger=None):
return full_name
+class PoolMGSynchronization(list):
+
+ def _get_param(self, param, domain=SynchDomain.Time):
+ """
+ Extract parameter from synchronization dict. If there is only
+ one group in the synchronization than returns float with the value.
+ Otherwise a list of floats with different values.
+
+ :param param: parameter type
+ :type param: SynchParam
+ :param domain: domain
+ :type param: SynchDomain
+ :return:
+ :rtype float or [float]
+ """
+
+ if len(self) == 1:
+ return self[0][param][domain]
+
+ values = []
+ for group in self:
+ value = group[param][domain]
+ repeats = group[SynchParam.Repeats]
+ values += [value] * repeats
+ return values
+
+ @property
+ def repetitions(self):
+ repetitions = 0
+ for group in self:
+ repetitions += group[SynchParam.Repeats]
+ return repetitions
+
+ @property
+ def integration_time(self):
+ return self._get_param(SynchParam.Active)
+
+ @property
+ def total_time(self):
+ return self._get_param(SynchParam.Total)
+
+
class PoolMeasurementGroup(PoolGroupElement):
DFT_DESC = 'General purpose measurement group'
From 72376ebd189e44740c1f4c36ffd3e99b2c44a4c1 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Fri, 28 Sep 2018 08:28:40 +0200
Subject: [PATCH 116/652] Adapt to use PoolMGSynchronization
---
src/sardana/pool/poolacquisition.py | 48 +-----------------------
src/sardana/pool/poolmeasurementgroup.py | 14 +++----
2 files changed, 9 insertions(+), 53 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 3d5719ed6c..63353ec383 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -164,50 +164,6 @@ def getTGConfiguration(MGcfg):
return TGcfg, _tg_element_list
-def extract_integ_time(synchronization):
- """Extract integration time(s) from synchronization dict. If there is only
- one group in the synchronization than returns float with the integration
- time. Otherwise a list of floats with different integration times.
-
- TODO: (technical debt) All the MeasurementGroup synchronization
- logic should be encapsulate in a dedicated class instead of
- using a basic data structures like dict or lists...
-
- :param synchronization: group(s) where each group is described by
- SynchParam(s)
- :type synchronization: list(dict)
- :return list(float) or float
- """
- if len(synchronization) == 1:
- integ_time = synchronization[0][SynchParam.Active][SynchDomain.Time]
- else:
- integ_time = []
- for group in synchronization:
- active_time = group[SynchParam.Active][SynchDomain.Time]
- repeats = group[SynchParam.Repeats]
- integ_time += [active_time] * repeats
- return integ_time
-
-
-def extract_repetitions(synchronization):
- """Extract repetitions from synchronization dict.
-
- TODO: (technical debt) All the MeasurementGroup synchronization
- logic should be encapsulate in a dedicated class instead of
- using a basic data structures like dict or lists...
-
- :param synchronization: group(s) where each group is described by
- SynchParam(s)
- :type synchronization: list(dict)
- :return: number of repetitions
- :rtype: int
- """
- repetitions = 0
- for group in synchronization:
- repetitions += group[SynchParam.Repeats]
- return repetitions
-
-
def is_value_error(value):
if isinstance(value, SardanaValue) and value.error:
return True
@@ -308,8 +264,8 @@ def run(self, *args, **kwargs):
pseudo_elem.clear_value_buffer()
config = kwargs['config']
synchronization = kwargs["synchronization"]
- integ_time = extract_integ_time(synchronization)
- repetitions = extract_repetitions(synchronization)
+ integ_time = synchronization.integration_time
+ repetitions = synchronization.repetitions
# TODO: this code splits the global mg configuration into
# experimental channels triggered by hw and experimental channels
# triggered by sw. Refactor it!!!!
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 0d0462bb1b..d5f56e4716 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -180,7 +180,7 @@ def __init__(self, **kwargs):
# by default software synchronizer initial domain is set to Position
self._sw_synch_initial_domain = SynchDomain.Position
- self._synchronization = []
+ self._synchronization = PoolMGSynchronization()
# dict with channel and its acquisition synchronization
# key: PoolBaseChannel; value: AcqSynch
self._channel_to_acq_synch = {}
@@ -623,14 +623,14 @@ def get_timer(self):
# -------------------------------------------------------------------------
def get_integration_time(self):
- if len(self._synchronization) == 0:
+ integration_time = self._synchronization.integration_time
+ if type(integration_time) == float:
+ return integration_time
+ elif len(integration_time) == 0:
raise Exception("The synchronization group has not been"
" initialized")
- elif len(self._synchronization) > 1:
+ elif len(integration_time) > 1:
raise Exception("There are more than one synchronization groups")
- else:
- return self._synchronization[0][SynchParam.Active][
- SynchDomain.Time]
def set_integration_time(self, integration_time, propagate=1):
total_time = integration_time + self.latency_time
@@ -691,7 +691,7 @@ def get_synchronization(self):
return self._synchronization
def set_synchronization(self, synchronization, propagate=1):
- self._synchronization = synchronization
+ self._synchronization = PoolMGSynchronization(synchronization)
self._config_dirty = True # acquisition mode goes to configuration
if not propagate:
return
From becdfed1663d01374cc886734395f95ce6ac8d89 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Fri, 28 Sep 2018 17:15:55 +0200
Subject: [PATCH 117/652] Rename PoolMgSynchronization to
SynchronizationDescription
---
src/sardana/pool/poolmeasurementgroup.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index d5f56e4716..d090a67263 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -122,7 +122,7 @@ def _to_fqdn(name, logger=None):
return full_name
-class PoolMGSynchronization(list):
+class SynchronizationDescription(list):
def _get_param(self, param, domain=SynchDomain.Time):
"""
@@ -180,7 +180,7 @@ def __init__(self, **kwargs):
# by default software synchronizer initial domain is set to Position
self._sw_synch_initial_domain = SynchDomain.Position
- self._synchronization = PoolMGSynchronization()
+ self._synchronization = SynchronizationDescription()
# dict with channel and its acquisition synchronization
# key: PoolBaseChannel; value: AcqSynch
self._channel_to_acq_synch = {}
@@ -691,7 +691,7 @@ def get_synchronization(self):
return self._synchronization
def set_synchronization(self, synchronization, propagate=1):
- self._synchronization = PoolMGSynchronization(synchronization)
+ self._synchronization = SynchronizationDescription(synchronization)
self._config_dirty = True # acquisition mode goes to configuration
if not propagate:
return
From 5487ba537e283eadeedc8fad3cf3160b66d587d5 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Fri, 28 Sep 2018 17:16:21 +0200
Subject: [PATCH 118/652] Complement docstrings
---
src/sardana/pool/poolmeasurementgroup.py | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index d090a67263..d6de4f723e 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -123,18 +123,23 @@ def _to_fqdn(name, logger=None):
class SynchronizationDescription(list):
+ """Synchronization description. It is composed from groups - repetitions
+ of equidistant synchronization events. Each group is described by
+ :class:`~sardana.pool.pooldefs.SynchParam` parameters which may have
+ values in :class:`~sardana.pool.pooldefs.SynchDomain` domains.
+ """
def _get_param(self, param, domain=SynchDomain.Time):
"""
- Extract parameter from synchronization dict. If there is only
- one group in the synchronization than returns float with the value.
- Otherwise a list of floats with different values.
+ Extract parameter from synchronization description and its groups. If
+ there is only one group in the synchronization then returns float
+ with the value. Otherwise a list of floats with different values.
:param param: parameter type
- :type param: SynchParam
+ :type param: :class:`~sardana.pool.pooldefs.SynchParam`
:param domain: domain
- :type param: SynchDomain
- :return:
+ :type param: :class:`~sardana.pool.pooldefs.SynchDomain`
+ :return: parameter value(s)
:rtype float or [float]
"""
From 164a724657edee9034060551fdfb4d25a9719f97 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Fri, 28 Sep 2018 17:17:13 +0200
Subject: [PATCH 119/652] Expose class properties first
---
src/sardana/pool/poolmeasurementgroup.py | 30 ++++++++++++------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index d6de4f723e..b7e0abd019 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -129,6 +129,21 @@ class SynchronizationDescription(list):
values in :class:`~sardana.pool.pooldefs.SynchDomain` domains.
"""
+ @property
+ def repetitions(self):
+ repetitions = 0
+ for group in self:
+ repetitions += group[SynchParam.Repeats]
+ return repetitions
+
+ @property
+ def integration_time(self):
+ return self._get_param(SynchParam.Active)
+
+ @property
+ def total_time(self):
+ return self._get_param(SynchParam.Total)
+
def _get_param(self, param, domain=SynchDomain.Time):
"""
Extract parameter from synchronization description and its groups. If
@@ -153,21 +168,6 @@ def _get_param(self, param, domain=SynchDomain.Time):
values += [value] * repeats
return values
- @property
- def repetitions(self):
- repetitions = 0
- for group in self:
- repetitions += group[SynchParam.Repeats]
- return repetitions
-
- @property
- def integration_time(self):
- return self._get_param(SynchParam.Active)
-
- @property
- def total_time(self):
- return self._get_param(SynchParam.Total)
-
class PoolMeasurementGroup(PoolGroupElement):
From 91c0debbb1e666eeb7cc68a1826c567ac0df307e Mon Sep 17 00:00:00 2001
From: zreszela
Date: Fri, 28 Sep 2018 17:19:33 +0200
Subject: [PATCH 120/652] Move SynchronizationDescription to
poolsynchronization module
---
src/sardana/pool/poolmeasurementgroup.py | 48 +----------------------
src/sardana/pool/poolsynchronization.py | 50 +++++++++++++++++++++++-
2 files changed, 50 insertions(+), 48 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index b7e0abd019..280821e746 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -46,6 +46,7 @@
SynchDomain)
from sardana.pool.poolgroupelement import PoolGroupElement
from sardana.pool.poolacquisition import PoolAcquisition
+from sardana.pool.poolsynchronization import SynchronizationDescription
from sardana.pool.poolexternal import PoolExternalObject
from sardana.taurus.core.tango.sardana import PlotType, Normalization
@@ -122,53 +123,6 @@ def _to_fqdn(name, logger=None):
return full_name
-class SynchronizationDescription(list):
- """Synchronization description. It is composed from groups - repetitions
- of equidistant synchronization events. Each group is described by
- :class:`~sardana.pool.pooldefs.SynchParam` parameters which may have
- values in :class:`~sardana.pool.pooldefs.SynchDomain` domains.
- """
-
- @property
- def repetitions(self):
- repetitions = 0
- for group in self:
- repetitions += group[SynchParam.Repeats]
- return repetitions
-
- @property
- def integration_time(self):
- return self._get_param(SynchParam.Active)
-
- @property
- def total_time(self):
- return self._get_param(SynchParam.Total)
-
- def _get_param(self, param, domain=SynchDomain.Time):
- """
- Extract parameter from synchronization description and its groups. If
- there is only one group in the synchronization then returns float
- with the value. Otherwise a list of floats with different values.
-
- :param param: parameter type
- :type param: :class:`~sardana.pool.pooldefs.SynchParam`
- :param domain: domain
- :type param: :class:`~sardana.pool.pooldefs.SynchDomain`
- :return: parameter value(s)
- :rtype float or [float]
- """
-
- if len(self) == 1:
- return self[0][param][domain]
-
- values = []
- for group in self:
- value = group[param][domain]
- repeats = group[SynchParam.Repeats]
- values += [value] * repeats
- return values
-
-
class PoolMeasurementGroup(PoolGroupElement):
DFT_DESC = 'General purpose measurement group'
diff --git a/src/sardana/pool/poolsynchronization.py b/src/sardana/pool/poolsynchronization.py
index 11d536779a..850dff9378 100644
--- a/src/sardana/pool/poolsynchronization.py
+++ b/src/sardana/pool/poolsynchronization.py
@@ -27,13 +27,14 @@
"""This module is part of the Python Pool libray. It defines the class for the
trigger/gate generation"""
-__all__ = ["PoolSynchronization", "TGChannel"]
+__all__ = ["PoolSynchronization", "SynchronizationDescription", "TGChannel"]
import time
from functools import partial
from taurus.core.util.log import DebugIt
from sardana import State
from sardana.sardanathreadpool import get_thread_pool
+from sardana.pool.pooldefs import SynchDomain, SynchParam
from sardana.pool.poolaction import ActionContext, PoolActionItem, PoolAction
from sardana.util.funcgenerator import FunctionGenerator
@@ -60,6 +61,53 @@ def __getattr__(self, name):
return getattr(self.element, name)
+class SynchronizationDescription(list):
+ """Synchronization description. It is composed from groups - repetitions
+ of equidistant synchronization events. Each group is described by
+ :class:`~sardana.pool.pooldefs.SynchParam` parameters which may have
+ values in :class:`~sardana.pool.pooldefs.SynchDomain` domains.
+ """
+
+ @property
+ def repetitions(self):
+ repetitions = 0
+ for group in self:
+ repetitions += group[SynchParam.Repeats]
+ return repetitions
+
+ @property
+ def integration_time(self):
+ return self._get_param(SynchParam.Active)
+
+ @property
+ def total_time(self):
+ return self._get_param(SynchParam.Total)
+
+ def _get_param(self, param, domain=SynchDomain.Time):
+ """
+ Extract parameter from synchronization description and its groups. If
+ there is only one group in the synchronization then returns float
+ with the value. Otherwise a list of floats with different values.
+
+ :param param: parameter type
+ :type param: :class:`~sardana.pool.pooldefs.SynchParam`
+ :param domain: domain
+ :type param: :class:`~sardana.pool.pooldefs.SynchDomain`
+ :return: parameter value(s)
+ :rtype float or [float]
+ """
+
+ if len(self) == 1:
+ return self[0][param][domain]
+
+ values = []
+ for group in self:
+ value = group[param][domain]
+ repeats = group[SynchParam.Repeats]
+ values += [value] * repeats
+ return values
+
+
class PoolSynchronization(PoolAction):
'''Action class responsible for trigger/gate generation
'''
From 72fc3bc19d4a47d9abbcd5e275a24ceb5b29e879 Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Sat, 29 Sep 2018 00:52:18 +0200
Subject: [PATCH 121/652] Add implementation chapter
---
doc/source/sep/SEP18.md | 49 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 47 insertions(+), 2 deletions(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index e0d95ee7dd..23264e1a90 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -58,5 +58,50 @@ Design
* Per acquisition preparation with repetitions=1 e.g. Load(One|All)
6. Modify acquisition actions (and synchronization action if necessary) so
they support the new concepts added in points 2 and 4.
-5. Extend GSF (step mode) with measurement preparation (repetitions=n) if
-possible i.e. scan macro knows beforehand the number of points.
+5. *Extend Generic Scan Framework* (GSF), more preciselly scan in step mode
+with measurement preparation (repetitions=n) if possible i.e. scan macro knows
+beforehand the number of points.
+
+Implementation
+--------------
+
+Measurement group is extended by the *prepare* command with two parameters:
+synchronization description and number of repeats (these repeats is a
+different concept then the one from the synchronization description). The
+second one indicates how many times measurement group will be started, with
+the *start* command, to measure according to the synchronization description.
+
+1. Measurement group - Tango device class
+ * Add `Prepare` command. TODO: investigate the best way to pass
+ synchronization description, as JSON serialized string, together with the
+ repeats integer.
+ * Remove `synchronization` attribute (experimental API) - no backwards
+ compatibility.
+2. Measurement group - core class
+ * Add `prepare(synchronization, repeats=1)` method
+ * Remove `synchronization` property (experimental API) - no backwards
+ compatibility.
+3. Measurement group - Taurus extension
+ * Add `prepare` method which simply maps to `Prepare` Tango command
+ * Add `acquire` method according to the following pseudo code:
+ * `Start()`
+ * `waitFinish()`
+ * Implement `count` method according to the following pseudo code:
+ * `prepare(synchronization & repeats = 1)` where synchronization
+ contains the integration time
+ * `acquire()`
+ * Implement `count_continuous` (previous `measure`) method according to
+ the following pseudo code:
+ * `prepare(synchronization & repeats = 1)` where synchronization may
+ contain the continuous acquisition description
+ * `subscribeValueBuffer()`
+ * `acquire()`
+ * `unsubscribeValueBuffer()`
+4. GSF - step scan
+ * `SScan` implemented according to the following pseudo code:
+ * If number of points is known:
+ * `prepare(synchronization, repeats=n)` where synchronization
+ contains the integration time and n means number of points
+ * `for step in range(n): acquire()`
+ * If number of points is unknown:
+ * `while new_step: acquire()`
From f1af662c707e1149a652e4a0ecaa358f9a896237 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 26 Sep 2018 17:57:15 +0200
Subject: [PATCH 122/652] Add Preparable controller interface
---
src/sardana/pool/controller.py | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py
index 6b948902fb..f96dd2fcf8 100644
--- a/src/sardana/pool/controller.py
+++ b/src/sardana/pool/controller.py
@@ -655,6 +655,28 @@ def ReadOne(self, axis):
raise NotImplementedError("ReadOne must be defined in the controller")
+class Preparable(object):
+ """A Preparable interface. A controller for which its axis are
+ 'pareparable' for a measurement (like a counter, 1D or 2D for example)
+ should implement this interface
+
+ .. note: Do not inherit directly from Preparable."""
+
+ def PrepareOne(self, axis, value, repetitions):
+ """**Controller API**. Override if necessary.
+ Called to load the integration time / monitor value and number of
+ repetitions.
+ Default implementation does nothing.
+
+ :param int axis: axis number
+ :param float value: integration time /monitor value
+ :param int repetitions: number of repetitions
+ :param float value: integration time /monitor value
+ """
+ raise NotImplementedError("PrepareOne must be defined in the "
+ "controller")
+
+
class Loadable(object):
"""A Loadable interface. A controller for which it's axis are 'loadable'
(like a counter, 1D or 2D for example) should implement this interface
From 8140e23fbb66051463f9c39cfe23b92bfefcbf20 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 26 Sep 2018 18:02:41 +0200
Subject: [PATCH 123/652] Use Preparable interface for C/T, 1D and 2D
controllers
---
src/sardana/pool/controller.py | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py
index f96dd2fcf8..65c5aa762a 100644
--- a/src/sardana/pool/controller.py
+++ b/src/sardana/pool/controller.py
@@ -891,7 +891,8 @@ def DefinePosition(self, axis, position):
pass
-class CounterTimerController(Controller, Readable, Startable, Stopable, Loadable):
+class CounterTimerController(Controller, Readable, Startable, Stopable,
+ Loadable, Preparable):
"""Base class for a counter/timer controller. Inherit from this class to
implement your own counter/timer controller for the device pool.
@@ -1054,7 +1055,8 @@ def AbortOne(self, axis):
pass
-class OneDController(Controller, Readable, Startable, Stopable, Loadable):
+class OneDController(Controller, Readable, Startable, Stopable, Loadable,
+ Preparable):
"""Base class for a 1D controller. Inherit from this class to
implement your own 1D controller for the device pool.
@@ -1092,7 +1094,8 @@ def GetAxisPar(self, axis, parameter):
return self.GetPar(axis, parameter)
-class TwoDController(Controller, Readable, Startable, Stopable, Loadable):
+class TwoDController(Controller, Readable, Startable, Stopable, Loadable,
+ Preparable):
"""Base class for a 2D controller. Inherit from this class to
implement your own 2D controller for the device pool."""
From a93101517411e2d49d44d4b2d8c037c4728c8f4d Mon Sep 17 00:00:00 2001
From: zreszela
Date: Thu, 27 Sep 2018 16:44:16 +0200
Subject: [PATCH 124/652] Remove integration time / monitor counts from
PrepareOne
---
src/sardana/pool/controller.py | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py
index 65c5aa762a..ebafa22bf6 100644
--- a/src/sardana/pool/controller.py
+++ b/src/sardana/pool/controller.py
@@ -662,14 +662,12 @@ class Preparable(object):
.. note: Do not inherit directly from Preparable."""
- def PrepareOne(self, axis, value, repetitions):
+ def PrepareOne(self, axis, repetitions):
"""**Controller API**. Override if necessary.
- Called to load the integration time / monitor value and number of
- repetitions.
+ Called to load the number of repetitions.
Default implementation does nothing.
:param int axis: axis number
- :param float value: integration time /monitor value
:param int repetitions: number of repetitions
:param float value: integration time /monitor value
"""
From dbebd400f4e4fc5e04a8885da7146700fdc9c735 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Thu, 27 Sep 2018 16:44:42 +0200
Subject: [PATCH 125/652] Make PrepareOne implementation optional
---
src/sardana/pool/controller.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/sardana/pool/controller.py b/src/sardana/pool/controller.py
index ebafa22bf6..e441e44c29 100644
--- a/src/sardana/pool/controller.py
+++ b/src/sardana/pool/controller.py
@@ -671,8 +671,7 @@ def PrepareOne(self, axis, repetitions):
:param int repetitions: number of repetitions
:param float value: integration time /monitor value
"""
- raise NotImplementedError("PrepareOne must be defined in the "
- "controller")
+ pass
class Loadable(object):
From 2ef1b06de9ebd257d1687e8f980ce3f3f2e68595 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 1 Oct 2018 12:27:55 +0200
Subject: [PATCH 126/652] Add MGConfiguration class
---
src/sardana/pool/poolmeasurementgroup.py | 380 +++++++++++++++++++++++
1 file changed, 380 insertions(+)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 0ad982b431..796aba6cf3 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -31,6 +31,7 @@
__docformat__ = 'restructuredtext'
import threading
+import weakref
try:
from taurus.core.taurusvalidator import AttributeNameValidator as\
@@ -122,6 +123,385 @@ def _to_fqdn(name, logger=None):
return full_name
+class MGConfiguration(object):
+
+ DFT_DESC = 'General purpose measurement group'
+
+ def __init__(self, mg):
+ self._mg = weakref.ref(mg)()
+ self._config = None
+ self.use_fqdn = True
+ # list of controller with channels enabled.
+ self.enabled_ctrls = []
+ # dict with channel and its acquisition synchronization
+ # key: PoolBaseChannel; value: AcqSynch
+ self.channel_to_acq_synch = {}
+ # dict with controller and its acquisition synchronization
+ # key: PoolController; value: AcqSynch
+ self.ctrl_to_acq_synch = {}
+
+ def __check_config(func):
+ def wrapper(self, *args, **kwargs):
+ if self._config is None:
+ raise RuntimeError('The configuration is empty.')
+ return func(self, *args, **kwargs)
+ return wrapper
+
+ def __getitem__(self, item):
+ return self._config.__getitem__(item)
+
+ @property
+ @__check_config
+ def timer(self):
+ return self._config['timer']
+
+ @property
+ @__check_config
+ def monitor(self):
+ return self._config['monitor']
+
+ @property
+ @__check_config
+ def controllers(self):
+ return self._config['controllers']
+
+ @property
+ @__check_config
+ def configuration(self):
+ return self._config
+
+ @configuration.setter
+ def configuration(self, config=None):
+ self._build_configuration(config)
+
+ def set_configuration_from_user(self, cfg, to_fqdn=True):
+ config = {}
+ user_elements = self._mg.get_user_elements()
+ pool = self._mg.pool
+ if len(user_elements) == 0:
+ # All timers were disabled
+ default_timer = None
+ else:
+ default_timer = user_elements[0].full_name
+
+ timer_name = cfg.get('timer', default_timer)
+ monitor_name = cfg.get('monitor', default_timer)
+ if to_fqdn:
+ timer_name = _to_fqdn(timer_name, logger=self._mg)
+ config['timer'] = pool.get_element_by_full_name(timer_name)
+ if to_fqdn:
+ monitor_name = _to_fqdn(monitor_name, logger=self._mg)
+ config['monitor'] = pool.get_element_by_full_name(monitor_name)
+ config['controllers'] = controllers = {}
+
+ for c_name, c_data in cfg['controllers'].items():
+ # backwards compatibility for measurement groups created before
+ # implementing feature-372:
+ # https://sourceforge.net/p/sardana/tickets/372/
+ # WARNING: this is one direction backwards compatibility - it just
+ # reads channels from the units, but does not write channels to the
+ # units back
+ if 'units' in c_data:
+ c_data = c_data['units']['0']
+ # discard controllers which don't have items (garbage)
+ ch_count = len(c_data['channels'])
+ if ch_count == 0:
+ continue
+
+ external = c_name.startswith('__')
+ if external:
+ ctrl = c_name
+ else:
+ if to_fqdn:
+ c_name = _to_fqdn(c_name, logger=self._mg)
+ ctrl = pool.get_element_by_full_name(c_name)
+ assert ctrl.get_type() == ElementType.Controller
+ controllers[ctrl] = ctrl_data = {}
+
+ # exclude external and not timerable elements
+ if not external and ctrl.is_timerable():
+ timer_name = c_data['timer']
+ if to_fqdn:
+ timer_name = _to_fqdn(timer_name, logger=self._mg)
+ timer = pool.get_element_by_full_name(timer_name)
+ ctrl_data['timer'] = timer
+ monitor_name = c_data['monitor']
+ if to_fqdn:
+ monitor_name = _to_fqdn(monitor_name, logger=self._mg)
+ monitor = pool.get_element_by_full_name(monitor_name)
+ ctrl_data['monitor'] = monitor
+ synchronizer = c_data.get('synchronizer')
+ # for backwards compatibility purposes
+ # protect measurement groups without synchronizer defined
+ if synchronizer is None:
+ synchronizer = 'software'
+ elif synchronizer != 'software':
+ if to_fqdn:
+ synchronizer = _to_fqdn(synchronizer, logger=self._mg)
+ synchronizer = pool.get_element_by_full_name(synchronizer)
+ ctrl_data['synchronizer'] = synchronizer
+ try:
+ synchronization = c_data['synchronization']
+ except KeyError:
+ # backwards compatibility for configurations before SEP6
+ synchronization = c_data['trigger_type']
+ msg = ("trigger_type configuration parameter is deprecated"
+ " in favor of synchronization. Re-apply "
+ "configuration in order to upgrade.")
+ self._mg.warning(msg)
+ ctrl_data['synchronization'] = synchronization
+ ctrl_data['channels'] = channels = {}
+ for ch_name, ch_data in c_data['channels'].items():
+ if external:
+ validator = TangoAttributeNameValidator()
+ params = validator.getParams(ch_data['full_name'])
+ params['pool'] = self._mg.pool
+ channel = PoolExternalObject(**params)
+ else:
+ if to_fqdn:
+ ch_name = _to_fqdn(ch_name, logger=self._mg)
+ channel = pool.get_element_by_full_name(ch_name)
+ channels[channel] = dict(ch_data)
+
+ config['label'] = cfg.get('label', self._mg.name)
+ config['description'] = cfg.get('description', self.DFT_DESC)
+ self.use_fqdn = to_fqdn
+ self._build_configuration(config)
+
+ def get_configuration_for_user(self):
+ cfg = self._config
+ config = {}
+
+ config['timer'] = cfg['timer'].full_name
+ config['monitor'] = cfg['monitor'].full_name
+ config['controllers'] = controllers = {}
+
+ for c, c_data in cfg['controllers'].items():
+ ctrl_name = c
+ if not isinstance(c, (str, unicode)):
+ ctrl_name = c.full_name
+ external = ctrl_name.startswith('__')
+ controllers[ctrl_name] = ctrl_data = {}
+ if not external and c.is_timerable():
+ if 'timer' in c_data:
+ ctrl_data['timer'] = c_data['timer'].full_name
+ if 'monitor' in c_data:
+ ctrl_data['monitor'] = c_data['monitor'].full_name
+ if 'synchronizer' in c_data:
+ synchronizer = c_data['synchronizer']
+ if synchronizer != 'software':
+ synchronizer = synchronizer.full_name
+ ctrl_data['synchronizer'] = synchronizer
+ if 'synchronization' in c_data:
+ ctrl_data['synchronization'] = c_data['synchronization']
+ ctrl_data['channels'] = channels = {}
+ for ch, ch_data in c_data['channels'].items():
+ channels[ch.full_name] = dict(ch_data)
+
+ config['label'] = cfg['label']
+ config['description'] = cfg['description']
+ return config
+
+ def _build_channel_defaults(self, channel_data, channel):
+ """
+ Fills the channel default values for the given channel dictionary
+ """
+
+ external_from_name = isinstance(channel, (str, unicode))
+ ndim = None
+ if external_from_name:
+ name = full_name = source = channel
+ ndim = 0 # TODO: this should somehow verify the dimension
+ else:
+ name = channel.name
+ full_name = channel.full_name
+ source = channel.get_source()
+ ndim = None
+ ctype = channel.get_type()
+ if ctype == ElementType.CTExpChannel:
+ ndim = 0
+ elif ctype == ElementType.PseudoCounter:
+ ndim = 0
+ elif ctype == ElementType.ZeroDExpChannel:
+ ndim = 0
+ elif ctype == ElementType.OneDExpChannel:
+ ndim = 1
+ elif ctype == ElementType.TwoDExpChannel:
+ ndim = 2
+ elif ctype == ElementType.External:
+ config = channel.get_config()
+ if config is not None:
+ ndim = int(config.data_format)
+ elif ctype == ElementType.IORegister:
+ ndim = 0
+
+ # Definitively should be initialized by measurement group
+ # index MUST be here already (asserting this in the following line)
+ channel_data['index'] = channel_data['index']
+ channel_data['name'] = channel_data.get('name', name)
+ channel_data['full_name'] = channel_data.get('full_name', full_name)
+ channel_data['source'] = channel_data.get('source', source)
+ channel_data['enabled'] = channel_data.get('enabled', True)
+ channel_data['label'] = channel_data.get('label', channel_data['name'])
+ channel_data['ndim'] = ndim
+ # Probably should be initialized by measurement group
+ channel_data['output'] = channel_data.get('output', True)
+
+ # Perhaps should NOT be initialized by measurement group
+ channel_data['plot_type'] = channel_data.get('plot_type', PlotType.No)
+ channel_data['plot_axes'] = channel_data.get('plot_axes', [])
+ channel_data['conditioning'] = channel_data.get('conditioning', '')
+ channel_data['normalization'] = channel_data.get(
+ 'normalization', Normalization.No)
+
+ return channel_data
+
+ def _build_configuration(self, config=None):
+ """Builds a configuration object from the list of elements"""
+ if config is None:
+ config = {}
+ user_elements = self._mg.get_user_elements()
+ ctrls = self._mg.get_pool_controllers()
+
+ # find the first CT
+ first_timerable = None
+ for elem in user_elements:
+ if elem.get_type() in TYPE_TIMERABLE_ELEMENTS:
+ first_timerable = elem
+ break
+ if first_timerable is None:
+ raise Exception("It is not possible to construct a "
+ "measurement group without at least one "
+ "timer able channel (Counter/timer, 1D or 2D)")
+ g_timer = g_monitor = first_timerable
+ config['timer'] = g_timer
+ config['monitor'] = g_monitor
+ config['controllers'] = controllers = {}
+
+ external_user_elements = []
+ self.enabled_ctrls = []
+ for index, element in enumerate(user_elements):
+ elem_type = element.get_type()
+ if elem_type == ElementType.External:
+ external_user_elements.append((index, element))
+ continue
+
+ ctrl = element.controller
+ ctrl_data = controllers.get(ctrl)
+ # include all controller in the enabled list
+ self.enabled_ctrls.append(ctrl)
+ if ctrl_data is None:
+ controllers[ctrl] = ctrl_data = {}
+ ctrl_data['channels'] = channels = {}
+ if elem_type in TYPE_TIMERABLE_ELEMENTS:
+ elements = ctrls[ctrl]
+ if g_timer in elements:
+ ctrl_data['timer'] = g_timer
+ else:
+ ctrl_data['timer'] = elements[0]
+ if g_monitor in elements:
+ ctrl_data['monitor'] = g_monitor
+ else:
+ ctrl_data['monitor'] = elements[0]
+ ctrl_data['synchronization'] = AcqSynchType.Trigger
+ ctrl_data['synchronizer'] = 'software'
+ self.ctrl_to_acq_synch[ctrl] = AcqSynch.SoftwareTrigger
+ self.channel_to_acq_synch[
+ element] = AcqSynch.SoftwareTrigger
+ else:
+ channels = ctrl_data['channels']
+ channels[element] = channel_data = {}
+ channel_data['index'] = user_elements.index(element)
+ channel_data = self._build_channel_defaults(channel_data,
+ element)
+ config['label'] = self._mg.name
+ config['description'] = self.DFT_DESC
+
+ if len(external_user_elements) > 0:
+ controllers['__tango__'] = ctrl_data = {}
+ ctrl_data['channels'] = channels = {}
+ for index, element in external_user_elements:
+ channels[element] = channel_data = {}
+ channel_data['index'] = index
+ channel_data = self._build_channel_defaults(channel_data,
+ element)
+ else:
+ # create a configuration based on a new configuration
+ self.enabled_ctrls = []
+ user_elem_ids = {}
+ tg_elem_ids = []
+ pool = self._mg.pool
+ for c, c_data in config['controllers'].items():
+ synchronizer = c_data.get('synchronizer')
+ acq_synch_type = c_data.get('synchronization')
+ software = synchronizer == 'software'
+ external = isinstance(c, (str, unicode))
+ # only timerable elements are configured with acq_synch
+ acq_synch = None
+ ctrl_enabled = False
+ ctrl_to_acq_synch = False
+ if not external and c.is_timerable():
+ acq_synch = AcqSynch.from_synch_type(
+ software, acq_synch_type)
+ for channel_data in c_data['channels'].values():
+ if external:
+ element = _id = channel_data['full_name']
+ channel_data['source'] = _id
+ else:
+ full_name = channel_data['full_name']
+ if self.use_fqdn:
+ full_name = _to_fqdn(full_name, logger=self._mg)
+ element = pool.get_element_by_full_name(full_name)
+ _id = element.id
+ channel_data = self._build_channel_defaults(
+ channel_data, element)
+ if channel_data["enabled"]:
+ ctrl_enabled = True
+ if acq_synch is not None:
+ ctrl_to_acq_synch = True
+ self.channel_to_acq_synch[element] = acq_synch
+ if not software:
+ tg_elem_ids.append(synchronizer.id)
+ user_elem_ids[channel_data['index']] = _id
+
+ if ctrl_to_acq_synch:
+ self.ctrl_to_acq_synch[c] = acq_synch
+ if ctrl_enabled:
+ self.enabled_ctrls.append(c)
+
+ # sorted ids may not be consecutive (if a channel is disabled)
+ indexes = sorted(user_elem_ids.keys())
+ user_elem_ids_list = [user_elem_ids[idx] for idx in indexes]
+ user_elem_ids_list.extend(tg_elem_ids)
+ self._mg.set_user_element_ids(user_elem_ids_list)
+
+ g_timer, g_monitor = config['timer'], config['monitor']
+
+ timer_ctrl_data = config['controllers'][g_timer.controller]
+ if timer_ctrl_data['timer'] != g_timer:
+ self._mg.warning('controller timer and global timer '
+ 'mismatch. Using global timer')
+ self._mg.debug('For controller %s, timer is defined as '
+ 'channel %s. The global timer is set to '
+ 'channel %s which belongs to the same '
+ 'controller', g_timer.controller.name,
+ timer_ctrl_data['timer'].name, g_timer.name)
+ timer_ctrl_data['timer'] = g_timer
+
+ monitor_ctrl_data = config['controllers'][g_monitor.controller]
+ if monitor_ctrl_data['monitor'] != g_monitor:
+ self._mg.warning('controller monitor and global '
+ 'monitor mismatch. Using global monitor')
+ self._mg.debug('For controller %s, monitor is defined as '
+ 'channel %s. The global timer is set to '
+ 'channel %s which belongs to the same '
+ 'controller', g_monitor.controller.name,
+ monitor_ctrl_data['monitor'].name,
+ g_monitor.name)
+ monitor_ctrl_data['monitor'] != g_monitor
+ self._config = config
+
+
class PoolMeasurementGroup(PoolGroupElement):
DFT_DESC = 'General purpose measurement group'
From 75b4cfc49e2cfa6daa233b9742fa5d0d50d6d329 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 1 Oct 2018 12:29:50 +0200
Subject: [PATCH 127/652] Adapt to use MGConfiguration
---
src/sardana/pool/poolmeasurementgroup.py | 356 ++---------------------
1 file changed, 21 insertions(+), 335 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 796aba6cf3..9143ee1569 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -504,14 +504,12 @@ def _build_configuration(self, config=None):
class PoolMeasurementGroup(PoolGroupElement):
- DFT_DESC = 'General purpose measurement group'
-
def __init__(self, **kwargs):
self._state_lock = threading.Lock()
self._monitor_count = None
self._repetitions = 1
self._acquisition_mode = AcqMode.Timer
- self._config = None
+ self._config = MGConfiguration(self)
self._config_dirty = True
self._moveable = None
self._moveable_obj = None
@@ -519,24 +517,17 @@ def __init__(self, **kwargs):
self._sw_synch_initial_domain = SynchDomain.Position
self._synchronization = []
- # dict with channel and its acquisition synchronization
- # key: PoolBaseChannel; value: AcqSynch
- self._channel_to_acq_synch = {}
- # dict with controller and its acquisition synchronization
- # key: PoolController; value: AcqSynch
- self._ctrl_to_acq_synch = {}
- # list of controller with channels enabled.
- self._enabled_ctrls = []
+
kwargs['elem_type'] = ElementType.MeasurementGroup
PoolGroupElement.__init__(self, **kwargs)
configuration = kwargs.get("configuration")
self.set_configuration(configuration)
# if the configuration was never "really" written e.g. newly created MG
- # just sets it now so the _channe_to_acq_synch and _ctrl_to_acq_synch
+ # just sets it now so the _channe_to_acq_synch and ctrl_to_acq_synch
# are properly populated
# TODO: make it more elegant
if configuration is None:
- configuration = self.get_configuration()
+ configuration = self.configuration.configuration
self.set_configuration(configuration, propagate=0, to_fqdn=False)
def _create_action_cache(self):
@@ -592,344 +583,39 @@ def _is_managed_element(self, element):
return (element_type in TYPE_EXP_CHANNEL_ELEMENTS or
element_type is ElementType.TriggerGate)
- """Fills the channel default values for the given channel dictionary"""
-
- def _build_channel_defaults(self, channel_data, channel):
-
- external_from_name = isinstance(channel, (str, unicode))
- ndim = None
- if external_from_name:
- name = full_name = source = channel
- ndim = 0 # TODO: this should somehow verify the dimension
- else:
- name = channel.name
- full_name = channel.full_name
- source = channel.get_source()
- ndim = None
- ctype = channel.get_type()
- if ctype == ElementType.CTExpChannel:
- ndim = 0
- elif ctype == ElementType.PseudoCounter:
- ndim = 0
- elif ctype == ElementType.ZeroDExpChannel:
- ndim = 0
- elif ctype == ElementType.OneDExpChannel:
- ndim = 1
- elif ctype == ElementType.TwoDExpChannel:
- ndim = 2
- elif ctype == ElementType.External:
- config = channel.get_config()
- if config is not None:
- ndim = int(config.data_format)
- elif ctype == ElementType.IORegister:
- ndim = 0
-
- # Definitively should be initialized by measurement group
- # index MUST be here already (asserting this in the following line)
- channel_data['index'] = channel_data['index']
- channel_data['name'] = channel_data.get('name', name)
- channel_data['full_name'] = channel_data.get('full_name', full_name)
- channel_data['source'] = channel_data.get('source', source)
- channel_data['enabled'] = channel_data.get('enabled', True)
- channel_data['label'] = channel_data.get('label', channel_data['name'])
- channel_data['ndim'] = ndim
- # Probably should be initialized by measurement group
- channel_data['output'] = channel_data.get('output', True)
-
- # Perhaps should NOT be initialized by measurement group
- channel_data['plot_type'] = channel_data.get('plot_type', PlotType.No)
- channel_data['plot_axes'] = channel_data.get('plot_axes', [])
- channel_data['conditioning'] = channel_data.get('conditioning', '')
- channel_data['normalization'] = channel_data.get(
- 'normalization', Normalization.No)
-
- return channel_data
-
- def _build_configuration(self):
- """Builds a configuration object from the list of elements"""
- config = {}
- user_elements = self.get_user_elements()
- ctrls = self.get_pool_controllers()
-
- # find the first CT
- first_timerable = None
- for elem in user_elements:
- if elem.get_type() in TYPE_TIMERABLE_ELEMENTS:
- first_timerable = elem
- break
- if first_timerable is None:
- raise Exception("It is not possible to construct a measurement "
- "group without at least one timer able channel "
- "(Counter/timer, 1D or 2D)")
- g_timer = g_monitor = first_timerable
- config['timer'] = g_timer
- config['monitor'] = g_monitor
- config['controllers'] = controllers = {}
-
- external_user_elements = []
- self._enabled_ctrls = []
- for index, element in enumerate(user_elements):
- elem_type = element.get_type()
- if elem_type == ElementType.External:
- external_user_elements.append((index, element))
- continue
-
- ctrl = element.controller
- ctrl_data = controllers.get(ctrl)
- # include all controller in the enabled list
- self._enabled_ctrls.append(ctrl)
- if ctrl_data is None:
- controllers[ctrl] = ctrl_data = {}
- ctrl_data['channels'] = channels = {}
- if elem_type in TYPE_TIMERABLE_ELEMENTS:
- elements = ctrls[ctrl]
- if g_timer in elements:
- ctrl_data['timer'] = g_timer
- else:
- ctrl_data['timer'] = elements[0]
- if g_monitor in elements:
- ctrl_data['monitor'] = g_monitor
- else:
- ctrl_data['monitor'] = elements[0]
- ctrl_data['synchronization'] = AcqSynchType.Trigger
- ctrl_data['synchronizer'] = 'software'
- self._ctrl_to_acq_synch[ctrl] = AcqSynch.SoftwareTrigger
- self._channel_to_acq_synch[
- element] = AcqSynch.SoftwareTrigger
- else:
- channels = ctrl_data['channels']
- channels[element] = channel_data = {}
- channel_data['index'] = user_elements.index(element)
- channel_data = self._build_channel_defaults(channel_data, element)
- config['label'] = self.name
- config['description'] = self.DFT_DESC
-
- if len(external_user_elements) > 0:
- controllers['__tango__'] = ctrl_data = {}
- ctrl_data['channels'] = channels = {}
- for index, element in external_user_elements:
- channels[element] = channel_data = {}
- channel_data['index'] = index
- channel_data = self._build_channel_defaults(
- channel_data, element)
- return config
+ @property
+ def configuration(self):
+ return self._config
def set_configuration(self, config=None, propagate=1, to_fqdn=True):
- self._enabled_ctrls = []
- if config is None:
- config = self._build_configuration()
- else:
- # create a configuration based on a new configuration
- user_elem_ids = {}
- tg_elem_ids = []
- pool = self.pool
- for c, c_data in config['controllers'].items():
- synchronizer = c_data.get('synchronizer')
- acq_synch_type = c_data.get('synchronization')
- software = synchronizer == 'software'
- external = isinstance(c, (str, unicode))
- # only timerable elements are configured with acq_synch
- acq_synch = None
- ctrl_enabled = False
- ctrl_to_acq_synch = False
- if not external and c.is_timerable():
- acq_synch = AcqSynch.from_synch_type(
- software, acq_synch_type)
- for channel_data in c_data['channels'].values():
- if external:
- element = _id = channel_data['full_name']
- channel_data['source'] = _id
- else:
- full_name = channel_data['full_name']
- if to_fqdn:
- full_name = _to_fqdn(full_name, logger=self)
- element = pool.get_element_by_full_name(full_name)
- _id = element.id
- channel_data = self._build_channel_defaults(
- channel_data, element)
- if channel_data["enabled"]:
- ctrl_enabled = True
- if acq_synch is not None:
- ctrl_to_acq_synch = True
- self._channel_to_acq_synch[element] = acq_synch
- if not software:
- tg_elem_ids.append(synchronizer.id)
- user_elem_ids[channel_data['index']] = _id
-
- if ctrl_to_acq_synch:
- self._ctrl_to_acq_synch[c] = acq_synch
- if ctrl_enabled:
- self._enabled_ctrls.append(c)
-
- # sorted ids may not be consecutive (if a channel is disabled)
- indexes = sorted(user_elem_ids.keys())
- user_elem_ids_list = [user_elem_ids[idx] for idx in indexes]
- user_elem_ids_list.extend(tg_elem_ids)
- self.set_user_element_ids(user_elem_ids_list)
-
- g_timer, g_monitor = config['timer'], config['monitor']
-
- timer_ctrl_data = config['controllers'][g_timer.controller]
- if timer_ctrl_data['timer'] != g_timer:
- self.warning('controller timer and global timer mismatch. '
- 'Using global timer')
- self.debug('For controller %s, timer is defined as channel %s. '
- 'The global timer is set to channel %s which belongs '
- 'to the same controller', g_timer.controller.name,
- timer_ctrl_data['timer'].name, g_timer.name)
- timer_ctrl_data['timer'] = g_timer
-
- monitor_ctrl_data = config['controllers'][g_monitor.controller]
- if monitor_ctrl_data['monitor'] != g_monitor:
- self.warning('controller monitor and global monitor mismatch. '
- 'Using global monitor')
- self.debug('For controller %s, monitor is defined as channel %s. '
- 'The global timer is set to channel %s which belongs '
- 'to the same controller', g_monitor.controller.name,
- monitor_ctrl_data['monitor'].name, g_monitor.name)
- monitor_ctrl_data['monitor'] != g_monitor
-
- self._config = config
+ self._config.use_fqdn = to_fqdn
+ self._config.configuration = config
self._config_dirty = True
if not propagate:
return
self.fire_event(EventType("configuration", priority=propagate), config)
def set_configuration_from_user(self, cfg, propagate=1, to_fqdn=True):
- config = {}
- user_elements = self.get_user_elements()
- pool = self.pool
- timer_name = cfg.get('timer', user_elements[0].full_name)
- monitor_name = cfg.get('monitor', user_elements[0].full_name)
- if to_fqdn:
- timer_name = _to_fqdn(timer_name, logger=self)
- config['timer'] = pool.get_element_by_full_name(timer_name)
- if to_fqdn:
- monitor_name = _to_fqdn(monitor_name, logger=self)
- config['monitor'] = pool.get_element_by_full_name(monitor_name)
- config['controllers'] = controllers = {}
-
- for c_name, c_data in cfg['controllers'].items():
- # backwards compatibility for measurement groups created before
- # implementing feature-372:
- # https://sourceforge.net/p/sardana/tickets/372/
- # WARNING: this is one direction backwards compatibility - it just
- # reads channels from the units, but does not write channels to the
- # units back
- if 'units' in c_data:
- c_data = c_data['units']['0']
- # discard controllers which don't have items (garbage)
- ch_count = len(c_data['channels'])
- if ch_count == 0:
- continue
-
- external = c_name.startswith('__')
- if external:
- ctrl = c_name
- else:
- if to_fqdn:
- c_name = _to_fqdn(c_name, logger=self)
- ctrl = pool.get_element_by_full_name(c_name)
- assert ctrl.get_type() == ElementType.Controller
- controllers[ctrl] = ctrl_data = {}
-
- # exclude external and not timerable elements
- if not external and ctrl.is_timerable():
- timer_name = c_data['timer']
- if to_fqdn:
- timer_name = _to_fqdn(timer_name, logger=self)
- timer = pool.get_element_by_full_name(timer_name)
- ctrl_data['timer'] = timer
- monitor_name = c_data['monitor']
- if to_fqdn:
- monitor_name = _to_fqdn(monitor_name, logger=self)
- monitor = pool.get_element_by_full_name(monitor_name)
- ctrl_data['monitor'] = monitor
- synchronizer = c_data.get('synchronizer')
- # for backwards compatibility purposes
- # protect measurement groups without synchronizer defined
- if synchronizer is None:
- synchronizer = 'software'
- elif synchronizer != 'software':
- if to_fqdn:
- synchronizer = _to_fqdn(synchronizer, logger=self)
- synchronizer = pool.get_element_by_full_name(synchronizer)
- ctrl_data['synchronizer'] = synchronizer
- try:
- synchronization = c_data['synchronization']
- except KeyError:
- # backwards compatibility for configurations before SEP6
- synchronization = c_data['trigger_type']
- msg = ("trigger_type configuration parameter is deprecated"
- " in favor of synchronization. Re-apply "
- "configuration in order to upgrade.")
- self.warning(msg)
- ctrl_data['synchronization'] = synchronization
- ctrl_data['channels'] = channels = {}
- for ch_name, ch_data in c_data['channels'].items():
- if external:
- validator = TangoAttributeNameValidator()
- params = validator.getParams(ch_data['full_name'])
- params['pool'] = self.pool
- channel = PoolExternalObject(**params)
- else:
- if to_fqdn:
- ch_name = _to_fqdn(ch_name, logger=self)
- channel = pool.get_element_by_full_name(ch_name)
- channels[channel] = dict(ch_data)
-
- config['label'] = cfg.get('label', self.name)
- config['description'] = cfg.get('description', self.DFT_DESC)
-
- self.set_configuration(config, propagate=propagate, to_fqdn=to_fqdn)
-
- def get_configuration(self):
- return self._config
+ self._config.set_configuration_from_user(cfg, to_fqdn)
+ self._config_dirty = True
+ if not propagate:
+ return
+ self.fire_event(EventType("configuration", priority=propagate),
+ self._config.configuration)
def get_user_configuration(self):
- cfg = self.get_configuration()
- config = {}
-
- config['timer'] = cfg['timer'].full_name
- config['monitor'] = cfg['monitor'].full_name
- config['controllers'] = controllers = {}
-
- for c, c_data in cfg['controllers'].items():
- ctrl_name = c
- if not isinstance(c, (str, unicode)):
- ctrl_name = c.full_name
- external = ctrl_name.startswith('__')
- controllers[ctrl_name] = ctrl_data = {}
- if not external and c.is_timerable():
- if 'timer' in c_data:
- ctrl_data['timer'] = c_data['timer'].full_name
- if 'monitor' in c_data:
- ctrl_data['monitor'] = c_data['monitor'].full_name
- if 'synchronizer' in c_data:
- synchronizer = c_data['synchronizer']
- if synchronizer != 'software':
- synchronizer = synchronizer.full_name
- ctrl_data['synchronizer'] = synchronizer
- if 'synchronization' in c_data:
- ctrl_data['synchronization'] = c_data['synchronization']
- ctrl_data['channels'] = channels = {}
- for ch, ch_data in c_data['channels'].items():
- channels[ch.full_name] = dict(ch_data)
-
- config['label'] = cfg['label']
- config['description'] = cfg['description']
- return config
+ return self._config.get_configuration_for_user()
def load_configuration(self, force=False):
"""Loads the current configuration to all involved controllers"""
- cfg = self.get_configuration()
+
# g_timer, g_monitor = cfg['timer'], cfg['monitor']
- for ctrl, ctrl_data in cfg['controllers'].items():
+ for ctrl, ctrl_data in self._config.controllers.items():
if isinstance(ctrl, str): # skip external channels
continue
if not ctrl.is_online():
continue
- if ctrl not in self._enabled_ctrls:
+ if ctrl not in self._config.enabled_ctrls:
continue
ctrl.set_ctrl_par('acquisition_mode', self.acquisition_mode)
@@ -944,7 +630,7 @@ def load_configuration(self, force=False):
# ctrl.set_ctrl_par('monitor', g_monitor.axis)
ctrl.set_ctrl_par('timer', ctrl_data['timer'].axis)
ctrl.set_ctrl_par('monitor', ctrl_data['monitor'].axis)
- synchronization = self._ctrl_to_acq_synch.get(ctrl)
+ synchronization = self._config.ctrl_to_acq_synch.get(ctrl)
self.debug('load_configuration: setting trigger_type: %s '
'to ctrl: %s' % (synchronization, ctrl))
ctrl.set_ctrl_par('synchronization', synchronization)
@@ -952,7 +638,7 @@ def load_configuration(self, force=False):
self._config_dirty = False
def get_timer(self):
- return self.get_configuration()['timer']
+ return self._config.timer
timer = property(get_timer)
From 64e9f96dd9e3690cf336f929cf5d2041732524ba Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 1 Oct 2018 12:51:03 +0200
Subject: [PATCH 128/652] Adapt PoolAcquisition
---
src/sardana/pool/poolacquisition.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 63353ec383..1bfc2f3987 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -299,7 +299,8 @@ def _get_action_for_element(self, element):
elem_type = element.get_type()
if elem_type in TYPE_TIMERABLE_ELEMENTS:
main_element = self.main_element
- channel_to_acq_synch = main_element._channel_to_acq_synch
+ channel_to_acq_synch = \
+ main_element.configuration.channel_to_acq_synch
acq_synch = channel_to_acq_synch.get(element)
if acq_synch in (AcqSynch.SoftwareTrigger,
AcqSynch.SoftwareGate):
From 706fae20a86d83208d7dc722ca450e87f4cf69de Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 1 Oct 2018 15:21:02 +0200
Subject: [PATCH 129/652] Rename acquire to count_single
---
doc/source/sep/SEP18.md | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index 23264e1a90..f6ae6a96eb 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -83,25 +83,27 @@ the *start* command, to measure according to the synchronization description.
compatibility.
3. Measurement group - Taurus extension
* Add `prepare` method which simply maps to `Prepare` Tango command
- * Add `acquire` method according to the following pseudo code:
+ * Add `count_single` (TODO: find the best name for this
+ method, other candidates are `count_raw`, `acquire`) method according to
+ the following pseudo code:
* `Start()`
* `waitFinish()`
* Implement `count` method according to the following pseudo code:
* `prepare(synchronization & repeats = 1)` where synchronization
contains the integration time
- * `acquire()`
+ * `count_single()`
* Implement `count_continuous` (previous `measure`) method according to
the following pseudo code:
* `prepare(synchronization & repeats = 1)` where synchronization may
contain the continuous acquisition description
* `subscribeValueBuffer()`
- * `acquire()`
+ * `count_single()`
* `unsubscribeValueBuffer()`
4. GSF - step scan
* `SScan` implemented according to the following pseudo code:
* If number of points is known:
* `prepare(synchronization, repeats=n)` where synchronization
contains the integration time and n means number of points
- * `for step in range(n): acquire()`
+ * `for step in range(n): count_single()`
* If number of points is unknown:
- * `while new_step: acquire()`
+ * `while new_step: count()`
From f57d0be0893b3c44146980e302f166c85023158b Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 1 Oct 2018 15:25:58 +0200
Subject: [PATCH 130/652] Add controllers implementation
---
doc/source/sep/SEP18.md | 133 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 133 insertions(+)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index f6ae6a96eb..655afc58f5 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -65,6 +65,8 @@ beforehand the number of points.
Implementation
--------------
+### Measurement Group
+
Measurement group is extended by the *prepare* command with two parameters:
synchronization description and number of repeats (these repeats is a
different concept then the one from the synchronization description). The
@@ -107,3 +109,134 @@ the *start* command, to measure according to the synchronization description.
* `for step in range(n): count_single()`
* If number of points is unknown:
* `while new_step: count()`
+ * `CTScan` does not require changes, it simply calls `count_continuous`
+
+### Controllers
+
+C/T, 1D and 2D controllers (plugins) API is extended. TODO: Choose between
+the following options:
+
+#### Option 1
+
+* Add `Preparable` interface with `PrepareOne(axis, starts)` method`
+* Make C/T, 1D and 2D controllers inherit from this interface
+* Add extra argument to `LoadOne`, etc. methods of the `Loadable` interface
+`latency_time`: `LoadOne(axis, integ_time, repeats, latency_time)`
+
+This option maintains backwards compatibility.
+
+The following examples demonstrates the sequence of calls (only the ones
+relevant to the SEP18) of one channel (axis 1) involved in the given
+acquisition. This channel is at the same time the timer.
+
+* **step scan** 5 acquisitions x 0.1 s of integration time
+```python
+PrepareOne(1, 5)
+for acquisition in range(5):
+ LoadOne(1, 0.1, 1, 0)
+ StartOne(1)
+```
+
+* **continuous scan (hw trigger)** 5 acquisitions x 0.1 s of integration
+time and 0.05 s of latency time
+```python
+PrepareOne(1, 1)
+LoadOne(1, 0.1, 5, 0.05) # latency time can be ignored
+StartOne(1)
+```
+
+* **continuous scan (sw trigger)**
+```python
+PrepareOne(1, 5)
+for trigger in range(5):
+ LoadOne(1, 0.1, 1, 0.05) # latency time can be ignored
+ StartOne(1)
+```
+
+* **continuous scan (hw gate)**
+```python
+PrepareOne(1, 1)
+LoadOne(1, 0.1, 5, 0.05) # integration time and latency time can be ignored
+StartOne(1)
+```
+
+* **continuous scan (sw gate)**
+```python
+PrepareOne(1, 5)
+for gate in range(5):
+ LoadOne(1, 0.1, 1, 0.05) # integration time and latency time can be ignored
+ StartOne(1)
+```
+* **continuous scan (hw start)**
+```python
+PrepareOne(1, 1)
+LoadOne(1, 0.1, 5, 0.05)
+StartOne(1)
+```
+
+* **continuous scan (sw start)**
+```python
+PrepareOne(1, 1)
+LoadOne(1, 0.1, 5, 0.05)
+StartOne(1)
+```
+
+#### Option 2
+
+* Add extra arguments to `LoadOne`, etc. methods of the `Loadable` interface
+`latency_time` and `starts` and switch the order of arguments so the API is:
+`LoadOne(axis, integ_time, latency_time, repeats, starts)`
+* Make the `LoadOne`, etc. be called only once, in the measurement group
+prepare command, per measurement.
+
+This option **breaks** backwards compatibility.
+
+The following examples demonstrates the sequence of calls (only the ones
+relevant to the SEP18) of one channel (axis 1) involved in the given
+acquisition. This channel is at the same time the timer.
+
+* **step scan** 5 acquisitions x 0.1 s of integration time
+```python
+LoadOne(1, 0.1, 0, 1, 5)
+for acquisition in range(5):
+ StartOne(1)
+```
+
+* **continuous scan (hw trigger)** 5 acquisitions x 0.1 s of integration
+time and 0.05 s of latency time
+```python
+LoadOne(1, 0.1, 0.05, 5, 1) # latency time can be ignored
+StartOne(1)
+```
+
+* **continuous scan (sw trigger)**
+```python
+LoadOne(1, 0.1, 0.05, 1, 5) # latency time can be ignored
+for trigger in range(5):
+ StartOne(1)
+```
+
+* **continuous scan (hw gate)**
+```python
+LoadOne(1, 0.1, 0.05, 5, 1) # integration time and latency time can be ignored
+StartOne(1)
+```
+
+* **continuous scan (sw gate)**
+```python
+LoadOne(1, 0.1, 0.05, 1, 5) # integration time and latency time can be ignored
+for gate in range(5):
+ StartOne(1)
+```
+
+* **continuous scan (hw start)**
+```python
+LoadOne(1, 0.1, 0.05, 5, 1)
+StartOne(1)
+```
+
+* **continuous scan (sw start)**
+```python
+LoadOne(1, 0.1, 0.05, 5, 1)
+StartOne(1)
+```
From 82bfe477d5745d1f0b13d3354f2da246ab7811b6 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 1 Oct 2018 15:41:52 +0200
Subject: [PATCH 131/652] Rename repeats to start
---
doc/source/sep/SEP18.md | 27 +++++++++++++--------------
1 file changed, 13 insertions(+), 14 deletions(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index 655afc58f5..212ad1c0a2 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -67,21 +67,20 @@ Implementation
### Measurement Group
-Measurement group is extended by the *prepare* command with two parameters:
-synchronization description and number of repeats (these repeats is a
-different concept then the one from the synchronization description). The
-second one indicates how many times measurement group will be started, with
-the *start* command, to measure according to the synchronization description.
+Measurement group is extended by the *prepare* command with two parameters:
+synchronization description and number of starts. The second one indicates
+how many times measurement group will be started, with the *start* command,
+to measure according to the synchronization description.
1. Measurement group - Tango device class
* Add `Prepare` command. TODO: investigate the best way to pass
- synchronization description, as JSON serialized string, together with the
- repeats integer.
- * Remove `synchronization` attribute (experimental API) - no backwards
+ synchronization description, as JSON serialized string, together with the
+ starts integer.
+ * Remove `synchronization` attribute (experimental API) - no backwards
compatibility.
2. Measurement group - core class
- * Add `prepare(synchronization, repeats=1)` method
- * Remove `synchronization` property (experimental API) - no backwards
+ * Add `prepare(synchronization, starts=1)` method
+ * Remove `synchronization` property (experimental API) - no backwards
compatibility.
3. Measurement group - Taurus extension
* Add `prepare` method which simply maps to `Prepare` Tango command
@@ -91,12 +90,12 @@ the *start* command, to measure according to the synchronization description.
* `Start()`
* `waitFinish()`
* Implement `count` method according to the following pseudo code:
- * `prepare(synchronization & repeats = 1)` where synchronization
+ * `prepare(synchronization & starts = 1)` where synchronization
contains the integration time
* `count_single()`
- * Implement `count_continuous` (previous `measure`) method according to
+ * Implement `count_continuous` (previous `measure`) method according to
the following pseudo code:
- * `prepare(synchronization & repeats = 1)` where synchronization may
+ * `prepare(synchronization & starts = 1)` where synchronization may
contain the continuous acquisition description
* `subscribeValueBuffer()`
* `count_single()`
@@ -104,7 +103,7 @@ the *start* command, to measure according to the synchronization description.
4. GSF - step scan
* `SScan` implemented according to the following pseudo code:
* If number of points is known:
- * `prepare(synchronization, repeats=n)` where synchronization
+ * `prepare(synchronization, start=n)` where synchronization
contains the integration time and n means number of points
* `for step in range(n): count_single()`
* If number of points is unknown:
From cbe7f3fa5f7186a4dbf82dca22d6e91f3c0c995d Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 1 Oct 2018 18:32:25 +0200
Subject: [PATCH 132/652] Move GSF to a separate chapter
---
doc/source/sep/SEP18.md | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index 212ad1c0a2..b1f0e22049 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -65,6 +65,18 @@ beforehand the number of points.
Implementation
--------------
+### GSF
+
+* `SScan` (step scan) implemented according to the following pseudo code:
+ * If number of points is known:
+ * `prepare(synchronization, start=n)` where synchronization
+ contains the integration time and n means number of points
+ * `for step in range(n): count_single()`
+ * If number of points is unknown:
+ * `while new_step: count()`
+* `CTScan` (continuous scan) does not require changes, it simply calls
+`count_continuous`
+
### Measurement Group
Measurement group is extended by the *prepare* command with two parameters:
@@ -100,15 +112,6 @@ to measure according to the synchronization description.
* `subscribeValueBuffer()`
* `count_single()`
* `unsubscribeValueBuffer()`
-4. GSF - step scan
- * `SScan` implemented according to the following pseudo code:
- * If number of points is known:
- * `prepare(synchronization, start=n)` where synchronization
- contains the integration time and n means number of points
- * `for step in range(n): count_single()`
- * If number of points is unknown:
- * `while new_step: count()`
- * `CTScan` does not require changes, it simply calls `count_continuous`
### Controllers
From d606dc086d30df2115f109fbe77a040e3206a125 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Wed, 3 Oct 2018 11:07:20 +0200
Subject: [PATCH 133/652] Add method to prepare acquisition variable
Include variables for the acquisition actions.
Implement methods to prepare the configuration for the acquisition
actions.
---
src/sardana/pool/poolmeasurementgroup.py | 78 +++++++++++++++++++++++-
1 file changed, 76 insertions(+), 2 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 0d5cd7e29f..f398532a51 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -132,6 +132,9 @@ def __init__(self, mg):
self._mg = weakref.ref(mg)()
self._config = None
self.use_fqdn = True
+ self._init_data()
+
+ def _init_data(self):
# list of controller with channels enabled.
self.enabled_ctrls = []
# dict with channel and its acquisition synchronization
@@ -140,6 +143,18 @@ def __init__(self, mg):
# dict with controller and its acquisition synchronization
# key: PoolController; value: AcqSynch
self.ctrl_to_acq_synch = {}
+ # TODO: Do the documentation
+ self.ctrl_sw_sync = {}
+ self.ctrl_hw_sync = {}
+ self.ctrl_sw_start = {}
+ self.ctrl_0d_sync = {}
+ # TODO: Do the documentation
+ self.sw_sync_timer = None
+ self.sw_sync_monitor = None
+ self.sw_start_timer = None
+ self.sw_start_monitor = None
+ self.hw_sync_timer = None
+ self.hw_sync_monitor = None
def __check_config(func):
def wrapper(self, *args, **kwargs):
@@ -359,6 +374,8 @@ def _build_channel_defaults(self, channel_data, channel):
def _build_configuration(self, config=None):
"""Builds a configuration object from the list of elements"""
+ self._init_data()
+
if config is None:
config = {}
user_elements = self._mg.get_user_elements()
@@ -380,7 +397,6 @@ def _build_configuration(self, config=None):
config['controllers'] = controllers = {}
external_user_elements = []
- self.enabled_ctrls = []
for index, element in enumerate(user_elements):
elem_type = element.get_type()
if elem_type == ElementType.External:
@@ -415,6 +431,7 @@ def _build_configuration(self, config=None):
channel_data['index'] = user_elements.index(element)
channel_data = self._build_channel_defaults(channel_data,
element)
+
config['label'] = self._mg.name
config['description'] = self.DFT_DESC
@@ -428,7 +445,6 @@ def _build_configuration(self, config=None):
element)
else:
# create a configuration based on a new configuration
- self.enabled_ctrls = []
user_elem_ids = {}
tg_elem_ids = []
pool = self._mg.pool
@@ -500,7 +516,65 @@ def _build_configuration(self, config=None):
monitor_ctrl_data['monitor'].name,
g_monitor.name)
monitor_ctrl_data['monitor'] != g_monitor
+
self._config = config
+ self._prepare_data()
+
+ def _prepare_data(self):
+ """
+ Split MeasurementGroup configuration with channels
+ triggered by SW Trigger and channels triggered by HW trigger
+
+ """
+
+ ctrls_in = self._config['controllers']
+ for ctrl, ctrl_info in ctrls_in.items():
+ external = isinstance(ctrl, str) and ctrl.startswith('__')
+ # skipping external controllers e.g. Tango attributes
+ if external:
+ continue
+ # splitting ZeroD based on the type
+ if ctrl.get_ctrl_types()[0] == ElementType.ZeroDExpChannel:
+ self.ctrl_0d_sync[ctrl] = ctrl_info
+ # ignoring PseudoCounter
+ elif ctrl.get_ctrl_types()[0] == ElementType.PseudoCounter:
+ pass
+ # splitting rest of the channels based on the assigned trigger
+ else:
+ synchronizer = ctrl_info.get('synchronizer')
+ if synchronizer is None or synchronizer == 'software':
+ synchronization = ctrl_info.get('synchronization')
+ if synchronization in [AcqSynch.SoftwareTrigger,
+ AcqSynch.SoftwareGate]:
+ self.ctrl_sw_sync[ctrl] = ctrl_info
+ else:
+ self.ctrl_sw_start[ctrl] = ctrl_info
+ else:
+ self.ctrl_hw_sync[ctrl] = ctrl_info
+
+ def find_master(ctrls, role):
+ master_idx = float("+inf")
+ master = None
+ for ctrl_info in ctrls.values():
+ element = ctrl_info[role]
+ if element in ctrl_info["channels"]:
+ element_idx = ctrl_info["channels"][element]["index"]
+ element_enabled = ctrl_info["channels"][element]["enabled"]
+ # Find master only if is enabled
+ if element_idx < master_idx and element_enabled:
+ master = element
+ master_idx = element_idx
+ return master
+
+ if len(self.ctrl_sw_sync):
+ self.sw_sync_timer = find_master(self.ctrl_sw_sync, "timer")
+ self.sw_sync_monitor = find_master(self.ctrl_sw_sync, "monitor")
+ if len(self.ctrl_sw_start):
+ self.sw_start_timer = find_master(self.ctrl_sw_start, "timer")
+ self.sw_start_monitor = find_master(self.ctrl_sw_start, "monitor")
+ if len(self.ctrl_hw_sync):
+ self.hw_sync_timer = find_master(self.ctrl_hw_sync, "timer")
+ self.hw_sync_monitor = find_master(self.ctrl_hw_sync, "monitor")
class PoolMeasurementGroup(PoolGroupElement):
From 1eb0cd786df2ab030f30845c36be2167158e03e3 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Wed, 3 Oct 2018 11:13:21 +0200
Subject: [PATCH 134/652] Adapt PoolAcquisition to use MGConfiguration API
---
src/sardana/pool/poolacquisition.py | 87 +++++------------------------
1 file changed, 14 insertions(+), 73 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 1bfc2f3987..c948643778 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -62,62 +62,6 @@
}
-def split_MGConfigurations(mg_cfg_in):
- """Split MeasurementGroup configuration with channels
- triggered by SW Trigger and channels triggered by HW trigger
-
- TODO: (technical debt) All the MeasurementGroup configuration
- logic should be encapsulate in a dedicated class instead of
- using a basic data structures like dict or lists...
- """
- ctrls_in = mg_cfg_in['controllers']
- mg_sw_cfg_out = {}
- mg_0d_cfg_out = {}
- mg_hw_cfg_out = {}
- mg_sw_cfg_out['controllers'] = ctrls_sw_out = {}
- mg_0d_cfg_out['controllers'] = ctrls_0d_out = {}
- mg_hw_cfg_out['controllers'] = ctrls_hw_out = {}
- for ctrl, ctrl_info in ctrls_in.items():
- external = isinstance(ctrl, str) and ctrl.startswith('__')
- # skipping external controllers e.g. Tango attributes
- if external:
- continue
- # splitting ZeroD based on the type
- if ctrl.get_ctrl_types()[0] == ElementType.ZeroDExpChannel:
- ctrls_0d_out[ctrl] = ctrl_info
- # ignoring PseudoCounter
- elif ctrl.get_ctrl_types()[0] == ElementType.PseudoCounter:
- pass
- # splitting rest of the channels based on the assigned trigger
- else:
- synchronizer = ctrl_info.get('synchronizer')
- if synchronizer is None or synchronizer == 'software':
- ctrls_sw_out[ctrl] = ctrl_info
- else:
- ctrls_hw_out[ctrl] = ctrl_info
-
- def find_master(ctrls, role):
- master_idx = float("+inf")
- master = None
- for ctrl_info in ctrls.values():
- element = ctrl_info[role]
- element_idx = ctrl_info["channels"][element]["index"]
- element_enabled = ctrl_info["channels"][element]["enabled"]
- # Find master only if is enabled
- if element_idx < master_idx and element_enabled:
- master = element
- master_idx = element_idx
- return master
-
- if len(ctrls_sw_out):
- mg_sw_cfg_out["timer"] = find_master(ctrls_sw_out, "timer")
- mg_sw_cfg_out["monitor"] = find_master(ctrls_sw_out, "monitor")
- if len(ctrls_hw_out):
- mg_hw_cfg_out["timer"] = find_master(ctrls_hw_out, "timer")
- mg_hw_cfg_out["monitor"] = find_master(ctrls_hw_out, "monitor")
- return (mg_hw_cfg_out, mg_sw_cfg_out, mg_0d_cfg_out)
-
-
def getTGConfiguration(MGcfg):
'''Build TG configuration from complete MG configuration.
@@ -266,30 +210,24 @@ def run(self, *args, **kwargs):
synchronization = kwargs["synchronization"]
integ_time = synchronization.integration_time
repetitions = synchronization.repetitions
- # TODO: this code splits the global mg configuration into
- # experimental channels triggered by hw and experimental channels
- # triggered by sw. Refactor it!!!!
- (hw_acq_cfg, sw_acq_cfg, zerod_acq_cfg) = split_MGConfigurations(
- config)
+
synch_cfg, _ = getTGConfiguration(config)
# starting continuous acquisition only if there are any controllers
- if len(hw_acq_cfg['controllers']):
+ if len(config.ctrl_hw_sync):
cont_acq_kwargs = dict(kwargs)
- cont_acq_kwargs['config'] = hw_acq_cfg
cont_acq_kwargs['integ_time'] = integ_time
cont_acq_kwargs['repetitions'] = repetitions
self._hw_acq.run(*args, **cont_acq_kwargs)
- if len(sw_acq_cfg['controllers']) or len(zerod_acq_cfg['controllers']):
+ if len(config.ctrl_sw_sync) or len(config.ctrl_0d_sync):
self._synch.add_listener(self)
- if len(sw_acq_cfg['controllers']):
+ if len(config.ctrl_sw_sync):
sw_acq_kwargs = dict(kwargs)
- sw_acq_kwargs['config'] = sw_acq_cfg
sw_acq_kwargs['integ_time'] = integ_time
sw_acq_kwargs['repetitions'] = 1
self.set_sw_config(sw_acq_kwargs)
- if len(zerod_acq_cfg['controllers']):
+ if len(config.ctrl_0d_sync):
zerod_acq_kwargs = dict(kwargs)
- zerod_acq_kwargs['config'] = zerod_acq_cfg
+ # TODO: Ask why
self.set_0d_config(zerod_acq_kwargs)
synch_kwargs = dict(kwargs)
synch_kwargs['config'] = synch_cfg
@@ -453,7 +391,6 @@ def start_action(self, *args, **kwargs):
involved controllers and channels)
"""
pool = self.pool
-
self._aborted = False
self._stopped = False
@@ -475,14 +412,18 @@ def start_action(self, *args, **kwargs):
_ = kwargs.get("items", self.get_elements())
cfg = kwargs['config']
# determine which is the controller which holds the master channel
-
+ master = None
+ if integ_time is not None and mon_count is not None:
+ raise RuntimeError('The acquisition must have only one role: '
+ 'timer or count')
if integ_time is not None:
master_key = 'timer'
master_value = integ_time
+ master = cfg.timer
if mon_count is not None:
master_key = 'monitor'
master_value = -mon_count
- master = cfg[master_key]
+ master = cfg.monitor
if master is None:
self.main_element.set_state(State.Fault, propagate=2)
msg = "master {0} is unknown (probably disabled)".format(
@@ -490,7 +431,7 @@ def start_action(self, *args, **kwargs):
raise RuntimeError(msg)
master_ctrl = master.controller
- pool_ctrls_dict = dict(cfg['controllers'])
+ pool_ctrls_dict = dict(cfg.controllers)
pool_ctrls_dict.pop('__tango__', None)
# controllers to be started (only enabled) in the right order
@@ -906,7 +847,7 @@ def start_action(self, *args, **kwargs):
items = self.get_elements()
cfg = kwargs['config']
- pool_ctrls_dict = dict(cfg['controllers'])
+ pool_ctrls_dict = dict(cfg.controllers)
pool_ctrls_dict.pop('__tango__', None)
pool_ctrls = []
for ctrl in pool_ctrls_dict:
From 756c82d5e438200876f1ab6356915324b32c45de Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 3 Oct 2018 11:42:23 +0200
Subject: [PATCH 135/652] SEP: Add integ_time, repeats and latency time to
PrepapreOne
---
doc/source/sep/SEP18.md | 33 ++++++++++++++++++++-------------
1 file changed, 20 insertions(+), 13 deletions(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index b1f0e22049..23a58ca653 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -120,7 +120,9 @@ the following options:
#### Option 1
-* Add `Preparable` interface with `PrepareOne(axis, starts)` method`
+* Add `Preparable` interface with
+`PrepareOne(axis, integ_time, repeats, latency_time, starts)` TODO: or
+directly add it to the Loadable interface
* Make C/T, 1D and 2D controllers inherit from this interface
* Add extra argument to `LoadOne`, etc. methods of the `Loadable` interface
`latency_time`: `LoadOne(axis, integ_time, repeats, latency_time)`
@@ -133,7 +135,7 @@ acquisition. This channel is at the same time the timer.
* **step scan** 5 acquisitions x 0.1 s of integration time
```python
-PrepareOne(1, 5)
+PrepareOne(1, 0.1, 1, 0, 5)
for acquisition in range(5):
LoadOne(1, 0.1, 1, 0)
StartOne(1)
@@ -142,43 +144,48 @@ for acquisition in range(5):
* **continuous scan (hw trigger)** 5 acquisitions x 0.1 s of integration
time and 0.05 s of latency time
```python
-PrepareOne(1, 1)
+PrepareOne(1, 0.1, 5, 0.05, 1) # latency time can be ignored
LoadOne(1, 0.1, 5, 0.05) # latency time can be ignored
StartOne(1)
```
-* **continuous scan (sw trigger)**
+* **continuous scan (sw trigger)** 5 acquisitions x 0.1 s of integration
+time and 0.05 s of latency time
```python
-PrepareOne(1, 5)
+PrepareOne(1, 0.1, 1, 0.05, 5) # latency time can be ignored
for trigger in range(5):
LoadOne(1, 0.1, 1, 0.05) # latency time can be ignored
StartOne(1)
```
-* **continuous scan (hw gate)**
+* **continuous scan (hw gate)** 5 acquisitions x 0.1 s of integration
+time and 0.05 s of latency time
```python
-PrepareOne(1, 1)
+PrepareOne(1, 0.1, 5, 0.05, 1) # integration time and latency time can be ignored
LoadOne(1, 0.1, 5, 0.05) # integration time and latency time can be ignored
StartOne(1)
```
-* **continuous scan (sw gate)**
+* **continuous scan (sw gate)** 5 acquisitions x 0.1 s of integration
+time and 0.05 s of latency time
```python
-PrepareOne(1, 5)
+PrepareOne(1, 0.1, 1, 0.05, 5)
for gate in range(5):
LoadOne(1, 0.1, 1, 0.05) # integration time and latency time can be ignored
StartOne(1)
```
-* **continuous scan (hw start)**
+* **continuous scan (hw start)** 5 acquisitions x 0.1 s of integration
+time and 0.05 s of latency time
```python
-PrepareOne(1, 1)
+PrepareOne(1, 0.1, 5, 0.05, 1)
LoadOne(1, 0.1, 5, 0.05)
StartOne(1)
```
-* **continuous scan (sw start)**
+* **continuous scan (sw start)** 5 acquisitions x 0.1 s of integration
+time and 0.05 s of latency time
```python
-PrepareOne(1, 1)
+PrepareOne(1, 0.1, 5, 0.05, 1)
LoadOne(1, 0.1, 5, 0.05)
StartOne(1)
```
From 88cfc331235d03536b2d68c481ffadc6a6545e90 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 3 Oct 2018 11:49:27 +0200
Subject: [PATCH 136/652] SEP: Re-phrase option 2 to be more readable
---
doc/source/sep/SEP18.md | 6 +++---
src/sardana/macroserver/scan/gscan.py | 1 +
src/sardana/pool/poolacquisition.py | 9 +++++++++
src/sardana/pool/poolmeasurementgroup.py | 7 +++++++
4 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index 23a58ca653..1e8311c7a8 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -194,9 +194,9 @@ StartOne(1)
* Add extra arguments to `LoadOne`, etc. methods of the `Loadable` interface
`latency_time` and `starts` and switch the order of arguments so the API is:
-`LoadOne(axis, integ_time, latency_time, repeats, starts)`
-* Make the `LoadOne`, etc. be called only once, in the measurement group
-prepare command, per measurement.
+`LoadOne(axis, integ_time, latency_time, repeats, starts)`.
+* Make the `LoadOne`, etc. be called only once per measurement, in the
+measurement group prepare command.
This option **breaks** backwards compatibility.
diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py
index a61da2bee4..380fcba53f 100644
--- a/src/sardana/macroserver/scan/gscan.py
+++ b/src/sardana/macroserver/scan/gscan.py
@@ -1007,6 +1007,7 @@ def scan_loop(self):
scream = False
if hasattr(macro, "nr_points"):
+ self.measurement_group.prepare()
nr_points = float(macro.nr_points)
scream = True
else:
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 63353ec383..9a998b35d2 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -241,6 +241,15 @@ def event_received(self, *args, **kwargs):
self.debug('Stopping ZeroD acquisition.')
self._0d_acq.stop_action()
+ def prepare(self, config, repetitions):
+ """Prepare measurement."""
+ timers = config.get_timers(enabled=True)
+ for timer in timers:
+ axis = timer.axis
+ timer_ctrl = timer.controller
+ ctrl = timer_ctrl.ctrl
+ ctrl.PrepareOne(axis, repetitions)
+
def is_running(self):
return self._0d_acq.is_running() or\
self._sw_acq.is_running() or\
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 280821e746..c7279ee7ee 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -715,6 +715,13 @@ def set_sw_synch_initial_domain(self, domain):
# acquisition
# -------------------------------------------------------------------------
+ def prepare(self):
+ self.load_configuration()
+ config = self.get_configuration()
+ repetitions = self.synchronization.repetitions
+ self.acquisition.prepare(config, repetitions)
+
+
def start_acquisition(self, value=None, multiple=1):
self._aborted = False
if not self._simulation_mode:
From d7491210548a947fc67f67977d7dadb6acd9205d Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 3 Oct 2018 12:49:09 +0200
Subject: [PATCH 137/652] SEP: Add software sync, acquisitions and dummy C/T
implementation
---
doc/source/sep/SEP18.md | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index 1e8311c7a8..9878067caf 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -113,6 +113,17 @@ to measure according to the synchronization description.
* `count_single()`
* `unsubscribeValueBuffer()`
+### Software synchronizer
+
+* Add `start` and `end` events. Start is emitted before the first `active`
+event and end is emitted after the last `passive` event.
+
+### Acquisition actions
+
+* Add `PoolAcquisitionSoftwareStart` action that will start channels on
+software synchronizer `start` event and stop channels on software
+synchronizer `end` event.
+
### Controllers
C/T, 1D and 2D controllers (plugins) API is extended. TODO: Choose between
@@ -249,3 +260,8 @@ StartOne(1)
LoadOne(1, 0.1, 0.05, 5, 1)
StartOne(1)
```
+
+### Dummy C/T controller
+Implement `SoftwareStart` and `HardwareStart` in the
+`DummyCounterTimerController` - minimal implementation.
+
\ No newline at end of file
From ceeb292472121e64b303dbb8435d1f9ba7a06cd8 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 3 Oct 2018 13:27:44 +0200
Subject: [PATCH 138/652] SEP: add more information to design chapter
---
doc/source/sep/SEP18.md | 44 +++++++++++++++++++++++++----------------
1 file changed, 27 insertions(+), 17 deletions(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index 9878067caf..f7e190a0bf 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -40,27 +40,37 @@ Overcome the above limitations.
Design
------
-
-1. Document well that:
- * Software(Trigger|Gate) are synonyms to Internal (Trigger|Gate).
+1. Use of the measurement group will change:
+ * From now on, in order to start the measurement group, it is mandatory
+ to prepare it.
+ * The measurement group will be armed for as many starts as specified in
+ the preparation and the preparation will expire whenever all starts gets
+ called or in case of stop/abort.
+ * Setting the integration time via the attribute will de deprecated in
+ favor of using prepare command with the synchronization description, but
+ backwards compatibility will be maintained.
+2. Allow different types of preparation of channels - this still depends on
+the option selected in the implementation of controllers. The following
+assumes option 1.
+ * Per measurement preparation with number of starts = n e.g.
+ Prepare(One|All) or a controller parameter
+ * Per acquisition preparation with repetitions = n e.g. Load(One|All)
+3. Extend AcqSynch with two new options:
+ * SoftwareStart (which means internal start)
+ * HardwareStart (which means external start)
+4. Extend AcqSynchType with one new option (supported from expconf):
+ * Start
+5. Modify acquisition actions (and synchronization action if necessary) so
+they support the new concepts added in points 2 and 3.
+6. *Extend Generic Scan Framework* (GSF), more precisely scan in step mode
+with measurement preparation (number of starts = n) if possible i.e. scan
+macro knows beforehand the number of points.
+7. Document well that:
+ * Software(Trigger|Gate) are synonyms to Internal (Trigger|Gate).
Internal means that Sardana will synchronize the acquisitions.
* Hardware(Trigger|Gate) are synonyms to External(Trigger|Gate).
External means that an external to Sardana object (could be hardware)
will synchronize the acquisitions.
-2. Extend AcqSynch with two new options:
- * SoftwareStart (which means internal start)
- * HardwareStart (which means external start)
-3. Extend AcqSynchType with one new option (supported from expconf):
- * Start
-4. Allow different types of preparation of channels:
- * Per measurement preparation with repetitions=n e.g. Prepare(One|All)
- or a controller parameter
- * Per acquisition preparation with repetitions=1 e.g. Load(One|All)
-6. Modify acquisition actions (and synchronization action if necessary) so
-they support the new concepts added in points 2 and 4.
-5. *Extend Generic Scan Framework* (GSF), more preciselly scan in step mode
-with measurement preparation (repetitions=n) if possible i.e. scan macro knows
-beforehand the number of points.
Implementation
--------------
From 0cfd3130905efad796de00c07e69d226ae5bd9ef Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 3 Oct 2018 13:28:06 +0200
Subject: [PATCH 139/652] SEP: add backwards compatibility for integration time
---
doc/source/sep/SEP18.md | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index f7e190a0bf..d0490772ed 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -104,7 +104,10 @@ to measure according to the synchronization description.
* Add `prepare(synchronization, starts=1)` method
* Remove `synchronization` property (experimental API) - no backwards
compatibility.
-3. Measurement group - Taurus extension
+3. Backwards compatibility for integration time attribute will be solved in
+the following way: setting of integration time attribute will allow starts
+until the next preparation.
+4. Measurement group - Taurus extension
* Add `prepare` method which simply maps to `Prepare` Tango command
* Add `count_single` (TODO: find the best name for this
method, other candidates are `count_raw`, `acquire`) method according to
From 6e99f91ea1fba9efcfff0eb63959d8dcf1fd2011 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 3 Oct 2018 13:35:27 +0200
Subject: [PATCH 140/652] SEP: add option 3 to controllers implementation
---
doc/source/sep/SEP18.md | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index d0490772ed..bcc8b19115 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -274,7 +274,15 @@ LoadOne(1, 0.1, 0.05, 5, 1)
StartOne(1)
```
+#### Option 3
+
+The same as option 2 but maintaining the backwards compatibility in the
+following way:
+* Developing a new C/T, 1D, 2D will require implementing `ControllerAPI`
+class member with 2.6 value.
+* Acquisition actions will call the `LoadOne`, etc. methods depending on the
+`ControllerAPI` values.
+
### Dummy C/T controller
Implement `SoftwareStart` and `HardwareStart` in the
`DummyCounterTimerController` - minimal implementation.
-
\ No newline at end of file
From 79d8c509ddf705240ced6519c9d437e10217f0a6 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 3 Oct 2018 14:57:49 +0200
Subject: [PATCH 141/652] SEP: fix typos
---
doc/source/sep/SEP18.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index bcc8b19115..2979b7f7e3 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -46,7 +46,7 @@ Design
* The measurement group will be armed for as many starts as specified in
the preparation and the preparation will expire whenever all starts gets
called or in case of stop/abort.
- * Setting the integration time via the attribute will de deprecated in
+ * Setting the integration time via the attribute will be deprecated in
favor of using prepare command with the synchronization description, but
backwards compatibility will be maintained.
2. Allow different types of preparation of channels - this still depends on
@@ -79,9 +79,9 @@ Implementation
* `SScan` (step scan) implemented according to the following pseudo code:
* If number of points is known:
- * `prepare(synchronization, start=n)` where synchronization
- contains the integration time and n means number of points
- * `for step in range(n): count_single()`
+ * `prepare(synchronization, starts=n)` where synchronization
+ contains the integration time and n means number of scan points
+ * `for step in range(n): count_raw()`
* If number of points is unknown:
* `while new_step: count()`
* `CTScan` (continuous scan) does not require changes, it simply calls
From 9721ad7f0f265567b2f88a7dd79cadd74e2b06ce Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 3 Oct 2018 14:58:33 +0200
Subject: [PATCH 142/652] SEP: rename count_single to count_raw
---
doc/source/sep/SEP18.md | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index 2979b7f7e3..d731581034 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -109,21 +109,19 @@ the following way: setting of integration time attribute will allow starts
until the next preparation.
4. Measurement group - Taurus extension
* Add `prepare` method which simply maps to `Prepare` Tango command
- * Add `count_single` (TODO: find the best name for this
- method, other candidates are `count_raw`, `acquire`) method according to
- the following pseudo code:
- * `Start()`
+ * Add `count_raw` method according to the following pseudo code:
+ * `start()`
* `waitFinish()`
* Implement `count` method according to the following pseudo code:
* `prepare(synchronization & starts = 1)` where synchronization
contains the integration time
- * `count_single()`
+ * `count_raw()`
* Implement `count_continuous` (previous `measure`) method according to
the following pseudo code:
* `prepare(synchronization & starts = 1)` where synchronization may
contain the continuous acquisition description
* `subscribeValueBuffer()`
- * `count_single()`
+ * `count_raw()`
* `unsubscribeValueBuffer()`
### Software synchronizer
From b84d0b58e26103044c7d46a34f94d3ee0e035401 Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Wed, 3 Oct 2018 17:19:45 +0200
Subject: [PATCH 143/652] Update SEP18.md
---
doc/source/sep/SEP18.md | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index d731581034..7cee6e1400 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -132,8 +132,11 @@ event and end is emitted after the last `passive` event.
### Acquisition actions
* Add `PoolAcquisitionSoftwareStart` action that will start channels on
-software synchronizer `start` event and stop channels on software
-synchronizer `end` event.
+software synchronizer `start` event.
+* `PoolAcquisitionSoftware` will stop channels on software synchronizer
+`end` event. TODO: decide if we wait for the acquisition in progress
+until it finises or we stop immediatelly (finish hook could be used if
+we choose to wait).
### Controllers
From 8bfce2307c5286e600cbeef2c80fed8f46a7d126 Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Wed, 3 Oct 2018 17:27:03 +0200
Subject: [PATCH 144/652] SEP: refine option 3
---
doc/source/sep/SEP18.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index 7cee6e1400..6c03386b67 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -279,10 +279,10 @@ StartOne(1)
The same as option 2 but maintaining the backwards compatibility in the
following way:
-* Developing a new C/T, 1D, 2D will require implementing `ControllerAPI`
-class member with 2.6 value.
* Acquisition actions will call the `LoadOne`, etc. methods depending on the
-`ControllerAPI` values.
+controllers implementations (more preciselly using the `inspect.getargspec`
+and counting the number of arguments). This will require much more complicated
+acquisition actions.
### Dummy C/T controller
Implement `SoftwareStart` and `HardwareStart` in the
From 684ee26c4e5437969d593688798ef3bbe9681645 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Thu, 4 Oct 2018 12:46:03 +0200
Subject: [PATCH 145/652] In step scan prepare MG if nr_points and integ_time
is known
---
src/sardana/macroserver/scan/gscan.py | 6 +++++-
src/sardana/taurus/core/tango/sardana/pool.py | 2 ++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py
index 380fcba53f..efe88df81a 100644
--- a/src/sardana/macroserver/scan/gscan.py
+++ b/src/sardana/macroserver/scan/gscan.py
@@ -1007,8 +1007,12 @@ def scan_loop(self):
scream = False
if hasattr(macro, "nr_points"):
- self.measurement_group.prepare()
nr_points = float(macro.nr_points)
+ if hasattr(macro, "integ_time"):
+ integ_time = macro.integ_time
+ group = {SynchParam.Active: {SynchDomain.Time: integ_time}}
+ synchronization = [group]
+ self.measurement_group.prepare(synchronization, nr_points)
scream = True
else:
yield 0.0
diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py
index c0978afed0..bed33eea26 100644
--- a/src/sardana/taurus/core/tango/sardana/pool.py
+++ b/src/sardana/taurus/core/tango/sardana/pool.py
@@ -1795,6 +1795,8 @@ def _start(self, *args, **kwargs):
self.debug("stopped")
raise e
+ def prepare(self, synchronization, nr_of_starts=1):
+ pass
def go(self, *args, **kwargs):
start_time = time.time()
From afc040fc4bde8f8c4dc3e64ecba47e9a1bf058c5 Mon Sep 17 00:00:00 2001
From: Carlos Pascual
Date: Fri, 5 Oct 2018 11:04:47 +0200
Subject: [PATCH 146/652] (m) (doc) Fix typo
---
doc/source/devel/howto_macros/macros_general.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst
index d1681eb938..3ef5acfccb 100644
--- a/doc/source/devel/howto_macros/macros_general.rst
+++ b/doc/source/devel/howto_macros/macros_general.rst
@@ -331,7 +331,7 @@ Optional parameters
~~~~~~~~~~~~~~~~~~~
A special parameter default value is the ``Optional``. It allows to
-execute a macro even the given parameter value is not specified by the user.
+execute a macro even if the given parameter value is not specified by the user.
So, here is an example how to define and use the optional parameter::
From 6f761a3fc61eb0df971abc36a02c57486c1abfeb Mon Sep 17 00:00:00 2001
From: zreszela
Date: Thu, 27 Sep 2018 18:02:06 +0200
Subject: [PATCH 147/652] Add start event to funtion generator
---
src/sardana/util/funcgenerator.py | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/sardana/util/funcgenerator.py b/src/sardana/util/funcgenerator.py
index 340595b9d9..44b7088da2 100644
--- a/src/sardana/util/funcgenerator.py
+++ b/src/sardana/util/funcgenerator.py
@@ -64,6 +64,7 @@ def __init__(self, name="FunctionGenerator"):
self._initial_domain = None
self._active_domain = None
self._position_event = threading.Event()
+ self._position = None
self._initial_domain_in_use = None
self._active_domain_in_use = None
self._active_events = list()
@@ -75,6 +76,7 @@ def __init__(self, name="FunctionGenerator"):
self._direction = None
self._condition = None
self._id = None
+ self._start_fired = False
def get_name(self):
return self._name
@@ -167,6 +169,7 @@ def start(self):
self._stopped = False
self._started = True
self._position = None
+ self._start_fired = False
self._position_event.clear()
self._id = 0
self.fire_event(EventType("state"), State.Moving)
@@ -211,6 +214,13 @@ def sleep(self, period):
break
time.sleep(nap)
+ def fire_start(self):
+ self.fire_event(EventType("start"))
+ self._start_fired = True
+ if self._id > 0:
+ msg = "start was fired with {0} delay".format(self._id)
+ self.warning(msg)
+
def wait_active(self):
candidate = self.active_events[0]
if self.initial_domain_in_use == SynchDomain.Time:
@@ -244,6 +254,8 @@ def fire_active(self):
else:
break
self._id += i
+ if not self._start_fired:
+ self.fire_start()
self.fire_event(EventType("active"), self._id)
self.active_events = self.active_events[i + 1:]
self.passive_events = self.passive_events[i:]
From 3818ab9b780a7281a231f2a6735b24288712b848 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Fri, 5 Oct 2018 12:22:58 +0200
Subject: [PATCH 148/652] Fix optional param for python 2.6
---
src/sardana/macroserver/msparameter.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/sardana/macroserver/msparameter.py b/src/sardana/macroserver/msparameter.py
index 0d75e69e1c..bf7f8c67d5 100644
--- a/src/sardana/macroserver/msparameter.py
+++ b/src/sardana/macroserver/msparameter.py
@@ -50,8 +50,9 @@ def __init__(self, obj):
attributes = dir(self)
for attr in attributes:
+ # iteritems is necessary fo python 2.6 implementation of json
if attr in ['__setattr__', '__repr__', 'raise_error', '__class__',
- '__dict__', '__weakref__']:
+ '__dict__', '__weakref__', 'iteritems']:
continue
self.__setattr__(attr, self.raise_error)
self.__setattr__ = self.raise_error
From 5890002a8f1117a939b01dc49127792bcd3c04e9 Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Fri, 5 Oct 2018 12:46:17 +0200
Subject: [PATCH 149/652] Update CHANGELOG.md
---
CHANGELOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6614b25863..6dbda64311 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,7 +7,7 @@ This file follows the formats and conventions from [keepachangelog.com]
### Added
- Possibility to define macros with optional parameters. These must be the last
- ones in the definition (#285, #876, #943, #941)
+ ones in the definition (#285, #876, #943, #941, #955)
- Workaround for API_DeviceTimedOut errors on MeasurementGroup Start. Call Stop
in case this error occured (#764).
- Optional measurement group parameter to `ct` and `uct` macros (#940, #473)
From f30aa97757349e4fb856be34c0c4d8f1f4525021 Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Fri, 5 Oct 2018 12:49:37 +0200
Subject: [PATCH 150/652] Update funcgenerator.py
---
src/sardana/util/funcgenerator.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sardana/util/funcgenerator.py b/src/sardana/util/funcgenerator.py
index 44b7088da2..f30fddd237 100644
--- a/src/sardana/util/funcgenerator.py
+++ b/src/sardana/util/funcgenerator.py
@@ -215,7 +215,7 @@ def sleep(self, period):
time.sleep(nap)
def fire_start(self):
- self.fire_event(EventType("start"))
+ self.fire_event(EventType("start"), self._id)
self._start_fired = True
if self._id > 0:
msg = "start was fired with {0} delay".format(self._id)
From 738c839ec783ed9d77dbdf14d09ef506bcec0a81 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 8 Oct 2018 10:46:13 +0200
Subject: [PATCH 151/652] Allow to create a raw configuration
---
src/sardana/pool/poolmeasurementgroup.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index f398532a51..7835e3b93a 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -128,8 +128,10 @@ class MGConfiguration(object):
DFT_DESC = 'General purpose measurement group'
- def __init__(self, mg):
- self._mg = weakref.ref(mg)()
+ def __init__(self, mg=None):
+ self._mg = None
+ if mg is not None:
+ self._mg = weakref.ref(mg)()
self._config = None
self.use_fqdn = True
self._init_data()
From b6580081e2dd7f271103721367c25fe53b2c31e5 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 8 Oct 2018 10:51:08 +0200
Subject: [PATCH 152/652] Add internal getters to PoolAcquisitionBase
Add getters to extract the controllers, timer and monitor according to
the acquisition synchronization (hardware or software).
---
src/sardana/pool/poolacquisition.py | 86 +++++++++++++++++++++++++++--
1 file changed, 80 insertions(+), 6 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index c948643778..37f67acb30 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -363,6 +363,30 @@ def __init__(self, main_element, name):
# acquisition actions, uncomment this line
# self.add_finish_hook(self.clear_value_buffers, True)
+ def _get_ctrls(self):
+ """
+ Method to get the controller dict for the acquisition type
+ :return:
+ :rtype dict
+ """
+ raise NotImplementedError()
+
+ def _get_timer(self):
+ """
+ Method to get the controller dict for the acquisition type
+ :return:
+ :rtype dict
+ """
+ raise NotImplementedError()
+
+ def _get_monitor(self):
+ """
+ Method to get the controller dict for the acquisition type
+ :return:
+ :rtype dict
+ """
+ raise NotImplementedError()
+
def in_acquisition(self, states):
"""Determines if we are in acquisition or if the acquisition has ended
based on the current unit trigger modes and states returned by the
@@ -413,25 +437,25 @@ def start_action(self, *args, **kwargs):
cfg = kwargs['config']
# determine which is the controller which holds the master channel
master = None
+
if integ_time is not None and mon_count is not None:
raise RuntimeError('The acquisition must have only one role: '
'timer or count')
if integ_time is not None:
master_key = 'timer'
master_value = integ_time
- master = cfg.timer
+ master = self._get_timer()
if mon_count is not None:
master_key = 'monitor'
master_value = -mon_count
- master = cfg.monitor
+ master = self._get_monitor()
if master is None:
self.main_element.set_state(State.Fault, propagate=2)
- msg = "master {0} is unknown (probably disabled)".format(
- master_key)
+ msg = "master {0} ({1})is unknown (probably disabled)".format(
+ master_key, master)
raise RuntimeError(msg)
master_ctrl = master.controller
-
- pool_ctrls_dict = dict(cfg.controllers)
+ pool_ctrls_dict = dict(self._get_ctrls())
pool_ctrls_dict.pop('__tango__', None)
# controllers to be started (only enabled) in the right order
@@ -587,6 +611,31 @@ class PoolAcquisitionHardware(PoolAcquisitionBase):
def __init__(self, main_element, name="AcquisitionHardware"):
PoolAcquisitionBase.__init__(self, main_element, name)
+ def _get_ctrls(self):
+ """
+ Method to get the controller dict for the acquisition type
+ :return:
+ :rtype dict
+ """
+ return self.main_element.configuration.ctrl_hw_sync
+
+ def _get_timer(self):
+ """
+ Method to get the controller dict for the acquisition type
+ :return:
+ :rtype dict
+ """
+ return self.main_element.configuration.hw_sync_timer
+
+ def _get_monitor(self):
+ """
+ Method to get the controller dict for the acquisition type
+ :return:
+ :rtype dict
+ """
+ return self.main_element.configuration.hw_sync_monitor
+
+
@DebugIt()
def action_loop(self):
i = 0
@@ -657,6 +706,31 @@ def __init__(self, main_element, name="AcquisitionSoftware", slaves=None):
slaves = ()
self._slaves = slaves
+ def _get_ctrls(self):
+ """
+ Method to get the controller dict for the acquisition type
+ :return:
+ :rtype dict
+ """
+ return self.main_element.configuration.ctrl_sw_sync
+
+ def _get_timer(self):
+ """
+ Method to get the controller dict for the acquisition type
+ :return:
+ :rtype dict
+ """
+
+ return self.main_element.configuration.sw_sync_timer
+
+ def _get_monitor(self):
+ """
+ Method to get the controller dict for the acquisition type
+ :return:
+ :rtype dict
+ """
+ return self.main_element.configuration.sw_sync_monitor
+
@DebugIt()
def start_action(self, *args, **kwargs):
"""Prepares everything for acquisition and starts it.
From d0368e300af07c80b5deb9af69c97557788a3586 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 8 Oct 2018 10:53:02 +0200
Subject: [PATCH 153/652] Use the main_element configuration
---
src/sardana/pool/poolacquisition.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 37f67acb30..c154abf8e6 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -206,7 +206,8 @@ def run(self, *args, **kwargs):
# participate directly in the acquisition
for pseudo_elem in elem.get_pseudo_elements():
pseudo_elem.clear_value_buffer()
- config = kwargs['config']
+
+ config = self.main_element.configuration
synchronization = kwargs["synchronization"]
integ_time = synchronization.integration_time
repetitions = synchronization.repetitions
@@ -434,7 +435,6 @@ def start_action(self, *args, **kwargs):
raise Exception(msg)
_ = kwargs.get("items", self.get_elements())
- cfg = kwargs['config']
# determine which is the controller which holds the master channel
master = None
From af253c32842359482e6224f68adbae40705a6ef7 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 8 Oct 2018 10:54:15 +0200
Subject: [PATCH 154/652] Adapt helpers to use MGConfiguration class
---
src/sardana/pool/test/helper.py | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/src/sardana/pool/test/helper.py b/src/sardana/pool/test/helper.py
index 927395549b..0f4597f709 100644
--- a/src/sardana/pool/test/helper.py
+++ b/src/sardana/pool/test/helper.py
@@ -41,7 +41,8 @@
from sardana.pool.poolmotor import PoolMotor
from sardana.pool.poolpseudocounter import PoolPseudoCounter
from sardana.pool.poolpseudomotor import PoolPseudoMotor
-from sardana.pool.poolmeasurementgroup import PoolMeasurementGroup
+from sardana.pool.poolmeasurementgroup import PoolMeasurementGroup, \
+ MGConfiguration
def createPoolController(pool, conf):
@@ -195,7 +196,7 @@ def createCTAcquisitionConfiguration(ctrls, ctrl_channels):
master_idx = 0
configuration = {}
ctrls_configuration = {}
- configuration['timer'] = ctrl_channels[master_ctrl_idx][master_idx]
+ configuration['timer'] = timer = ctrl_channels[master_ctrl_idx][master_idx]
for ctrl, channels in zip(ctrls, ctrl_channels):
ctrl_data = createConfFromObj(ctrl)
ctrl_data['channels'] = {}
@@ -205,7 +206,16 @@ def createCTAcquisitionConfiguration(ctrls, ctrl_channels):
ctrl_data['timer'] = channels[master_idx]
ctrls_configuration[ctrl] = ctrl_data
configuration['controllers'] = ctrls_configuration
- return configuration
+ mg_cfg = MGConfiguration()
+ mg_cfg._config = configuration
+ mg_cfg.hw_sync_monitor = timer
+ mg_cfg.hw_sync_timer = timer
+ mg_cfg.sw_sync_timer = timer
+ mg_cfg.sw_sync_monitor = timer
+ mg_cfg.ctrl_hw_sync = ctrls_configuration
+ mg_cfg.ctrl_sw_sync = ctrls_configuration
+
+ return mg_cfg
def createMGUserConfiguration(pool, channels):
From de0d7d051fe14508c85da6f703614ecc615dc831 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 8 Oct 2018 11:09:56 +0200
Subject: [PATCH 155/652] Adapt aquisition test to use MGConfiguration class
---
src/sardana/pool/test/test_acquisition.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/sardana/pool/test/test_acquisition.py b/src/sardana/pool/test/test_acquisition.py
index 6e09954ad6..953e738e85 100644
--- a/src/sardana/pool/test/test_acquisition.py
+++ b/src/sardana/pool/test/test_acquisition.py
@@ -311,6 +311,9 @@ def continuous_acquisition(self, offset, active_interval, passive_interval,
self.sw_acq_cfg = createCTAcquisitionConfiguration((ct_ctrl_2,),
((ct_2_1,),))
# creating acquisition actions
+ # TODO: The CTExpChannel should have a configuration
+ ct_1_1.configuration = self.hw_acq_cfg
+ ct_2_1.configuration = self.sw_acq_cfg
self.hw_acq = PoolAcquisitionHardware(ct_1_1)
self.sw_acq = PoolAcquisitionSoftware(ct_2_1)
# Since we deposit the software acquisition action on the PoolThread's
From cede9f930613109ed7a4138aa0cc0959da3c1236 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 8 Oct 2018 11:25:04 +0200
Subject: [PATCH 156/652] Remove typo error
Remove line copied by mistake
---
src/sardana/pool/poolmeasurementgroup.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 30a443049c..1b883c3475 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -517,7 +517,6 @@ def _build_configuration(self, config=None):
'controller', g_monitor.controller.name,
monitor_ctrl_data['monitor'].name,
g_monitor.name)
- monitor_ctrl_data['monitor'] != g_monitor
self._config = config
self._prepare_data()
From 10e40f8f6803326cf8388a3d052c17c54559e092 Mon Sep 17 00:00:00 2001
From: Daniel Roldan
Date: Mon, 8 Oct 2018 11:26:38 +0200
Subject: [PATCH 157/652] Add prepare method in MeasurementGroup Taurus ext
---
src/sardana/taurus/core/tango/sardana/pool.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py
index bed33eea26..2ec007f69d 100644
--- a/src/sardana/taurus/core/tango/sardana/pool.py
+++ b/src/sardana/taurus/core/tango/sardana/pool.py
@@ -1795,8 +1795,8 @@ def _start(self, *args, **kwargs):
self.debug("stopped")
raise e
- def prepare(self, synchronization, nr_of_starts=1):
- pass
+ def prepare(self):
+ self.command_inout("Prepare")
def go(self, *args, **kwargs):
start_time = time.time()
From 38e217a4beb175551e48493533777bd0e9a89751 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 8 Oct 2018 11:28:23 +0200
Subject: [PATCH 158/652] Rename MGConfiguration to MeasurementConfiguration
The class will be used by the measurement groups and the counter/timer
channels.
---
src/sardana/pool/poolmeasurementgroup.py | 4 ++--
src/sardana/pool/test/helper.py | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 1b883c3475..a80da25005 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -124,7 +124,7 @@ def _to_fqdn(name, logger=None):
return full_name
-class MGConfiguration(object):
+class MeasurementConfiguration(object):
DFT_DESC = 'General purpose measurement group'
@@ -585,7 +585,7 @@ def __init__(self, **kwargs):
self._monitor_count = None
self._repetitions = 1
self._acquisition_mode = AcqMode.Timer
- self._config = MGConfiguration(self)
+ self._config = MeasurementConfiguration(self)
self._config_dirty = True
self._moveable = None
self._moveable_obj = None
diff --git a/src/sardana/pool/test/helper.py b/src/sardana/pool/test/helper.py
index 0f4597f709..5d9cc97f8e 100644
--- a/src/sardana/pool/test/helper.py
+++ b/src/sardana/pool/test/helper.py
@@ -42,7 +42,7 @@
from sardana.pool.poolpseudocounter import PoolPseudoCounter
from sardana.pool.poolpseudomotor import PoolPseudoMotor
from sardana.pool.poolmeasurementgroup import PoolMeasurementGroup, \
- MGConfiguration
+ MeasurementConfiguration
def createPoolController(pool, conf):
@@ -206,7 +206,7 @@ def createCTAcquisitionConfiguration(ctrls, ctrl_channels):
ctrl_data['timer'] = channels[master_idx]
ctrls_configuration[ctrl] = ctrl_data
configuration['controllers'] = ctrls_configuration
- mg_cfg = MGConfiguration()
+ mg_cfg = MeasurementConfiguration()
mg_cfg._config = configuration
mg_cfg.hw_sync_monitor = timer
mg_cfg.hw_sync_timer = timer
From a3656ecddde6d63c15dc154471364cf9fab834e2 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 8 Oct 2018 11:31:58 +0200
Subject: [PATCH 159/652] Rename internal variable
Change "mg" to "parent" as it is more appropriate to the new use of
the class.
---
src/sardana/pool/poolmeasurementgroup.py | 76 ++++++++++++------------
1 file changed, 39 insertions(+), 37 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index a80da25005..afc9165210 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -128,10 +128,10 @@ class MeasurementConfiguration(object):
DFT_DESC = 'General purpose measurement group'
- def __init__(self, mg=None):
- self._mg = None
- if mg is not None:
- self._mg = weakref.ref(mg)()
+ def __init__(self, parent=None):
+ self._parent = None
+ if parent is not None:
+ self._parent = weakref.ref(parent)()
self._config = None
self.use_fqdn = True
self._init_data()
@@ -194,8 +194,8 @@ def configuration(self, config=None):
def set_configuration_from_user(self, cfg, to_fqdn=True):
config = {}
- user_elements = self._mg.get_user_elements()
- pool = self._mg.pool
+ user_elements = self._parent.get_user_elements()
+ pool = self._parent.pool
if len(user_elements) == 0:
# All timers were disabled
default_timer = None
@@ -205,10 +205,10 @@ def set_configuration_from_user(self, cfg, to_fqdn=True):
timer_name = cfg.get('timer', default_timer)
monitor_name = cfg.get('monitor', default_timer)
if to_fqdn:
- timer_name = _to_fqdn(timer_name, logger=self._mg)
+ timer_name = _to_fqdn(timer_name, logger=self._parent)
config['timer'] = pool.get_element_by_full_name(timer_name)
if to_fqdn:
- monitor_name = _to_fqdn(monitor_name, logger=self._mg)
+ monitor_name = _to_fqdn(monitor_name, logger=self._parent)
config['monitor'] = pool.get_element_by_full_name(monitor_name)
config['controllers'] = controllers = {}
@@ -231,7 +231,7 @@ def set_configuration_from_user(self, cfg, to_fqdn=True):
ctrl = c_name
else:
if to_fqdn:
- c_name = _to_fqdn(c_name, logger=self._mg)
+ c_name = _to_fqdn(c_name, logger=self._parent)
ctrl = pool.get_element_by_full_name(c_name)
assert ctrl.get_type() == ElementType.Controller
controllers[ctrl] = ctrl_data = {}
@@ -240,12 +240,12 @@ def set_configuration_from_user(self, cfg, to_fqdn=True):
if not external and ctrl.is_timerable():
timer_name = c_data['timer']
if to_fqdn:
- timer_name = _to_fqdn(timer_name, logger=self._mg)
+ timer_name = _to_fqdn(timer_name, logger=self._parent)
timer = pool.get_element_by_full_name(timer_name)
ctrl_data['timer'] = timer
monitor_name = c_data['monitor']
if to_fqdn:
- monitor_name = _to_fqdn(monitor_name, logger=self._mg)
+ monitor_name = _to_fqdn(monitor_name, logger=self._parent)
monitor = pool.get_element_by_full_name(monitor_name)
ctrl_data['monitor'] = monitor
synchronizer = c_data.get('synchronizer')
@@ -255,7 +255,8 @@ def set_configuration_from_user(self, cfg, to_fqdn=True):
synchronizer = 'software'
elif synchronizer != 'software':
if to_fqdn:
- synchronizer = _to_fqdn(synchronizer, logger=self._mg)
+ synchronizer = _to_fqdn(synchronizer,
+ logger=self._parent)
synchronizer = pool.get_element_by_full_name(synchronizer)
ctrl_data['synchronizer'] = synchronizer
try:
@@ -266,22 +267,22 @@ def set_configuration_from_user(self, cfg, to_fqdn=True):
msg = ("trigger_type configuration parameter is deprecated"
" in favor of synchronization. Re-apply "
"configuration in order to upgrade.")
- self._mg.warning(msg)
+ self._parent.warning(msg)
ctrl_data['synchronization'] = synchronization
ctrl_data['channels'] = channels = {}
for ch_name, ch_data in c_data['channels'].items():
if external:
validator = TangoAttributeNameValidator()
params = validator.getParams(ch_data['full_name'])
- params['pool'] = self._mg.pool
+ params['pool'] = self._parent.pool
channel = PoolExternalObject(**params)
else:
if to_fqdn:
- ch_name = _to_fqdn(ch_name, logger=self._mg)
+ ch_name = _to_fqdn(ch_name, logger=self._parent)
channel = pool.get_element_by_full_name(ch_name)
channels[channel] = dict(ch_data)
- config['label'] = cfg.get('label', self._mg.name)
+ config['label'] = cfg.get('label', self._parent.name)
config['description'] = cfg.get('description', self.DFT_DESC)
self.use_fqdn = to_fqdn
self._build_configuration(config)
@@ -380,8 +381,8 @@ def _build_configuration(self, config=None):
if config is None:
config = {}
- user_elements = self._mg.get_user_elements()
- ctrls = self._mg.get_pool_controllers()
+ user_elements = self._parent.get_user_elements()
+ ctrls = self._parent.get_pool_controllers()
# find the first CT
first_timerable = None
@@ -434,7 +435,7 @@ def _build_configuration(self, config=None):
channel_data = self._build_channel_defaults(channel_data,
element)
- config['label'] = self._mg.name
+ config['label'] = self._parent.name
config['description'] = self.DFT_DESC
if len(external_user_elements) > 0:
@@ -449,7 +450,7 @@ def _build_configuration(self, config=None):
# create a configuration based on a new configuration
user_elem_ids = {}
tg_elem_ids = []
- pool = self._mg.pool
+ pool = self._parent.pool
for c, c_data in config['controllers'].items():
synchronizer = c_data.get('synchronizer')
acq_synch_type = c_data.get('synchronization')
@@ -469,7 +470,8 @@ def _build_configuration(self, config=None):
else:
full_name = channel_data['full_name']
if self.use_fqdn:
- full_name = _to_fqdn(full_name, logger=self._mg)
+ full_name = _to_fqdn(full_name,
+ logger=self._parent)
element = pool.get_element_by_full_name(full_name)
_id = element.id
channel_data = self._build_channel_defaults(
@@ -492,31 +494,31 @@ def _build_configuration(self, config=None):
indexes = sorted(user_elem_ids.keys())
user_elem_ids_list = [user_elem_ids[idx] for idx in indexes]
user_elem_ids_list.extend(tg_elem_ids)
- self._mg.set_user_element_ids(user_elem_ids_list)
+ self._parent.set_user_element_ids(user_elem_ids_list)
g_timer, g_monitor = config['timer'], config['monitor']
timer_ctrl_data = config['controllers'][g_timer.controller]
if timer_ctrl_data['timer'] != g_timer:
- self._mg.warning('controller timer and global timer '
- 'mismatch. Using global timer')
- self._mg.debug('For controller %s, timer is defined as '
- 'channel %s. The global timer is set to '
- 'channel %s which belongs to the same '
- 'controller', g_timer.controller.name,
- timer_ctrl_data['timer'].name, g_timer.name)
+ self._parent.warning('controller timer and global timer '
+ 'mismatch. Using global timer')
+ self._parent.debug('For controller %s, timer is defined as '
+ 'channel %s. The global timer is set to '
+ 'channel %s which belongs to the same '
+ 'controller', g_timer.controller.name,
+ timer_ctrl_data['timer'].name, g_timer.name)
timer_ctrl_data['timer'] = g_timer
monitor_ctrl_data = config['controllers'][g_monitor.controller]
if monitor_ctrl_data['monitor'] != g_monitor:
- self._mg.warning('controller monitor and global '
- 'monitor mismatch. Using global monitor')
- self._mg.debug('For controller %s, monitor is defined as '
- 'channel %s. The global timer is set to '
- 'channel %s which belongs to the same '
- 'controller', g_monitor.controller.name,
- monitor_ctrl_data['monitor'].name,
- g_monitor.name)
+ self._parent.warning('controller monitor and global '
+ 'monitor mismatch. Using global monitor')
+ self._parent.debug('For controller %s, monitor is defined as '
+ 'channel %s. The global timer is set to '
+ 'channel %s which belongs to the same '
+ 'controller', g_monitor.controller.name,
+ monitor_ctrl_data['monitor'].name,
+ g_monitor.name)
self._config = config
self._prepare_data()
From 34354a674461ccf9a5ea96ad1fbbb7cba545c28a Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 8 Oct 2018 12:01:35 +0200
Subject: [PATCH 160/652] SEP: remove arguments from prepare command
---
doc/source/sep/SEP18.md | 42 ++++++++++++++++++++---------------------
1 file changed, 21 insertions(+), 21 deletions(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index 6c03386b67..165debefd4 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -89,36 +89,36 @@ Implementation
### Measurement Group
-Measurement group is extended by the *prepare* command with two parameters:
-synchronization description and number of starts. The second one indicates
-how many times measurement group will be started, with the *start* command,
-to measure according to the synchronization description.
+Measurement group is extended by the *prepare* command (with no arguments)
+*number of starts* attribute. The use of the attribute is optional and
+it indicates how many times measurement group will be started, with the
+*start* command, to measure according to the synchronization description or
+integration time. When it is not used number of starts of 1 will be assumed.
1. Measurement group - Tango device class
- * Add `Prepare` command. TODO: investigate the best way to pass
- synchronization description, as JSON serialized string, together with the
- starts integer.
- * Remove `synchronization` attribute (experimental API) - no backwards
- compatibility.
+ * Add `Prepare` command.
+ * Add `NrOfStarts` (`DevLong`) attribute.
2. Measurement group - core class
- * Add `prepare(synchronization, starts=1)` method
- * Remove `synchronization` property (experimental API) - no backwards
- compatibility.
-3. Backwards compatibility for integration time attribute will be solved in
-the following way: setting of integration time attribute will allow starts
-until the next preparation.
+ * Add `prepare()` method.
+ * Add `nr_of_starts` property.
+3. Backwards compatibility for using just the integration time attribute with
+the start command (without calling prepare command in-between) will be
+solved in the following way: start command will internally call the prepare.
4. Measurement group - Taurus extension
- * Add `prepare` method which simply maps to `Prepare` Tango command
+ * Add `prepare()` method which simply maps to `Prepare` Tango command
* Add `count_raw` method according to the following pseudo code:
* `start()`
* `waitFinish()`
- * Implement `count` method according to the following pseudo code:
- * `prepare(synchronization & starts = 1)` where synchronization
- contains the integration time
+ * Implement `count(integration_time)` method according to the following
+ pseudo
+ code:
+ * set `integration_time`
+ * set `nr_of_starts=1`
+ * `prepare()`
* `count_raw()`
* Implement `count_continuous` (previous `measure`) method according to
the following pseudo code:
- * `prepare(synchronization & starts = 1)` where synchronization may
+ * `prepare()` where synchronization may
contain the continuous acquisition description
* `subscribeValueBuffer()`
* `count_raw()`
@@ -280,7 +280,7 @@ StartOne(1)
The same as option 2 but maintaining the backwards compatibility in the
following way:
* Acquisition actions will call the `LoadOne`, etc. methods depending on the
-controllers implementations (more preciselly using the `inspect.getargspec`
+controllers implementations (more precisely using the `inspect.getargspec`
and counting the number of arguments). This will require much more complicated
acquisition actions.
From be43ea58e9d3e6a7bc8ea244ea65d1abc75b9215 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 8 Oct 2018 12:06:29 +0200
Subject: [PATCH 161/652] Fix bug on test_aquisition
---
src/sardana/pool/test/test_acquisition.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/sardana/pool/test/test_acquisition.py b/src/sardana/pool/test/test_acquisition.py
index 953e738e85..910abfbe29 100644
--- a/src/sardana/pool/test/test_acquisition.py
+++ b/src/sardana/pool/test/test_acquisition.py
@@ -181,6 +181,9 @@ def hw_step_acquisition(self, repetitions, integ_time):
(channels,))
# creating acquisition actions
main_element = FakeElement(self.pool)
+ # TODO: The main_element should have a configuration
+ main_element.configuration = self.acq_cfg
+
self.ct_acq = PoolAcquisitionSoftware(main_element)
for channel in channels:
self.ct_acq.add_element(channel)
From 6f05436b00ee1e735312059cce873420f436512d Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 8 Oct 2018 12:12:44 +0200
Subject: [PATCH 162/652] Add Prepare cmd to MG (Tango)
---
src/sardana/tango/pool/MeasurementGroup.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/sardana/tango/pool/MeasurementGroup.py b/src/sardana/tango/pool/MeasurementGroup.py
index 027236e7f4..c6adc9bd00 100644
--- a/src/sardana/tango/pool/MeasurementGroup.py
+++ b/src/sardana/tango/pool/MeasurementGroup.py
@@ -269,6 +269,9 @@ def write_SoftwareSynchronizerInitialDomain(self, attr):
raise Exception("Invalid domain (can be either Position or Time)")
self.measurement_group.sw_synch_initial_domain = domain
+ def Prepare(self):
+ self.measurement_group.prepare()
+
def Start(self):
try:
self.wait_for_operation()
@@ -300,6 +303,7 @@ class MeasurementGroupClass(PoolGroupDeviceClass):
# Command definitions
cmd_list = {
+ 'Prepare': [[DevVoid, ""], [DevVoid, ""]],
'Start': [[DevVoid, ""], [DevVoid, ""]],
'StartMultiple': [[DevLong, ""], [DevVoid, ""]],
}
From cfa965d4233d5159c79146f5a7cadc82ee3874cb Mon Sep 17 00:00:00 2001
From: Daniel Roldan
Date: Mon, 8 Oct 2018 12:21:12 +0200
Subject: [PATCH 163/652] Add NrOfStarts to MG taurus ext
---
src/sardana/taurus/core/tango/sardana/pool.py | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py
index 2ec007f69d..5bafd0068c 100644
--- a/src/sardana/taurus/core/tango/sardana/pool.py
+++ b/src/sardana/taurus/core/tango/sardana/pool.py
@@ -1670,6 +1670,16 @@ def setSynchronization(self, synchronization):
self.getSynchronizationObj().write(data)
self._last_integ_time = None
+ # NrOfStarts Methods
+ def getNrOfStartsObj(self):
+ return self._getAttrEG('NrOfStarts')
+
+ def setNrOfStarts(self, starts):
+ self.getNrOfStartsObj().write(starts)
+
+ def getNrOfStarts(self):
+ return self._getAttrValue('NrOfStarts')
+
def getMoveableObj(self):
return self._getAttrEG('Moveable')
From 68c79e373ce5dfb79f4d7faa9d9e1a93fe7662f9 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 8 Oct 2018 12:27:29 +0200
Subject: [PATCH 164/652] Add nr_of_starts MG property
---
src/sardana/pool/poolmeasurementgroup.py | 29 ++++++++++++++++++++----
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index c7279ee7ee..76c872ce56 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -130,7 +130,7 @@ class PoolMeasurementGroup(PoolGroupElement):
def __init__(self, **kwargs):
self._state_lock = threading.Lock()
self._monitor_count = None
- self._repetitions = 1
+ self._nr_of_starts = 1
self._acquisition_mode = AcqMode.Timer
self._config = None
self._config_dirty = True
@@ -698,6 +698,10 @@ def get_latency_time(self):
doc="latency time between two consecutive "
"acquisitions")
+ # -------------------------------------------------------------------------
+ # software synchronizer initial domain
+ # -------------------------------------------------------------------------
+
def get_sw_synch_initial_domain(self):
return self._sw_synch_initial_domain
@@ -711,16 +715,33 @@ def set_sw_synch_initial_domain(self, domain):
"or SynchDomain.Position)"
)
+ # -------------------------------------------------------------------------
+ # number of starts
+ # -------------------------------------------------------------------------
+
+ def get_nr_of_starts(self):
+ return self._nr_of_starts
+
+ def set_nr_of_starts(self, nr_of_starts, propagate=1):
+ self._nr_of_starts = nr_of_starts
+ if not propagate:
+ return
+ self.fire_event(EventType("nr_of_starts", priority=propagate),
+ nr_of_starts)
+
+ nr_of_starts = property(get_nr_of_starts, set_nr_of_starts,
+ doc="current number of starts")
+
# -------------------------------------------------------------------------
# acquisition
# -------------------------------------------------------------------------
def prepare(self):
+ # load configuration into controller(s) if necessary
self.load_configuration()
config = self.get_configuration()
- repetitions = self.synchronization.repetitions
- self.acquisition.prepare(config, repetitions)
-
+ nr_of_starts = self.nr_of_starts
+ self.acquisition.prepare(config, nr_of_starts)
def start_acquisition(self, value=None, multiple=1):
self._aborted = False
From b2781a24ab82a1ad522317406d1c7720585894c9 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 8 Oct 2018 12:38:24 +0200
Subject: [PATCH 165/652] Rename Repetitions (unused) Tango attr to NrOfStarts
of MG
---
src/sardana/tango/pool/MeasurementGroup.py | 24 +++++++++++-----------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/sardana/tango/pool/MeasurementGroup.py b/src/sardana/tango/pool/MeasurementGroup.py
index c6adc9bd00..aebfa64a49 100644
--- a/src/sardana/tango/pool/MeasurementGroup.py
+++ b/src/sardana/tango/pool/MeasurementGroup.py
@@ -74,9 +74,9 @@ def init_device(self):
PoolGroupDevice.init_device(self)
# state and status are already set by the super class
detect_evts = "latencytime", "moveable", "synchronization", \
- "softwaresynchronizerinitialdomain"
+ "softwaresynchronizerinitialdomain", "nrofstarts"
non_detect_evts = "configuration", "integrationtime", "monitorcount", \
- "acquisitionmode", "elementlist", "repetitions"
+ "acquisitionmode", "elementlist"
self.set_change_events(detect_evts, non_detect_evts)
self.Elements = list(self.Elements)
@@ -220,14 +220,14 @@ def write_Configuration(self, attr):
cfg = CodecFactory().decode(('json', data), ensure_ascii=True)
self.measurement_group.set_configuration_from_user(cfg)
- def read_Repetitions(self, attr):
- repetitions = self.measurement_group.repetitions
- if repetitions is None:
- repetitions = int('nan')
- attr.set_value(repetitions)
+ def read_NrOfStarts(self, attr):
+ nr_of_starts = self.measurement_group.nr_of_starts
+ if nr_of_starts is None:
+ nr_of_starts = int('nan')
+ attr.set_value(nr_of_starts)
- def write_Repetitions(self, attr):
- self.measurement_group.repetitions = attr.get_write_value()
+ def write_NrOfStarts(self, attr):
+ self.measurement_group.nr_of_starts = attr.get_write_value()
def read_Moveable(self, attr):
moveable = self.measurement_group.moveable
@@ -323,9 +323,9 @@ class MeasurementGroupClass(PoolGroupDeviceClass):
'Configuration': [[DevString, SCALAR, READ_WRITE],
{'Memorized': "true",
'Display level': DispLevel.EXPERT}],
- 'Repetitions': [[DevLong, SCALAR, READ_WRITE],
- {'Memorized': "true",
- 'Display level': DispLevel.OPERATOR}],
+ 'NrOfStarts': [[DevLong, SCALAR, READ_WRITE],
+ {'Memorized': "true",
+ 'Display level': DispLevel.OPERATOR}],
'Moveable': [[DevString, SCALAR, READ_WRITE],
{'Memorized': "true",
'Display level': DispLevel.EXPERT}],
From 9e1961f6bc502fe5f91694fd832fa4cf3c74126a Mon Sep 17 00:00:00 2001
From: Daniel Roldan
Date: Mon, 8 Oct 2018 13:02:58 +0200
Subject: [PATCH 166/652] Add count method to MG taurus ext
---
src/sardana/taurus/core/tango/sardana/pool.py | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py
index 5bafd0068c..1dbcfbad5b 100644
--- a/src/sardana/taurus/core/tango/sardana/pool.py
+++ b/src/sardana/taurus/core/tango/sardana/pool.py
@@ -1864,9 +1864,17 @@ class on a provisional basis. Backwards incompatible changes
self._total_go_time = time.time() - start_time
return ret
+
+ def count(self, integration_time):
+ self.setIntegrationTime(integration_time)
+ self.setNrOfStarts(1)
+ self.prepare()
+ self.count_raw(self)
+
startCount = PoolElement.start
waitCount = PoolElement.waitFinish
count = go
+ count_raw = PoolElement.go
stopCount = PoolElement.abort
stop = PoolElement.stop
From 331a0c87d00341732fecab494a0f85e63c295958 Mon Sep 17 00:00:00 2001
From: Daniel Roldan
Date: Mon, 8 Oct 2018 13:04:10 +0200
Subject: [PATCH 167/652] Rename measure method to count_continuous, MG taurus
ext
---
src/sardana/macroserver/scan/gscan.py | 4 ++--
src/sardana/taurus/core/tango/sardana/pool.py | 12 ++++++------
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py
index efe88df81a..0a085e8ce8 100644
--- a/src/sardana/macroserver/scan/gscan.py
+++ b/src/sardana/macroserver/scan/gscan.py
@@ -2662,8 +2662,8 @@ def scan_loop(self):
hook()
yield 0
- measurement_group.measure(synchronization,
- self.value_buffer_changed)
+ measurement_group.count_continuous(synchronization,
+ self.value_buffer_changed)
self.debug("Waiting for value buffer events to be processed")
self.wait_value_buffer()
self.join_thread_pool()
diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py
index 1dbcfbad5b..a23f2a251b 100644
--- a/src/sardana/taurus/core/tango/sardana/pool.py
+++ b/src/sardana/taurus/core/tango/sardana/pool.py
@@ -1812,12 +1812,12 @@ def go(self, *args, **kwargs):
start_time = time.time()
cfg = self.getConfiguration()
cfg.prepare()
- duration = args[0]
- if duration is None or duration == 0:
+ integration_time = args[0]
+ if integration_time is None or integration_time == 0:
return self.getStateEG().readValue(), self.getValues()
- self.putIntegrationTime(duration)
+ self.putIntegrationTime(integration_time)
self.setMoveable(None)
- PoolElement.go(self, *args, **kwargs)
+ self.count_raw(self)
state = self.getStateEG().readValue()
if state == Fault:
msg = "Measurement group ended acquisition with Fault state"
@@ -1827,7 +1827,7 @@ def go(self, *args, **kwargs):
self._total_go_time = time.time() - start_time
return ret
- def measure(self, synchronization, value_buffer_cb=None):
+ def count_continuous(self, synchronization, value_buffer_cb=None):
"""Execute measurement process according to the given synchronization
description.
@@ -1850,7 +1850,7 @@ class on a provisional basis. Backwards incompatible changes
cfg.prepare()
self.setSynchronization(synchronization)
self.subscribeValueBuffer(value_buffer_cb)
- PoolElement.go(self)
+ self.count_raw(self)
self.unsubscribeValueBuffer(value_buffer_cb)
state = self.getStateEG().readValue()
if state == Fault:
From 4ba148da4b4fba47ef5e945ee20913efd53edfb6 Mon Sep 17 00:00:00 2001
From: Carlos Pascual
Date: Mon, 8 Oct 2018 13:29:56 +0200
Subject: [PATCH 168/652] (doc) Extend explanation of optional parameters
The current explanation of optional macro parameters is too terse.
Extend it.
---
doc/source/devel/howto_macros/macros_general.rst | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/doc/source/devel/howto_macros/macros_general.rst b/doc/source/devel/howto_macros/macros_general.rst
index 3ef5acfccb..b179dea6c8 100644
--- a/doc/source/devel/howto_macros/macros_general.rst
+++ b/doc/source/devel/howto_macros/macros_general.rst
@@ -330,8 +330,13 @@ all available sardana interfaces (:obj:`~sardana.sardanadefs.Interface`)
Optional parameters
~~~~~~~~~~~~~~~~~~~
-A special parameter default value is the ``Optional``. It allows to
-execute a macro even if the given parameter value is not specified by the user.
+A special parameter default value is the ``Optional`` keyword. A parameter
+whose default value is set to ``Optional`` behaves just as one with a default
+value, except that if the user does not provide a value explicitly, the
+handling of its value is deferred to the run method (which gets ``None`` as
+the parameter value). This allows for more complex handling of the value
+(e.g. interactive prompting to the user, system introspection, reading from
+files, etc.)
So, here is an example how to define and use the optional parameter::
From 85828e42afed6b2a0c88064151592020773e3c14 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 8 Oct 2018 14:22:01 +0200
Subject: [PATCH 169/652] Add backwards compatibility for direct use of MG
start
---
src/sardana/pool/poolmeasurementgroup.py | 21 +++++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 76c872ce56..6a65ecd050 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -131,6 +131,7 @@ def __init__(self, **kwargs):
self._state_lock = threading.Lock()
self._monitor_count = None
self._nr_of_starts = 1
+ self._pending_starts = 0
self._acquisition_mode = AcqMode.Timer
self._config = None
self._config_dirty = True
@@ -741,13 +742,24 @@ def prepare(self):
self.load_configuration()
config = self.get_configuration()
nr_of_starts = self.nr_of_starts
+ self._pending_starts = nr_of_starts
self.acquisition.prepare(config, nr_of_starts)
def start_acquisition(self, value=None, multiple=1):
+ if self._pending_starts == 0:
+ msg = "starting acquisition without prior preparing is " \
+ "deprecated since version Jan18."
+ self.warning(msg)
+ self.debug("Preparing with number_of_starts equal to 1")
+ nr_of_starts = self.nr_of_starts
+ self.set_nr_of_starts(1, propagate=0)
+ try:
+ self.prepare()
+ finally:
+ self.set_nr_of_starts(nr_of_starts, propagate=0)
self._aborted = False
+ self._pending_starts -= 1
if not self._simulation_mode:
- # load configuration into controller(s) if necessary
- self.load_configuration()
# determining the acquisition parameters
kwargs = dict(head=self, config=self._config, multiple=multiple)
acquisition_mode = self.acquisition_mode
@@ -770,5 +782,10 @@ def get_acquisition(self):
acquisition = property(get_acquisition, doc="acquisition object")
def stop(self):
+ self._pending_starts = 0
self.acquisition._synch._synch_soft.stop()
PoolGroupElement.stop(self)
+
+ def abort(self):
+ self._pending_starts = 0
+ PoolGroupElement.abort(self)
From f0aae07cbbfe62572fc73f8bd43fc0b43265e693 Mon Sep 17 00:00:00 2001
From: cfalcon
Date: Mon, 8 Oct 2018 14:30:58 +0200
Subject: [PATCH 170/652] Add support to new synchronization types in dummy C/T
controller
Extend LoadOne input parameters according SEP18
and add support to new synchronization types.
---
.../DummyCounterTimerController.py | 37 ++++++++++++++-----
1 file changed, 27 insertions(+), 10 deletions(-)
diff --git a/src/sardana/pool/poolcontrollers/DummyCounterTimerController.py b/src/sardana/pool/poolcontrollers/DummyCounterTimerController.py
index 3f94eb7886..94b4a7bdda 100644
--- a/src/sardana/pool/poolcontrollers/DummyCounterTimerController.py
+++ b/src/sardana/pool/poolcontrollers/DummyCounterTimerController.py
@@ -30,17 +30,25 @@
Description, Memorize, NotMemorized
-class Channel:
+class Channel(object):
def __init__(self, idx):
self.idx = idx # 1 based index
self.value = 0.0
self.is_counting = False
self.active = True
- self.repetitions = 0
+ self.repetitions = 1
+ self.acq_latency_time = 0
self._counter = 0
self.mode = AcqSynch.SoftwareTrigger
self.buffer_values = []
+ self.estimated_duration = None
+
+ def calculate_duration(self, intergration_time):
+ if self.mode not in (AcqSynch.SoftwareStart, AcqSynch.HardwareStart):
+ self.acq_latency_time = 0
+ self.estimated_duration = (intergration_time
+ + self.acq_latency_time) * self.repetitions
class DummyCounterTimerController(CounterTimerController):
@@ -117,12 +125,13 @@ def _updateChannelState(self, ind, elapsed_time):
v = int(elapsed_time * 100 * ind)
if v >= self.monitor_count:
self._finish(elapsed_time)
- elif channel.mode in (AcqSynch.HardwareTrigger, AcqSynch.HardwareGate):
+ elif channel.mode in (AcqSynch.HardwareTrigger,
+ AcqSynch.HardwareGate,
+ AcqSynch.SoftwareStart,
+ AcqSynch.HardwareStart):
if self.integ_time is not None:
# counting in time
- # if elapsed_time >= self.integ_time*channel.repetitions:
- if elapsed_time > channel.repetitions * (self.integ_time +
- self._latency_time):
+ if elapsed_time > channel.estimated_duration:
# if elapsed_time >= self.integ_time:
self._finish(elapsed_time)
@@ -142,7 +151,10 @@ def _updateChannelValue(self, ind, elapsed_time):
if ind == self._monitor:
if not channel.is_counting:
channel.value = self.monitor_count
- elif channel.mode in (AcqSynch.HardwareTrigger, AcqSynch.HardwareGate):
+ elif channel.mode in (AcqSynch.HardwareTrigger,
+ AcqSynch.HardwareGate,
+ AcqSynch.SoftwareStart,
+ AcqSynch.HardwareStart):
if self.integ_time is not None:
t = elapsed_time
n = int(t / self.integ_time)
@@ -192,7 +204,10 @@ def ReadOne(self, ind):
self._log.debug('ReadOne(%d): entering...' % ind)
channel = self.read_channels[ind]
ret = None
- if channel.mode in (AcqSynch.HardwareTrigger, AcqSynch.HardwareGate):
+ if channel.mode in (AcqSynch.HardwareTrigger,
+ AcqSynch.HardwareGate,
+ AcqSynch.SoftwareStart,
+ AcqSynch.HardwareStart):
values = copy.deepcopy(channel.buffer_values)
ret = []
for v in values:
@@ -225,17 +240,19 @@ def StartOne(self, ind, value=None):
def StartAll(self):
self.start_time = time.time()
- def LoadOne(self, ind, value, repetitions):
+ def LoadOne(self, ind, value, repetitions, latency_time):
if value > 0:
self.integ_time = value
self.monitor_count = None
else:
self.integ_time = None
self.monitor_count = -value
- self._repetitions = repetitions
+
for channel in self.channels:
if channel:
channel.repetitions = repetitions
+ channel.acq_latency_time = latency_time
+ channel.calculate_duration(value)
def AbortOne(self, ind):
now = time.time()
From 44082184ac177665875db618b36a11399301fbb0 Mon Sep 17 00:00:00 2001
From: Daniel Roldan
Date: Mon, 8 Oct 2018 15:15:40 +0200
Subject: [PATCH 171/652] flake8 fix
---
src/sardana/taurus/core/tango/sardana/pool.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py
index a23f2a251b..859e40b716 100644
--- a/src/sardana/taurus/core/tango/sardana/pool.py
+++ b/src/sardana/taurus/core/tango/sardana/pool.py
@@ -1864,7 +1864,6 @@ class on a provisional basis. Backwards incompatible changes
self._total_go_time = time.time() - start_time
return ret
-
def count(self, integration_time):
self.setIntegrationTime(integration_time)
self.setNrOfStarts(1)
@@ -1873,7 +1872,6 @@ def count(self, integration_time):
startCount = PoolElement.start
waitCount = PoolElement.waitFinish
- count = go
count_raw = PoolElement.go
stopCount = PoolElement.abort
stop = PoolElement.stop
From 322febdfde6fc6757aa7827cccc68a5cccc88273 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 8 Oct 2018 12:01:35 +0200
Subject: [PATCH 172/652] SEP: remove arguments from prepare command
---
doc/source/sep/SEP18.md | 42 ++++++++++++++++++++---------------------
1 file changed, 21 insertions(+), 21 deletions(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index 6c03386b67..165debefd4 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -89,36 +89,36 @@ Implementation
### Measurement Group
-Measurement group is extended by the *prepare* command with two parameters:
-synchronization description and number of starts. The second one indicates
-how many times measurement group will be started, with the *start* command,
-to measure according to the synchronization description.
+Measurement group is extended by the *prepare* command (with no arguments)
+*number of starts* attribute. The use of the attribute is optional and
+it indicates how many times measurement group will be started, with the
+*start* command, to measure according to the synchronization description or
+integration time. When it is not used number of starts of 1 will be assumed.
1. Measurement group - Tango device class
- * Add `Prepare` command. TODO: investigate the best way to pass
- synchronization description, as JSON serialized string, together with the
- starts integer.
- * Remove `synchronization` attribute (experimental API) - no backwards
- compatibility.
+ * Add `Prepare` command.
+ * Add `NrOfStarts` (`DevLong`) attribute.
2. Measurement group - core class
- * Add `prepare(synchronization, starts=1)` method
- * Remove `synchronization` property (experimental API) - no backwards
- compatibility.
-3. Backwards compatibility for integration time attribute will be solved in
-the following way: setting of integration time attribute will allow starts
-until the next preparation.
+ * Add `prepare()` method.
+ * Add `nr_of_starts` property.
+3. Backwards compatibility for using just the integration time attribute with
+the start command (without calling prepare command in-between) will be
+solved in the following way: start command will internally call the prepare.
4. Measurement group - Taurus extension
- * Add `prepare` method which simply maps to `Prepare` Tango command
+ * Add `prepare()` method which simply maps to `Prepare` Tango command
* Add `count_raw` method according to the following pseudo code:
* `start()`
* `waitFinish()`
- * Implement `count` method according to the following pseudo code:
- * `prepare(synchronization & starts = 1)` where synchronization
- contains the integration time
+ * Implement `count(integration_time)` method according to the following
+ pseudo
+ code:
+ * set `integration_time`
+ * set `nr_of_starts=1`
+ * `prepare()`
* `count_raw()`
* Implement `count_continuous` (previous `measure`) method according to
the following pseudo code:
- * `prepare(synchronization & starts = 1)` where synchronization may
+ * `prepare()` where synchronization may
contain the continuous acquisition description
* `subscribeValueBuffer()`
* `count_raw()`
@@ -280,7 +280,7 @@ StartOne(1)
The same as option 2 but maintaining the backwards compatibility in the
following way:
* Acquisition actions will call the `LoadOne`, etc. methods depending on the
-controllers implementations (more preciselly using the `inspect.getargspec`
+controllers implementations (more precisely using the `inspect.getargspec`
and counting the number of arguments). This will require much more complicated
acquisition actions.
From 294326138497779825594d44ceef6ad1dc46a588 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 8 Oct 2018 12:12:44 +0200
Subject: [PATCH 173/652] Add Prepare cmd to MG (Tango)
---
src/sardana/tango/pool/MeasurementGroup.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/sardana/tango/pool/MeasurementGroup.py b/src/sardana/tango/pool/MeasurementGroup.py
index 027236e7f4..c6adc9bd00 100644
--- a/src/sardana/tango/pool/MeasurementGroup.py
+++ b/src/sardana/tango/pool/MeasurementGroup.py
@@ -269,6 +269,9 @@ def write_SoftwareSynchronizerInitialDomain(self, attr):
raise Exception("Invalid domain (can be either Position or Time)")
self.measurement_group.sw_synch_initial_domain = domain
+ def Prepare(self):
+ self.measurement_group.prepare()
+
def Start(self):
try:
self.wait_for_operation()
@@ -300,6 +303,7 @@ class MeasurementGroupClass(PoolGroupDeviceClass):
# Command definitions
cmd_list = {
+ 'Prepare': [[DevVoid, ""], [DevVoid, ""]],
'Start': [[DevVoid, ""], [DevVoid, ""]],
'StartMultiple': [[DevLong, ""], [DevVoid, ""]],
}
From c9480ab86d15b4dbb25459073e24c2b4cc5c77de Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 8 Oct 2018 12:27:29 +0200
Subject: [PATCH 174/652] Add nr_of_starts MG property
---
src/sardana/pool/poolmeasurementgroup.py | 29 ++++++++++++++++++++----
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index c7279ee7ee..76c872ce56 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -130,7 +130,7 @@ class PoolMeasurementGroup(PoolGroupElement):
def __init__(self, **kwargs):
self._state_lock = threading.Lock()
self._monitor_count = None
- self._repetitions = 1
+ self._nr_of_starts = 1
self._acquisition_mode = AcqMode.Timer
self._config = None
self._config_dirty = True
@@ -698,6 +698,10 @@ def get_latency_time(self):
doc="latency time between two consecutive "
"acquisitions")
+ # -------------------------------------------------------------------------
+ # software synchronizer initial domain
+ # -------------------------------------------------------------------------
+
def get_sw_synch_initial_domain(self):
return self._sw_synch_initial_domain
@@ -711,16 +715,33 @@ def set_sw_synch_initial_domain(self, domain):
"or SynchDomain.Position)"
)
+ # -------------------------------------------------------------------------
+ # number of starts
+ # -------------------------------------------------------------------------
+
+ def get_nr_of_starts(self):
+ return self._nr_of_starts
+
+ def set_nr_of_starts(self, nr_of_starts, propagate=1):
+ self._nr_of_starts = nr_of_starts
+ if not propagate:
+ return
+ self.fire_event(EventType("nr_of_starts", priority=propagate),
+ nr_of_starts)
+
+ nr_of_starts = property(get_nr_of_starts, set_nr_of_starts,
+ doc="current number of starts")
+
# -------------------------------------------------------------------------
# acquisition
# -------------------------------------------------------------------------
def prepare(self):
+ # load configuration into controller(s) if necessary
self.load_configuration()
config = self.get_configuration()
- repetitions = self.synchronization.repetitions
- self.acquisition.prepare(config, repetitions)
-
+ nr_of_starts = self.nr_of_starts
+ self.acquisition.prepare(config, nr_of_starts)
def start_acquisition(self, value=None, multiple=1):
self._aborted = False
From ca4ff0cbfee798c0bfa4c386791c5712fed0b081 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 8 Oct 2018 12:38:24 +0200
Subject: [PATCH 175/652] Rename Repetitions (unused) Tango attr to NrOfStarts
of MG
---
src/sardana/tango/pool/MeasurementGroup.py | 24 +++++++++++-----------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/sardana/tango/pool/MeasurementGroup.py b/src/sardana/tango/pool/MeasurementGroup.py
index c6adc9bd00..aebfa64a49 100644
--- a/src/sardana/tango/pool/MeasurementGroup.py
+++ b/src/sardana/tango/pool/MeasurementGroup.py
@@ -74,9 +74,9 @@ def init_device(self):
PoolGroupDevice.init_device(self)
# state and status are already set by the super class
detect_evts = "latencytime", "moveable", "synchronization", \
- "softwaresynchronizerinitialdomain"
+ "softwaresynchronizerinitialdomain", "nrofstarts"
non_detect_evts = "configuration", "integrationtime", "monitorcount", \
- "acquisitionmode", "elementlist", "repetitions"
+ "acquisitionmode", "elementlist"
self.set_change_events(detect_evts, non_detect_evts)
self.Elements = list(self.Elements)
@@ -220,14 +220,14 @@ def write_Configuration(self, attr):
cfg = CodecFactory().decode(('json', data), ensure_ascii=True)
self.measurement_group.set_configuration_from_user(cfg)
- def read_Repetitions(self, attr):
- repetitions = self.measurement_group.repetitions
- if repetitions is None:
- repetitions = int('nan')
- attr.set_value(repetitions)
+ def read_NrOfStarts(self, attr):
+ nr_of_starts = self.measurement_group.nr_of_starts
+ if nr_of_starts is None:
+ nr_of_starts = int('nan')
+ attr.set_value(nr_of_starts)
- def write_Repetitions(self, attr):
- self.measurement_group.repetitions = attr.get_write_value()
+ def write_NrOfStarts(self, attr):
+ self.measurement_group.nr_of_starts = attr.get_write_value()
def read_Moveable(self, attr):
moveable = self.measurement_group.moveable
@@ -323,9 +323,9 @@ class MeasurementGroupClass(PoolGroupDeviceClass):
'Configuration': [[DevString, SCALAR, READ_WRITE],
{'Memorized': "true",
'Display level': DispLevel.EXPERT}],
- 'Repetitions': [[DevLong, SCALAR, READ_WRITE],
- {'Memorized': "true",
- 'Display level': DispLevel.OPERATOR}],
+ 'NrOfStarts': [[DevLong, SCALAR, READ_WRITE],
+ {'Memorized': "true",
+ 'Display level': DispLevel.OPERATOR}],
'Moveable': [[DevString, SCALAR, READ_WRITE],
{'Memorized': "true",
'Display level': DispLevel.EXPERT}],
From c7cbd13feb285d784844ae9d2e8f69a9721ef023 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 8 Oct 2018 14:22:01 +0200
Subject: [PATCH 176/652] Add backwards compatibility for direct use of MG
start
---
src/sardana/pool/poolmeasurementgroup.py | 21 +++++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 76c872ce56..6a65ecd050 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -131,6 +131,7 @@ def __init__(self, **kwargs):
self._state_lock = threading.Lock()
self._monitor_count = None
self._nr_of_starts = 1
+ self._pending_starts = 0
self._acquisition_mode = AcqMode.Timer
self._config = None
self._config_dirty = True
@@ -741,13 +742,24 @@ def prepare(self):
self.load_configuration()
config = self.get_configuration()
nr_of_starts = self.nr_of_starts
+ self._pending_starts = nr_of_starts
self.acquisition.prepare(config, nr_of_starts)
def start_acquisition(self, value=None, multiple=1):
+ if self._pending_starts == 0:
+ msg = "starting acquisition without prior preparing is " \
+ "deprecated since version Jan18."
+ self.warning(msg)
+ self.debug("Preparing with number_of_starts equal to 1")
+ nr_of_starts = self.nr_of_starts
+ self.set_nr_of_starts(1, propagate=0)
+ try:
+ self.prepare()
+ finally:
+ self.set_nr_of_starts(nr_of_starts, propagate=0)
self._aborted = False
+ self._pending_starts -= 1
if not self._simulation_mode:
- # load configuration into controller(s) if necessary
- self.load_configuration()
# determining the acquisition parameters
kwargs = dict(head=self, config=self._config, multiple=multiple)
acquisition_mode = self.acquisition_mode
@@ -770,5 +782,10 @@ def get_acquisition(self):
acquisition = property(get_acquisition, doc="acquisition object")
def stop(self):
+ self._pending_starts = 0
self.acquisition._synch._synch_soft.stop()
PoolGroupElement.stop(self)
+
+ def abort(self):
+ self._pending_starts = 0
+ PoolGroupElement.abort(self)
From c0b3f6247cf802aff760ad9579e9bb4bd50b9dfd Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 8 Oct 2018 15:05:59 +0200
Subject: [PATCH 177/652] Use new API of prepare in step scan
---
src/sardana/macroserver/scan/gscan.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py
index 0a085e8ce8..17d7794918 100644
--- a/src/sardana/macroserver/scan/gscan.py
+++ b/src/sardana/macroserver/scan/gscan.py
@@ -1010,9 +1010,9 @@ def scan_loop(self):
nr_points = float(macro.nr_points)
if hasattr(macro, "integ_time"):
integ_time = macro.integ_time
- group = {SynchParam.Active: {SynchDomain.Time: integ_time}}
- synchronization = [group]
- self.measurement_group.prepare(synchronization, nr_points)
+ self.measurement_group.putIntegrationTime(integ_time)
+ self.measurement_group.setNrOfStarts(nr_points)
+ self.measurement_group.prepare()
scream = True
else:
yield 0.0
From 38f66fc6d1393df88e33848ad134c162eff3f53e Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 8 Oct 2018 15:07:12 +0200
Subject: [PATCH 178/652] Comment calls to new configuration API
---
src/sardana/pool/poolacquisition.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 9a998b35d2..92f271a423 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -241,14 +241,14 @@ def event_received(self, *args, **kwargs):
self.debug('Stopping ZeroD acquisition.')
self._0d_acq.stop_action()
- def prepare(self, config, repetitions):
+ def prepare(self, config, nr_of_starts):
"""Prepare measurement."""
- timers = config.get_timers(enabled=True)
+ timers = [] #config.get_timers(enabled=True)
for timer in timers:
axis = timer.axis
timer_ctrl = timer.controller
ctrl = timer_ctrl.ctrl
- ctrl.PrepareOne(axis, repetitions)
+ ctrl.PrepareOne(axis, nr_of_starts)
def is_running(self):
return self._0d_acq.is_running() or\
From f26022b7fa60a463718bea03632bd5b1a51cdf47 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 8 Oct 2018 15:21:32 +0200
Subject: [PATCH 179/652] Rename internal methods
---
src/sardana/pool/poolmeasurementgroup.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index afc9165210..097f4ba80e 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -134,9 +134,9 @@ def __init__(self, parent=None):
self._parent = weakref.ref(parent)()
self._config = None
self.use_fqdn = True
- self._init_data()
+ self._clean_variables()
- def _init_data(self):
+ def _clean_variables(self):
# list of controller with channels enabled.
self.enabled_ctrls = []
# dict with channel and its acquisition synchronization
@@ -377,7 +377,7 @@ def _build_channel_defaults(self, channel_data, channel):
def _build_configuration(self, config=None):
"""Builds a configuration object from the list of elements"""
- self._init_data()
+ self._clean_variables()
if config is None:
config = {}
@@ -521,9 +521,9 @@ def _build_configuration(self, config=None):
g_monitor.name)
self._config = config
- self._prepare_data()
+ self._split_sync()
- def _prepare_data(self):
+ def _split_sync(self):
"""
Split MeasurementGroup configuration with channels
triggered by SW Trigger and channels triggered by HW trigger
From 1919226ae8a979cc847951e5d1541ac5a1603791 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 8 Oct 2018 15:23:08 +0200
Subject: [PATCH 180/652] Add trigger gate configuration
Add method to extract the trigger/gate configurations
---
src/sardana/pool/poolmeasurementgroup.py | 35 ++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 097f4ba80e..7e7d9eaa2d 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -150,6 +150,7 @@ def _clean_variables(self):
self.ctrl_hw_sync = {}
self.ctrl_sw_start = {}
self.ctrl_0d_sync = {}
+ self.ctrl_tg_sync = {}
# TODO: Do the documentation
self.sw_sync_timer = None
self.sw_sync_monitor = None
@@ -522,6 +523,7 @@ def _build_configuration(self, config=None):
self._config = config
self._split_sync()
+ self._split_tg()
def _split_sync(self):
"""
@@ -579,6 +581,39 @@ def find_master(ctrls, role):
self.hw_sync_timer = find_master(self.ctrl_hw_sync, "timer")
self.hw_sync_monitor = find_master(self.ctrl_hw_sync, "monitor")
+ def _split_tg(self):
+ """
+ Build TG configuration from complete configuration.
+ """
+
+ # Create list with not repeated elements
+ _tg_element_list = []
+
+ for ctrl in self._config["controllers"]:
+ tg_element = self._config["controllers"][ctrl].get('synchronizer',
+ None)
+ if (tg_element is not None and
+ tg_element != "software" and
+ tg_element not in _tg_element_list):
+ _tg_element_list.append(tg_element)
+
+ # Intermediate dictionary to organize each ctrl with its elements.
+ ctrl_tgelem_dict = {}
+ for tgelem in _tg_element_list:
+ tg_ctrl = tgelem.get_controller()
+ if tg_ctrl not in ctrl_tgelem_dict.keys():
+ ctrl_tgelem_dict[tg_ctrl] = [tgelem]
+ else:
+ ctrl_tgelem_dict[tg_ctrl].append(tgelem)
+
+ # Build TG configuration dictionary.
+ for ctrl in ctrl_tgelem_dict:
+ self.ctrl_tg_sync[ctrl] = ctrls = {}
+ ctrls['channels'] = {}
+ for tg_elem in ctrl_tgelem_dict[ctrl]:
+ ch = ctrls['channels'][tg_elem] = {}
+ ch['full_name'] = tg_elem.full_name
+
class PoolMeasurementGroup(PoolGroupElement):
From 29f59746d8c9976a5de5e07ed29d2afb7879ff88 Mon Sep 17 00:00:00 2001
From: Daniel Roldan
Date: Mon, 8 Oct 2018 15:24:19 +0200
Subject: [PATCH 181/652] Adapt go method to SEP18
---
src/sardana/taurus/core/tango/sardana/pool.py | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/src/sardana/taurus/core/tango/sardana/pool.py b/src/sardana/taurus/core/tango/sardana/pool.py
index 859e40b716..5ce37c56ba 100644
--- a/src/sardana/taurus/core/tango/sardana/pool.py
+++ b/src/sardana/taurus/core/tango/sardana/pool.py
@@ -1817,6 +1817,8 @@ def go(self, *args, **kwargs):
return self.getStateEG().readValue(), self.getValues()
self.putIntegrationTime(integration_time)
self.setMoveable(None)
+ self.setNrOfStarts(1)
+ self.prepare()
self.count_raw(self)
state = self.getStateEG().readValue()
if state == Fault:
@@ -1864,14 +1866,9 @@ class on a provisional basis. Backwards incompatible changes
self._total_go_time = time.time() - start_time
return ret
- def count(self, integration_time):
- self.setIntegrationTime(integration_time)
- self.setNrOfStarts(1)
- self.prepare()
- self.count_raw(self)
-
startCount = PoolElement.start
waitCount = PoolElement.waitFinish
+ count = go
count_raw = PoolElement.go
stopCount = PoolElement.abort
stop = PoolElement.stop
From ab28ce6210abb6bfe2789cde8b69c2120376253c Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 8 Oct 2018 15:27:16 +0200
Subject: [PATCH 182/652] Use trigger/gate configuration from
MeasurementConfiguration
---
src/sardana/pool/poolacquisition.py | 48 -----------------------------
1 file changed, 48 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 22e69f7de6..9bd8406ebe 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -62,52 +62,6 @@
}
-def getTGConfiguration(MGcfg):
- '''Build TG configuration from complete MG configuration.
-
- TODO: (technical debt) All the MeasurementGroup configuration
- logic should be encapsulate in a dedicated class instead of
- using a basic data structures like dict or lists...
-
- :param MGcfg: configuration dictionary of the whole Measurement Group.
- :type MGcfg: dict<>
- :return: a configuration dictionary of TG elements organized by controller
- :rtype: dict<>
- '''
-
- # Create list with not repeated elements
- _tg_element_list = []
-
- for ctrl in MGcfg["controllers"]:
- tg_element = MGcfg["controllers"][ctrl].get('synchronizer', None)
- if (tg_element is not None and
- tg_element != "software" and
- tg_element not in _tg_element_list):
- _tg_element_list.append(tg_element)
-
- # Intermediate dictionary to organize each ctrl with its elements.
- ctrl_tgelem_dict = {}
- for tgelem in _tg_element_list:
- tg_ctrl = tgelem.get_controller()
- if tg_ctrl not in ctrl_tgelem_dict.keys():
- ctrl_tgelem_dict[tg_ctrl] = [tgelem]
- else:
- ctrl_tgelem_dict[tg_ctrl].append(tgelem)
-
- # Build TG configuration dictionary.
- TGcfg = {}
- TGcfg['controllers'] = {}
-
- for ctrl in ctrl_tgelem_dict:
- TGcfg['controllers'][ctrl] = ctrls = {}
- ctrls['channels'] = {}
- for tg_elem in ctrl_tgelem_dict[ctrl]:
- ch = ctrls['channels'][tg_elem] = {}
- ch['full_name'] = tg_elem.full_name
- # TODO: temporary returning tg_elements
- return TGcfg, _tg_element_list
-
-
def is_value_error(value):
if isinstance(value, SardanaValue) and value.error:
return True
@@ -221,7 +175,6 @@ def run(self, *args, **kwargs):
integ_time = synchronization.integration_time
repetitions = synchronization.repetitions
- synch_cfg, _ = getTGConfiguration(config)
# starting continuous acquisition only if there are any controllers
if len(config.ctrl_hw_sync):
cont_acq_kwargs = dict(kwargs)
@@ -240,7 +193,6 @@ def run(self, *args, **kwargs):
# TODO: Ask why
self.set_0d_config(zerod_acq_kwargs)
synch_kwargs = dict(kwargs)
- synch_kwargs['config'] = synch_cfg
self._synch.run(*args, **synch_kwargs)
def _get_action_for_element(self, element):
From 1d796ba903e4c82ca53f740ba44e380acbfb3cba Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 8 Oct 2018 15:28:10 +0200
Subject: [PATCH 183/652] Adapt to use MeasurementConfiguration class
---
src/sardana/pool/poolsynchronization.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/sardana/pool/poolsynchronization.py b/src/sardana/pool/poolsynchronization.py
index 850dff9378..1cfca7ba09 100644
--- a/src/sardana/pool/poolsynchronization.py
+++ b/src/sardana/pool/poolsynchronization.py
@@ -140,11 +140,12 @@ def start_action(self, *args, **kwargs):
- sw_synch_initial_domain (optional) - initial domain for software
synchronizer, can be either SynchDomain.Time or SynchDomain.Position
'''
- cfg = kwargs['config']
+
+ cfg = self.main_element.configuration
synchronization = kwargs.get('synchronization')
moveable = kwargs.get('moveable')
sw_synch_initial_domain = kwargs.get('sw_synch_initial_domain', None)
- ctrls_config = cfg.get('controllers')
+ ctrls_config = cfg.ctrl_tg_sync
pool_ctrls = ctrls_config.keys()
# Prepare a dictionary with the involved channels
From d7fdbf7cf931d4f9ce3a6ee50a47d203e1929e7e Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 8 Oct 2018 15:28:59 +0200
Subject: [PATCH 184/652] Adapt test to use MeasurementConfiguration class
---
src/sardana/pool/test/helper.py | 6 +++++-
src/sardana/pool/test/test_acquisition.py | 8 +++++---
src/sardana/pool/test/test_poolsynchronization.py | 2 ++
src/sardana/pool/test/test_synchronization.py | 5 ++++-
4 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/src/sardana/pool/test/helper.py b/src/sardana/pool/test/helper.py
index 5d9cc97f8e..6cd87dfe33 100644
--- a/src/sardana/pool/test/helper.py
+++ b/src/sardana/pool/test/helper.py
@@ -174,7 +174,11 @@ def createPoolSynchronizationConfiguration(ctrls, ctrl_channels):
ctrl_data['channels'][channel] = channel_conf
ctrls_configuration[ctrl] = ctrl_data
configuration = {'controllers': ctrls_configuration}
- return configuration
+
+ mc = MeasurementConfiguration()
+ mc._config = configuration
+ mc.ctrl_tg_sync = ctrls_configuration
+ return mc
def createCTAcquisitionConfiguration(ctrls, ctrl_channels):
diff --git a/src/sardana/pool/test/test_acquisition.py b/src/sardana/pool/test/test_acquisition.py
index 910abfbe29..dd18c2c51e 100644
--- a/src/sardana/pool/test/test_acquisition.py
+++ b/src/sardana/pool/test/test_acquisition.py
@@ -99,8 +99,10 @@ def setUp(self):
self.l = AttributeListener()
self.channel_names = []
- def createPoolSynchronization(self, tg_list):
+ def createPoolSynchronization(self, tg_list, tg_config=None):
self.main_element = FakeElement(self.pool)
+ # TODO: The TriggerGate should have a configuration
+ self.main_element.configuration = tg_config
self.tggeneration = PoolSynchronization(self.main_element)
for tg in tg_list:
self.tggeneration.add_element(tg)
@@ -119,7 +121,7 @@ def hw_continuous_acquisition(self, offset, active_interval,
tg_cfg = createPoolSynchronizationConfiguration((tg_ctrl,),
((tg,),))
# creating PoolSynchronization action
- self.createPoolSynchronization([tg])
+ self.createPoolSynchronization([tg], tg_config=tg_cfg)
channels = []
for name in self.channel_names:
@@ -305,7 +307,7 @@ def continuous_acquisition(self, offset, active_interval, passive_interval,
tg_cfg = createPoolSynchronizationConfiguration((tg_ctrl_2,),
((tg_2_1,),))
# creating TGGeneration action
- self.createPoolSynchronization([tg_2_1])
+ self.createPoolSynchronization([tg_2_1], tg_config=tg_cfg)
# add_listeners
self.addListeners([ct_1_1, ct_2_1])
# creating acquisition configurations
diff --git a/src/sardana/pool/test/test_poolsynchronization.py b/src/sardana/pool/test/test_poolsynchronization.py
index 9e4fcb0014..22c2c706ff 100644
--- a/src/sardana/pool/test/test_poolsynchronization.py
+++ b/src/sardana/pool/test/test_poolsynchronization.py
@@ -63,6 +63,8 @@ def setUp(self):
self.mock_tg_ctrl.StateOne.return_value = (State.Moving, 'triggering')
dummy_tg_ctrl.ctrl = self.mock_tg_ctrl
+ # TODO: The TriggerGate should have a configuration
+ self.dummy_tg.configuration = self.cfg
self.tgaction = PoolSynchronization(self.dummy_tg)
self.tgaction.add_element(self.dummy_tg)
diff --git a/src/sardana/pool/test/test_synchronization.py b/src/sardana/pool/test/test_synchronization.py
index 53f36cb8fc..abe73a0f67 100644
--- a/src/sardana/pool/test/test_synchronization.py
+++ b/src/sardana/pool/test/test_synchronization.py
@@ -61,7 +61,10 @@ def createElements(self, ctrl_klass, ctrl_lib, ctrl_props):
self.pool.add_element(self.tg_elem)
# create Synchronization action and its configuration
self.tg_cfg = createPoolSynchronizationConfiguration((self.tg_ctrl,),
- ((self.tg_elem,),),)
+ ((self.tg_elem,),)
+ ,)
+ # TODO: The TriggerGate should have a configuration
+ self.tg_elem.configuration = self.tg_cfg
self.tgaction = PoolSynchronization(self.tg_elem)
self.tgaction.add_element(self.tg_elem)
From 91323fe397b5041681f113112a3dfb514f53a737 Mon Sep 17 00:00:00 2001
From: Daniel Roldan
Date: Mon, 8 Oct 2018 15:55:56 +0200
Subject: [PATCH 185/652] Fix flake8
---
src/sardana/pool/poolacquisition.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 92f271a423..8eeba4a104 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -243,7 +243,7 @@ def event_received(self, *args, **kwargs):
def prepare(self, config, nr_of_starts):
"""Prepare measurement."""
- timers = [] #config.get_timers(enabled=True)
+ timers = [] # config.get_timers(enabled=True)
for timer in timers:
axis = timer.axis
timer_ctrl = timer.controller
From 1ea9b6661c64667bd06add1393b0eeb96574e40e Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 8 Oct 2018 16:53:09 +0200
Subject: [PATCH 186/652] Remove unused properties
---
src/sardana/pool/poolmeasurementgroup.py | 10 ----------
1 file changed, 10 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 4fef0bf8c3..4223fc3701 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -169,16 +169,6 @@ def wrapper(self, *args, **kwargs):
def __getitem__(self, item):
return self._config.__getitem__(item)
- @property
- @__check_config
- def timer(self):
- return self._config['timer']
-
- @property
- @__check_config
- def monitor(self):
- return self._config['monitor']
-
@property
@__check_config
def controllers(self):
From 56aaeab4825916b60b34838d6591a4816b85aa50 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 8 Oct 2018 17:15:10 +0200
Subject: [PATCH 187/652] Add new attributes for acquisition actions
Add lists for timers and monitor for each the acquisition action type.
---
src/sardana/pool/poolmeasurementgroup.py | 52 +++++++++++++++++++-----
1 file changed, 41 insertions(+), 11 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 4223fc3701..fa24573ad1 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -139,6 +139,13 @@ def __init__(self, parent=None):
def _clean_variables(self):
# list of controller with channels enabled.
self.enabled_ctrls = []
+ # list of timers and monitors by synchronization type.
+ self.sw_sync_timers_enabled = []
+ self.sw_sync_monitors_enabled = []
+ self.sw_start_timers_enabled = []
+ self.sw_start_monitors_enabled = []
+ self.hw_sync_timers_enabled = []
+ self.hw_sync_monitors_enabled = []
# dict with channel and its acquisition synchronization
# key: PoolBaseChannel; value: AcqSynch
self.channel_to_acq_synch = {}
@@ -547,29 +554,52 @@ def _split_sync(self):
else:
self.ctrl_hw_sync[ctrl] = ctrl_info
- def find_master(ctrls, role):
+ def get_master_enables(ctrls, role):
master_idx = float("+inf")
master = None
+ elements = []
for ctrl_info in ctrls.values():
element = ctrl_info[role]
if element in ctrl_info["channels"]:
element_idx = ctrl_info["channels"][element]["index"]
element_enabled = ctrl_info["channels"][element]["enabled"]
# Find master only if is enabled
- if element_idx < master_idx and element_enabled:
- master = element
- master_idx = element_idx
- return master
+ if element_enabled:
+ elements.append(element)
+ if element_idx < master_idx:
+ master = element
+ master_idx = element_idx
+ return master, elements
if len(self.ctrl_sw_sync):
- self.sw_sync_timer = find_master(self.ctrl_sw_sync, "timer")
- self.sw_sync_monitor = find_master(self.ctrl_sw_sync, "monitor")
+ timer, timers = get_master_enables(self.ctrl_sw_sync, "timer")
+ self.sw_sync_timer = timer
+ self.sw_sync_timers_enabled = timers
+
+ monitor, monitors = get_master_enables(self.ctrl_sw_sync,
+ "monitor")
+ self.sw_sync_monitor = monitor
+ self.sw_sync_monitors_enabled = monitors
+
if len(self.ctrl_sw_start):
- self.sw_start_timer = find_master(self.ctrl_sw_start, "timer")
- self.sw_start_monitor = find_master(self.ctrl_sw_start, "monitor")
+ timer, timers = get_master_enables(self.ctrl_sw_start, "timer")
+ self.sw_start_timer = timer
+ self.sw_start_timers_enabled = timers
+
+ monitor, monitors = get_master_enables(self.ctrl_sw_start,
+ "monitor")
+ self.sw_start_monitor = monitor
+ self.sw_start_monitors_enabled = monitors
+
if len(self.ctrl_hw_sync):
- self.hw_sync_timer = find_master(self.ctrl_hw_sync, "timer")
- self.hw_sync_monitor = find_master(self.ctrl_hw_sync, "monitor")
+ timer, timers = get_master_enables(self.ctrl_hw_sync, "timer")
+ self.hw_sync_timer = timer
+ self.hw_sync_timers_enabled = timers
+
+ monitor, monitors = get_master_enables(self.ctrl_hw_sync,
+ "monitor")
+ self.hw_sync_monitor = monitor
+ self.hw_sync_monitors_enabled = monitors
def _split_tg(self):
"""
From 26dda09efe0e94a467d626aab1c50d9a1f5fa2da Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 8 Oct 2018 17:15:36 +0200
Subject: [PATCH 188/652] Use MeasurementConfiguration API
---
src/sardana/pool/poolacquisition.py | 5 ++++-
src/sardana/pool/poolmeasurementgroup.py | 2 +-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 9bd8406ebe..ae819cac8c 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -141,7 +141,10 @@ def event_received(self, *args, **kwargs):
def prepare(self, config, repetitions):
"""Prepare measurement."""
- timers = config.get_timers(enabled=True)
+ timers = config.sw_sync_timers_enabled + \
+ config.sw_start_timers_enabled + \
+ config.hw_sync_timers_enabled
+
for timer in timers:
axis = timer.axis
timer_ctrl = timer.controller
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index fa24573ad1..71c49e7174 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -938,7 +938,7 @@ def set_nr_of_starts(self, nr_of_starts, propagate=1):
def prepare(self):
# load configuration into controller(s) if necessary
self.load_configuration()
- config = self.get_configuration()
+ config = self.configuration
nr_of_starts = self.nr_of_starts
self._pending_starts = nr_of_starts
self.acquisition.prepare(config, nr_of_starts)
From 7b3e315417734e70ef186a13b5fc21fe142f9911 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 8 Oct 2018 17:49:47 +0200
Subject: [PATCH 189/652] Add backwards compat. for loadable ctrls without
latency time
---
src/sardana/pool/poolacquisition.py | 63 ++++++++++++++++++-----------
1 file changed, 40 insertions(+), 23 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 8eeba4a104..a30fffd4ba 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -551,36 +551,53 @@ def start_action(self, *args, **kwargs):
pool_ctrls.remove(master_ctrl)
pool_ctrls.append(master_ctrl)
+ def load(ctrl, master_axis, value, repetitions, latency=0):
+ ctrl.PreLoadAll()
+ try:
+ res = ctrl.PreLoadOne(master_axis, value, repetitions,
+ latency)
+ except TypeError:
+ try:
+ res = ctrl.PreLoadOne(master_axis, value, repetitions)
+ msg = ("PreLoadOne(axis, value, repetitions) is "
+ "deprecated since version Jan19. Use PreLoadOne("
+ "axis, value, repetitions, latency_time) instead.")
+ self.warning(msg)
+ except TypeError:
+ res = ctrl.PreLoadOne(master_axis, value)
+ msg = ("PreLoadOne(axis, value) is deprecated since "
+ "version 2.3.0. Use PreLoadOne(axis, value, "
+ "repetitions, latency_time) instead.")
+ self.warning(msg)
+ if not res:
+ msg = ("%s.PreLoadOne(%d) returned False" %
+ (pool_ctrl.name, master_axis))
+ raise Exception(msg)
+ try:
+ ctrl.LoadOne(master_axis, value, repetitions, latency)
+ except TypeError:
+ try:
+ ctrl.LoadOne(master_axis, value, repetitions)
+ msg = ("LoadOne(axis, value, repetitions) is deprecated "
+ "since version Jan18. Use LoadOne(axis, value, "
+ "repetitions, latency_time) instead.")
+ self.warning(msg)
+ except TypeError:
+ ctrl.LoadOne(master_axis, value)
+ msg = ("LoadOne(axis, value) is deprecated since "
+ "version 2.3.0. Use LoadOne(axis, value, "
+ "repetitions) instead.")
+ self.warning(msg)
+ ctrl.LoadAll()
+
with ActionContext(self):
# PreLoadAll, PreLoadOne, LoadOne and LoadAll
for pool_ctrl in pool_ctrls:
try:
ctrl = pool_ctrl.ctrl
pool_ctrl_data = pool_ctrls_dict[pool_ctrl]
- ctrl.PreLoadAll()
master = pool_ctrl_data[master_key]
- axis = master.axis
- try:
- res = ctrl.PreLoadOne(axis, master_value, repetitions)
- except TypeError:
- msg = ("PreLoadOne(axis, value) is deprecated since "
- "version 2.3.0. Use PreLoadOne(axis, value, "
- "repetitions) instead.")
- self.warning(msg)
- res = ctrl.PreLoadOne(axis, master_value)
- if not res:
- msg = ("%s.PreLoadOne(%d) returned False" %
- (pool_ctrl.name, axis))
- raise Exception(msg)
- try:
- ctrl.LoadOne(axis, master_value, repetitions)
- except TypeError:
- msg = ("LoadOne(axis, value) is deprecated since "
- "version 2.3.0. Use LoadOne(axis, value, "
- "repetitions) instead.")
- self.warning(msg)
- ctrl.LoadOne(axis, master_value)
- ctrl.LoadAll()
+ load(ctrl, master.axis, master_value, repetitions)
except Exception, e:
self.debug(e, exc_info=True)
master.set_state(State.Fault, propagate=2)
From 90e336a51f521641bf6cf5d150684289c434968c Mon Sep 17 00:00:00 2001
From: flyingbot91
Date: Mon, 8 Oct 2018 23:17:35 +0200
Subject: [PATCH 190/652] Centralized PyTango.DevState match
---
.../taurus/core/tango/sardana/motion.py | 42 ++++++++++---------
1 file changed, 22 insertions(+), 20 deletions(-)
diff --git a/src/sardana/taurus/core/tango/sardana/motion.py b/src/sardana/taurus/core/tango/sardana/motion.py
index 1d33b5512a..93618ad089 100644
--- a/src/sardana/taurus/core/tango/sardana/motion.py
+++ b/src/sardana/taurus/core/tango/sardana/motion.py
@@ -34,6 +34,26 @@
from taurus.core.util.containers import CaselessDict
+def get_pytango_devstate_match(states):
+ """
+ Retrieve PyTango.DevState match
+ :param states:
+ :return:
+ """
+
+ import PyTango
+ state = PyTango.DevState.ON
+ if PyTango.DevState.FAULT in states:
+ state = PyTango.DevState.FAULT
+ elif PyTango.DevState.ALARM in states:
+ state = PyTango.DevState.ALARM
+ elif PyTango.DevState.UNKNOWN in states:
+ state = PyTango.DevState.UNKNOWN
+ elif PyTango.DevState.MOVING in states:
+ state = PyTango.DevState.MOVING
+ return state
+
+
class Moveable:
""" An item that can 'move'. In order to move it you need to provide a list
of values (normally interpreted as motor positions).
@@ -181,16 +201,7 @@ def move(self, new_pos, timeout=None):
res = moveable.move(pos, timeout=timeout)
states.append(res[0])
positions.extend(res[1])
- import PyTango
- state = PyTango.DevState.ON
- if PyTango.DevState.FAULT in states:
- state = PyTango.DevState.FAULT
- elif PyTango.DevState.ALARM in states:
- state = PyTango.DevState.ALARM
- elif PyTango.DevState.UNKNOWN in states:
- state = PyTango.DevState.UNKNOWN
- elif PyTango.DevState.MOVING in states:
- state = PyTango.DevState.MOVING
+ state = get_pytango_devstate_match(states)
self.__total_motion_time = time.time() - start_time
return state, positions
@@ -388,16 +399,7 @@ def move(self, new_pos, timeout=None):
for moveable, id in zip(self.moveable_list, ids):
moveable.waitMove(id=id, timeout=timeout)
states, positions = self.readState(), self.readPosition()
- import PyTango
- state = PyTango.DevState.ON
- if PyTango.DevState.FAULT in states:
- state = PyTango.DevState.FAULT
- elif PyTango.DevState.ALARM in states:
- state = PyTango.DevState.ALARM
- elif PyTango.DevState.UNKNOWN in states:
- state = PyTango.DevState.UNKNOWN
- elif PyTango.DevState.MOVING in states:
- state = PyTango.DevState.MOVING
+ state = get_pytango_devstate_match(states)
ret = state, positions
self.__total_motion_time = time.time()
return ret
From 7598291746d35099ed2335eb7c6e50c04d923828 Mon Sep 17 00:00:00 2001
From: flyingbot91
Date: Mon, 8 Oct 2018 23:17:59 +0200
Subject: [PATCH 191/652] Removed unused variable
---
src/sardana/taurus/core/tango/sardana/motion.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/sardana/taurus/core/tango/sardana/motion.py b/src/sardana/taurus/core/tango/sardana/motion.py
index 93618ad089..fdf360963c 100644
--- a/src/sardana/taurus/core/tango/sardana/motion.py
+++ b/src/sardana/taurus/core/tango/sardana/motion.py
@@ -384,7 +384,6 @@ def waitMove(self, timeout=None, id=None):
moveable.waitMove(timeout=timeout, id=id[i])
def move(self, new_pos, timeout=None):
- start_time = time.time()
if len(self.moveable_list) == 1:
moveable = self.moveable_list[0]
ret = moveable.move(new_pos, timeout=timeout)
From c6220d5dd3238be8a2b251ffe4d378cfe3c3b4b4 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Tue, 9 Oct 2018 08:39:08 +0200
Subject: [PATCH 192/652] Fix flake8 errors
---
src/sardana/pool/poolacquisition.py | 5 ++---
src/sardana/pool/test/test_synchronization.py | 13 +++++++------
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index ae819cac8c..a326d5125f 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -142,8 +142,8 @@ def event_received(self, *args, **kwargs):
def prepare(self, config, repetitions):
"""Prepare measurement."""
timers = config.sw_sync_timers_enabled + \
- config.sw_start_timers_enabled + \
- config.hw_sync_timers_enabled
+ config.sw_start_timers_enabled + \
+ config.hw_sync_timers_enabled
for timer in timers:
axis = timer.axis
@@ -599,7 +599,6 @@ def _get_monitor(self):
"""
return self.main_element.configuration.hw_sync_monitor
-
@DebugIt()
def action_loop(self):
i = 0
diff --git a/src/sardana/pool/test/test_synchronization.py b/src/sardana/pool/test/test_synchronization.py
index abe73a0f67..869a2f5c04 100644
--- a/src/sardana/pool/test/test_synchronization.py
+++ b/src/sardana/pool/test/test_synchronization.py
@@ -60,9 +60,8 @@ def createElements(self, ctrl_klass, ctrl_lib, ctrl_props):
self.pool.add_element(self.tg_ctrl)
self.pool.add_element(self.tg_elem)
# create Synchronization action and its configuration
- self.tg_cfg = createPoolSynchronizationConfiguration((self.tg_ctrl,),
- ((self.tg_elem,),)
- ,)
+ self.tg_cfg = createPoolSynchronizationConfiguration(
+ (self.tg_ctrl,), ((self.tg_elem,),),)
# TODO: The TriggerGate should have a configuration
self.tg_elem.configuration = self.tg_cfg
self.tgaction = PoolSynchronization(self.tg_elem)
@@ -86,7 +85,8 @@ def tggeneration(self, ctrl_lib, ctrl_klass, ctrl_props,
:type offset: float
:param active_interval: signal at which triggers will be generated
:type active_interval: float
- :param passive_interval: temporal passive period between two active periods
+ :param passive_interval: temporal passive period between two active
+ periods
:type passive_interval: float
:param repetitions: number of generated triggers
:type repetitions: int
@@ -131,11 +131,12 @@ def abort_tggeneration(self, ctrl_lib, ctrl_klass, ctrl_props,
:type offset: float
:param active_interval: signal at which triggers will be generated
:type active_interval: float
- :param passive_interval: temporal passive period between two active periods
+ :param passive_interval: temporal passive period between two active
+ periods
:type passive_interval: float
:param repetitions: number of generated triggers
:type repetitions: int
- :param abort_time: wait this time before stopping the trigger generation.
+ :param abort_time: wait this time before stopping the trigger generation
:type abort_time: float
"""
From 89a76f80d789341a5bf21d1e000d26b081f5a55b Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Tue, 9 Oct 2018 08:53:38 +0200
Subject: [PATCH 193/652] Change message error
---
src/sardana/pool/poolacquisition.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index e1bf077253..f5428893b7 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -404,7 +404,7 @@ def start_action(self, *args, **kwargs):
if integ_time is not None and mon_count is not None:
raise RuntimeError('The acquisition must have only one role: '
- 'timer or count')
+ 'timer or monitor')
if integ_time is not None:
master_key = 'timer'
master_value = integ_time
From 28045a9fcb30836fdaab498776e2a5fd88426149 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Tue, 9 Oct 2018 08:56:57 +0200
Subject: [PATCH 194/652] Fix docstrings
---
src/sardana/pool/poolacquisition.py | 35 ++---------------------------
1 file changed, 2 insertions(+), 33 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index f5428893b7..963a444fec 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -338,7 +338,7 @@ def _get_ctrls(self):
def _get_timer(self):
"""
- Method to get the controller dict for the acquisition type
+ Method to get the master timer for the acquisition type
:return:
:rtype dict
"""
@@ -346,7 +346,7 @@ def _get_timer(self):
def _get_monitor(self):
"""
- Method to get the controller dict for the acquisition type
+ Method to get the master monitor for the acquisition type
:return:
:rtype dict
"""
@@ -593,27 +593,12 @@ def __init__(self, main_element, name="AcquisitionHardware"):
PoolAcquisitionBase.__init__(self, main_element, name)
def _get_ctrls(self):
- """
- Method to get the controller dict for the acquisition type
- :return:
- :rtype dict
- """
return self.main_element.configuration.ctrl_hw_sync
def _get_timer(self):
- """
- Method to get the controller dict for the acquisition type
- :return:
- :rtype dict
- """
return self.main_element.configuration.hw_sync_timer
def _get_monitor(self):
- """
- Method to get the controller dict for the acquisition type
- :return:
- :rtype dict
- """
return self.main_element.configuration.hw_sync_monitor
@DebugIt()
@@ -687,28 +672,12 @@ def __init__(self, main_element, name="AcquisitionSoftware", slaves=None):
self._slaves = slaves
def _get_ctrls(self):
- """
- Method to get the controller dict for the acquisition type
- :return:
- :rtype dict
- """
return self.main_element.configuration.ctrl_sw_sync
def _get_timer(self):
- """
- Method to get the controller dict for the acquisition type
- :return:
- :rtype dict
- """
-
return self.main_element.configuration.sw_sync_timer
def _get_monitor(self):
- """
- Method to get the controller dict for the acquisition type
- :return:
- :rtype dict
- """
return self.main_element.configuration.sw_sync_monitor
@DebugIt()
From 722cf7754e0ecfb0a19df1577b5f75cf182f8893 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Tue, 9 Oct 2018 09:04:11 +0200
Subject: [PATCH 195/652] Adapt Pool0DAquisition to use
MeasurementConfiguration API
---
src/sardana/pool/poolacquisition.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 963a444fec..2d815ba1c1 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -868,9 +868,9 @@ def start_action(self, *args, **kwargs):
items = kwargs.get("items")
if items is None:
items = self.get_elements()
- cfg = kwargs['config']
+ cfg = self.main_element.configuration
- pool_ctrls_dict = dict(cfg.controllers)
+ pool_ctrls_dict = dict(cfg.ctrl_0d_sync)
pool_ctrls_dict.pop('__tango__', None)
pool_ctrls = []
for ctrl in pool_ctrls_dict:
From e2dc6016db156d923b614d821a445210222fc136 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Tue, 9 Oct 2018 09:07:58 +0200
Subject: [PATCH 196/652] Remove TODO
---
src/sardana/pool/poolacquisition.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 2d815ba1c1..26ff8292b6 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -193,7 +193,6 @@ def run(self, *args, **kwargs):
self.set_sw_config(sw_acq_kwargs)
if len(config.ctrl_0d_sync):
zerod_acq_kwargs = dict(kwargs)
- # TODO: Ask why
self.set_0d_config(zerod_acq_kwargs)
synch_kwargs = dict(kwargs)
self._synch.run(*args, **synch_kwargs)
From 4de2b6e8ee4a70a4320e65acc1393de1374662b9 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Tue, 9 Oct 2018 10:03:03 +0200
Subject: [PATCH 197/652] Use nr_of_starts instead of repetitions
---
src/sardana/pool/poolacquisition.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 26ff8292b6..bb4f284778 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -139,7 +139,7 @@ def event_received(self, *args, **kwargs):
self.debug('Stopping ZeroD acquisition.')
self._0d_acq.stop_action()
- def prepare(self, config, repetitions):
+ def prepare(self, config, nr_of_starts):
"""Prepare measurement."""
timers = config.sw_sync_timers_enabled + \
config.sw_start_timers_enabled + \
@@ -149,7 +149,7 @@ def prepare(self, config, repetitions):
axis = timer.axis
timer_ctrl = timer.controller
ctrl = timer_ctrl.ctrl
- ctrl.PrepareOne(axis, repetitions)
+ ctrl.PrepareOne(axis, nr_of_starts)
def is_running(self):
return self._0d_acq.is_running() or\
From a7ff748f46c558bc93dedc894f928aa312b6512a Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Tue, 9 Oct 2018 10:06:07 +0200
Subject: [PATCH 198/652] Adapt test to use MeasurementConfiguration API
---
.../poolcontrollers/test/test_DummyTriggerGateController.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/sardana/pool/poolcontrollers/test/test_DummyTriggerGateController.py b/src/sardana/pool/poolcontrollers/test/test_DummyTriggerGateController.py
index 9119a6377d..f44063b0c7 100644
--- a/src/sardana/pool/poolcontrollers/test/test_DummyTriggerGateController.py
+++ b/src/sardana/pool/poolcontrollers/test/test_DummyTriggerGateController.py
@@ -46,6 +46,9 @@ def setUp(self):
self.cfg = createPoolSynchronizationConfiguration((dummy_tg_ctrl,),
((self.dummy_tg,),))
+ # TODO: The TriggerGate should have a configuration
+ self.dummy_tg.configuration = self.cfg
+
# marrying the element with the action
self.tg_action = PoolSynchronization(self.dummy_tg)
self.tg_action.add_element(self.dummy_tg)
From 39bff386ecbcd6fecac140d242f019c954728e62 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Tue, 9 Oct 2018 15:37:48 +0200
Subject: [PATCH 199/652] Implement "end" event
---
src/sardana/util/funcgenerator.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/sardana/util/funcgenerator.py b/src/sardana/util/funcgenerator.py
index f30fddd237..dadd72c426 100644
--- a/src/sardana/util/funcgenerator.py
+++ b/src/sardana/util/funcgenerator.py
@@ -279,6 +279,11 @@ def wait_passive(self):
def fire_passive(self):
self.fire_event(EventType("passive"), self._id)
self.set_passive_events(self.passive_events[1:])
+ if len(self.passive_events) == 0:
+ self.fire_end()
+
+ def fire_end(self):
+ self.fire_event("end", self._id)
def set_configuration(self, configuration):
# make a copy since we may inject the initial time
From 0f97a83846b8fa987e599b7e39d8e81f6448b5b1 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Tue, 9 Oct 2018 15:43:30 +0200
Subject: [PATCH 200/652] SEP: better explain backwards comp. for direct start
of MG
---
doc/source/sep/SEP18.md | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index 165debefd4..9899a7f37f 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -43,12 +43,15 @@ Design
1. Use of the measurement group will change:
* From now on, in order to start the measurement group, it is mandatory
to prepare it.
- * The measurement group will be armed for as many starts as specified in
- the preparation and the preparation will expire whenever all starts gets
- called or in case of stop/abort.
- * Setting the integration time via the attribute will be deprecated in
- favor of using prepare command with the synchronization description, but
- backwards compatibility will be maintained.
+ * The measurement group is prepared for a number of starts. The
+ preparation will expire whenever all starts gets called or in case of
+ stop/abort. Afterwards measurement group requires another preparation.
+ **IMPORTANT**: Whenever we drop backwards compatibility explained in the
+ following point starting of the measurement group without prior
+ preparation will be considered as wrong usage and will cause exception.
+ * Direct start of the measurement group (after prior configuration of
+ the integration time or synchronization) will be supported as backwards
+ compatibility and the corresponding warning will be logged.
2. Allow different types of preparation of channels - this still depends on
the option selected in the implementation of controllers. The following
assumes option 1.
From c2e23309a0230e477e1370f6f7ca618d2d691ed7 Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Wed, 10 Oct 2018 09:47:14 +0200
Subject: [PATCH 201/652] Rename funtion to _get_tango_devstate_match
Better use *private* scope for this internal helper.
---
src/sardana/taurus/core/tango/sardana/motion.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/sardana/taurus/core/tango/sardana/motion.py b/src/sardana/taurus/core/tango/sardana/motion.py
index fdf360963c..19e7973643 100644
--- a/src/sardana/taurus/core/tango/sardana/motion.py
+++ b/src/sardana/taurus/core/tango/sardana/motion.py
@@ -34,7 +34,7 @@
from taurus.core.util.containers import CaselessDict
-def get_pytango_devstate_match(states):
+def _get_tango_devstate_match(states):
"""
Retrieve PyTango.DevState match
:param states:
@@ -201,7 +201,7 @@ def move(self, new_pos, timeout=None):
res = moveable.move(pos, timeout=timeout)
states.append(res[0])
positions.extend(res[1])
- state = get_pytango_devstate_match(states)
+ state = _get_tango_devstate_match(states)
self.__total_motion_time = time.time() - start_time
return state, positions
@@ -398,7 +398,7 @@ def move(self, new_pos, timeout=None):
for moveable, id in zip(self.moveable_list, ids):
moveable.waitMove(id=id, timeout=timeout)
states, positions = self.readState(), self.readPosition()
- state = get_pytango_devstate_match(states)
+ state = _get_tango_devstate_match(states)
ret = state, positions
self.__total_motion_time = time.time()
return ret
From f8d96f8a7f79184551786d45f23681ad168b4086 Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Wed, 10 Oct 2018 10:01:58 +0200
Subject: [PATCH 202/652] Fix calculation of total motion time in Motion.move
---
src/sardana/taurus/core/tango/sardana/motion.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/sardana/taurus/core/tango/sardana/motion.py b/src/sardana/taurus/core/tango/sardana/motion.py
index 19e7973643..099c83fcdb 100644
--- a/src/sardana/taurus/core/tango/sardana/motion.py
+++ b/src/sardana/taurus/core/tango/sardana/motion.py
@@ -384,6 +384,7 @@ def waitMove(self, timeout=None, id=None):
moveable.waitMove(timeout=timeout, id=id[i])
def move(self, new_pos, timeout=None):
+ start_time = time.time()
if len(self.moveable_list) == 1:
moveable = self.moveable_list[0]
ret = moveable.move(new_pos, timeout=timeout)
@@ -400,7 +401,7 @@ def move(self, new_pos, timeout=None):
states, positions = self.readState(), self.readPosition()
state = _get_tango_devstate_match(states)
ret = state, positions
- self.__total_motion_time = time.time()
+ self.__total_motion_time = time.time() - start_time
return ret
def iterMove(self, new_pos, timeout=None):
From 9a396fb468508a2c706c0050ff8cba6de71ededb Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Wed, 10 Oct 2018 11:04:58 +0200
Subject: [PATCH 203/652] Remove unused property
---
src/sardana/pool/poolmeasurementgroup.py | 5 -----
1 file changed, 5 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 71c49e7174..389c6c0fa9 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -176,11 +176,6 @@ def wrapper(self, *args, **kwargs):
def __getitem__(self, item):
return self._config.__getitem__(item)
- @property
- @__check_config
- def controllers(self):
- return self._config['controllers']
-
@property
@__check_config
def configuration(self):
From 23d7c70d6bb7258b7e798fdca15f5475ba50f6d2 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 10 Oct 2018 11:28:21 +0200
Subject: [PATCH 204/652] Fix fire end event
---
src/sardana/util/funcgenerator.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sardana/util/funcgenerator.py b/src/sardana/util/funcgenerator.py
index dadd72c426..f72bac46a7 100644
--- a/src/sardana/util/funcgenerator.py
+++ b/src/sardana/util/funcgenerator.py
@@ -283,7 +283,7 @@ def fire_passive(self):
self.fire_end()
def fire_end(self):
- self.fire_event("end", self._id)
+ self.fire_event(EventType("end"), self._id)
def set_configuration(self, configuration):
# make a copy since we may inject the initial time
From 8c41da7de846187c80b5be37956bfee8d8aab713 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 10 Oct 2018 11:30:19 +0200
Subject: [PATCH 205/652] funcgenerator tests: add asserts for start and end
events
---
src/sardana/util/test/test_funcgenerator.py | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/sardana/util/test/test_funcgenerator.py b/src/sardana/util/test/test_funcgenerator.py
index 3feb15dca2..b117fec21d 100644
--- a/src/sardana/util/test/test_funcgenerator.py
+++ b/src/sardana/util/test/test_funcgenerator.py
@@ -73,8 +73,10 @@ def __init__(self):
self.init()
def init(self):
+ self.start = False
self.active_event_ids = list()
self.passive_event_ids = list()
+ self.end = False
def event_received(self, *args, **kwargs):
_, type_, value = args
@@ -83,6 +85,10 @@ def event_received(self, *args, **kwargs):
self.active_event_ids.append(value)
elif name == "passive":
self.passive_event_ids.append(value)
+ elif name == "start":
+ self.start = True
+ elif name == "end":
+ self.end = True
else:
ValueError("wrong event type")
@@ -122,9 +128,11 @@ def test_run_time(self):
self.event.wait(100)
active_event_ids = self.listener.active_event_ids
active_event_ids_ok = range(0, 10)
- msg = "Received active event ids: %s, expected: %s" % (active_event_ids,
- active_event_ids_ok)
+ msg = "Received active event ids: %s, expected: %s" % (
+ active_event_ids, active_event_ids_ok)
self.assertListEqual(active_event_ids, active_event_ids_ok, msg)
+ self.assertTrue(self.listener.start, "Start event is missing")
+ self.assertTrue(self.listener.end, "End event is missing")
def test_stop_time(self):
self.func_generator.initial_domain = SynchDomain.Time
From d56d514901222de240d04fb295c9020c9be6b06a Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Wed, 10 Oct 2018 11:37:22 +0200
Subject: [PATCH 206/652] Adapt load_configuration method
---
src/sardana/pool/poolmeasurementgroup.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 389c6c0fa9..e2486052dd 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -739,13 +739,11 @@ def load_configuration(self, force=False):
"""Loads the current configuration to all involved controllers"""
# g_timer, g_monitor = cfg['timer'], cfg['monitor']
- for ctrl, ctrl_data in self._config.controllers.items():
+ for ctrl in self._config.enabled_ctrls:
if isinstance(ctrl, str): # skip external channels
continue
if not ctrl.is_online():
continue
- if ctrl not in self._config.enabled_ctrls:
- continue
ctrl.set_ctrl_par('acquisition_mode', self.acquisition_mode)
# @TODO: fix optimization and enable it again
@@ -753,6 +751,8 @@ def load_configuration(self, force=False):
continue
ctrl.operator = self
if ctrl.is_timerable():
+ # TODO: Implement API to extract controller data
+ ctrl_data = self._config.configuration['controllers'][ctrl]
# if ctrl == g_timer.controller:
# ctrl.set_ctrl_par('timer', g_timer.axis)
# if ctrl == g_monitor.controller:
From 1ef22b0f77bc0a21def5e8e9b2cedd80156ebb4e Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Wed, 10 Oct 2018 12:07:12 +0200
Subject: [PATCH 207/652] Fix bug on get_timer method
---
src/sardana/pool/poolmeasurementgroup.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index e2486052dd..49f764605c 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -767,7 +767,8 @@ def load_configuration(self, force=False):
self._config_dirty = False
def get_timer(self):
- return self._config.timer
+ # TODO: Adapt to the new future MeasurementConfiguration API
+ return self._config.configuration['timer']
timer = property(get_timer)
From 68d0c1413d504dac4ad27ff4bbb8cad7cab37965 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 10 Oct 2018 12:50:08 +0200
Subject: [PATCH 208/652] SEP: warn about problems with step scan and hooks
---
doc/source/sep/SEP18.md | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index 9899a7f37f..bc36582711 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -48,9 +48,11 @@ Design
stop/abort. Afterwards measurement group requires another preparation.
**IMPORTANT**: Whenever we drop backwards compatibility explained in the
following point starting of the measurement group without prior
- preparation will be considered as wrong usage and will cause exception.
- * Direct start of the measurement group (after prior configuration of
- the integration time or synchronization) will be supported as backwards
+ preparation will be considered as wrong usage and will cause exception.
+ This will break step scans with attached hooks which measure with the
+ same measurement group as used by the scan.
+ * Direct start of the measurement group (after prior configuration of
+ the integration time or synchronization) will be supported as backwards
compatibility and the corresponding warning will be logged.
2. Allow different types of preparation of channels - this still depends on
the option selected in the implementation of controllers. The following
From c3bdc5530c55a7cbe5eb5d9d20d8803926095d4e Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 10 Oct 2018 13:23:22 +0200
Subject: [PATCH 209/652] SEP: small corrections
---
doc/source/sep/SEP18.md | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index bc36582711..47e4e5bf08 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -57,9 +57,8 @@ Design
2. Allow different types of preparation of channels - this still depends on
the option selected in the implementation of controllers. The following
assumes option 1.
- * Per measurement preparation with number of starts = n e.g.
- Prepare(One|All) or a controller parameter
- * Per acquisition preparation with repetitions = n e.g. Load(One|All)
+ * Per measurement preparation with number of starts = n - `PrepareOne`
+ * Per acquisition preparation with repetitions = n - `Load(One|All)`
3. Extend AcqSynch with two new options:
* SoftwareStart (which means internal start)
* HardwareStart (which means external start)
@@ -95,9 +94,9 @@ Implementation
### Measurement Group
Measurement group is extended by the *prepare* command (with no arguments)
-*number of starts* attribute. The use of the attribute is optional and
-it indicates how many times measurement group will be started, with the
-*start* command, to measure according to the synchronization description or
+and *number of starts* attribute. The use of the attribute is optional and
+it indicates how many times measurement group will be started (with the
+*start* command) to measure according to the synchronization description or
integration time. When it is not used number of starts of 1 will be assumed.
1. Measurement group - Tango device class
@@ -111,7 +110,7 @@ the start command (without calling prepare command in-between) will be
solved in the following way: start command will internally call the prepare.
4. Measurement group - Taurus extension
* Add `prepare()` method which simply maps to `Prepare` Tango command
- * Add `count_raw` method according to the following pseudo code:
+ * Add `count_raw()` method according to the following pseudo code:
* `start()`
* `waitFinish()`
* Implement `count(integration_time)` method according to the following
@@ -140,7 +139,7 @@ event and end is emitted after the last `passive` event.
software synchronizer `start` event.
* `PoolAcquisitionSoftware` will stop channels on software synchronizer
`end` event. TODO: decide if we wait for the acquisition in progress
-until it finises or we stop immediatelly (finish hook could be used if
+until it finishes or we stop immediately (finish hook could be used if
we choose to wait).
### Controllers
From 96c365a6b51d1184865a04d84fbdf11266073fac Mon Sep 17 00:00:00 2001
From: reszelaz
Date: Thu, 11 Oct 2018 16:46:30 +0200
Subject: [PATCH 210/652] SEP: DRAFT -> CANDIDATE
---
doc/source/sep/SEP18.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/source/sep/SEP18.md b/doc/source/sep/SEP18.md
index 47e4e5bf08..cb9f79a4d2 100644
--- a/doc/source/sep/SEP18.md
+++ b/doc/source/sep/SEP18.md
@@ -1,6 +1,6 @@
Title: Extend acquisition and synchronization concepts for SEP2 needs.
SEP: 18
- State: DRAFT
+ State: CANDIDATE
Reason:
New acquisition and synchronization concepts are necessary in order to
properly integrate 1D and 2D experimental channels in Sardana (SEP2).
From 4e24e6081e730c44446a0979292b0ddd6eb69d2e Mon Sep 17 00:00:00 2001
From: zreszela
Date: Thu, 11 Oct 2018 16:58:57 +0200
Subject: [PATCH 211/652] Refactors acqusition base action
---
src/sardana/pool/poolacquisition.py | 248 +++++++++++++++-------------
1 file changed, 132 insertions(+), 116 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index bb4f284778..431f9c8e92 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -61,6 +61,14 @@
AS.Invalid: State.Invalid,
}
+MeasurementActions = Enumeration("MeasurementActions", (
+ "AcquisitionHardware",
+ "AcquisitionSoftware",
+ "AcquisitionSoftwareStart",
+ "Acquisition0D",
+ "Synchronization")
+)
+
def is_value_error(value):
if isinstance(value, SardanaValue) and value.error:
@@ -177,7 +185,8 @@ def run(self, *args, **kwargs):
synchronization = kwargs["synchronization"]
integ_time = synchronization.integration_time
repetitions = synchronization.repetitions
-
+ latency_time = 0
+ ctrls_channels_acq_hw = config.get_ctrls_channels()
# starting continuous acquisition only if there are any controllers
if len(config.ctrl_hw_sync):
cont_acq_kwargs = dict(kwargs)
@@ -316,7 +325,7 @@ class PoolAcquisitionBase(PoolAction):
def __init__(self, main_element, name):
PoolAction.__init__(self, main_element, name)
- self._channels = None
+ self._channels = []
# TODO: for the moment we can not clear value buffers at the end of
# the acquisition. This is because of the pseudo counters that are
# based on channels synchronized by hardware and software.
@@ -367,7 +376,9 @@ def in_acquisition(self, states):
return True
@DebugIt()
- def start_action(self, *args, **kwargs):
+ def start_action(self, ctrls_channels, ctrls_loadables, value,
+ repetitions=1, latency=0, master=None, *args,
+ **kwargs):
"""Prepares everything for acquisition and starts it.
:param acq_sleep_time: sleep time between state queries
:param nb_states_per_value: how many state queries between readouts
@@ -387,123 +398,131 @@ def start_action(self, *args, **kwargs):
self._nb_states_per_value = kwargs.pop("nb_states_per_value",
pool.acq_loop_states_per_value)
- self._integ_time = integ_time = kwargs.get("integ_time")
- self._mon_count = mon_count = kwargs.get("monitor_count")
- self._repetitions = repetitions = kwargs.get("repetitions")
- if integ_time is None and mon_count is None:
- raise Exception("must give integration time or monitor counts")
- if integ_time is not None and mon_count is not None:
- msg = ("must give either integration time or monitor counts "
- "(not both)")
- raise Exception(msg)
-
- _ = kwargs.get("items", self.get_elements())
- # determine which is the controller which holds the master channel
- master = None
-
- if integ_time is not None and mon_count is not None:
- raise RuntimeError('The acquisition must have only one role: '
- 'timer or monitor')
- if integ_time is not None:
- master_key = 'timer'
- master_value = integ_time
- master = self._get_timer()
- if mon_count is not None:
- master_key = 'monitor'
- master_value = -mon_count
- master = self._get_monitor()
- if master is None:
- self.main_element.set_state(State.Fault, propagate=2)
- msg = "master {0} ({1})is unknown (probably disabled)".format(
- master_key, master)
- raise RuntimeError(msg)
- master_ctrl = master.controller
- pool_ctrls_dict = dict(self._get_ctrls())
- pool_ctrls_dict.pop('__tango__', None)
+ # self._integ_time = integ_time = kwargs.get("integ_time")
+ # self._mon_count = mon_count = kwargs.get("monitor_count")
+ # self._repetitions = repetitions = kwargs.get("repetitions")
+ # # if integ_time is None and mon_count is None:
+ # raise Exception("must give integration time or monitor counts")
+ # if integ_time is not None and mon_count is not None:
+ # msg = ("must give either integration time or monitor counts "
+ # "(not both)")
+ # raise Exception(msg)
+ #
+ # _ = kwargs.get("items", self.get_elements())
+ # # determine which is the controller which holds the master channel
+ # master = None
+ #
+ # if integ_time is not None and mon_count is not None:
+ # raise RuntimeError('The acquisition must have only one role: '
+ # 'timer or monitor')
+ # if integ_time is not None:
+ # master_key = 'timer'
+ # master_value = integ_time
+ # master = self._get_timer()
+ # if mon_count is not None:
+ # master_key = 'monitor'
+ # master_value = -mon_count
+ # master = self._get_monitor()
+ # if master is None:
+ # self.main_element.set_state(State.Fault, propagate=2)
+ # msg = "master {0} ({1})is unknown (probably disabled)".format(
+ # master_key, master)
+ # raise RuntimeError(msg)
# controllers to be started (only enabled) in the right order
- pool_ctrls = []
+ pool_ctrls = ctrls_channels.keys()
+
+ # make sure the controller which has the master channel is the last to
+ # be called
+ if master is not None:
+ master_ctrl = master.controller
+ pool_ctrls.remove(master_ctrl)
+ pool_ctrls.append(master_ctrl)
+
+ # pool_ctrls_dict = dict(self._get_ctrls())
+ # pool_ctrls_dict.pop('__tango__', None)
+
# controllers that will be read at the end of the action
- self._pool_ctrl_dict_loop = _pool_ctrl_dict_loop = {}
+ self._pool_ctrl_dict_loop = ctrls_channels
# channels that are acquired (only enabled)
- self._channels = channels = {}
-
- # select only suitable e.g. enabled, timerable controllers & channels
- for ctrl, pool_ctrl_data in pool_ctrls_dict.items():
- # skip not timerable controllers e.g. 0D
- if not ctrl.is_timerable():
- continue
- ctrl_enabled = False
- elements = pool_ctrl_data['channels']
- for element, element_info in elements.items():
- # skip disabled elements
- if not element_info['enabled']:
- continue
- # Add only the enabled channels
- channel = Channel(element, info=element_info)
- channels[element] = channel
- ctrl_enabled = True
- # check if the ctrl has enabled channels
- if ctrl_enabled:
- # enabled controller can no be offline
- if not ctrl.is_online():
- self.main_element.set_state(State.Fault, propagate=2)
- msg = "controller {0} is offline".format(ctrl.name)
- raise RuntimeError(msg)
- pool_ctrls.append(ctrl)
- # only CT will be read in the loop, 1D and 2D not
- if ElementType.CTExpChannel in ctrl.get_ctrl_types():
- _pool_ctrl_dict_loop[ctrl] = pool_ctrl_data
+ self._channels = []
+
+ # # select only suitable e.g. enabled, timerable controllers & channels
+ # for ctrl, pool_ctrl_data in pool_ctrls_dict.items():
+ # # skip not timerable controllers e.g. 0D
+ # if not ctrl.is_timerable():
+ # continue
+ # ctrl_enabled = False
+ # elements = pool_ctrl_data['channels']
+ # for element, element_info in elements.items():
+ # # skip disabled elements
+ # if not element_info['enabled']:
+ # continue
+ # # Add only the enabled channels
+ # channel = Channel(element, info=element_info)
+ # channels[element] = channel
+ # ctrl_enabled = True
+ # # check if the ctrl has enabled channels
+ # if ctrl_enabled:
+ # # enabled controller can no be offline
+ # if not ctrl.is_online():
+ # self.main_element.set_state(State.Fault, propagate=2)
+ # msg = "controller {0} is offline".format(ctrl.name)
+ # raise RuntimeError(msg)
+ # pool_ctrls.append(ctrl)
+ # # only CT will be read in the loop, 1D and 2D not
+ # if ElementType.CTExpChannel in ctrl.get_ctrl_types():
+ # _pool_ctrl_dict_loop[ctrl] = pool_ctrl_data
# timer/monitor channels can not be disabled
- for pool_ctrl in pool_ctrls:
- ctrl = pool_ctrl.ctrl
- pool_ctrl_data = pool_ctrls_dict[pool_ctrl]
- timer_monitor = pool_ctrl_data[master_key]
- if timer_monitor not in channels:
- self.main_element.set_state(State.Fault, propagate=2)
- msg = "timer/monitor ({0}) of {1} controller is "\
- "disabled)".format(timer_monitor.name, pool_ctrl.name)
- raise RuntimeError(msg)
+ # for pool_ctrl in pool_ctrls:
+ # ctrl = pool_ctrl.ctrl
+ # pool_ctrl_data = pool_ctrls_dict[pool_ctrl]
+ # timer_monitor = pool_ctrl_data[master_key]
+ # if timer_monitor not in channels:
+ # self.main_element.set_state(State.Fault, propagate=2)
+ # msg = "timer/monitor ({0}) of {1} controller is "\
+ # "disabled)".format(timer_monitor.name, pool_ctrl.name)
+ # raise RuntimeError(msg)
- # make sure the controller which has the master channel is the last to
- # be called
- pool_ctrls.remove(master_ctrl)
- pool_ctrls.append(master_ctrl)
- def load(ctrl, master_axis, value, repetitions, latency=0):
+
+ def load(channel, value, repetitions, latency=0):
+ axis = channel.axis
+ pool_ctrl = channel.controller
+ ctrl = pool_ctrl.ctrl
ctrl.PreLoadAll()
try:
- res = ctrl.PreLoadOne(master_axis, value, repetitions,
+ res = ctrl.PreLoadOne(axis, value, repetitions,
latency)
except TypeError:
try:
- res = ctrl.PreLoadOne(master_axis, value, repetitions)
+ res = ctrl.PreLoadOne(axis, value, repetitions)
msg = ("PreLoadOne(axis, value, repetitions) is "
"deprecated since version Jan19. Use PreLoadOne("
"axis, value, repetitions, latency_time) instead.")
self.warning(msg)
except TypeError:
- res = ctrl.PreLoadOne(master_axis, value)
+ res = ctrl.PreLoadOne(axis, value)
msg = ("PreLoadOne(axis, value) is deprecated since "
"version 2.3.0. Use PreLoadOne(axis, value, "
"repetitions, latency_time) instead.")
self.warning(msg)
if not res:
msg = ("%s.PreLoadOne(%d) returned False" %
- (pool_ctrl.name, master_axis))
+ (pool_ctrl.name, axis))
raise Exception(msg)
try:
- ctrl.LoadOne(master_axis, value, repetitions, latency)
+ ctrl.LoadOne(axis, value, repetitions, latency)
except TypeError:
try:
- ctrl.LoadOne(master_axis, value, repetitions)
+ ctrl.LoadOne(axis, value, repetitions)
msg = ("LoadOne(axis, value, repetitions) is deprecated "
"since version Jan18. Use LoadOne(axis, value, "
"repetitions, latency_time) instead.")
self.warning(msg)
except TypeError:
- ctrl.LoadOne(master_axis, value)
+ ctrl.LoadOne(axis, value)
msg = ("LoadOne(axis, value) is deprecated since "
"version 2.3.0. Use LoadOne(axis, value, "
"repetitions) instead.")
@@ -512,64 +531,61 @@ def load(ctrl, master_axis, value, repetitions, latency=0):
with ActionContext(self):
# PreLoadAll, PreLoadOne, LoadOne and LoadAll
- for pool_ctrl in pool_ctrls:
- try:
- ctrl = pool_ctrl.ctrl
- pool_ctrl_data = pool_ctrls_dict[pool_ctrl]
- master = pool_ctrl_data[master_key]
- load(ctrl, master.axis, master_value, repetitions)
- except Exception, e:
- self.debug(e, exc_info=True)
- master.set_state(State.Fault, propagate=2)
- msg = ("Load sequence of %s failed" % pool_ctrl.name)
- raise Exception(msg)
+ loadables = ctrls_loadables.values()
+ for channel in loadables:
+ load(channel, value, repetitions, latency)
+
+ # TODO: remove when the action allows to use tango attributes
+ try:
+ pool_ctrls.pop('__tango__')
+ except Exception:
+ pass
# PreStartAll on all enabled controllers
for pool_ctrl in pool_ctrls:
pool_ctrl.ctrl.PreStartAll()
+ channels_started = []
# PreStartOne & StartOne on all enabled elements
for pool_ctrl in pool_ctrls:
+ channels = ctrls_channels[pool_ctrl]
ctrl = pool_ctrl.ctrl
- pool_ctrl_data = pool_ctrls_dict[pool_ctrl]
- elements = pool_ctrl_data['channels'].keys()
- timer_monitor = pool_ctrl_data[master_key]
+
# make sure that the timer/monitor is started as the last one
- elements.remove(timer_monitor)
- elements.append(timer_monitor)
- for element in elements:
- try:
- channel = channels[element]
- except KeyError:
- continue
- axis = element.axis
- ret = ctrl.PreStartOne(axis, master_value)
+ loadable = ctrls_loadables[pool_ctrl]
+ channels.remove(loadable)
+ channels.append(loadable)
+ for channel in channels:
+ axis = channel.axis
+ ret = ctrl.PreStartOne(axis, value)
if not ret:
msg = ("%s.PreStartOne(%d) returns False" %
(pool_ctrl.name, axis))
raise Exception(msg)
try:
- ctrl.StartOne(axis, master_value)
+ ctrl.StartOne(axis, value)
except Exception, e:
self.debug(e, exc_info=True)
- element.set_state(State.Fault, propagate=2)
+ channel.set_state(State.Fault, propagate=2)
msg = ("%s.StartOne(%d) failed" %
(pool_ctrl.name, axis))
raise Exception(msg)
+ self._channels.append(channel)
+
# set the state of all elements to and inform their listeners
- for channel in channels:
+ for channel in self._channels:
channel.set_state(State.Moving, propagate=2)
# StartAll on all enabled controllers
for pool_ctrl in pool_ctrls:
+ channels = ctrls_channels[pool_ctrl]
try:
pool_ctrl.ctrl.StartAll()
except Exception, e:
self.debug(e, exc_info=True)
- elements = pool_ctrl_data['channels'].keys()
- for element in elements:
- element.set_state(State.Fault, propagate=2)
+ for channel in channels:
+ channel.set_state(State.Fault, propagate=2)
msg = ("%s.StartAll() failed" % pool_ctrl.name)
raise Exception(msg)
From 7b093274e14ce02694379289b55499630627de60 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Thu, 11 Oct 2018 16:59:29 +0200
Subject: [PATCH 212/652] Adapt acquisition test case to new API
---
src/sardana/pool/test/test_acquisition.py | 37 ++++++++---------------
1 file changed, 13 insertions(+), 24 deletions(-)
diff --git a/src/sardana/pool/test/test_acquisition.py b/src/sardana/pool/test/test_acquisition.py
index dd18c2c51e..eab0329cba 100644
--- a/src/sardana/pool/test/test_acquisition.py
+++ b/src/sardana/pool/test/test_acquisition.py
@@ -281,8 +281,8 @@ def event_received(self, *args, **kwargs):
return
else:
self.sw_acq_busy.set()
- args = dict(self.sw_acq_args)
- kwargs = dict(self.sw_acq_kwargs)
+ args = self.sw_acq_args
+ kwargs = self.sw_acq_kwargs
kwargs['idx'] = value
get_thread_pool().add(self.sw_acq.run,
None,
@@ -303,6 +303,11 @@ def continuous_acquisition(self, offset, active_interval, passive_interval,
ct_ctrl_2 = ct_2_1.get_controller()
self.channel_names.append('_test_ct_1_1')
self.channel_names.append('_test_ct_2_1')
+
+ acq_hw_ctrl_channels = {ct_ctrl_1: [ct_1_1]}
+ acq_hw_ctrl_loadable = {ct_ctrl_1: ct_1_1}
+ acq_sw_ctrl_channels = {ct_ctrl_2: [ct_2_1]}
+ acq_sw_ctrl_loadable = {ct_ctrl_2: ct_2_1}
# crating configuration for TGGeneration
tg_cfg = createPoolSynchronizationConfiguration((tg_ctrl_2,),
((tg_2_1,),))
@@ -310,15 +315,7 @@ def continuous_acquisition(self, offset, active_interval, passive_interval,
self.createPoolSynchronization([tg_2_1], tg_config=tg_cfg)
# add_listeners
self.addListeners([ct_1_1, ct_2_1])
- # creating acquisition configurations
- self.hw_acq_cfg = createCTAcquisitionConfiguration((ct_ctrl_1,),
- ((ct_1_1,),))
- self.sw_acq_cfg = createCTAcquisitionConfiguration((ct_ctrl_2,),
- ((ct_2_1,),))
# creating acquisition actions
- # TODO: The CTExpChannel should have a configuration
- ct_1_1.configuration = self.hw_acq_cfg
- ct_2_1.configuration = self.sw_acq_cfg
self.hw_acq = PoolAcquisitionHardware(ct_1_1)
self.sw_acq = PoolAcquisitionSoftware(ct_2_1)
# Since we deposit the software acquisition action on the PoolThread's
@@ -338,21 +335,13 @@ def continuous_acquisition(self, offset, active_interval, passive_interval,
# get the current number of jobs
jobs_before = get_thread_pool().qsize
- self.sw_acq_args = ()
- self.sw_acq_kwargs = {
- 'synch': True,
- 'integ_time': integ_time,
- 'repetitions': 1,
- 'config': self.sw_acq_cfg
- }
+ self.sw_acq_args = (acq_sw_ctrl_channels, acq_sw_ctrl_loadable,
+ integ_time)
+ self.sw_acq_kwargs = {"master": ct_2_1}
ct_ctrl_1.set_ctrl_par('synchronization', AcqSynch.HardwareTrigger)
- hw_acq_args = ()
- hw_acq_kwargs = {
- 'integ_time': integ_time,
- 'repetitions': repetitions,
- 'config': self.hw_acq_cfg,
- }
- self.hw_acq.run(*hw_acq_args, **hw_acq_kwargs)
+ hw_acq_args = (acq_hw_ctrl_channels, acq_hw_ctrl_loadable,
+ integ_time, repetitions)
+ self.hw_acq.run(*hw_acq_args)
tg_args = ()
total_interval = active_interval + passive_interval
synchronization = [{SynchParam.Delay: {SynchDomain.Time: offset},
From 00cdf04b1b2adec1211759ee1724c7b57a3d0108 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Thu, 11 Oct 2018 17:00:08 +0200
Subject: [PATCH 213/652] Add TODO for measurement configuration
---
src/sardana/pool/poolmeasurementgroup.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 49f764605c..176fb02047 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -125,6 +125,10 @@ def _to_fqdn(name, logger=None):
class MeasurementConfiguration(object):
+ """
+ .. todo: Reject configuration with errors:
+ * Controllers with timer and monitor disable
+ """
DFT_DESC = 'General purpose measurement group'
From e1484daaf9b40b77e1c146f98c4b9685e8b00460 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 15 Oct 2018 10:10:52 +0200
Subject: [PATCH 214/652] Remove unused code
---
src/sardana/pool/poolacquisition.py | 74 -----------------------------
1 file changed, 74 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 431f9c8e92..844eaa47a2 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -398,37 +398,6 @@ def start_action(self, ctrls_channels, ctrls_loadables, value,
self._nb_states_per_value = kwargs.pop("nb_states_per_value",
pool.acq_loop_states_per_value)
- # self._integ_time = integ_time = kwargs.get("integ_time")
- # self._mon_count = mon_count = kwargs.get("monitor_count")
- # self._repetitions = repetitions = kwargs.get("repetitions")
- # # if integ_time is None and mon_count is None:
- # raise Exception("must give integration time or monitor counts")
- # if integ_time is not None and mon_count is not None:
- # msg = ("must give either integration time or monitor counts "
- # "(not both)")
- # raise Exception(msg)
- #
- # _ = kwargs.get("items", self.get_elements())
- # # determine which is the controller which holds the master channel
- # master = None
- #
- # if integ_time is not None and mon_count is not None:
- # raise RuntimeError('The acquisition must have only one role: '
- # 'timer or monitor')
- # if integ_time is not None:
- # master_key = 'timer'
- # master_value = integ_time
- # master = self._get_timer()
- # if mon_count is not None:
- # master_key = 'monitor'
- # master_value = -mon_count
- # master = self._get_monitor()
- # if master is None:
- # self.main_element.set_state(State.Fault, propagate=2)
- # msg = "master {0} ({1})is unknown (probably disabled)".format(
- # master_key, master)
- # raise RuntimeError(msg)
-
# controllers to be started (only enabled) in the right order
pool_ctrls = ctrls_channels.keys()
@@ -439,54 +408,11 @@ def start_action(self, ctrls_channels, ctrls_loadables, value,
pool_ctrls.remove(master_ctrl)
pool_ctrls.append(master_ctrl)
- # pool_ctrls_dict = dict(self._get_ctrls())
- # pool_ctrls_dict.pop('__tango__', None)
-
# controllers that will be read at the end of the action
self._pool_ctrl_dict_loop = ctrls_channels
# channels that are acquired (only enabled)
self._channels = []
- # # select only suitable e.g. enabled, timerable controllers & channels
- # for ctrl, pool_ctrl_data in pool_ctrls_dict.items():
- # # skip not timerable controllers e.g. 0D
- # if not ctrl.is_timerable():
- # continue
- # ctrl_enabled = False
- # elements = pool_ctrl_data['channels']
- # for element, element_info in elements.items():
- # # skip disabled elements
- # if not element_info['enabled']:
- # continue
- # # Add only the enabled channels
- # channel = Channel(element, info=element_info)
- # channels[element] = channel
- # ctrl_enabled = True
- # # check if the ctrl has enabled channels
- # if ctrl_enabled:
- # # enabled controller can no be offline
- # if not ctrl.is_online():
- # self.main_element.set_state(State.Fault, propagate=2)
- # msg = "controller {0} is offline".format(ctrl.name)
- # raise RuntimeError(msg)
- # pool_ctrls.append(ctrl)
- # # only CT will be read in the loop, 1D and 2D not
- # if ElementType.CTExpChannel in ctrl.get_ctrl_types():
- # _pool_ctrl_dict_loop[ctrl] = pool_ctrl_data
-
- # timer/monitor channels can not be disabled
- # for pool_ctrl in pool_ctrls:
- # ctrl = pool_ctrl.ctrl
- # pool_ctrl_data = pool_ctrls_dict[pool_ctrl]
- # timer_monitor = pool_ctrl_data[master_key]
- # if timer_monitor not in channels:
- # self.main_element.set_state(State.Fault, propagate=2)
- # msg = "timer/monitor ({0}) of {1} controller is "\
- # "disabled)".format(timer_monitor.name, pool_ctrl.name)
- # raise RuntimeError(msg)
-
-
-
def load(channel, value, repetitions, latency=0):
axis = channel.axis
pool_ctrl = channel.controller
From 4f4f639e57f2952943bd0eed552eb759be8970b4 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 15 Oct 2018 15:55:23 +0200
Subject: [PATCH 215/652] Remove get configuration method
Remove internal method to get the configuration for each action type.
---
src/sardana/pool/poolacquisition.py | 42 -----------------------------
1 file changed, 42 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 844eaa47a2..66839a925b 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -336,30 +336,6 @@ def __init__(self, main_element, name):
# acquisition actions, uncomment this line
# self.add_finish_hook(self.clear_value_buffers, True)
- def _get_ctrls(self):
- """
- Method to get the controller dict for the acquisition type
- :return:
- :rtype dict
- """
- raise NotImplementedError()
-
- def _get_timer(self):
- """
- Method to get the master timer for the acquisition type
- :return:
- :rtype dict
- """
- raise NotImplementedError()
-
- def _get_monitor(self):
- """
- Method to get the master monitor for the acquisition type
- :return:
- :rtype dict
- """
- raise NotImplementedError()
-
def in_acquisition(self, states):
"""Determines if we are in acquisition or if the acquisition has ended
based on the current unit trigger modes and states returned by the
@@ -533,15 +509,6 @@ class PoolAcquisitionHardware(PoolAcquisitionBase):
def __init__(self, main_element, name="AcquisitionHardware"):
PoolAcquisitionBase.__init__(self, main_element, name)
- def _get_ctrls(self):
- return self.main_element.configuration.ctrl_hw_sync
-
- def _get_timer(self):
- return self.main_element.configuration.hw_sync_timer
-
- def _get_monitor(self):
- return self.main_element.configuration.hw_sync_monitor
-
@DebugIt()
def action_loop(self):
i = 0
@@ -612,15 +579,6 @@ def __init__(self, main_element, name="AcquisitionSoftware", slaves=None):
slaves = ()
self._slaves = slaves
- def _get_ctrls(self):
- return self.main_element.configuration.ctrl_sw_sync
-
- def _get_timer(self):
- return self.main_element.configuration.sw_sync_timer
-
- def _get_monitor(self):
- return self.main_element.configuration.sw_sync_monitor
-
@DebugIt()
def start_action(self, *args, **kwargs):
"""Prepares everything for acquisition and starts it.
From d415179fe46e3599807b6cf2eb9ff470cb3ecbcf Mon Sep 17 00:00:00 2001
From: Grzegorz Kowalski
Date: Mon, 15 Oct 2018 16:20:20 +0200
Subject: [PATCH 216/652] add limits checking before scan
---
src/sardana/macroserver/scan/gscan.py | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py
index 83862aea0a..06087d68a2 100644
--- a/src/sardana/macroserver/scan/gscan.py
+++ b/src/sardana/macroserver/scan/gscan.py
@@ -271,6 +271,8 @@ def __init__(self, macro, generator=None, moveables=[], env={},
moveable_names.append(moveable.moveable.getName())
self._moveables.append(moveable)
+ self._check_moveables_limits()
+
name = self.__class__.__name__
self.call__init__(Logger, name)
@@ -369,6 +371,30 @@ def __init__(self, macro, generator=None, moveables=[], env={},
# ---------------------------------------------------------------------
self._setupEnvironment(env)
+ def _check_moveables_limits(self):
+ for m in self._moveables:
+ config = PyTango.AttributeProxy(
+ m.moveable.getName() + '/position').get_config()
+ try:
+ high = float(config.max_value)
+ except ValueError:
+ high = None
+ try:
+ low = float(config.min_value)
+ except ValueError:
+ low = None
+ for pos in (m.min_value, m.max_value):
+ if high is not None:
+ if float(pos) > high:
+ raise RuntimeError(
+ "requested movement of %s is above its upper limit"
+ % m.moveable.getName())
+ if low is not None:
+ if float(pos) < low:
+ raise RuntimeError(
+ "requested movement of %s is below its lower limit"
+ % m.moveable.getName())
+
def _getExtraColumns(self):
ret = []
try:
From 71bcea73076cb8b8540a91e3b847d40da0ce0f99 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 15 Oct 2018 16:27:34 +0200
Subject: [PATCH 217/652] Add explicit parameters on start_action method
---
src/sardana/pool/poolacquisition.py | 23 ++++++++++++++++++-----
1 file changed, 18 insertions(+), 5 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 66839a925b..82055e1c16 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -326,6 +326,11 @@ class PoolAcquisitionBase(PoolAction):
def __init__(self, main_element, name):
PoolAction.__init__(self, main_element, name)
self._channels = []
+ self._index = None
+ self._nb_states_per_value = None
+ self._acq_sleep_time = None
+ self._pool_ctrl_dict_loop = None
+
# TODO: for the moment we can not clear value buffers at the end of
# the acquisition. This is because of the pseudo counters that are
# based on channels synchronized by hardware and software.
@@ -353,7 +358,9 @@ def in_acquisition(self, states):
@DebugIt()
def start_action(self, ctrls_channels, ctrls_loadables, value,
- repetitions=1, latency=0, master=None, *args,
+ repetitions=1, latency=0, master=None,
+ index=None, acq_sleep_time=None,
+ nb_states_per_value=None, *args,
**kwargs):
"""Prepares everything for acquisition and starts it.
:param acq_sleep_time: sleep time between state queries
@@ -369,10 +376,16 @@ def start_action(self, ctrls_channels, ctrls_loadables, value,
self._aborted = False
self._stopped = False
- self._acq_sleep_time = kwargs.pop("acq_sleep_time",
- pool.acq_loop_sleep_time)
- self._nb_states_per_value = kwargs.pop("nb_states_per_value",
- pool.acq_loop_states_per_value)
+ self._index = index
+
+ self._acq_sleep_time = acq_sleep_time
+ if self._acq_sleep_time is None:
+ self._acq_sleep_time = pool.acq_loop_sleep_time
+
+ self._nb_states_per_value = nb_states_per_value
+ if self._nb_states_per_value is None:
+ self._nb_states_per_value = pool.acq_loop_states_per_value
+
# controllers to be started (only enabled) in the right order
pool_ctrls = ctrls_channels.keys()
From 25ddec1bb1da26a04b81ab7b0497c89a4954cb0d Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 15 Oct 2018 16:32:42 +0200
Subject: [PATCH 218/652] Add python 3 compatibility
---
src/sardana/pool/poolacquisition.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 82055e1c16..71b6e96332 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -479,7 +479,7 @@ def load(channel, value, repetitions, latency=0):
raise Exception(msg)
try:
ctrl.StartOne(axis, value)
- except Exception, e:
+ except Exception as e:
self.debug(e, exc_info=True)
channel.set_state(State.Fault, propagate=2)
msg = ("%s.StartOne(%d) failed" %
@@ -497,7 +497,7 @@ def load(channel, value, repetitions, latency=0):
channels = ctrls_channels[pool_ctrl]
try:
pool_ctrl.ctrl.StartAll()
- except Exception, e:
+ except Exception as e:
self.debug(e, exc_info=True)
for channel in channels:
channel.set_state(State.Fault, propagate=2)
From 02429c9c7bb59b7a1391f9154776263b642349ee Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 15 Oct 2018 16:35:33 +0200
Subject: [PATCH 219/652] Adapt to use PoolAcquisitionBase class
---
src/sardana/pool/poolacquisition.py | 20 +-------------------
1 file changed, 1 insertion(+), 19 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 71b6e96332..68fb425c14 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -592,24 +592,6 @@ def __init__(self, main_element, name="AcquisitionSoftware", slaves=None):
slaves = ()
self._slaves = slaves
- @DebugIt()
- def start_action(self, *args, **kwargs):
- """Prepares everything for acquisition and starts it.
- :param acq_sleep_time: sleep time between state queries
- :param nb_states_per_value: how many state queries between readouts
- :param integ_time: integration time(s)
- :type integ_time: float or seq
- :param repetitions: repetitions
- :type repetitions: int
- :param config: configuration dictionary (with information about
- involved controllers and channels)
- :param index: trigger index that will be assigned to the acquired value
- :type index: int
- """
-
- PoolAcquisitionBase.start_action(self, *args, **kwargs)
- self.index = kwargs.get("idx")
-
@DebugIt()
def action_loop(self):
states, values = {}, {}
@@ -656,7 +638,7 @@ def action_loop(self):
if is_value_error(value):
self.error("Loop final read value error for: %s" %
acquirable.name)
- acquirable.append_value_buffer(value, self.index)
+ acquirable.append_value_buffer(value, self._index)
with acquirable:
acquirable.clear_operation()
state_info = acquirable._from_ctrl_state_info(state_info)
From 322ec9f23ffd6a42db8076016488a0b64dd5ff99 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 15 Oct 2018 17:20:06 +0200
Subject: [PATCH 220/652] Rename start_action parameters
---
src/sardana/pool/poolacquisition.py | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 68fb425c14..a0c8ac1151 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -357,7 +357,7 @@ def in_acquisition(self, states):
return True
@DebugIt()
- def start_action(self, ctrls_channels, ctrls_loadables, value,
+ def start_action(self, ctrl_channels, ctrl_loadable, value,
repetitions=1, latency=0, master=None,
index=None, acq_sleep_time=None,
nb_states_per_value=None, *args,
@@ -388,7 +388,7 @@ def start_action(self, ctrls_channels, ctrls_loadables, value,
# controllers to be started (only enabled) in the right order
- pool_ctrls = ctrls_channels.keys()
+ pool_ctrls = ctrl_channels.keys()
# make sure the controller which has the master channel is the last to
# be called
@@ -398,7 +398,7 @@ def start_action(self, ctrls_channels, ctrls_loadables, value,
pool_ctrls.append(master_ctrl)
# controllers that will be read at the end of the action
- self._pool_ctrl_dict_loop = ctrls_channels
+ self._pool_ctrl_dict_loop = ctrl_channels
# channels that are acquired (only enabled)
self._channels = []
@@ -446,7 +446,7 @@ def load(channel, value, repetitions, latency=0):
with ActionContext(self):
# PreLoadAll, PreLoadOne, LoadOne and LoadAll
- loadables = ctrls_loadables.values()
+ loadables = ctrl_loadable.values()
for channel in loadables:
load(channel, value, repetitions, latency)
@@ -463,11 +463,11 @@ def load(channel, value, repetitions, latency=0):
channels_started = []
# PreStartOne & StartOne on all enabled elements
for pool_ctrl in pool_ctrls:
- channels = ctrls_channels[pool_ctrl]
+ channels = ctrl_channels[pool_ctrl]
ctrl = pool_ctrl.ctrl
# make sure that the timer/monitor is started as the last one
- loadable = ctrls_loadables[pool_ctrl]
+ loadable = ctrl_loadable[pool_ctrl]
channels.remove(loadable)
channels.append(loadable)
for channel in channels:
@@ -494,7 +494,7 @@ def load(channel, value, repetitions, latency=0):
# StartAll on all enabled controllers
for pool_ctrl in pool_ctrls:
- channels = ctrls_channels[pool_ctrl]
+ channels = ctrl_channels[pool_ctrl]
try:
pool_ctrl.ctrl.StartAll()
except Exception as e:
From 33ff25044bbc4a1058795574c04b40f3dabc6427 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 15 Oct 2018 17:22:02 +0200
Subject: [PATCH 221/652] Update start_action docstring
---
src/sardana/pool/poolacquisition.py | 31 ++++++++++++++++++++++-------
1 file changed, 24 insertions(+), 7 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index a0c8ac1151..c3af02673f 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -362,16 +362,33 @@ def start_action(self, ctrl_channels, ctrl_loadable, value,
index=None, acq_sleep_time=None,
nb_states_per_value=None, *args,
**kwargs):
- """Prepares everything for acquisition and starts it.
- :param acq_sleep_time: sleep time between state queries
- :param nb_states_per_value: how many state queries between readouts
- :param integ_time: integration time(s)
- :type integ_time: float or seq
+ """
+ Prepares everything for acquisition and starts it
+ :param ctrl_channels: Dictionary with controllers as key and its
+ enabled channels
+ :type ctrl_channels: dict
+ :param ctrl_loadable: Dictionary with controllers as key and its
+ timerable channel
+ :type ctrl_loadable: dict
+ :param value: integration time/monitor counts
+ :type value: float/int or seq
:param repetitions: repetitions
:type repetitions: int
- :param config: configuration dictionary (with information about
- involved controllers and channels)
+ :param latency:
+ :type latency: float
+ :param master: master channel is the last one to start
+ :type master: Channel
+ :param index:
+ :type index: int
+ :param acq_sleep_time: sleep time between state queries
+ :type acq_sleep_time: float
+ :param nb_states_per_value: how many state queries between readouts
+ :type nb_states_per_value: int
+ :param args:
+ :param kwargs:
+ :return:
"""
+
pool = self.pool
self._aborted = False
self._stopped = False
From e78fd6b89524335304b6cc78566893e2b4b9b567 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Mon, 15 Oct 2018 17:43:47 +0200
Subject: [PATCH 222/652] Do use_fqdn to private attribute
---
src/sardana/pool/poolmeasurementgroup.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 176fb02047..185bba5d39 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -137,7 +137,7 @@ def __init__(self, parent=None):
if parent is not None:
self._parent = weakref.ref(parent)()
self._config = None
- self.use_fqdn = True
+ self._use_fqdn = True
self._clean_variables()
def _clean_variables(self):
@@ -281,7 +281,7 @@ def set_configuration_from_user(self, cfg, to_fqdn=True):
config['label'] = cfg.get('label', self._parent.name)
config['description'] = cfg.get('description', self.DFT_DESC)
- self.use_fqdn = to_fqdn
+ self._use_fqdn = to_fqdn
self._build_configuration(config)
def get_configuration_for_user(self):
@@ -466,7 +466,7 @@ def _build_configuration(self, config=None):
channel_data['source'] = _id
else:
full_name = channel_data['full_name']
- if self.use_fqdn:
+ if self._use_fqdn:
full_name = _to_fqdn(full_name,
logger=self._parent)
element = pool.get_element_by_full_name(full_name)
@@ -721,7 +721,7 @@ def configuration(self):
return self._config
def set_configuration(self, config=None, propagate=1, to_fqdn=True):
- self._config.use_fqdn = to_fqdn
+ self._config._use_fqdn = to_fqdn
self._config.configuration = config
self._config_dirty = True
if not propagate:
From b0d16caf69d9a81eddb6772a71d1c700c5abb558 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Tue, 16 Oct 2018 15:24:16 +0200
Subject: [PATCH 223/652] Fix weak reference
Use a weak reference of the parent instead of the object itself.
---
src/sardana/pool/poolmeasurementgroup.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 185bba5d39..987d914295 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -135,7 +135,7 @@ class MeasurementConfiguration(object):
def __init__(self, parent=None):
self._parent = None
if parent is not None:
- self._parent = weakref.ref(parent)()
+ self._parent = weakref.proxy(parent)
self._config = None
self._use_fqdn = True
self._clean_variables()
From 3df878b7d7ffe09426f94509f04483594adc852e Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 17 Oct 2018 18:38:15 +0200
Subject: [PATCH 224/652] Refactor acquisition and its tests to use
ControllerConfiguration objects
ControllerConfiguration objects will have information about its channels
and its master channel. Assume it in start_action and tests.
---
src/sardana/pool/poolacquisition.py | 37 +++++++++--------------
src/sardana/pool/test/test_acquisition.py | 14 ++++-----
2 files changed, 21 insertions(+), 30 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index c3af02673f..a7ff6c17ab 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -357,19 +357,14 @@ def in_acquisition(self, states):
return True
@DebugIt()
- def start_action(self, ctrl_channels, ctrl_loadable, value,
- repetitions=1, latency=0, master=None,
- index=None, acq_sleep_time=None,
+ def start_action(self, pool_ctrls, value, repetitions=1, latency=0,
+ master=None, index=None, acq_sleep_time=None,
nb_states_per_value=None, *args,
**kwargs):
"""
Prepares everything for acquisition and starts it
- :param ctrl_channels: Dictionary with controllers as key and its
- enabled channels
- :type ctrl_channels: dict
- :param ctrl_loadable: Dictionary with controllers as key and its
- timerable channel
- :type ctrl_loadable: dict
+ :param pool_ctrls: List of enabled controllers
+ :type pool_ctrls: list
:param value: integration time/monitor counts
:type value: float/int or seq
:param repetitions: repetitions
@@ -403,10 +398,6 @@ def start_action(self, ctrl_channels, ctrl_loadable, value,
if self._nb_states_per_value is None:
self._nb_states_per_value = pool.acq_loop_states_per_value
-
- # controllers to be started (only enabled) in the right order
- pool_ctrls = ctrl_channels.keys()
-
# make sure the controller which has the master channel is the last to
# be called
if master is not None:
@@ -415,7 +406,11 @@ def start_action(self, ctrl_channels, ctrl_loadable, value,
pool_ctrls.append(master_ctrl)
# controllers that will be read at the end of the action
+ ctrl_channels = {}
+ for pool_ctrl in pool_ctrls:
+ ctrl_channels[pool_ctrl] = pool_ctrl.channels
self._pool_ctrl_dict_loop = ctrl_channels
+
# channels that are acquired (only enabled)
self._channels = []
@@ -463,9 +458,8 @@ def load(channel, value, repetitions, latency=0):
with ActionContext(self):
# PreLoadAll, PreLoadOne, LoadOne and LoadAll
- loadables = ctrl_loadable.values()
- for channel in loadables:
- load(channel, value, repetitions, latency)
+ for pool_ctrl in pool_ctrls:
+ load(pool_ctrl.master, value, repetitions, latency)
# TODO: remove when the action allows to use tango attributes
try:
@@ -477,16 +471,15 @@ def load(channel, value, repetitions, latency=0):
for pool_ctrl in pool_ctrls:
pool_ctrl.ctrl.PreStartAll()
- channels_started = []
# PreStartOne & StartOne on all enabled elements
for pool_ctrl in pool_ctrls:
- channels = ctrl_channels[pool_ctrl]
+ channels = pool_ctrl.channels
ctrl = pool_ctrl.ctrl
- # make sure that the timer/monitor is started as the last one
- loadable = ctrl_loadable[pool_ctrl]
- channels.remove(loadable)
- channels.append(loadable)
+ # make sure that the master timer/monitor is started as the
+ # last one
+ channels.remove(pool_ctrl.master)
+ channels.append(pool_ctrl.master)
for channel in channels:
axis = channel.axis
ret = ctrl.PreStartOne(axis, value)
diff --git a/src/sardana/pool/test/test_acquisition.py b/src/sardana/pool/test/test_acquisition.py
index eab0329cba..6c49bc2541 100644
--- a/src/sardana/pool/test/test_acquisition.py
+++ b/src/sardana/pool/test/test_acquisition.py
@@ -304,10 +304,10 @@ def continuous_acquisition(self, offset, active_interval, passive_interval,
self.channel_names.append('_test_ct_1_1')
self.channel_names.append('_test_ct_2_1')
- acq_hw_ctrl_channels = {ct_ctrl_1: [ct_1_1]}
- acq_hw_ctrl_loadable = {ct_ctrl_1: ct_1_1}
- acq_sw_ctrl_channels = {ct_ctrl_2: [ct_2_1]}
- acq_sw_ctrl_loadable = {ct_ctrl_2: ct_2_1}
+ ct_ctrl_1.channels = [ct_1_1]
+ ct_ctrl_1.master = ct_1_1
+ ct_ctrl_2.channels = [ct_2_1]
+ ct_ctrl_2.master = ct_2_1
# crating configuration for TGGeneration
tg_cfg = createPoolSynchronizationConfiguration((tg_ctrl_2,),
((tg_2_1,),))
@@ -335,12 +335,10 @@ def continuous_acquisition(self, offset, active_interval, passive_interval,
# get the current number of jobs
jobs_before = get_thread_pool().qsize
- self.sw_acq_args = (acq_sw_ctrl_channels, acq_sw_ctrl_loadable,
- integ_time)
+ self.sw_acq_args = ([ct_ctrl_2], integ_time)
self.sw_acq_kwargs = {"master": ct_2_1}
ct_ctrl_1.set_ctrl_par('synchronization', AcqSynch.HardwareTrigger)
- hw_acq_args = (acq_hw_ctrl_channels, acq_hw_ctrl_loadable,
- integ_time, repetitions)
+ hw_acq_args = ([ct_ctrl_1], integ_time, repetitions)
self.hw_acq.run(*hw_acq_args)
tg_args = ()
total_interval = active_interval + passive_interval
From 434fcf94a91ff261caee5a990f0f519839aec7bd Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Thu, 18 Oct 2018 11:48:00 +0200
Subject: [PATCH 225/652] Add configuration helpers classes
Add classes to help the use of the static measurement configuration.
---
src/sardana/pool/poolmeasurementgroup.py | 29 ++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 987d914295..6dad6ee9e1 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -124,6 +124,35 @@ def _to_fqdn(name, logger=None):
return full_name
+class ConfigurationItem(object):
+ def __init__(self, element, conf=None):
+ self._element = weakref.ref(element)
+ if conf is not None:
+ self.__dict__.update(conf)
+
+ def __getattr__(self, item):
+ return getattr(self.element, item)
+
+ def get_element(self):
+ """Returns the element associated with this item"""
+ return self._element()
+
+ def set_element(self, element):
+ """Sets the element for this item"""
+ self._element = weakref.ref(element)
+
+ element = property(get_element)
+
+
+class ControllerConfiguration(ConfigurationItem):
+ """Configuration: 'timer', 'monitor', 'synchronization', 'channels'"""
+
+
+class ChannelConfiguration(ConfigurationItem):
+ """Configuration: 'id', 'enabled', 'output', 'plot_type', 'plot_axes',
+ 'label', 'scale', 'plot_color'"""
+
+
class MeasurementConfiguration(object):
"""
.. todo: Reject configuration with errors:
From 310312b6b2c40c9e37076f889be00b51126b95f0 Mon Sep 17 00:00:00 2001
From: Grzegorz Kowalski
Date: Thu, 18 Oct 2018 18:41:05 +0200
Subject: [PATCH 226/652] check if moveable has any defined position
---
src/sardana/macroserver/scan/gscan.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/sardana/macroserver/scan/gscan.py b/src/sardana/macroserver/scan/gscan.py
index 06087d68a2..9f349bd063 100644
--- a/src/sardana/macroserver/scan/gscan.py
+++ b/src/sardana/macroserver/scan/gscan.py
@@ -384,6 +384,10 @@ def _check_moveables_limits(self):
except ValueError:
low = None
for pos in (m.min_value, m.max_value):
+ if pos is None:
+ self._macro.warning("Macro did not define position for %s. "
+ "Limit check was not possible." % m.moveable.getName())
+ continue
if high is not None:
if float(pos) > high:
raise RuntimeError(
From 21563facdd2320ff229d0f0b134b52b7d89115b1 Mon Sep 17 00:00:00 2001
From: Daniel Roldan
Date: Mon, 22 Oct 2018 11:23:59 +0200
Subject: [PATCH 227/652] Add Unit Test for macros examples
---
src/sardana/spock/test/test_parser.py | 292 +++++++++++++++++++++++++-
1 file changed, 291 insertions(+), 1 deletion(-)
diff --git a/src/sardana/spock/test/test_parser.py b/src/sardana/spock/test/test_parser.py
index cbe99b068c..29377ecb8f 100644
--- a/src/sardana/spock/test/test_parser.py
+++ b/src/sardana/spock/test/test_parser.py
@@ -76,6 +76,16 @@ def parse(self, params_str, params):
}
]
+pt2_params_def = [
+ {
+ "default_value": None,
+ "description": "some bloody motor",
+ "max": None,
+ "min": 1,
+ "name": "motor",
+ "type": "Motor"
+ }
+]
pt3_params_def = [
{
"default_value": None,
@@ -116,6 +126,26 @@ def parse(self, params_str, params):
}
]
+pt4_params_def = [
+ {
+ "default_value": None,
+ "description": "List of motors",
+ "max": None,
+ "min": 1,
+ "name": "motor_list",
+ "type": [
+ {
+ "default_value": None,
+ "description": "motor name",
+ "max": None,
+ "min": 1,
+ "name": "motor",
+ "type": "Motor"
+ }
+ ]
+ }
+]
+
pt5_params_def = [
{
"default_value": None,
@@ -137,7 +167,35 @@ def parse(self, params_str, params):
"description": "value",
"max": None,
"min": 1,
- "name": "position",
+ "name": "pos",
+ "type": "Float"
+ }
+ ]
+ }
+]
+
+pt6_params_def = [
+ {
+ "default_value": None,
+ "description": "Motor to move",
+ "max": None,
+ "min": 1,
+ "name": "motor",
+ "type": "Motor"
+ },
+ {
+ "default_value": None,
+ "description": "List of values",
+ "max": None,
+ "min": 1,
+ "name": "numb_list",
+ "type": [
+ {
+ "default_value": None,
+ "description": "value",
+ "max": None,
+ "min": 1,
+ "name": "pos",
"type": "Float"
}
]
@@ -172,6 +230,118 @@ def parse(self, params_str, params):
}
]
+pt7d1_params_def = [
+ {
+ "default_value": None,
+ "description": "List of motor/position pairs",
+ "max": None,
+ "min": 1,
+ "name": "m_p_pair",
+ "type": [
+ {
+ "default_value": None,
+ "description": "Motor to move",
+ "max": None,
+ "min": 1,
+ "name": "motor",
+ "type": "Motor"
+ },
+ {
+ "default_value": 2,
+ "description": "Position to move to",
+ "max": None,
+ "min": 1,
+ "name": "pos",
+ "type": "Float"
+ }
+ ]
+ }
+]
+
+pt7d2_params_def = [
+ {
+ "default_value": None,
+ "description": "List of motor/position pairs",
+ "max": None,
+ "min": 1,
+ "name": "m_p_pair",
+ "type": [
+ {
+ "default_value": 'mot1',
+ "description": "Motor to move",
+ "max": None,
+ "min": 1,
+ "name": "motor",
+ "type": "Motor"
+ },
+ {
+ "default_value": 2,
+ "description": "Position to move to",
+ "max": None,
+ "min": 1,
+ "name": "pos",
+ "type": "Float"
+ }
+ ]
+ }
+]
+
+pt8_params_def = [
+ {
+ "default_value": None,
+ "description": "List of motor/position pairs",
+ "max": None,
+ "min": 1,
+ "name": "m_p_pair",
+ "type": [
+ {
+ "default_value": None,
+ "description": "Motor to move",
+ "max": None,
+ "min": 1,
+ "name": "motor",
+ "type": "Motor"
+ },
+ {
+ "default_value": None,
+ "description": "Position to move to",
+ "max": 2,
+ "min": 1,
+ "name": "pos",
+ "type": "Float"
+ }
+ ]
+ }
+]
+
+pt9_params_def = [
+ {
+ "default_value": None,
+ "description": "List of motor/position pairs",
+ "max": None,
+ "min": 1,
+ "name": "m_p_pair",
+ "type": [
+ {
+ "default_value": None,
+ "description": "Motor to move",
+ "max": None,
+ "min": 1,
+ "name": "motor",
+ "type": "Motor"
+ },
+ {
+ "default_value": None,
+ "description": "Position to move to",
+ "max": 2,
+ "min": 1,
+ "name": "pos",
+ "type": "Float"
+ }
+ ]
+ }
+]
+
pt10_params_def = [
{
"default_value": None,
@@ -200,6 +370,81 @@ def parse(self, params_str, params):
},
]
+pt11_params_def = [
+
+ {
+ "default_value": None,
+ "description": "Counter to count",
+ "max": None,
+ "min": 1,
+ "name": "counter",
+ "type": "ExpChannel"
+ },
+ {
+ "default_value": None,
+ "description": "List of values",
+ "max": None,
+ "min": 1,
+ "name": "numb_list",
+ "type": [
+ {
+ "default_value": None,
+ "description": "value",
+ "max": None,
+ "min": 1,
+ "name": "pos",
+ "type": "Float"
+ },
+ ]
+ },
+ {
+ "default_value": None,
+ "description": "Motor to move",
+ "max": None,
+ "min": 1,
+ "name": "motor",
+ "type": "Motor"
+ },
+]
+
+pt12_params_def = [
+
+ {
+ "default_value": None,
+ "description": "List of values",
+ "max": None,
+ "min": 1,
+ "name": "numb_list",
+ "type": [
+ {
+ "default_value": None,
+ "description": "value",
+ "max": None,
+ "min": 1,
+ "name": "pos",
+ "type": "Float"
+ },
+ ]
+ },
+ {
+ "default_value": None,
+ "description": "List of Motors",
+ "max": None,
+ "min": 1,
+ "name": "motor_list",
+ "type": [
+ {
+ "default_value": 'mot1',
+ "description": "Motor to move",
+ "max": None,
+ "min": 1,
+ "name": "motor",
+ "type": "Motor"
+ },
+ ]
+ },
+]
+
pt13_params_def = [
{
"default_value": None,
@@ -273,6 +518,8 @@ def parse(self, params_str, params):
params_str="1", params=["1"])
@insertTest(helper_name="parse", params_def=pt1d_params_def,
params_str="", params=[])
+@insertTest(helper_name="parse", params_def=pt2_params_def,
+ params_str="mot1", params=["mot1"])
@insertTest(helper_name="parse", params_def=pt3_params_def,
params_str="1 34 15", params=[["1", "34", "15"]])
@insertTest(helper_name="parse", params_def=pt3_params_def,
@@ -283,20 +530,63 @@ def parse(self, params_str, params):
params_str="[1 34 15]", params=[["1", "34", "15"]])
@insertTest(helper_name="parse", params_def=pt3d_params_def,
params_str="[1 [] 15]", params=[["1", [], "15"]])
+@insertTest(helper_name="parse", params_def=pt4_params_def,
+ params_str="[mot1 mot2 mot3]", params=[["mot1", "mot2", "mot3"]])
+@insertTest(helper_name="parse", params_def=pt4_params_def,
+ params_str="mot1 mot2 mot3", params=[["mot1", "mot2", "mot3"]])
@insertTest(helper_name="parse", params_def=pt5_params_def,
params_str="mot1 1 3", params=["mot1", ["1", "3"]])
@insertTest(helper_name="parse", params_def=pt5_params_def,
params_str="mot1 [1 3]", params=["mot1", ["1", "3"]])
+@insertTest(helper_name="parse", params_def=pt6_params_def,
+ params_str="mot1 [1 34 1]", params=["mot1", ["1", "34", "1"]])
+@insertTest(helper_name="parse", params_def=pt6_params_def,
+ params_str="mot1 1 34 1", params=["mot1", ["1", "34", "1"]])
@insertTest(helper_name="parse", params_def=pt7_params_def,
params_str="mot1 1 mot2 3",
params=[[["mot1", "1"], ["mot2", "3"]]])
@insertTest(helper_name="parse", params_def=pt7_params_def,
params_str="[[mot1 1] [mot2 3]]",
params=[[["mot1", "1"], ["mot2", "3"]]])
+@insertTest(helper_name="parse", params_def=pt7d1_params_def,
+ params_str="[[mot1 1] [mot2 3]]",
+ params=[[["mot1", "1"], ["mot2", "3"]]])
+@insertTest(helper_name="parse", params_def=pt7d1_params_def,
+ params_str="mot1 1 mot2 3",
+ params=[[["mot1", "1"], ["mot2", "3"]]])
+@insertTest(helper_name="parse", params_def=pt7d1_params_def,
+ params_str="[[mot1] [mot2 3]]",
+ params=[[["mot1"], ["mot2", "3"]]])
+@insertTest(helper_name="parse", params_def=pt7d2_params_def,
+ params_str="[[mot1 1] [mot2 3]]",
+ params=[[["mot1", "1"], ["mot2", "3"]]])
+@insertTest(helper_name="parse", params_def=pt7d2_params_def,
+ params_str="mot1 1 mot2 3",
+ params=[[["mot1", "1"], ["mot2", "3"]]])
+@insertTest(helper_name="parse", params_def=pt7d2_params_def,
+ params_str="[[] [mot2 3] []]",
+ params=[[[], ["mot2", "3"], []]])
+@insertTest(helper_name="parse", params_def=pt8_params_def,
+ params_str="[[mot1 1] [mot2 3]]",
+ params=[[["mot1", "1"], ["mot2", "3"]]])
+@insertTest(helper_name="parse", params_def=pt8_params_def,
+ params_str="mot1 1 mot2 3",
+ params=[[["mot1", "1"], ["mot2", "3"]]])
+@insertTest(helper_name="parse", params_def=pt9_params_def,
+ params_str="[[mot1 1] [mot2 3]]",
+ params=[[["mot1", "1"], ["mot2", "3"]]])
+@insertTest(helper_name="parse", params_def=pt9_params_def,
+ params_str="mot1 1 mot2 3",
+ params=[[["mot1", "1"], ["mot2", "3"]]])
@insertTest(helper_name="parse", params_def=pt10_params_def,
params_str="[1 3] mot1", params=[["1", "3"], "mot1"])
@insertTest(helper_name="parse", params_def=pt10_params_def,
params_str="1 mot1", params=[["1"], "mot1"])
+@insertTest(helper_name="parse", params_def=pt11_params_def,
+ params_str="ct1 [1 3] mot1", params=["ct1", ["1", "3"], "mot1"])
+@insertTest(helper_name="parse", params_def=pt12_params_def,
+ params_str="[1 3 4] [mot1 mot2]",
+ params=[["1", "3", "4"], ["mot1", "mot2"]])
@insertTest(helper_name="parse", params_def=pt13_params_def,
params_str="[[mot1 mot2] [mot3 mot4]]",
params=[[["mot1", "mot2"], ["mot3", "mot4"]]])
From 7a7ad30970b6916b5a7dbd93ef8bed619de879b4 Mon Sep 17 00:00:00 2001
From: Daniel Roldan
Date: Mon, 22 Oct 2018 11:24:46 +0200
Subject: [PATCH 228/652] Add Unit Test for custom test
---
src/sardana/spock/test/test_parser.py | 297 ++++++++++++++++++++++++--
1 file changed, 278 insertions(+), 19 deletions(-)
diff --git a/src/sardana/spock/test/test_parser.py b/src/sardana/spock/test/test_parser.py
index 29377ecb8f..403ff2c94f 100644
--- a/src/sardana/spock/test/test_parser.py
+++ b/src/sardana/spock/test/test_parser.py
@@ -30,25 +30,6 @@
from sardana.spock.parser import ParamParser
-# @insertTest(helper_name="parse",
-# params_str='ScanFile "[\\"file.nxs\\", \\"file.dat\\"]"',
-# params=["ScanFile", '["file.nxs", "file.dat"]'])
-# @insertTest(helper_name="parse", params_str="[1 [] 3]",
-# params=[["1", [], "3"]])
-# @insertTest(helper_name="parse",
-# params_str="2 3 ['Hello world!' 'How are you?']",
-# params=["2", "3", ["Hello world!", "How are you?"]])
-# @insertTest(helper_name="parse", params_str="ScanFile file.dat",
-# params=["ScanFile", "file.dat"])
-# @insertTest(helper_name="parse", params_str="'2 3'", params=["2 3"])
-# @insertTest(helper_name="parse", params_str='"2 3"', params=["2 3"])
-# @insertTest(helper_name="parse", params_str="[[mot01 3][mot02 5]] ct01 999",
-# params=[[["mot01", "3"], ["mot02", "5"]], "ct01", "999"])
-# @insertTest(helper_name="parse", params_str="[[2 3][4 5]]",
-# params=[[["2", "3"], ["4", "5"]]])
-# @insertTest(helper_name="parse", params_str="1 [2 3]",
-# params=["1", ["2", "3"]])
-# @insertTest(helper_name="parse", params_str="2 3", params=["2", "3"])
class ParamParserTestCase(unittest.TestCase):
"""Unit tests for ParamParser class."""
@@ -511,6 +492,260 @@ def parse(self, params_str, params):
}
]
+pt15_params_def = [
+ {
+ "default_value": None,
+ "description": "Parameter",
+ "max": None,
+ "min": 1,
+ "name": "param",
+ "type": "String"
+ },
+ {
+ "default_value": None,
+ "description": "List of Scan files",
+ "max": None,
+ "min": 1,
+ "name": "ScanFiles List",
+ "type": [
+ {
+ "default_value": None,
+ "description": "ScanFile",
+ "max": None,
+ "min": 1,
+ "name": "ScanFile",
+ "type": "String",
+ }
+ ]
+ }
+]
+
+pt16_params_def = [
+ {
+ "default_value": None,
+ "description": "List of values",
+ "max": None,
+ "min": 1,
+ "name": "numb_list",
+ "type": [
+ {
+ "default_value": 2,
+ "description": "value",
+ "max": None,
+ "min": 1,
+ "name": "position",
+ "type": "Float"
+ }
+ ]
+ }
+]
+
+pt17_params_def = [
+
+ {
+ "default_value": None,
+ "description": "Value 1",
+ "max": None,
+ "min": 1,
+ "name": "value1",
+ "type": "Float"
+ },
+ {
+ "default_value": None,
+ "description": "Value 2",
+ "max": None,
+ "min": 1,
+ "name": "value2",
+ "type": "float"
+ },
+ {
+ "default_value": None,
+ "description": "List of Strings",
+ "max": None,
+ "min": 1,
+ "name": "string_list",
+ "type": [
+ {
+ "default_value": None,
+ "description": "string",
+ "max": None,
+ "min": 1,
+ "name": "string",
+ "type": "String"
+ },
+ ]
+ },
+]
+pt18_params_def = [
+
+ {
+ "default_value": None,
+ "description": "param",
+ "max": None,
+ "min": 1,
+ "name": "param",
+ "type": "String"
+ },
+ {
+ "default_value": None,
+ "description": "Value",
+ "max": None,
+ "min": 1,
+ "name": "value",
+ "type": "String"
+ },
+]
+
+pt19_params_def = [
+
+ {
+ "default_value": None,
+ "description": "value 1",
+ "max": None,
+ "min": 1,
+ "name": "value1",
+ "type": "Float"
+ },
+ {
+ "default_value": None,
+ "description": "Value 2",
+ "max": None,
+ "min": 1,
+ "name": "value2",
+ "type": "Float"
+ },
+]
+
+
+pt20_params_def = [
+
+ {
+ "default_value": None,
+ "description": "List of Motor and Values",
+ "max": None,
+ "min": 1,
+ "name": "numb_list",
+ "type": [
+ {
+ "default_value": None,
+ "description": "Motor",
+ "max": None,
+ "min": 1,
+ "name": "pos",
+ "type": "Motor"
+ },
+ {
+ "default_value": None,
+ "description": "Position to move to",
+ "max": 2,
+ "min": 1,
+ "name": "pos",
+ "type": "Float"
+ }
+ ]
+ },
+ {
+ "default_value": None,
+ "description": "Counter to use",
+ "max": None,
+ "min": 1,
+ "name": "counter",
+ "type": "ExpChan"
+ },
+ {
+ "default_value": None,
+ "description": "Value",
+ "max": None,
+ "min": 1,
+ "name": "Value",
+ "type": "Float"
+ }
+]
+pt21_params_def = [
+
+ {
+ "default_value": None,
+ "description": "List of Values",
+ "max": None,
+ "min": 1,
+ "name": "numb_list",
+ "type": [
+ {
+ "default_value": None,
+ "description": "Value 1",
+ "max": None,
+ "min": 1,
+ "name": "value1",
+ "type": "Float"
+ },
+ {
+ "default_value": None,
+ "description": "Value 2",
+ "max": None,
+ "min": 1,
+ "name": "value2",
+ "type": "Float"
+ }
+ ]
+ }
+]
+
+pt22_params_def = [
+
+ {
+ "default_value": None,
+ "description": "value 1",
+ "max": None,
+ "min": 1,
+ "name": "value1",
+ "type": "Float"
+ },
+ {
+ "default_value": None,
+ "description": "List of Values",
+ "max": None,
+ "min": 1,
+ "name": "numb_list",
+ "type": [
+ {
+ "default_value": None,
+ "description": "Value 2.1",
+ "max": None,
+ "min": 1,
+ "name": "value21",
+ "type": "Float"
+ },
+ {
+ "default_value": None,
+ "description": "Value 2.2",
+ "max": None,
+ "min": 1,
+ "name": "value22",
+ "type": "Float"
+ }
+ ]
+ }
+]
+pt23_params_def = [
+
+ {
+ "default_value": None,
+ "description": "value 1",
+ "max": None,
+ "min": 1,
+ "name": "value1",
+ "type": "Float"
+ },
+ {
+ "default_value": None,
+ "description": "Value 2",
+ "max": None,
+ "min": 1,
+ "name": "value2",
+ "type": "Float"
+ },
+]
+
@insertTest(helper_name="parse", params_def=pt0_params_def,
params_str="", params=[])
@@ -593,6 +828,30 @@ def parse(self, params_str, params):
@insertTest(helper_name="parse", params_def=pt14_params_def,
params_str="[[[mot1 mot2] 3] [[mot3] 5]]",
params=[[[["mot1", "mot2"], "3"], [["mot3"], "5"]]])
+@insertTest(helper_name="parse", params_def=pt15_params_def,
+ params_str="ScanFile ['file.nxs' 'file.dat']",
+ params=["ScanFile", ["file.nxs", "file.dat"]])
+@insertTest(helper_name="parse", params_def=pt16_params_def,
+ params_str="[1 [] 3]", params=[["1", [], "3"]])
+@insertTest(helper_name="parse", params_def=pt17_params_def,
+ params_str="2 3 ['Hello world!' 'How are you?']",
+ params=["2", "3", ["Hello world!", "How are you?"]])
+@insertTest(helper_name="parse", params_def=pt18_params_def,
+ params_str="ScanFile file.dat",
+ params=["ScanFile", "file.dat"])
+@insertTest(helper_name="parse", params_def=pt19_params_def,
+ params_str="'2 3'", params=["2 3"])
+@insertTest(helper_name="parse", params_def=pt20_params_def,
+ params_str="[[mot01 3][mot02 5]] ct01 999",
+ params=[[["mot01", "3"], ["mot02", "5"]], "ct01", "999"])
+@insertTest(helper_name="parse", params_def=pt21_params_def,
+ params_str="[[2 3][4 5]]",
+ params=[[["2", "3"], ["4", "5"]]])
+@insertTest(helper_name="parse", params_def=pt22_params_def,
+ params_str="1 [2 3]",
+ params=["1", ["2", "3"]])
+@insertTest(helper_name="parse", params_def=pt23_params_def,
+ params_str="2 3", params=["2", "3"])
class ParamParserWithDefTestCase(unittest.TestCase):
"""Unit tests for ParamParser class initialized with parameters
definition.
From f79d501d70af6903eb68dd5c818b39e17d39aedb Mon Sep 17 00:00:00 2001
From: zreszela
Date: Fri, 19 Oct 2018 16:07:55 +0200
Subject: [PATCH 229/652] Fix parsing of non-complete repeats
---
src/sardana/spock/parser.py | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/src/sardana/spock/parser.py b/src/sardana/spock/parser.py
index b01e57c6aa..65ca7b9df2 100644
--- a/src/sardana/spock/parser.py
+++ b/src/sardana/spock/parser.py
@@ -118,7 +118,7 @@ def _expect(self, toktype):
# Grammar rules follow
- def _params(self, params_def=None):
+ def _params(self, params_def=None, is_repeat=False):
"""Interpret parameter values by iterating over generated tokens
according to parameters definition.
@@ -151,7 +151,14 @@ def _params(self, params_def=None):
param_value = self._repeat_param(repeat_param_def,
last_param)
else:
- param_value = self._param()
+ try:
+ param_value = self._param()
+ except UnrecognizedParamValue:
+ # this exception may occur if repeat is not complete -
+ # uses default values
+ if is_repeat:
+ return params
+ raise
params.append(param_value)
return params
@@ -238,7 +245,7 @@ def _repeat(self, repeat_param_def):
if self._accept("RPAREN"):
repeat = []
else:
- repeat = self._params(repeat_param_def)
+ repeat = self._params(repeat_param_def, is_repeat=True)
# repetitions of single repeat parameters are not enclosed
# in parenthesis so remove it
if is_repeat_param_single(repeat_param_def):
From 0c870e460242f3f9495e1354f509a7b9182e2423 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Fri, 19 Oct 2018 16:17:44 +0200
Subject: [PATCH 230/652] (minor) rename variable
---
src/sardana/spock/parser.py | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/sardana/spock/parser.py b/src/sardana/spock/parser.py
index 65ca7b9df2..880ea77f60 100644
--- a/src/sardana/spock/parser.py
+++ b/src/sardana/spock/parser.py
@@ -144,12 +144,12 @@ def _params(self, params_def=None, is_repeat=False):
if self.nexttok is None:
break
if is_repeat_param(param_def):
- last_param = False
+ is_last_param = False
if param_idx == len_params_def - 1:
- last_param = True
+ is_last_param = True
repeat_param_def = param_def["type"]
param_value = self._repeat_param(repeat_param_def,
- last_param)
+ is_last_param)
else:
try:
param_value = self._param()
@@ -184,7 +184,7 @@ def _param(self):
raise UnrecognizedParamValue(msg)
return param
- def _repeat_param(self, repeat_param_def, last_param):
+ def _repeat_param(self, repeat_param_def, is_last_param):
"""Interpret repeat parameter.
Accepts repeat parameters using the following rules:
@@ -196,9 +196,9 @@ def _repeat_param(self, repeat_param_def, last_param):
:param repeat_param_def: repeat parameter definition
:type repeat_param_def: list
- :param last_param: whether this repeat parameter is the last in the
+ :param is_last_param: whether this repeat parameter is the last in the
definition
- :type last_param: bool
+ :type is_last_param: bool
:return: repeat parameter value
:rtype: list
"""
@@ -213,7 +213,7 @@ def _repeat_param(self, repeat_param_def, last_param):
self._expect("RPAREN")
else:
single = is_repeat_param_single(repeat_param_def)
- if last_param:
+ if is_last_param:
while True:
repeat = []
for _ in repeat_param_def:
From b63b10768470e021796ed04935da1047f54d1754 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 22 Oct 2018 12:48:56 +0200
Subject: [PATCH 231/652] Rename param_def variables
Rename param_def variables to not collide with eventual new parameter
examples.
---
src/sardana/spock/test/test_parser.py | 56 +++++++++------------------
1 file changed, 18 insertions(+), 38 deletions(-)
diff --git a/src/sardana/spock/test/test_parser.py b/src/sardana/spock/test/test_parser.py
index 403ff2c94f..80edc3ecfe 100644
--- a/src/sardana/spock/test/test_parser.py
+++ b/src/sardana/spock/test/test_parser.py
@@ -492,7 +492,7 @@ def parse(self, params_str, params):
}
]
-pt15_params_def = [
+extra1_params_def = [
{
"default_value": None,
"description": "Parameter",
@@ -520,27 +520,7 @@ def parse(self, params_str, params):
}
]
-pt16_params_def = [
- {
- "default_value": None,
- "description": "List of values",
- "max": None,
- "min": 1,
- "name": "numb_list",
- "type": [
- {
- "default_value": 2,
- "description": "value",
- "max": None,
- "min": 1,
- "name": "position",
- "type": "Float"
- }
- ]
- }
-]
-
-pt17_params_def = [
+extra2_params_def = [
{
"default_value": None,
@@ -576,7 +556,7 @@ def parse(self, params_str, params):
]
},
]
-pt18_params_def = [
+extra3_params_def = [
{
"default_value": None,
@@ -596,7 +576,7 @@ def parse(self, params_str, params):
},
]
-pt19_params_def = [
+extra4_params_def = [
{
"default_value": None,
@@ -617,7 +597,7 @@ def parse(self, params_str, params):
]
-pt20_params_def = [
+extra5_params_def = [
{
"default_value": None,
@@ -661,7 +641,7 @@ def parse(self, params_str, params):
"type": "Float"
}
]
-pt21_params_def = [
+extra6_params_def = [
{
"default_value": None,
@@ -690,7 +670,7 @@ def parse(self, params_str, params):
}
]
-pt22_params_def = [
+extra7_params_def = [
{
"default_value": None,
@@ -726,7 +706,7 @@ def parse(self, params_str, params):
]
}
]
-pt23_params_def = [
+extra8_params_def = [
{
"default_value": None,
@@ -747,6 +727,7 @@ def parse(self, params_str, params):
]
+# parameters examples tests
@insertTest(helper_name="parse", params_def=pt0_params_def,
params_str="", params=[])
@insertTest(helper_name="parse", params_def=pt1d_params_def,
@@ -828,29 +809,28 @@ def parse(self, params_str, params):
@insertTest(helper_name="parse", params_def=pt14_params_def,
params_str="[[[mot1 mot2] 3] [[mot3] 5]]",
params=[[[["mot1", "mot2"], "3"], [["mot3"], "5"]]])
-@insertTest(helper_name="parse", params_def=pt15_params_def,
+# extra tests for complex parameter values
+@insertTest(helper_name="parse", params_def=extra1_params_def,
params_str="ScanFile ['file.nxs' 'file.dat']",
params=["ScanFile", ["file.nxs", "file.dat"]])
-@insertTest(helper_name="parse", params_def=pt16_params_def,
- params_str="[1 [] 3]", params=[["1", [], "3"]])
-@insertTest(helper_name="parse", params_def=pt17_params_def,
+@insertTest(helper_name="parse", params_def=extra2_params_def,
params_str="2 3 ['Hello world!' 'How are you?']",
params=["2", "3", ["Hello world!", "How are you?"]])
-@insertTest(helper_name="parse", params_def=pt18_params_def,
+@insertTest(helper_name="parse", params_def=extra3_params_def,
params_str="ScanFile file.dat",
params=["ScanFile", "file.dat"])
-@insertTest(helper_name="parse", params_def=pt19_params_def,
+@insertTest(helper_name="parse", params_def=extra4_params_def,
params_str="'2 3'", params=["2 3"])
-@insertTest(helper_name="parse", params_def=pt20_params_def,
+@insertTest(helper_name="parse", params_def=extra5_params_def,
params_str="[[mot01 3][mot02 5]] ct01 999",
params=[[["mot01", "3"], ["mot02", "5"]], "ct01", "999"])
-@insertTest(helper_name="parse", params_def=pt21_params_def,
+@insertTest(helper_name="parse", params_def=extra6_params_def,
params_str="[[2 3][4 5]]",
params=[[["2", "3"], ["4", "5"]]])
-@insertTest(helper_name="parse", params_def=pt22_params_def,
+@insertTest(helper_name="parse", params_def=extra7_params_def,
params_str="1 [2 3]",
params=["1", ["2", "3"]])
-@insertTest(helper_name="parse", params_def=pt23_params_def,
+@insertTest(helper_name="parse", params_def=extra8_params_def,
params_str="2 3", params=["2", "3"])
class ParamParserWithDefTestCase(unittest.TestCase):
"""Unit tests for ParamParser class initialized with parameters
From 256fc31bf16a83da448a46085a0f410a6d42f11c Mon Sep 17 00:00:00 2001
From: zreszela
Date: Mon, 22 Oct 2018 12:51:17 +0200
Subject: [PATCH 232/652] Final rename of test class
---
src/sardana/spock/test/test_parser.py | 23 ++++++-----------------
1 file changed, 6 insertions(+), 17 deletions(-)
diff --git a/src/sardana/spock/test/test_parser.py b/src/sardana/spock/test/test_parser.py
index 80edc3ecfe..ad78f78b4c 100644
--- a/src/sardana/spock/test/test_parser.py
+++ b/src/sardana/spock/test/test_parser.py
@@ -30,20 +30,6 @@
from sardana.spock.parser import ParamParser
-class ParamParserTestCase(unittest.TestCase):
- """Unit tests for ParamParser class."""
-
- def parse(self, params_str, params):
- """Helper method to test parameters parsing. To be used with insertTest
- decorator.
- """
- p = ParamParser()
- result = p.parse(params_str)
- msg = "Parsing failed (result: %r; expected: %r)" %\
- (result, params)
- self.assertListEqual(result, params, msg)
-
-
pt0_params_def = []
pt1d_params_def = [
@@ -832,11 +818,14 @@ def parse(self, params_str, params):
params=["1", ["2", "3"]])
@insertTest(helper_name="parse", params_def=extra8_params_def,
params_str="2 3", params=["2", "3"])
-class ParamParserWithDefTestCase(unittest.TestCase):
- """Unit tests for ParamParser class initialized with parameters
- definition.
+class ParamParserTestCase(unittest.TestCase):
+ """Unit tests for ParamParser class. Mainly based on macro examples for
+ parameters definition.
"""
def parse(self, params_def, params_str, params):
+ """Helper method to test parameters parsing. To be used with
+ insertTest decorator.
+ """
p = ParamParser(params_def)
result = p.parse(params_str)
msg = "Parsing failed (result: %r; expected: %r)" % \
From 237fbe2b2ba8698688d1b30acdbfadfb9278c223 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Tue, 23 Oct 2018 09:37:38 +0200
Subject: [PATCH 233/652] (docs) Add link to plugins register in how-to-ctrls
---
doc/source/devel/howto_controllers/index.rst | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/doc/source/devel/howto_controllers/index.rst b/doc/source/devel/howto_controllers/index.rst
index 034f023086..34e093ebfb 100644
--- a/doc/source/devel/howto_controllers/index.rst
+++ b/doc/source/devel/howto_controllers/index.rst
@@ -6,7 +6,12 @@
Writing controllers
===================
-This chapter provides the necessary information to write controllers in sardana.
+This chapter provides the necessary information to write controllers in
+sardana.
+
+Before writing a new controller you should check the `controller plugin
+register `_.
+There's a high chance that somebody already wrote the plugin for your hardware.
An overview of the pool controller concept can be found
:ref:`here `.
From df2f61564f48bda046a3399a493cef7872151730 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Tue, 23 Oct 2018 14:37:50 +0200
Subject: [PATCH 234/652] Move parser from spock to util
---
src/sardana/{spock => util}/parser.py | 0
src/sardana/{spock => util}/test/test_parser.py | 2 +-
2 files changed, 1 insertion(+), 1 deletion(-)
rename src/sardana/{spock => util}/parser.py (100%)
rename src/sardana/{spock => util}/test/test_parser.py (99%)
diff --git a/src/sardana/spock/parser.py b/src/sardana/util/parser.py
similarity index 100%
rename from src/sardana/spock/parser.py
rename to src/sardana/util/parser.py
diff --git a/src/sardana/spock/test/test_parser.py b/src/sardana/util/test/test_parser.py
similarity index 99%
rename from src/sardana/spock/test/test_parser.py
rename to src/sardana/util/test/test_parser.py
index ad78f78b4c..c8a4c77eac 100644
--- a/src/sardana/spock/test/test_parser.py
+++ b/src/sardana/util/test/test_parser.py
@@ -27,7 +27,7 @@
from taurus.external import unittest
from taurus.test import insertTest
-from sardana.spock.parser import ParamParser
+from sardana.util.parser import ParamParser
pt0_params_def = []
From ef7bfcca6ac85ea6576f44d103f2bfbedc37134e Mon Sep 17 00:00:00 2001
From: zreszela
Date: Tue, 23 Oct 2018 14:38:17 +0200
Subject: [PATCH 235/652] Fix parser imports
---
src/sardana/macroserver/msmacromanager.py | 2 +-
src/sardana/spock/spockms.py | 2 +-
src/sardana/taurus/core/tango/sardana/test/test_macro.py | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/sardana/macroserver/msmacromanager.py b/src/sardana/macroserver/msmacromanager.py
index 992d0a03f8..38143f3055 100644
--- a/src/sardana/macroserver/msmacromanager.py
+++ b/src/sardana/macroserver/msmacromanager.py
@@ -70,7 +70,7 @@
from sardana.macroserver.msexception import UnknownMacroLibrary, \
LibraryError, UnknownMacro, MissingEnv, AbortException, StopException, \
MacroServerException, UnknownEnv
-from sardana.spock.parser import ParamParser
+from sardana.util.parser import ParamParser
# 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
diff --git a/src/sardana/spock/spockms.py b/src/sardana/spock/spockms.py
index cca3035748..2a661f45b4 100755
--- a/src/sardana/spock/spockms.py
+++ b/src/sardana/spock/spockms.py
@@ -37,7 +37,7 @@
from sardana.sardanautils import is_pure_str, is_non_str_seq
from sardana.spock import genutils
-from sardana.spock.parser import ParamParser
+from sardana.util.parser import ParamParser
from sardana.spock.inputhandler import SpockInputHandler, InputHandler
from sardana import sardanacustomsettings
diff --git a/src/sardana/taurus/core/tango/sardana/test/test_macro.py b/src/sardana/taurus/core/tango/sardana/test/test_macro.py
index 8ed8d271f5..0f6090537f 100755
--- a/src/sardana/taurus/core/tango/sardana/test/test_macro.py
+++ b/src/sardana/taurus/core/tango/sardana/test/test_macro.py
@@ -41,7 +41,7 @@
pt14d_param_def)
# TODO: Use unittest.mock instead of this fake class.
from sardana.macroserver.mstypemanager import TypeManager
-from sardana.spock.parser import ParamParser
+from sardana.util.parser import ParamParser
class FakeMacroServer(object):
From e1ce69ffcf6e40d8f5d5a3830142f186eb0e78cc Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Tue, 23 Oct 2018 16:22:04 +0200
Subject: [PATCH 236/652] Add method to ConfigurationItems classes
---
src/sardana/pool/poolmeasurementgroup.py | 78 +++++++++++++++++++++++-
1 file changed, 77 insertions(+), 1 deletion(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 6dad6ee9e1..6126237633 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -127,12 +127,22 @@ def _to_fqdn(name, logger=None):
class ConfigurationItem(object):
def __init__(self, element, conf=None):
self._element = weakref.ref(element)
+
+ if conf is None:
+ conf = {'acq_synch': None}
+ else:
+ conf['acq_synch'] = None
+
if conf is not None:
self.__dict__.update(conf)
def __getattr__(self, item):
return getattr(self.element, item)
+ def get_config(self):
+ """ Returns the configuration dictionary"""
+ raise NotImplementedError()
+
def get_element(self):
"""Returns the element associated with this item"""
return self._element()
@@ -147,11 +157,77 @@ def set_element(self, element):
class ControllerConfiguration(ConfigurationItem):
"""Configuration: 'timer', 'monitor', 'synchronization', 'channels'"""
+ def __init__(self, element, conf=None):
+ self._channels = []
+ self._channels_enabled = []
+ self._channels_disabled = []
+ self.enabled = False
+
+ ConfigurationItem.__init__(self, element, conf)
+
+ def get_config(self):
+ config = {}
+ # the external controllers: Tango attributes does not have timer and
+ # monitor
+ config['timer'] = self.timer.full_name
+ config['monitor'] = self.monitor.full_name
+
+ if isinstance(self.synchronizer, (str, unicode)):
+ synchronizer = self.synchronizer
+ else:
+ synchronizer = self.synchronizer.full_name
+ config['synchronizer'] = synchronizer
+ config['synchronization'] = self.synchronization
+ config['channels'] = {}
+ for ch_item in self._channels:
+ config['channels'][ch_item.full_name] = ch_item.get_config()
+ return config
+
+ def add_channel(self, channel_item):
+ self._channels.append(channel_item)
+ if channel_item.enabled:
+ self.enabled = True
+ if self._channels_enabled is None:
+ self._channels_enabled = []
+ self._channels_enabled.append(channel_item)
+ else:
+ if self._channels_disabled is None:
+ self._channels_disabled = []
+ self._channels_disabled.append(channel_item)
+
+ def get_channels(self, enabled=None):
+ if enabled is None:
+ return list(self.channels)
+ elif enabled:
+ return list(self._channels_enabled)
+ else:
+ return list(self._channels_disabled)
+
class ChannelConfiguration(ConfigurationItem):
- """Configuration: 'id', 'enabled', 'output', 'plot_type', 'plot_axes',
+ """Configuration: 'index', 'enabled', 'output', 'plot_type', 'plot_axes',
'label', 'scale', 'plot_color'"""
+ def get_config(self):
+ config = {}
+ config['index'] = self.index
+ config['name'] = self.name
+ config['full_name'] = self.full_name
+ config['source'] = self.source
+ config['enabled'] = self.enabled
+ config['label'] = self.label
+ config['ndim'] = self.ndim
+ config['output'] = self.output
+ config['plot_type'] = self.plot_type
+ config['plot_axes'] = self.plot_axes
+ config['conditioning'] = self.conditioning
+ config['normalization'] = self.normalization
+ config['data_type'] = self.data_type
+ config['data_units'] = self.data_units
+ config['nexus_path'] = self.nexus_path
+ config['shape'] = self.shape
+ return config
+
class MeasurementConfiguration(object):
"""
From 2bac2d67432e483cd574c216d5757c5caa9a4ccb Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 24 Oct 2018 10:52:51 +0200
Subject: [PATCH 237/652] Update doc strings
---
.../taurus/core/tango/sardana/macro.py | 20 +++++++------------
.../taurus/core/tango/sardana/macroserver.py | 12 ++++-------
2 files changed, 11 insertions(+), 21 deletions(-)
diff --git a/src/sardana/taurus/core/tango/sardana/macro.py b/src/sardana/taurus/core/tango/sardana/macro.py
index 4b73b24d68..8c6b45935a 100755
--- a/src/sardana/taurus/core/tango/sardana/macro.py
+++ b/src/sardana/taurus/core/tango/sardana/macro.py
@@ -1274,8 +1274,9 @@ def fromPlainText(self, plainText):
def ParamFactory(paramInfo):
- """Factory method returning param element, depends of the paramInfo argument."""
-
+ """Factory method returning param element, depends of the paramInfo
+ argument.
+ """
if isinstance(paramInfo.get('type'), list):
param = RepeatParamNode(param=paramInfo)
if param.min() > 0:
@@ -1286,21 +1287,14 @@ def ParamFactory(paramInfo):
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.
-
- Default values allow in ParamRepeat parameters or the last single ones
+ """Create of the macro node object.
:param macro_name: (str) macro name
:param params_def: (list) list of param definitions
- :param macro_params: (sequence[str]) list of parameter values
-
- :return (lxml.etree._Element) macro XML element
+ :param macro_params: (sequence[str]) list of parameter values, if repeat
+ parameters are used parameter values may be sequences itself.
- .. 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.
+ :return (MacroNode) macro node object
"""
macro_node = MacroNode(name=macro_name, params_def=params_def)
macro_node.fromList(macro_params)
diff --git a/src/sardana/taurus/core/tango/sardana/macroserver.py b/src/sardana/taurus/core/tango/sardana/macroserver.py
index ea381f2c9f..a21eeb02dd 100755
--- a/src/sardana/taurus/core/tango/sardana/macroserver.py
+++ b/src/sardana/taurus/core/tango/sardana/macroserver.py
@@ -490,16 +490,12 @@ def _clearRunMacro(self):
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
+ """Creation of the macro XML object.
:param macro_name: (str) macro name
- :param macro_params: (sequence[str]) list of parameter values
-
+ :param macro_params: (sequence[str]) list of parameter values,
+ if repeat parameters are used parameter values may be sequences
+ itself.
:return (lxml.etree._Element) macro XML element
"""
macro_info = self.macro_server.getMacroInfoObj(macro_name)
From 26feef05246441cd1903bcd6ccffa1065ddf5132 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Wed, 24 Oct 2018 12:42:30 +0200
Subject: [PATCH 238/652] Document spock syntax
---
doc/source/users/spock.rst | 139 ++++++++++++++++++++++++++++++++-----
1 file changed, 121 insertions(+), 18 deletions(-)
diff --git a/doc/source/users/spock.rst b/doc/source/users/spock.rst
index 43c43303a8..f974ce48f0 100755
--- a/doc/source/users/spock.rst
+++ b/doc/source/users/spock.rst
@@ -492,24 +492,127 @@ case a macro fails when being executed.
Valid values are ``output``, ``critical``, ``error``, ``warning``,
``info``, ``debug`` and ``result``.
-*Spock syntax* and *Advanced spock syntax*
-------------------------------------------
-
-*Spock syntax* is based on space separated list of parameter values. Not all macros
-are allowed to be used with the spock syntax. Restrictions appear for those macros
-using :ref:`repeat parameters ` as argument. The
-*Spock syntax* would not allow:
-
-1. macros defining more than one repeat parameter
-2. macros defining repeat parameter which is not at the end of the parameters definition
-3. macros defining nested repeat parameters
-
-To overcome these restrictions an *Advanced spock syntax* was developed, this syntax introduces the
-use of square brackets to group the repeat parameters and its repetitions.
-The *Spock Syntax* was extended for the cases 1 and 2 in case only one repetion of the repeat
-parameter is needed, this extension assumes that the parameter values passed by the user are a single
-repetition of the repeat parameter.
-A set of macro examples using both syntaxes can be found in :ref:`sardana-devel-macro-parameter-examples`.
+Spock syntax
+------------
+
+*Spock syntax* is used to execute macros. It is based on space
+separated list of parameter values. If the string parameter values contain
+spaces itself these **must** be enclosed in quotes, either single quotes
+``''`` or double quotes ``""``.
+
+The spock syntax was extended with the use of square brackets ``[]`` for
+macros which define
+:ref:`repeat parameters ` as arguments.
+Repeat parameter values must be enclosed in square brackets. If the repeat
+parameter is composed from more than one internal parameter its every
+repetition must be enclosed in another square brackets as well.
+
+For example, the ``move_with_timeout`` macro::
+
+ class move_with_timeout(Macro):
+ """Execute move with a timeout"""
+
+ param_def = [
+ ['m_p_pair',
+ [['motor', Type.Motor, None, 'Motor to move'],
+ ['pos', Type.Float, None, 'Position to move to']],
+ None, 'List of motor/position pairs'],
+ ['timeout', Type.Float, None, 'Timeout value']
+ ]
+
+ def run(self, *args, **kwargs):
+ pass
+
+Must use the square brackets for the ``m_p_pair`` parameter and its
+repeats:
+
+.. sourcecode:: spock
+
+ Door_1 [1]: move_with_timeout [[th 8.4] [tth 16.8]] 50
+
+However for the commodity reasons the square brackets may be skipped. The
+following examples explain in which cases.
+
+Repeat parameter is the last one
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When the repeat parameter is the last one in the parameters definition
+both square brackets (for the repeat parameter and for the repetition) may
+be skipped.
+
+For example, the ``move`` macro::
+
+ class move(Macro):
+ """Execute move"""
+
+ param_def = [
+ ['m_p_pair',
+ [['motor', Type.Motor, None, 'Motor to move'],
+ ['pos', Type.Float, None, 'Position to move to']],
+ None, 'List of motor/position pairs']
+ ]
+
+ def run(self, *args, **kwargs):
+ pass
+
+May skip the square brackets for the ``m_p_pair`` parameter and its
+repeats:
+
+.. sourcecode:: spock
+
+ Door_1 [1]: move th 8.4 tth 16.8
+
+This is equivalent to:
+
+.. sourcecode:: spock
+
+ Door_1 [1]: move [[th 8.4] [tth 16.8]]
+
+Repeat parameter has only one internal parameter
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When the repeat parameter contains only one internal parameter the square
+brackets for the repetition **must** be skipped.
+
+For example, the ``power_motor`` macro::
+
+ class power_motor(Macro):
+ """Power on/off motor(s)"""
+
+ param_def = [
+ ['motor_list', [['motor', Type.Motor, None, 'motor name']],
+ None, 'List of motors'],
+ ['power_on', Type.Boolean, None, 'motor power state']
+ ]
+
+ def run(self, *args, **kwargs):
+ pass
+
+Must use the square brackets for the ``motor_list`` parameter but not for
+its repeats:
+
+.. sourcecode:: spock
+
+ Door_1 [1]: power_motor [th tth] True
+
+Repeat parameter has only one internal parameter and only one repetition value
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When the repeat parameter contains only one internal parameter and you
+would like to pass only one repetition value then the square brackets for
+the repeat parameter may be skipped as well resulting in no square brackets
+being used.
+
+This assumes the ``power_motor`` macro from the previous example.
+The following two macro executions are equivalent:
+
+.. sourcecode:: spock
+
+ Door_1 [1]: power_motor th True
+ Door_1 [2]: power_motor [th] True
+
+A set of macro examples defining complex repeat parameters can be found in
+:ref:`sardana-devel-macro-parameter-examples`.
You can see the invocation example for each of these macros in its docstring.
From 9a6f0e03a2f4812f4419c43e5cf6e1f6f89a9eca Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Wed, 24 Oct 2018 15:25:03 +0200
Subject: [PATCH 239/652] Refactor configuration classes
Refactor classes to use them with the synchronizer controllers and
channels
---
src/sardana/pool/poolmeasurementgroup.py | 53 +++++-------------------
1 file changed, 11 insertions(+), 42 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 6126237633..725ae36337 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -128,11 +128,6 @@ class ConfigurationItem(object):
def __init__(self, element, conf=None):
self._element = weakref.ref(element)
- if conf is None:
- conf = {'acq_synch': None}
- else:
- conf['acq_synch'] = None
-
if conf is not None:
self.__dict__.update(conf)
@@ -165,24 +160,6 @@ def __init__(self, element, conf=None):
ConfigurationItem.__init__(self, element, conf)
- def get_config(self):
- config = {}
- # the external controllers: Tango attributes does not have timer and
- # monitor
- config['timer'] = self.timer.full_name
- config['monitor'] = self.monitor.full_name
-
- if isinstance(self.synchronizer, (str, unicode)):
- synchronizer = self.synchronizer
- else:
- synchronizer = self.synchronizer.full_name
- config['synchronizer'] = synchronizer
- config['synchronization'] = self.synchronization
- config['channels'] = {}
- for ch_item in self._channels:
- config['channels'][ch_item.full_name] = ch_item.get_config()
- return config
-
def add_channel(self, channel_item):
self._channels.append(channel_item)
if channel_item.enabled:
@@ -195,6 +172,17 @@ def add_channel(self, channel_item):
self._channels_disabled = []
self._channels_disabled.append(channel_item)
+ def update_state(self):
+ self.enabled = False
+ self._channels_enabled = []
+ self._channels_disabled = []
+ for channel_item in self._channels:
+ if channel_item.enabled:
+ self.enabled = True
+ self._channels_enabled.append(channel_item)
+ else:
+ self._channels_disabled.append(channel_item)
+
def get_channels(self, enabled=None):
if enabled is None:
return list(self.channels)
@@ -208,25 +196,6 @@ class ChannelConfiguration(ConfigurationItem):
"""Configuration: 'index', 'enabled', 'output', 'plot_type', 'plot_axes',
'label', 'scale', 'plot_color'"""
- def get_config(self):
- config = {}
- config['index'] = self.index
- config['name'] = self.name
- config['full_name'] = self.full_name
- config['source'] = self.source
- config['enabled'] = self.enabled
- config['label'] = self.label
- config['ndim'] = self.ndim
- config['output'] = self.output
- config['plot_type'] = self.plot_type
- config['plot_axes'] = self.plot_axes
- config['conditioning'] = self.conditioning
- config['normalization'] = self.normalization
- config['data_type'] = self.data_type
- config['data_units'] = self.data_units
- config['nexus_path'] = self.nexus_path
- config['shape'] = self.shape
- return config
class MeasurementConfiguration(object):
From 2a19c6165c6ea3310e424f1cdc2758731659b86d Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Wed, 24 Oct 2018 15:25:26 +0200
Subject: [PATCH 240/652] Add SyncrhonizerConfiguration class
---
src/sardana/pool/poolmeasurementgroup.py | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 725ae36337..5957778254 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -197,6 +197,13 @@ class ChannelConfiguration(ConfigurationItem):
'label', 'scale', 'plot_color'"""
+class SynchronizerConfiguration(ConfigurationItem):
+
+ def __init__(self, element, conf=None):
+ self.enabled = False
+
+ ConfigurationItem.__init__(self, element, conf)
+
class MeasurementConfiguration(object):
"""
From e64ad8870193146f110b8378f659ae7d576cc024 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Wed, 24 Oct 2018 16:59:56 +0200
Subject: [PATCH 241/652] Add enabled attribute to ConfigurationItem
Add enabled attribute to the ConfigurationItem class with True as
default value.
---
src/sardana/pool/poolmeasurementgroup.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 5957778254..f334499773 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -127,6 +127,7 @@ def _to_fqdn(name, logger=None):
class ConfigurationItem(object):
def __init__(self, element, conf=None):
self._element = weakref.ref(element)
+ self.enabled = True
if conf is not None:
self.__dict__.update(conf)
@@ -153,12 +154,11 @@ class ControllerConfiguration(ConfigurationItem):
"""Configuration: 'timer', 'monitor', 'synchronization', 'channels'"""
def __init__(self, element, conf=None):
+ ConfigurationItem.__init__(self, element, conf)
+ self.enabled = False
self._channels = []
self._channels_enabled = []
self._channels_disabled = []
- self.enabled = False
-
- ConfigurationItem.__init__(self, element, conf)
def add_channel(self, channel_item):
self._channels.append(channel_item)
@@ -200,9 +200,9 @@ class ChannelConfiguration(ConfigurationItem):
class SynchronizerConfiguration(ConfigurationItem):
def __init__(self, element, conf=None):
+ ConfigurationItem.__init__(self, element, conf)
self.enabled = False
- ConfigurationItem.__init__(self, element, conf)
class MeasurementConfiguration(object):
From cabf4c3ff5c90d128f549a6787d8128d85d2cb34 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Wed, 24 Oct 2018 17:02:29 +0200
Subject: [PATCH 242/652] Refactor MeasurementConfiguration class
Refactor class to update the internals attribute at set the
configuration.
Add protections to validate the configuration set by the user.
---
src/sardana/pool/poolmeasurementgroup.py | 713 +++++++++--------------
1 file changed, 281 insertions(+), 432 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index f334499773..13e9075685 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -217,224 +217,329 @@ def __init__(self, parent=None):
self._parent = None
if parent is not None:
self._parent = weakref.proxy(parent)
+
self._config = None
self._use_fqdn = True
- self._clean_variables()
-
- def _clean_variables(self):
- # list of controller with channels enabled.
- self.enabled_ctrls = []
- # list of timers and monitors by synchronization type.
- self.sw_sync_timers_enabled = []
- self.sw_sync_monitors_enabled = []
- self.sw_start_timers_enabled = []
- self.sw_start_monitors_enabled = []
- self.hw_sync_timers_enabled = []
- self.hw_sync_monitors_enabled = []
- # dict with channel and its acquisition synchronization
- # key: PoolBaseChannel; value: AcqSynch
- self.channel_to_acq_synch = {}
- # dict with controller and its acquisition synchronization
- # key: PoolController; value: AcqSynch
- self.ctrl_to_acq_synch = {}
- # TODO: Do the documentation
- self.ctrl_sw_sync = {}
- self.ctrl_hw_sync = {}
- self.ctrl_sw_start = {}
- self.ctrl_0d_sync = {}
- self.ctrl_tg_sync = {}
- # TODO: Do the documentation
- self.sw_sync_timer = None
- self.sw_sync_monitor = None
- self.sw_start_timer = None
- self.sw_start_monitor = None
- self.hw_sync_timer = None
- self.hw_sync_monitor = None
-
- def __check_config(func):
- def wrapper(self, *args, **kwargs):
- if self._config is None:
- raise RuntimeError('The configuration is empty.')
- return func(self, *args, **kwargs)
- return wrapper
-
- def __getitem__(self, item):
- return self._config.__getitem__(item)
- @property
- @__check_config
- def configuration(self):
- return self._config
+ # Structure to store the controllers and their channels
+ self._timerable_ctrls = {}
+ self._zerod_ctrls = []
+ self._synch_ctrls = {}
+ self._other_ctrls = []
+ self._master_timer_sw = None
+ self._master_monitor_sw = None
+ self._master_timer_sw_start = None
+ self._master_monitor_sw_start = None
+ self._label = None
+ self._description = None
+ self._master_timer = None
+ self._master_monitor = None
+ self._user_confg = {}
+ self._element_acq_synch = {}
+
+ def get_acq_synch_by_element(self, element):
+ return self._element_acq_synch[element.id]
+
+ def _filter_ctrls(self, ctrls, enabled):
+ if enabled is None:
+ return ctrls
+
+ filtered_ctrls = []
+ for ctrl in ctrls:
+ if ctrl.enabled == enabled:
+ filtered_ctrls.append(ctrl)
+ return filtered_ctrls
+
+ def get_timerable_ctrls(self, acq_synch=None, enabled=None):
+ timerable_ctrls = []
+ if acq_synch is None:
+ for ctrls in self._timerable_ctrls.values():
+ timerable_ctrls += ctrls
+ elif isinstance(acq_synch, list):
+ acq_synch_list = acq_synch
+ for acq_synch in acq_synch_list:
+ timerable_ctrls += self._timerable_ctrls[acq_synch]
+ else:
+ timerable_ctrls = list(self._timerable_ctrls[acq_synch])
+
+ return self._filter_ctrls(timerable_ctrls, enabled)
+
+ def get_zerod_ctrls(self, enabled=None):
+ return self._filter_ctrls(self._zerod_ctrls, enabled)
- @configuration.setter
- def configuration(self, config=None):
- self._build_configuration(config)
+ def get_synch_ctrls(self, enabled=None):
+ return self._filter_ctrls(self._synch_ctrls, enabled)
+
+ def get_master_timer_software(self):
+ return self._master_timer_sw
+
+ def get_master_monitor_software(self):
+ return self._master_monitor_sw
+
+ def get_master_timer_software_start(self):
+ return self._master_monitor_sw_start
+
+ def get_master_monitor_software_start(self):
+ return self._master_timer_sw_start
+
+ def get_configuration_for_user(self):
+ return self._user_confg
def set_configuration_from_user(self, cfg, to_fqdn=True):
- config = {}
user_elements = self._parent.get_user_elements()
- pool = self._parent.pool
if len(user_elements) == 0:
- # All timers were disabled
- default_timer = None
- else:
- default_timer = user_elements[0].full_name
-
- timer_name = cfg.get('timer', default_timer)
- monitor_name = cfg.get('monitor', default_timer)
- if to_fqdn:
- timer_name = _to_fqdn(timer_name, logger=self._parent)
- config['timer'] = pool.get_element_by_full_name(timer_name)
- if to_fqdn:
- monitor_name = _to_fqdn(monitor_name, logger=self._parent)
- config['monitor'] = pool.get_element_by_full_name(monitor_name)
- config['controllers'] = controllers = {}
-
- for c_name, c_data in cfg['controllers'].items():
+ # All channels were disabled
+ raise ValueError('The configuration has all the channels disabled')
+
+ pool = self._parent.pool
+
+ label = cfg.get('label', self._parent.name)
+ description = cfg.get('description', self.DFT_DESC)
+
+ timerable_ctrls = {AcqSynch.HardwareGate: [],
+ AcqSynch.HardwareStart: [],
+ AcqSynch.HardwareTrigger: [],
+ AcqSynch.SoftwareStart: [],
+ AcqSynch.SoftwareTrigger: [],
+ AcqSynch.SoftwareGate: []}
+ zerod_ctrls = []
+ synch_ctrls = []
+ other_ctrls = []
+ master_timer_sw = None
+ master_monitor_sw = None
+ master_timer_sw_start = None
+ master_monitor_sw_start = None
+ master_timer = None
+ master_monitor = None
+ element_acq_synch = {}
+ master_timer_idx_sw = float("+inf")
+ master_monitor_idx_sw = float("+inf")
+ user_elem_ids = {}
+ user_config = {}
+
+ # Update the configuration for user
+ user_config['controllers'] = {}
+ user_config = {}
+ if 'timer' in cfg:
+ user_config['timer'] = cfg['timer']
+ if 'monitor' in cfg:
+ user_config['monitor'] = cfg['monitor']
+ user_config['controllers'] = {}
+ user_config['label'] = label
+ user_config['description'] = description
+
+ for ctrl_name, ctrl_data in cfg['controllers'].items():
# backwards compatibility for measurement groups created before
# implementing feature-372:
# https://sourceforge.net/p/sardana/tickets/372/
# WARNING: this is one direction backwards compatibility - it just
# reads channels from the units, but does not write channels to the
# units back
- if 'units' in c_data:
- c_data = c_data['units']['0']
+ if 'units' in ctrl_data:
+ ctrl_data = ctrl_data['units']['0']
# discard controllers which don't have items (garbage)
- ch_count = len(c_data['channels'])
+ ch_count = len(ctrl_data['channels'])
if ch_count == 0:
continue
- external = c_name.startswith('__')
+ external = ctrl_name.startswith('__')
if external:
- ctrl = c_name
+ ctrl = ctrl_name
else:
if to_fqdn:
- c_name = _to_fqdn(c_name, logger=self._parent)
- ctrl = pool.get_element_by_full_name(c_name)
+ ctrl_name = _to_fqdn(ctrl_name, logger=self._parent)
+ ctrl = pool.get_element_by_full_name(ctrl_name)
assert ctrl.get_type() == ElementType.Controller
- controllers[ctrl] = ctrl_data = {}
- # exclude external and not timerable elements
- if not external and ctrl.is_timerable():
- timer_name = c_data['timer']
- if to_fqdn:
- timer_name = _to_fqdn(timer_name, logger=self._parent)
- timer = pool.get_element_by_full_name(timer_name)
- ctrl_data['timer'] = timer
- monitor_name = c_data['monitor']
+ user_config['controllers'][ctrl_name] = user_config_ctrl = {}
+ ctrl_conf = {}
+
+ synchronizer = ctrl_data.get('synchronizer', None)
+ conf_synch = None
+ if synchronizer is None or synchronizer == 'software':
+ ctrl_conf['synchronizer'] = 'software'
+ user_config_ctrl['synchronizer'] = 'software'
+ else:
if to_fqdn:
- monitor_name = _to_fqdn(monitor_name, logger=self._parent)
- monitor = pool.get_element_by_full_name(monitor_name)
- ctrl_data['monitor'] = monitor
- synchronizer = c_data.get('synchronizer')
- # for backwards compatibility purposes
- # protect measurement groups without synchronizer defined
- if synchronizer is None:
- synchronizer = 'software'
- elif synchronizer != 'software':
- if to_fqdn:
- synchronizer = _to_fqdn(synchronizer,
- logger=self._parent)
- synchronizer = pool.get_element_by_full_name(synchronizer)
- ctrl_data['synchronizer'] = synchronizer
+ synchronizer = _to_fqdn(synchronizer,
+ logger=self._parent)
+
+ user_config_ctrl['synchronizer'] = synchronizer
+ pool_synch = pool.get_element_by_full_name(synchronizer)
+ pool_synch_ctrl = pool_synch.controller
+ conf_synch = SynchronizerConfiguration(pool_synch)
+ conf_synch_ctrl = None
+ if len(synch_ctrls) > 0:
+ conf_synch_ctrl = None
+ for conf_ctrl in synch_ctrls:
+ if pool_synch_ctrl == conf_ctrl.element:
+ conf_synch_ctrl = conf_ctrl
+ if conf_synch_ctrl is None:
+ conf_synch_ctrl = ControllerConfiguration(pool_synch_ctrl)
+ conf_synch_ctrl.add_channel(conf_synch)
+ synch_ctrls.append(conf_synch_ctrl)
+ ctrl_conf['synchronizer'] = conf_synch
+
+ try:
+ synchronization = ctrl_data['synchronization']
+ except KeyError:
+ # backwards compatibility for configurations before SEP6
try:
- synchronization = c_data['synchronization']
- except KeyError:
- # backwards compatibility for configurations before SEP6
- synchronization = c_data['trigger_type']
+ synchronization = ctrl_data['trigger_type']
msg = ("trigger_type configuration parameter is deprecated"
" in favor of synchronization. Re-apply "
"configuration in order to upgrade.")
self._parent.warning(msg)
- ctrl_data['synchronization'] = synchronization
- ctrl_data['channels'] = channels = {}
- for ch_name, ch_data in c_data['channels'].items():
+ except KeyError:
+ synchronization = None
+
+ ctrl_conf['synchronization'] = synchronization
+ user_config_ctrl['synchronization'] = synchronization
+ ctrl_conf['timer'] = None
+ ctrl_conf['monitor'] = None
+ ctrl_item = ControllerConfiguration(ctrl, ctrl_conf)
+
+ acq_synch = None
+ if not external and ctrl.is_timerable():
+ is_software = synchronizer == 'software'
+ acq_synch = AcqSynch.from_synch_type(is_software,
+ synchronization)
+ element_acq_synch[ctrl.id] = acq_synch
+
+ ctrl_enabled = False
+ if 'channels' in ctrl_data:
+ user_config_ctrl['channels'] = user_config_channel = {}
+ for ch_name, ch_data in ctrl_data['channels'].items():
+
if external:
validator = TangoAttributeNameValidator()
params = validator.getParams(ch_data['full_name'])
- params['pool'] = self._parent.pool
+ params['pool'] = pool
channel = PoolExternalObject(**params)
else:
if to_fqdn:
ch_name = _to_fqdn(ch_name, logger=self._parent)
channel = pool.get_element_by_full_name(ch_name)
- channels[channel] = dict(ch_data)
- config['label'] = cfg.get('label', self._parent.name)
- config['description'] = cfg.get('description', self.DFT_DESC)
- self._use_fqdn = to_fqdn
- self._build_configuration(config)
+ ch_data = self._fill_channel_data(channel, ch_data)
+ user_config_channel[ch_name] = ch_data
+ ch_item = ChannelConfiguration(channel, ch_data)
+ ch_item.controller = ctrl_item
+ ctrl_item.add_channel(ch_item)
+ if ch_item.enabled:
+ user_elem_ids[ch_item.index] = channel.id
- def get_configuration_for_user(self):
- cfg = self._config
- config = {}
+ # Update controller timer
+ if ch_name == ctrl_data['timer']:
+ ctrl_item.timer = ch_item
- config['timer'] = cfg['timer'].full_name
- config['monitor'] = cfg['monitor'].full_name
- config['controllers'] = controllers = {}
+ # Update controller monitor
+ if ch_name == ctrl_data['monitor']:
+ ctrl_item.monitor = ch_item
- for c, c_data in cfg['controllers'].items():
- ctrl_name = c
- if not isinstance(c, (str, unicode)):
- ctrl_name = c.full_name
- external = ctrl_name.startswith('__')
- controllers[ctrl_name] = ctrl_data = {}
- if not external and c.is_timerable():
- if 'timer' in c_data:
- ctrl_data['timer'] = c_data['timer'].full_name
- if 'monitor' in c_data:
- ctrl_data['monitor'] = c_data['monitor'].full_name
- if 'synchronizer' in c_data:
- synchronizer = c_data['synchronizer']
- if synchronizer != 'software':
- synchronizer = synchronizer.full_name
- ctrl_data['synchronizer'] = synchronizer
- if 'synchronization' in c_data:
- ctrl_data['synchronization'] = c_data['synchronization']
- ctrl_data['channels'] = channels = {}
- for ch, ch_data in c_data['channels'].items():
- channels[ch.full_name] = dict(ch_data)
-
- config['label'] = cfg['label']
- config['description'] = cfg['description']
- return config
-
- def _build_channel_defaults(self, channel_data, channel):
+ # Update measurement configuration master timer
+ if ch_name == cfg.get('timer', None):
+ master_timer = ch_item
+
+ # Update measurement configuration master timer
+ if ch_name == cfg.get('monitor', None):
+ master_monitor = ch_item
+
+ if ch_item.enabled:
+ ctrl_enabled = True
+
+ if acq_synch is not None:
+ element_acq_synch[channel.id] = acq_synch
+
+ # Update syncrhonizer state
+ if conf_synch is not None:
+ conf_synch.enabled = ctrl_enabled
+
+ # validate if the timer and monitor are disabled if the
+ # controller is enabled
+ if ctrl_enabled and not ctrl_item.timer.enabled and \
+ not ctrl_item.monitor.enabled:
+ timer_label = ctrl_item.timer.label
+ monitor_label = ctrl_item.monitor.label
+
+ err_msg = 'The channel {0} used as timer and the channel ' \
+ '{1} used as monitor are disabled. One of them ' \
+ 'must be enabled'.format(timer_label, monitor_label)
+ raise ValueError(err_msg)
+
+ if not external and ctrl.is_timerable():
+ timerable_ctrls[acq_synch].append(ctrl_item)
+ # Find master timer/monitor the system take the channel with
+ # less index
+ if ctrl_item.synchronizer == 'software':
+ if ctrl_item.timer.index < master_timer_idx_sw:
+ master_timer_sw = ctrl_item.timer
+ master_timer_idx_sw = ctrl_item.timer.index
+ if ctrl_item.monitor.index < master_monitor_idx_sw:
+ master_monitor_sw = ctrl_item.monitor
+ master_monitor_idx_sw = ctrl_item.monitor.index
+ elif ctrl.get_ctrl_types()[0] == ElementType.ZeroDExpChannel:
+ zerod_ctrls.append(ctrl_item)
+ else:
+ other_ctrls.append(ctrl_item)
+
+ # Update synchronizer controller states
+ for conf_synch_ctrl in synch_ctrls:
+ conf_synch_ctrl.update_state()
+
+
+ # Update internals values
+ self._master_monitor = master_monitor
+ self._master_timer = master_timer
+ self._label = label
+ self._description = description
+ self._timerable_ctrls = timerable_ctrls
+ self._zerod_ctrls = zerod_ctrls
+ self._synch_ctrls = synch_ctrls
+ self._other_ctrls = other_ctrls
+ self._master_timer_sw = master_timer_sw
+ self._master_monitor_sw = master_monitor_sw
+ self._element_acq_synch = element_acq_synch
+ self._user_confg = user_config
+
+ # sorted ids may not be consecutive (if a channel is disabled)
+ indexes = sorted(user_elem_ids.keys())
+ user_elem_ids_list = [user_elem_ids[idx] for idx in indexes]
+ for conf_synch_ctrl in synch_ctrls:
+ for conf_synch in conf_synch_ctrl.get_channels(enabled=True):
+ user_elem_ids_list.append(conf_synch.id)
+ self._parent.set_user_element_ids(user_elem_ids_list)
+
+ def _fill_channel_data(self, channel, channel_data):
"""
Fills the channel default values for the given channel dictionary
"""
- external_from_name = isinstance(channel, (str, unicode))
+ name = channel.name
+ full_name = channel.full_name
+ source = channel.get_source()
ndim = None
- if external_from_name:
- name = full_name = source = channel
- ndim = 0 # TODO: this should somehow verify the dimension
- else:
- name = channel.name
- full_name = channel.full_name
- source = channel.get_source()
- ndim = None
- ctype = channel.get_type()
- if ctype == ElementType.CTExpChannel:
- ndim = 0
- elif ctype == ElementType.PseudoCounter:
- ndim = 0
- elif ctype == ElementType.ZeroDExpChannel:
- ndim = 0
- elif ctype == ElementType.OneDExpChannel:
- ndim = 1
- elif ctype == ElementType.TwoDExpChannel:
- ndim = 2
- elif ctype == ElementType.External:
- config = channel.get_config()
- if config is not None:
- ndim = int(config.data_format)
- elif ctype == ElementType.IORegister:
- ndim = 0
+ ctype = channel.get_type()
+ if ctype == ElementType.CTExpChannel:
+ ndim = 0
+ elif ctype == ElementType.PseudoCounter:
+ ndim = 0
+ elif ctype == ElementType.ZeroDExpChannel:
+ ndim = 0
+ elif ctype == ElementType.OneDExpChannel:
+ ndim = 1
+ elif ctype == ElementType.TwoDExpChannel:
+ ndim = 2
+ elif ctype == ElementType.External:
+ config = channel.get_config()
+ if config is not None:
+ ndim = int(config.data_format)
+ elif ctype == ElementType.IORegister:
+ ndim = 0
# Definitively should be initialized by measurement group
# index MUST be here already (asserting this in the following line)
- channel_data['index'] = channel_data['index']
+ channel_data['index'] = channel_data.get('index', None)
channel_data['name'] = channel_data.get('name', name)
channel_data['full_name'] = channel_data.get('full_name', full_name)
channel_data['source'] = channel_data.get('source', source)
@@ -448,272 +553,16 @@ def _build_channel_defaults(self, channel_data, channel):
channel_data['plot_type'] = channel_data.get('plot_type', PlotType.No)
channel_data['plot_axes'] = channel_data.get('plot_axes', [])
channel_data['conditioning'] = channel_data.get('conditioning', '')
- channel_data['normalization'] = channel_data.get(
- 'normalization', Normalization.No)
+ channel_data['normalization'] = channel_data.get('normalization',
+ Normalization.No)
+ # TODO use real values
+ channel_data['data_type'] = channel_data.get('data_type', None)
+ channel_data['data_units'] = channel_data.get('data_units', None)
+ channel_data['nexus_path'] = channel_data.get('nexus_path', '')
+ channel_data['shape'] = channel_data.get('shape', [])
return channel_data
- def _build_configuration(self, config=None):
- """Builds a configuration object from the list of elements"""
- self._clean_variables()
-
- if config is None:
- config = {}
- user_elements = self._parent.get_user_elements()
- ctrls = self._parent.get_pool_controllers()
-
- # find the first CT
- first_timerable = None
- for elem in user_elements:
- if elem.get_type() in TYPE_TIMERABLE_ELEMENTS:
- first_timerable = elem
- break
- if first_timerable is None:
- raise Exception("It is not possible to construct a "
- "measurement group without at least one "
- "timer able channel (Counter/timer, 1D or 2D)")
- g_timer = g_monitor = first_timerable
- config['timer'] = g_timer
- config['monitor'] = g_monitor
- config['controllers'] = controllers = {}
-
- external_user_elements = []
- for index, element in enumerate(user_elements):
- elem_type = element.get_type()
- if elem_type == ElementType.External:
- external_user_elements.append((index, element))
- continue
-
- ctrl = element.controller
- ctrl_data = controllers.get(ctrl)
- # include all controller in the enabled list
- self.enabled_ctrls.append(ctrl)
- if ctrl_data is None:
- controllers[ctrl] = ctrl_data = {}
- ctrl_data['channels'] = channels = {}
- if elem_type in TYPE_TIMERABLE_ELEMENTS:
- elements = ctrls[ctrl]
- if g_timer in elements:
- ctrl_data['timer'] = g_timer
- else:
- ctrl_data['timer'] = elements[0]
- if g_monitor in elements:
- ctrl_data['monitor'] = g_monitor
- else:
- ctrl_data['monitor'] = elements[0]
- ctrl_data['synchronization'] = AcqSynchType.Trigger
- ctrl_data['synchronizer'] = 'software'
- self.ctrl_to_acq_synch[ctrl] = AcqSynch.SoftwareTrigger
- self.channel_to_acq_synch[
- element] = AcqSynch.SoftwareTrigger
- else:
- channels = ctrl_data['channels']
- channels[element] = channel_data = {}
- channel_data['index'] = user_elements.index(element)
- channel_data = self._build_channel_defaults(channel_data,
- element)
-
- config['label'] = self._parent.name
- config['description'] = self.DFT_DESC
-
- if len(external_user_elements) > 0:
- controllers['__tango__'] = ctrl_data = {}
- ctrl_data['channels'] = channels = {}
- for index, element in external_user_elements:
- channels[element] = channel_data = {}
- channel_data['index'] = index
- channel_data = self._build_channel_defaults(channel_data,
- element)
- else:
- # create a configuration based on a new configuration
- user_elem_ids = {}
- tg_elem_ids = []
- pool = self._parent.pool
- for c, c_data in config['controllers'].items():
- synchronizer = c_data.get('synchronizer')
- acq_synch_type = c_data.get('synchronization')
- software = synchronizer == 'software'
- external = isinstance(c, (str, unicode))
- # only timerable elements are configured with acq_synch
- acq_synch = None
- ctrl_enabled = False
- ctrl_to_acq_synch = False
- if not external and c.is_timerable():
- acq_synch = AcqSynch.from_synch_type(
- software, acq_synch_type)
- for channel_data in c_data['channels'].values():
- if external:
- element = _id = channel_data['full_name']
- channel_data['source'] = _id
- else:
- full_name = channel_data['full_name']
- if self._use_fqdn:
- full_name = _to_fqdn(full_name,
- logger=self._parent)
- element = pool.get_element_by_full_name(full_name)
- _id = element.id
- channel_data = self._build_channel_defaults(
- channel_data, element)
- if channel_data["enabled"]:
- ctrl_enabled = True
- if acq_synch is not None:
- ctrl_to_acq_synch = True
- self.channel_to_acq_synch[element] = acq_synch
- if not software:
- tg_elem_ids.append(synchronizer.id)
- user_elem_ids[channel_data['index']] = _id
-
- if ctrl_to_acq_synch:
- self.ctrl_to_acq_synch[c] = acq_synch
- if ctrl_enabled:
- self.enabled_ctrls.append(c)
-
- # sorted ids may not be consecutive (if a channel is disabled)
- indexes = sorted(user_elem_ids.keys())
- user_elem_ids_list = [user_elem_ids[idx] for idx in indexes]
- user_elem_ids_list.extend(tg_elem_ids)
- self._parent.set_user_element_ids(user_elem_ids_list)
-
- g_timer, g_monitor = config['timer'], config['monitor']
-
- timer_ctrl_data = config['controllers'][g_timer.controller]
- if timer_ctrl_data['timer'] != g_timer:
- self._parent.warning('controller timer and global timer '
- 'mismatch. Using global timer')
- self._parent.debug('For controller %s, timer is defined as '
- 'channel %s. The global timer is set to '
- 'channel %s which belongs to the same '
- 'controller', g_timer.controller.name,
- timer_ctrl_data['timer'].name, g_timer.name)
- timer_ctrl_data['timer'] = g_timer
-
- monitor_ctrl_data = config['controllers'][g_monitor.controller]
- if monitor_ctrl_data['monitor'] != g_monitor:
- self._parent.warning('controller monitor and global '
- 'monitor mismatch. Using global monitor')
- self._parent.debug('For controller %s, monitor is defined as '
- 'channel %s. The global timer is set to '
- 'channel %s which belongs to the same '
- 'controller', g_monitor.controller.name,
- monitor_ctrl_data['monitor'].name,
- g_monitor.name)
-
- self._config = config
- self._split_sync()
- self._split_tg()
-
- def _split_sync(self):
- """
- Split MeasurementGroup configuration with channels
- triggered by SW Trigger and channels triggered by HW trigger
-
- """
-
- ctrls_in = self._config['controllers']
- for ctrl, ctrl_info in ctrls_in.items():
- external = isinstance(ctrl, str) and ctrl.startswith('__')
- # skipping external controllers e.g. Tango attributes
- if external:
- continue
- # splitting ZeroD based on the type
- if ctrl.get_ctrl_types()[0] == ElementType.ZeroDExpChannel:
- self.ctrl_0d_sync[ctrl] = ctrl_info
- # ignoring PseudoCounter
- elif ctrl.get_ctrl_types()[0] == ElementType.PseudoCounter:
- pass
- # splitting rest of the channels based on the assigned trigger
- else:
- synchronizer = ctrl_info.get('synchronizer')
- if synchronizer is None or synchronizer == 'software':
- synchronization = ctrl_info.get('synchronization')
- if synchronization in [AcqSynch.SoftwareTrigger,
- AcqSynch.SoftwareGate]:
- self.ctrl_sw_sync[ctrl] = ctrl_info
- else:
- self.ctrl_sw_start[ctrl] = ctrl_info
- else:
- self.ctrl_hw_sync[ctrl] = ctrl_info
-
- def get_master_enables(ctrls, role):
- master_idx = float("+inf")
- master = None
- elements = []
- for ctrl_info in ctrls.values():
- element = ctrl_info[role]
- if element in ctrl_info["channels"]:
- element_idx = ctrl_info["channels"][element]["index"]
- element_enabled = ctrl_info["channels"][element]["enabled"]
- # Find master only if is enabled
- if element_enabled:
- elements.append(element)
- if element_idx < master_idx:
- master = element
- master_idx = element_idx
- return master, elements
-
- if len(self.ctrl_sw_sync):
- timer, timers = get_master_enables(self.ctrl_sw_sync, "timer")
- self.sw_sync_timer = timer
- self.sw_sync_timers_enabled = timers
-
- monitor, monitors = get_master_enables(self.ctrl_sw_sync,
- "monitor")
- self.sw_sync_monitor = monitor
- self.sw_sync_monitors_enabled = monitors
-
- if len(self.ctrl_sw_start):
- timer, timers = get_master_enables(self.ctrl_sw_start, "timer")
- self.sw_start_timer = timer
- self.sw_start_timers_enabled = timers
-
- monitor, monitors = get_master_enables(self.ctrl_sw_start,
- "monitor")
- self.sw_start_monitor = monitor
- self.sw_start_monitors_enabled = monitors
-
- if len(self.ctrl_hw_sync):
- timer, timers = get_master_enables(self.ctrl_hw_sync, "timer")
- self.hw_sync_timer = timer
- self.hw_sync_timers_enabled = timers
-
- monitor, monitors = get_master_enables(self.ctrl_hw_sync,
- "monitor")
- self.hw_sync_monitor = monitor
- self.hw_sync_monitors_enabled = monitors
-
- def _split_tg(self):
- """
- Build TG configuration from complete configuration.
- """
-
- # Create list with not repeated elements
- _tg_element_list = []
-
- for ctrl in self._config["controllers"]:
- tg_element = self._config["controllers"][ctrl].get('synchronizer',
- None)
- if (tg_element is not None and
- tg_element != "software" and
- tg_element not in _tg_element_list):
- _tg_element_list.append(tg_element)
-
- # Intermediate dictionary to organize each ctrl with its elements.
- ctrl_tgelem_dict = {}
- for tgelem in _tg_element_list:
- tg_ctrl = tgelem.get_controller()
- if tg_ctrl not in ctrl_tgelem_dict.keys():
- ctrl_tgelem_dict[tg_ctrl] = [tgelem]
- else:
- ctrl_tgelem_dict[tg_ctrl].append(tgelem)
-
- # Build TG configuration dictionary.
- for ctrl in ctrl_tgelem_dict:
- self.ctrl_tg_sync[ctrl] = ctrls = {}
- ctrls['channels'] = {}
- for tg_elem in ctrl_tgelem_dict[ctrl]:
- ch = ctrls['channels'][tg_elem] = {}
- ch['full_name'] = tg_elem.full_name
-
class PoolMeasurementGroup(PoolGroupElement):
From f398116d445e64c114e883294a57302d8dd36518 Mon Sep 17 00:00:00 2001
From: flyingbot91
Date: Wed, 24 Oct 2018 21:43:06 +0200
Subject: [PATCH 243/652] Bugfix: If len(sys.argv) > 1 second conditional is
never executed
---
sardanaConfig/src/wizards/pool_page.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sardanaConfig/src/wizards/pool_page.py b/sardanaConfig/src/wizards/pool_page.py
index d77ebd3936..4bffa6ede2 100644
--- a/sardanaConfig/src/wizards/pool_page.py
+++ b/sardanaConfig/src/wizards/pool_page.py
@@ -149,7 +149,7 @@ def nextId(self):
def main():
tg_host, sardana = None, None
- if len(sys.argv) > 1:
+ if len(sys.argv) == 2:
tg_host = sys.argv[1]
if len(sys.argv) > 2:
sardana = sys.argv[2]
From 8b616b8eb2767d2181b7b8d8b1872d94b2155ea6 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Thu, 25 Oct 2018 00:27:18 +0200
Subject: [PATCH 244/652] (minor) Return None when device not found in
get_device_from_user
---
src/sardana/spock/ipython_01_00/genutils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sardana/spock/ipython_01_00/genutils.py b/src/sardana/spock/ipython_01_00/genutils.py
index 181d725da5..93a7d8e348 100644
--- a/src/sardana/spock/ipython_01_00/genutils.py
+++ b/src/sardana/spock/ipython_01_00/genutils.py
@@ -319,7 +319,7 @@ def get_device_from_user(expected_class, dft=None):
prompt += "? "
from_user = raw_input(prompt).strip() or dft
- name = ''
+ name = None
try:
full_name, name, _ = from_name_to_tango(from_user)
except:
From 2de6189721988a0da7c21452fb4e3c20ebcef896 Mon Sep 17 00:00:00 2001
From: zreszela
Date: Thu, 25 Oct 2018 00:30:09 +0200
Subject: [PATCH 245/652] Do cleanup when spock profile was not correctly
created
Spock profile may not be correctly created, for example when
no valid door was selected. In this case remove the profile directory
previously prepare for the profile file.
---
src/sardana/spock/ipython_01_00/genutils.py | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/src/sardana/spock/ipython_01_00/genutils.py b/src/sardana/spock/ipython_01_00/genutils.py
index 93a7d8e348..c486168148 100644
--- a/src/sardana/spock/ipython_01_00/genutils.py
+++ b/src/sardana/spock/ipython_01_00/genutils.py
@@ -753,6 +753,8 @@ def _create_config_file(location, door_name=None):
else:
full_door_name, door_name, _ = from_name_to_tango(door_name)
door_name = full_door_name
+ if door_name is None:
+ raise RuntimeError('unknown door name')
#
# Discover macro server name
@@ -786,7 +788,17 @@ def create_spock_profile(userdir, profile, door_name=None):
ipy_profile_dir = p_dir.location
- _create_config_file(ipy_profile_dir)
+ try:
+ _create_config_file(ipy_profile_dir)
+ except Exception:
+ import shutil
+ try:
+ shutil.rmtree(ipy_profile_dir)
+ except OSError:
+ msg = ('Could not remove spock profile directory {0}. '
+ 'Remove it by hand e.g. rmdir {0}').format(ipy_profile_dir)
+ print(msg)
+ sys.exit(-1)
def upgrade_spock_profile(ipy_profile_dir, door_name):
From 464fd8efdce43bfc9c0d76067393e03f4f54702d Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Thu, 25 Oct 2018 09:37:14 +0200
Subject: [PATCH 246/652] Change acq_synch lookup table
Use two lookup table one for channel and another for controller.
Use as key the element instead of the id.
---
src/sardana/pool/poolmeasurementgroup.py | 25 ++++++++++++++++--------
1 file changed, 17 insertions(+), 8 deletions(-)
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index 13e9075685..a2b41b63f0 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -204,7 +204,6 @@ def __init__(self, element, conf=None):
self.enabled = False
-
class MeasurementConfiguration(object):
"""
.. todo: Reject configuration with errors:
@@ -235,10 +234,18 @@ def __init__(self, parent=None):
self._master_timer = None
self._master_monitor = None
self._user_confg = {}
- self._element_acq_synch = {}
+ self._channel_acq_synch = {}
+ self._ctrl_acq_synch = {}
+
+ def get_acq_synch_by_channel(self, element):
+ if isinstance(element, ConfigurationItem):
+ element = element.element
+ return self._channel_acq_synch[element]
- def get_acq_synch_by_element(self, element):
- return self._element_acq_synch[element.id]
+ def get_acq_synch_by_controller(self, element):
+ if isinstance(element, ConfigurationItem):
+ element = element.element
+ return self._ctrl_acq_synch[element]
def _filter_ctrls(self, ctrls, enabled):
if enabled is None:
@@ -315,6 +322,8 @@ def set_configuration_from_user(self, cfg, to_fqdn=True):
master_timer_idx_sw = float("+inf")
master_monitor_idx_sw = float("+inf")
user_elem_ids = {}
+ channel_acq_synch = {}
+ ctrl_acq_synch = {}
user_config = {}
# Update the configuration for user
@@ -404,7 +413,7 @@ def set_configuration_from_user(self, cfg, to_fqdn=True):
is_software = synchronizer == 'software'
acq_synch = AcqSynch.from_synch_type(is_software,
synchronization)
- element_acq_synch[ctrl.id] = acq_synch
+ ctrl_acq_synch[ctrl] = acq_synch
ctrl_enabled = False
if 'channels' in ctrl_data:
@@ -449,7 +458,7 @@ def set_configuration_from_user(self, cfg, to_fqdn=True):
ctrl_enabled = True
if acq_synch is not None:
- element_acq_synch[channel.id] = acq_synch
+ channel_acq_synch[channel] = acq_synch
# Update syncrhonizer state
if conf_synch is not None:
@@ -487,7 +496,6 @@ def set_configuration_from_user(self, cfg, to_fqdn=True):
for conf_synch_ctrl in synch_ctrls:
conf_synch_ctrl.update_state()
-
# Update internals values
self._master_monitor = master_monitor
self._master_timer = master_timer
@@ -499,8 +507,9 @@ def set_configuration_from_user(self, cfg, to_fqdn=True):
self._other_ctrls = other_ctrls
self._master_timer_sw = master_timer_sw
self._master_monitor_sw = master_monitor_sw
- self._element_acq_synch = element_acq_synch
self._user_confg = user_config
+ self._channel_acq_synch = channel_acq_synch
+ self._ctrl_acq_synch = ctrl_acq_synch
# sorted ids may not be consecutive (if a channel is disabled)
indexes = sorted(user_elem_ids.keys())
From b94ff8c1e5d9f352a8c3801719bd0cdd8f0b711f Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Thu, 25 Oct 2018 09:42:20 +0200
Subject: [PATCH 247/652] Adapt classes to use new MeasurementConfiguration API
---
src/sardana/pool/poolacquisition.py | 173 +++++++++++++----------
src/sardana/pool/poolmeasurementgroup.py | 107 ++++++++------
src/sardana/pool/poolsynchronization.py | 72 +++++-----
3 files changed, 202 insertions(+), 150 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index a7ff6c17ab..3668ee3711 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -147,17 +147,15 @@ def event_received(self, *args, **kwargs):
self.debug('Stopping ZeroD acquisition.')
self._0d_acq.stop_action()
- def prepare(self, config, nr_of_starts):
+ def prepare(self, ctrl_lodeable, nr_of_starts):
"""Prepare measurement."""
- timers = config.sw_sync_timers_enabled + \
- config.sw_start_timers_enabled + \
- config.hw_sync_timers_enabled
- for timer in timers:
- axis = timer.axis
- timer_ctrl = timer.controller
- ctrl = timer_ctrl.ctrl
- ctrl.PrepareOne(axis, nr_of_starts)
+ for ctrl, lodeable in ctrl_lodeable.items():
+ axis = lodeable.axis
+ try:
+ ctrl.PrepareOne(axis, nr_of_starts)
+ except Exception:
+ pass
def is_running(self):
return self._0d_acq.is_running() or\
@@ -181,38 +179,61 @@ def run(self, *args, **kwargs):
for pseudo_elem in elem.get_pseudo_elements():
pseudo_elem.clear_value_buffer()
- config = self.main_element.configuration
- synchronization = kwargs["synchronization"]
- integ_time = synchronization.integration_time
+ if acq_mode is AcqMode.Timer:
+ value = synchronization.integration_time
repetitions = synchronization.repetitions
latency_time = 0
- ctrls_channels_acq_hw = config.get_ctrls_channels()
+
# starting continuous acquisition only if there are any controllers
- if len(config.ctrl_hw_sync):
- cont_acq_kwargs = dict(kwargs)
- cont_acq_kwargs['integ_time'] = integ_time
- cont_acq_kwargs['repetitions'] = repetitions
- self._hw_acq.run(*args, **cont_acq_kwargs)
- if len(config.ctrl_sw_sync) or len(config.ctrl_0d_sync):
+ acq_sync_hw = [AcqSynch.HardwareTrigger, AcqSynch.HardwareStart,
+ AcqSynch.HardwareGate]
+ ctrls_acq_hw = config.get_timerable_ctrls(acq_synch=acq_sync_hw,
+ enabled=True)
+
+
+ if len(ctrls_acq_hw):
+ self._hw_acq.run(conf_ctrls=ctrls_acq_hw,
+ value=value,
+ repetitions=repetitions,
+ latency_time=latency_time)
+
+ # starting software acquisition only if there are any controller
+ acq_sync_sw = [AcqSynch.SoftwareGate, AcqSynch.SoftwareTrigger]
+ ctrls_acq_sw = config.get_timerable_ctrls(acq_synch=acq_sync_sw,
+ enabled=True)
+ ctrls_acq_0d = config.get_zerod_ctrls(enabled=True)
+
+ if len(ctrls_acq_sw) or len(ctrls_acq_0d):
self._synch.add_listener(self)
- if len(config.ctrl_sw_sync):
- sw_acq_kwargs = dict(kwargs)
- sw_acq_kwargs['integ_time'] = integ_time
- sw_acq_kwargs['repetitions'] = 1
+ if len(ctrls_acq_sw):
+ master = None
+ if acq_mode is AcqMode.Timer:
+ master = config.get_master_timer_software()
+ elif acq_mode is AcqMode.Monitor:
+ master = config.get_master_monitor_software()
+
+ sw_acq_kwargs = dict(conf_ctrls=ctrls_acq_sw,
+ value=value,
+ repetitions=1,
+ latency_time=latency_time,
+ master=master)
self.set_sw_config(sw_acq_kwargs)
- if len(config.ctrl_0d_sync):
- zerod_acq_kwargs = dict(kwargs)
+ if len(ctrls_acq_0d):
+ zerod_acq_kwargs = dict(conf_ctrls=ctrls_acq_0d, value=value)
self.set_0d_config(zerod_acq_kwargs)
- synch_kwargs = dict(kwargs)
- self._synch.run(*args, **synch_kwargs)
+
+ #start the synchonization action
+ ctrls_synch = config.get_synch_ctrls(enabled=True)
+ self._synch.run(conf_ctrls=ctrls_synch,
+ synchronization=synchronization,
+ moveable=moveable,
+ sw_synch_initial_domain=sw_synch_initial_domain)
def _get_action_for_element(self, element):
elem_type = element.get_type()
if elem_type in TYPE_TIMERABLE_ELEMENTS:
- main_element = self.main_element
- channel_to_acq_synch = \
- main_element.configuration.channel_to_acq_synch
- acq_synch = channel_to_acq_synch.get(element)
+ config = self.main_element.configuration
+ acq_synch = config.get_acq_synch_by_channel(element)
if acq_synch in (AcqSynch.SoftwareTrigger,
AcqSynch.SoftwareGate):
return self._sw_acq
@@ -401,22 +422,18 @@ def start_action(self, pool_ctrls, value, repetitions=1, latency=0,
# make sure the controller which has the master channel is the last to
# be called
if master is not None:
- master_ctrl = master.controller
- pool_ctrls.remove(master_ctrl)
- pool_ctrls.append(master_ctrl)
+ conf_ctrls.remove(master.controller)
+ conf_ctrls.append(master.controller)
- # controllers that will be read at the end of the action
- ctrl_channels = {}
- for pool_ctrl in pool_ctrls:
- ctrl_channels[pool_ctrl] = pool_ctrl.channels
- self._pool_ctrl_dict_loop = ctrl_channels
+ # controllers that will be read at during the action
+ self._set_pool_ctrl_dict_loop(conf_ctrls)
# channels that are acquired (only enabled)
self._channels = []
- def load(channel, value, repetitions, latency=0):
- axis = channel.axis
- pool_ctrl = channel.controller
+ def load(conf_channel, value, repetitions, latency=0):
+ axis = conf_channel.axis
+ pool_ctrl = conf_channel.controller
ctrl = pool_ctrl.ctrl
ctrl.PreLoadAll()
try:
@@ -458,62 +475,73 @@ def load(channel, value, repetitions, latency=0):
with ActionContext(self):
# PreLoadAll, PreLoadOne, LoadOne and LoadAll
- for pool_ctrl in pool_ctrls:
- load(pool_ctrl.master, value, repetitions, latency)
+ for conf_ctrl in conf_ctrls:
+ # TODO find solution for master now sardana only use timer
+ load(conf_ctrl.timer, value, repetitions, latency)
# TODO: remove when the action allows to use tango attributes
try:
- pool_ctrls.pop('__tango__')
+ conf_ctrls.pop('__tango__')
except Exception:
pass
# PreStartAll on all enabled controllers
- for pool_ctrl in pool_ctrls:
- pool_ctrl.ctrl.PreStartAll()
+ for conf_ctrl in conf_ctrls:
+ conf_ctrl.ctrl.PreStartAll()
# PreStartOne & StartOne on all enabled elements
- for pool_ctrl in pool_ctrls:
- channels = pool_ctrl.channels
- ctrl = pool_ctrl.ctrl
+ for conf_ctrl in conf_ctrls:
+ conf_channels = conf_ctrl.get_channels(enabled=True)
# make sure that the master timer/monitor is started as the
# last one
- channels.remove(pool_ctrl.master)
- channels.append(pool_ctrl.master)
- for channel in channels:
- axis = channel.axis
- ret = ctrl.PreStartOne(axis, value)
+ conf_channels.remove(conf_ctrl.timer)
+ conf_channels.append(conf_ctrl.timer)
+ for conf_channel in conf_channels:
+ axis = conf_channel.axis
+ ret = conf_ctrl.ctrl.PreStartOne(axis, value)
if not ret:
msg = ("%s.PreStartOne(%d) returns False" %
- (pool_ctrl.name, axis))
+ (conf_ctrl.name, axis))
raise Exception(msg)
try:
- ctrl.StartOne(axis, value)
+ conf_ctrl.ctrl.StartOne(axis, value)
except Exception as e:
self.debug(e, exc_info=True)
- channel.set_state(State.Fault, propagate=2)
+ conf_channel.set_state(State.Fault, propagate=2)
msg = ("%s.StartOne(%d) failed" %
- (pool_ctrl.name, axis))
+ (conf_ctrl.name, axis))
raise Exception(msg)
- self._channels.append(channel)
+ self._channels.append(conf_channel)
# set the state of all elements to and inform their listeners
- for channel in self._channels:
- channel.set_state(State.Moving, propagate=2)
+ for conf_channel in self._channels:
+ conf_channel.set_state(State.Moving, propagate=2)
# StartAll on all enabled controllers
- for pool_ctrl in pool_ctrls:
- channels = ctrl_channels[pool_ctrl]
+ for conf_ctrl in conf_ctrls:
try:
- pool_ctrl.ctrl.StartAll()
+ conf_ctrl.ctrl.StartAll()
except Exception as e:
+ conf_channels = conf_ctrl.get_channels(enabled=True)
self.debug(e, exc_info=True)
- for channel in channels:
- channel.set_state(State.Fault, propagate=2)
- msg = ("%s.StartAll() failed" % pool_ctrl.name)
+ for conf_channel in conf_channels:
+ conf_channel.set_state(State.Fault, propagate=2)
+ msg = ("%s.StartAll() failed" % conf_ctrl.name)
raise Exception(msg)
+ def _set_pool_ctrl_dict_loop(self, conf_ctrls):
+ ctrl_channels = {}
+ for conf_ctrl in conf_ctrls:
+ pool_channels = []
+ pool_ctrl = conf_ctrl.element
+ # TODO: filter 1D and 2D for software synchronize acquisition
+ for conf_channel in conf_ctrl.get_channels(enabled=True):
+ pool_channels.append(conf_channel.element)
+ ctrl_channels[pool_ctrl] = pool_channels
+ self._pool_ctrl_dict_loop = ctrl_channels
+
def clear_value_buffers(self):
for channel in self._channels:
channel.clear_value_buffer()
@@ -537,7 +565,8 @@ def action_loop(self):
i = 0
states, values = {}, {}
- for element in self._channels:
+ for channel in self._channels:
+ element = channel.element
states[element] = None
values[element] = None
@@ -605,7 +634,8 @@ def __init__(self, main_element, name="AcquisitionSoftware", slaves=None):
@DebugIt()
def action_loop(self):
states, values = {}, {}
- for element in self._channels:
+ for channel in self._channels:
+ element = channel.element
states[element] = None
values[element] = None
@@ -774,8 +804,7 @@ def start_action(self, *args, **kwargs):
items = self.get_elements()
cfg = self.main_element.configuration
- pool_ctrls_dict = dict(cfg.ctrl_0d_sync)
- pool_ctrls_dict.pop('__tango__', None)
+ pool_ctrls_dict = cfg.get_zerod_ctrls(enabled=True)
pool_ctrls = []
for ctrl in pool_ctrls_dict:
if ElementType.ZeroDExpChannel in ctrl.get_ctrl_types():
diff --git a/src/sardana/pool/poolmeasurementgroup.py b/src/sardana/pool/poolmeasurementgroup.py
index a2b41b63f0..33480a4dc7 100644
--- a/src/sardana/pool/poolmeasurementgroup.py
+++ b/src/sardana/pool/poolmeasurementgroup.py
@@ -592,15 +592,16 @@ def __init__(self, **kwargs):
kwargs['elem_type'] = ElementType.MeasurementGroup
PoolGroupElement.__init__(self, **kwargs)
- configuration = kwargs.get("configuration")
- self.set_configuration(configuration)
- # if the configuration was never "really" written e.g. newly created MG
- # just sets it now so the _channe_to_acq_synch and ctrl_to_acq_synch
- # are properly populated
- # TODO: make it more elegant
- if configuration is None:
- configuration = self.configuration.configuration
- self.set_configuration(configuration, propagate=0, to_fqdn=False)
+ # TODO: Check if it needed
+ # configuration = kwargs.get("configuration")
+ # self.set_configuration(configuration)
+ # # if the configuration was never "really" written e.g. newly created MG
+ # # just sets it now so the _channe_to_acq_synch and ctrl_to_acq_synch
+ # # are properly populated
+ # # TODO: make it more elegant
+ # if configuration is None:
+ # configuration = self.configuration.configuration
+ # self.set_configuration(configuration, propagate=0, to_fqdn=False)
def _create_action_cache(self):
acq_name = "%s.Acquisition" % self._name
@@ -659,6 +660,7 @@ def _is_managed_element(self, element):
def configuration(self):
return self._config
+ # TODO: Check if it needed
def set_configuration(self, config=None, propagate=1, to_fqdn=True):
self._config._use_fqdn = to_fqdn
self._config.configuration = config
@@ -673,7 +675,7 @@ def set_configuration_from_user(self, cfg, propagate=1, to_fqdn=True):
if not propagate:
return
self.fire_event(EventType("configuration", priority=propagate),
- self._config.configuration)
+ self._config.get_configuration_for_user())
def get_user_configuration(self):
return self._config.get_configuration_for_user()
@@ -682,9 +684,9 @@ def load_configuration(self, force=False):
"""Loads the current configuration to all involved controllers"""
# g_timer, g_monitor = cfg['timer'], cfg['monitor']
- for ctrl in self._config.enabled_ctrls:
- if isinstance(ctrl, str): # skip external channels
- continue
+
+ for ctrl_item in self._config.get_timerable_ctrls(enabled=True):
+ ctrl = ctrl_item.element
if not ctrl.is_online():
continue
@@ -693,25 +695,23 @@ def load_configuration(self, force=False):
if ctrl.operator == self and not force and not self._config_dirty:
continue
ctrl.operator = self
- if ctrl.is_timerable():
- # TODO: Implement API to extract controller data
- ctrl_data = self._config.configuration['controllers'][ctrl]
- # if ctrl == g_timer.controller:
- # ctrl.set_ctrl_par('timer', g_timer.axis)
- # if ctrl == g_monitor.controller:
- # ctrl.set_ctrl_par('monitor', g_monitor.axis)
- ctrl.set_ctrl_par('timer', ctrl_data['timer'].axis)
- ctrl.set_ctrl_par('monitor', ctrl_data['monitor'].axis)
- synchronization = self._config.ctrl_to_acq_synch.get(ctrl)
- self.debug('load_configuration: setting trigger_type: %s '
- 'to ctrl: %s' % (synchronization, ctrl))
- ctrl.set_ctrl_par('synchronization', synchronization)
+ # ctrl_data = self._config.configuration['controllers'][ctrl]
+ # # if ctrl == g_timer.controller:
+ # # ctrl.set_ctrl_par('timer', g_timer.axis)
+ # # if ctrl == g_monitor.controller:
+ # # ctrl.set_ctrl_par('monitor', g_monitor.axis)
+ ctrl.set_ctrl_par('timer', ctrl_item.timer.axis)
+ ctrl.set_ctrl_par('monitor', ctrl_item.monitor.axis)
+ synchronization = self._config.get_acq_synch_by_controller(ctrl)
+ self.debug('load_configuration: setting trigger_type: %s '
+ 'to ctrl: %s' % (synchronization, ctrl))
+ ctrl.set_ctrl_par('synchronization', synchronization)
self._config_dirty = False
def get_timer(self):
# TODO: Adapt to the new future MeasurementConfiguration API
- return self._config.configuration['timer']
+ return self._config._master_timer
timer = property(get_timer)
@@ -877,10 +877,21 @@ def set_nr_of_starts(self, nr_of_starts, propagate=1):
def prepare(self):
# load configuration into controller(s) if necessary
self.load_configuration()
- config = self.configuration
+ ctrls = self.configuration.get_timerable_ctrls(enabled=True)
+ ctrl_lodeable = {}
+ for ctrl in ctrls:
+ if self.acquisition_mode == AcqMode.Timer:
+ lodeable = ctrl.timer
+ elif self.acquisition_mode == AcqMode.Monitor:
+ lodeable = ctrl.monitor
+ else:
+ continue
+ ctrl_lodeable[ctrl] = lodeable
+
nr_of_starts = self.nr_of_starts
self._pending_starts = nr_of_starts
- self.acquisition.prepare(config, nr_of_starts)
+
+ self.acquisition.prepare(ctrl_lodeable, nr_of_starts)
def start_acquisition(self, value=None, multiple=1):
if self._pending_starts == 0:
@@ -898,17 +909,33 @@ def start_acquisition(self, value=None, multiple=1):
self._pending_starts -= 1
if not self._simulation_mode:
# determining the acquisition parameters
- kwargs = dict(head=self, config=self._config, multiple=multiple)
- acquisition_mode = self.acquisition_mode
- if acquisition_mode is AcqMode.Timer:
- kwargs['integ_time'] = self.get_integration_time()
- elif acquisition_mode is AcqMode.Monitor:
- kwargs['monitor'] = self._monitor
- kwargs['synchronization'] = self._synchronization
- kwargs['moveable'] = self._moveable_obj
- kwargs['sw_synch_initial_domain'] = self._sw_synch_initial_domain
- # start acquisition
- self.acquisition.run(**kwargs)
+ if self._acquisition_mode is AcqMode.Timer:
+ value = self.get_integration_time()
+ elif self.acquisition_mode is AcqMode.Monitor:
+ value = self._monitor_count
+
+ self.acquisition.run(
+ head=self,
+ config=self._config,
+ multiple=multiple,
+ acq_mode=self.acquisition_mode,
+ value=value,
+ synchronization=self._synchronization,
+ moveable=self._moveable_obj,
+ sw_synch_initial_domain=self.sw_synch_initial_domain)
+
+ #
+ # kwargs = dict(head=self, config=self._config, multiple=multiple)
+ # acquisition_mode = self.acquisition_mode
+ # if acquisition_mode is AcqMode.Timer:
+ # kwargs['integ_time'] = self.get_integration_time()
+ # elif acquisition_mode is AcqMode.Monitor:
+ # kwargs['monitor'] = self._monitor
+ # kwargs['synchronization'] = self._synchronization
+ # kwargs['moveable'] = self._moveable_obj
+ # kwargs['sw_synch_initial_domain'] = self._sw_synch_initial_domain
+ # # start acquisition
+ # self.acquisition.run(**kwargs)
def set_acquisition(self, acq_cache):
self.set_action_cache(acq_cache)
diff --git a/src/sardana/pool/poolsynchronization.py b/src/sardana/pool/poolsynchronization.py
index 1cfca7ba09..a424e861b0 100644
--- a/src/sardana/pool/poolsynchronization.py
+++ b/src/sardana/pool/poolsynchronization.py
@@ -141,37 +141,32 @@ def start_action(self, *args, **kwargs):
synchronizer, can be either SynchDomain.Time or SynchDomain.Position
'''
- cfg = self.main_element.configuration
- synchronization = kwargs.get('synchronization')
- moveable = kwargs.get('moveable')
- sw_synch_initial_domain = kwargs.get('sw_synch_initial_domain', None)
- ctrls_config = cfg.ctrl_tg_sync
- pool_ctrls = ctrls_config.keys()
-
- # Prepare a dictionary with the involved channels
- self._channels = channels = {}
- for pool_ctrl in pool_ctrls:
- pool_ctrl_data = ctrls_config[pool_ctrl]
- elements = pool_ctrl_data['channels']
-
- for element, element_info in elements.items():
- channel = TGChannel(element, info=element_info)
- channels[element] = channel
+ # cfg = self.main_element.configuration
+ # ctrls_config = cfg.ctrl_tg_sync
+ # pool_ctrls = ctrls_config.keys()
+ #
+ # # Prepare a dictionary with the involved channels
+ # self._channels = channels = {}
+ # for pool_ctrl in pool_ctrls:
+ # pool_ctrl_data = ctrls_config[pool_ctrl]
+ # elements = pool_ctrl_data['channels']
+ #
+ # for element, element_info in elements.items():
+ # channel = TGChannel(element, info=element_info)
+ # channels[element] = channel
with ActionContext(self):
# loads synchronization description
- for pool_ctrl in pool_ctrls:
- ctrl = pool_ctrl.ctrl
- pool_ctrl_data = ctrls_config[pool_ctrl]
+ for conf_ctrl in conf_ctrls:
+ ctrl = conf_ctrl.ctrl
ctrl.PreSynchAll()
- elements = pool_ctrl_data['channels']
- for element in elements:
- axis = element.axis
+ for conf_channel in conf_ctrl.get_channels(enabled=True):
+ axis = conf_channel.axis
ret = ctrl.PreSynchOne(axis, synchronization)
if not ret:
msg = ("%s.PreSynchOne(%d) returns False" %
- (pool_ctrl.name, axis))
+ (conf_ctrl.name, axis))
raise Exception(msg)
ctrl.SynchOne(axis, synchronization)
ctrl.SynchAll()
@@ -206,30 +201,30 @@ def start_action(self, *args, **kwargs):
get_thread_pool().add(self._synch_soft.run)
# PreStartAll on all controllers
- for pool_ctrl in pool_ctrls:
- pool_ctrl.ctrl.PreStartAll()
+ for conf_ctrl in conf_ctrls:
+ conf_ctrl.ctrl.PreStartAll()
# PreStartOne & StartOne on all elements
- for pool_ctrl in pool_ctrls:
- ctrl = pool_ctrl.ctrl
- pool_ctrl_data = ctrls_config[pool_ctrl]
- elements = pool_ctrl_data['channels']
- for element in elements:
- axis = element.axis
- channel = channels[element]
+ for conf_ctrl in conf_ctrls:
+ ctrl = conf_ctrl.ctrl
+ for conf_channel in conf_ctrl.get_channels(enabled=True):
+ axis = conf_channel.axis
ret = ctrl.PreStartOne(axis)
if not ret:
raise Exception("%s.PreStartOne(%d) returns False"
- % (pool_ctrl.name, axis))
+ % (ctrl.name, axis))
ctrl.StartOne(axis)
# set the state of all elements to inform their listeners
- for channel in channels:
- channel.set_state(State.Moving, propagate=2)
+ self._channels = []
+ for conf_ctrl in conf_ctrls:
+ for conf_channel in conf_ctrl.get_channels(enabled=True):
+ conf_channel.set_state(State.Moving, propagate=2)
+ self._channels.append(conf_channel)
# StartAll on all controllers
- for pool_ctrl in pool_ctrls:
- pool_ctrl.ctrl.StartAll()
+ for conf_ctrl in conf_ctrls:
+ conf_ctrl.ctrl.StartAll()
def is_triggering(self, states):
"""Determines if we are triggering or if the triggering has ended
@@ -254,7 +249,8 @@ def action_loop(self):
'''action_loop method
'''
states = {}
- for element in self._channels:
+ for conf_channel in self._channels:
+ element = conf_channel.element
states[element] = None
# Triggering loop
From cee606bac9cda03b146b6c30cd864f1ac2120008 Mon Sep 17 00:00:00 2001
From: Roberto Javier Homs Puron
Date: Thu, 25 Oct 2018 09:43:55 +0200
Subject: [PATCH 248/652] Change start_action API
Do explicit the parameters needed by the action
---
src/sardana/pool/poolacquisition.py | 18 +++++++++++-------
src/sardana/pool/poolsynchronization.py | 3 ++-
2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/src/sardana/pool/poolacquisition.py b/src/sardana/pool/poolacquisition.py
index 3668ee3711..9adc1b5806 100755
--- a/src/sardana/pool/poolacquisition.py
+++ b/src/sardana/pool/poolacquisition.py
@@ -39,7 +39,7 @@
from sardana import SardanaValue, State, ElementType, TYPE_TIMERABLE_ELEMENTS
from sardana.sardanathreadpool import get_thread_pool
-from sardana.pool import SynchParam, SynchDomain, AcqSynch
+from sardana.pool import SynchParam, SynchDomain, AcqSynch, AcqMode
from sardana.pool.poolaction import ActionContext, PoolActionItem, PoolAction
from sardana.pool.poolsynchronization import PoolSynchronization
@@ -121,8 +121,9 @@ def event_received(self, *args, **kwargs):
self.debug('Executing software acquisition.')
args = ()
kwargs = self._sw_acq_config
+ # TODO: key synch is not used on the code, remove it
kwargs['synch'] = True
- kwargs['idx'] = value
+ kwargs['index'] = value
self._sw_acq._started = True
get_thread_pool().add(self._sw_acq.run, *args, **kwargs)
if self._0d_config:
@@ -135,8 +136,9 @@ def event_received(self, *args, **kwargs):
self.debug('Executing ZeroD acquisition.')
args = ()
kwargs = self._0d_config
+ # TODO: key synch is not used on the code, remove it
kwargs['synch'] = True
- kwargs['idx'] = value
+ kwargs['index'] = value
self._0d_acq._started = True
self._0d_acq._stopped = False
self._0d_acq._aborted = False
@@ -163,7 +165,9 @@ def is_running(self):
self._hw_acq.is_running() or\
self._synch.is_running()
- def run(self, *args, **kwargs):
+ def run(self, head, config, multiple, acq_mode, value, synchronization,
+ moveable, sw_synch_initial_domain=None, *args, **kwargs):
+
for elem in self.get_elements():
elem.put_state(None)
# TODO: temporarily clear value buffers at the beginning of the
@@ -378,14 +382,14 @@ def in_acquisition(self, states):
return True
@DebugIt()
- def start_action(self, pool_ctrls, value, repetitions=1, latency=0,
+ def start_action(self, conf_ctrls, value, repetitions=1, latency=0,
master=None, index=None, acq_sleep_time=None,
nb_states_per_value=None, *args,
**kwargs):
"""
Prepares everything for acquisition and starts it
- :param pool_ctrls: List of enabled controllers
- :type pool_ctrls: list
+ :param conf_ctrls: List of enabled controllers
+ :type conf_ctrls: list
:param value: integration time/monitor counts
:type value: float/int or seq