...
 
Commits (319)
......@@ -11,19 +11,17 @@ style:
stage: style
before_script:
- source activate tango3.6
- pip install black
- pip install black==18.6b4
script:
- LC_ALL=C.UTF-8 black --check .
- LC_ALL=C.UTF-8 black --check --fast .
tests:
stage: tests
before_script:
- conda create --name testenv python=2
- conda create --name testenv --channel http://bcu-ci.esrf.fr/stable python=2 --file requirements-conda.txt --file requirements-test-conda.txt
- source activate testenv
- conda install --yes --channel http://bcu-ci.esrf.fr/stable --file requirements-conda.txt
- conda install --yes --channel http://bcu-ci.esrf.fr/stable --file requirements-test-conda.txt
- pip install .
script:
- pip install .
- python setup.py test --addopts "--cov bliss --cov-report html --cov-report term"
artifacts:
paths:
......@@ -34,10 +32,10 @@ build_doc:
stage: build_doc
before_script:
- source activate tango2.7
- conda install --yes --channel http://bcu-ci.esrf.fr/stable --file requirements-conda.txt
- conda install --yes --channel http://bcu-ci.esrf.fr/stable --file requirements-doc-conda.txt
- conda install --yes --channel http://bcu-ci.esrf.fr/stable --file requirements-conda.txt --file requirements-doc-conda.txt
- pip install -r requirements-doc.txt
script: python setup.py build_sphinx
script:
- python setup.py build_sphinx
artifacts:
paths:
- build/
......@@ -46,10 +44,10 @@ build_doc:
build_user_doc:
stage: build_user_doc
before_script:
- conda install --yes --channel http://bcu-ci.esrf.fr/stable --file requirements-doc-conda.txt
- pip install -r requirements-doc.txt
- conda install --yes --channel http://bcu-ci.esrf.fr/stable --file requirements-doc-conda.txt
- pip install -r requirements-doc.txt
script:
- cd doc && mkdocs build
- cd doc && mkdocs build
artifacts:
paths:
- doc/site
......
repos:
- repo: https://github.com/ambv/black
rev: stable
rev: 18.6b4
hooks:
- id: black
args:
- "--fast"
language_version: python3.6
\ No newline at end of file
language_version: python3 # Should be >= 3.6
\ No newline at end of file
......@@ -328,7 +328,14 @@ def try_open(fu):
self._raw_handler.ibtmo(timeout)
self._timeout = timeout
with KillMask():
return fu(self, *args, **keys)
try:
return fu(self, *args, **keys)
except:
try:
self.close()
except:
pass
raise
return rfunc
......
......@@ -274,13 +274,13 @@ class Modbus_RTU:
def try_connect_modbustcp(fu):
def rfunc(self, *args, **kwarg):
timeout = kwarg.get("timeout")
def rfunc(self, *args, **kwargs):
timeout = kwargs.get("timeout")
if not self._connected:
self.connect(timeout=timeout)
try:
with KillMask():
return fu(self, *args, **kwarg)
return fu(self, *args, **kwargs)
except socket.error as e:
if e.errno == errno.EPIPE:
# some modbus controller close the connection
......@@ -288,7 +288,7 @@ def try_connect_modbustcp(fu):
gevent.sleep(0)
self.connect(timeout=timeout)
with KillMask():
return fu(self, *args, **kwarg)
return fu(self, *args, **kwargs)
else:
raise
......
......@@ -18,6 +18,7 @@ import weakref
import gevent
from gevent import socket, select, lock, event
from ..common.greenlet_utils import KillMask
from bliss.common.cleanup import capture_exceptions
import serial
......@@ -53,9 +54,18 @@ class SerialTimeout(CommunicationTimeout):
def try_open(fu):
def rfunc(self, *args, **kwarg):
with KillMask():
self.open()
return fu(self, *args, **kwarg)
try:
with KillMask():
self.open()
return fu(self, *args, **kwarg)
except gevent.Timeout:
raise
except:
try:
self.close()
except:
pass
raise
return rfunc
......@@ -91,26 +101,54 @@ class _BaseSerial:
def _readline(self, eol):
eol_pos = self._data.find(eol)
while eol_pos == -1:
self._event.wait()
self._event.clear()
eol_pos = self._data.find(eol)
msg = self._data[:eol_pos]
self._data = self._data[eol_pos + len(eol) :]
return msg
with capture_exceptions() as capture:
while eol_pos == -1:
with capture():
self._event.wait()
self._event.clear()
eol_pos = self._data.find(eol)
if capture.failed:
other_exc = [
x
for _, x, _ in capture.failed
if not isinstance(x, gevent.Timeout)
]
if not other_exc:
if eol_pos == -1:
continue
else:
break
msg = self._data[:eol_pos]
self._data = self._data[eol_pos + len(eol) :]
return msg
def read(self, size, timeout):
with self._timeout_context(timeout):
return self._read(size)
def _read(self, size):
while len(self._data) < size:
self._event.wait()
self._event.clear()
msg = self._data[:size]
self._data = self._data[size:]
return msg
with capture_exceptions() as capture:
while len(self._data) < size:
with capture():
self._event.wait()
self._event.clear()
if capture.failed:
other_exc = [
x
for _, x, _ in capture.failed
if not isinstance(x, gevent.Timeout)
]
if not other_exc:
if len(self._data) < size:
continue
else:
break
msg = self._data[:size]
self._data = self._data[size:]
return msg
def write(self, msg, timeout):
with self._timeout_context(timeout):
......@@ -247,7 +285,7 @@ class RFC2217(_BaseSerial):
local_host, local_port = match.group(2), match.group(3)
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._socket.connect((local_host, local_port))
self._socket.connect((local_host, int(local_port)))
self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
self._socket.setsockopt(socket.SOL_IP, socket.IP_TOS, 0x10)
self.fd = self._socket.fileno()
......
......@@ -18,12 +18,13 @@ from gevent import socket, event, queue, lock
import time
import logging
import weakref
from bliss.common.event import send
from .exceptions import CommunicationError, CommunicationTimeout
from ..common.greenlet_utils import KillMask
from .util import HexMsg
from bliss.common.cleanup import error_cleanup
from bliss.common.cleanup import error_cleanup, capture_exceptions
class SocketTimeout(CommunicationTimeout):
......@@ -54,7 +55,16 @@ def try_connect_socket(fu):
kwarg.update({"timeout": prev_timeout})
with KillMask():
return fu(self, *args, **kwarg)
try:
return fu(self, *args, **kwarg)
except gevent.Timeout:
raise
except:
try:
self.close()
except:
pass
raise
return rfunc
......@@ -122,6 +132,7 @@ class BaseSocket:
self._raw_read_task = gevent.spawn(
self._raw_read, weakref.proxy(self), self._fd
)
send(self, "connect", True)
return True
......@@ -138,13 +149,16 @@ class BaseSocket:
self._shutdown()
except: # probably closed one the server side
pass
self._fd.close()
if self._raw_read_task:
self._raw_read_task.kill()
self._raw_read_task = None
self._data = ""
self._connected = False
self._fd = None
try:
self._fd.close()
finally:
if self._raw_read_task:
self._raw_read_task.kill()
self._raw_read_task = None
self._data = ""
self._connected = False
self._fd = None
send(self, "connect", False)
def _shutdown(self):
"""
......@@ -173,15 +187,30 @@ class BaseSocket:
@try_connect_socket
def read(self, size=1, timeout=None):
timeout_errmsg = "timeout on socket(%s, %d)" % (self._host, self._port)
with gevent.Timeout(timeout or self._timeout, SocketTimeout(timeout_errmsg)):
while len(self._data) < size:
self._event.wait()
self._event.clear()
if not self._connected:
raise socket.error(errno.EPIPE, "Broken pipe")
msg = self._data[:size]
self._data = self._data[size:]
return msg
with capture_exceptions() as capture:
with gevent.Timeout(
timeout or self._timeout, SocketTimeout(timeout_errmsg)
):
while len(self._data) < size:
with capture():
self._event.wait()
self._event.clear()
if capture.failed:
other_exc = [
x
for _, x, _ in capture.failed
if not isinstance(x, gevent.Timeout)
]
if not other_exc:
if len(self._data) < size:
continue
else:
break
if not self._connected:
raise socket.error(errno.EPIPE, "Broken pipe")
msg = self._data[:size]
self._data = self._data[size:]
return msg
@try_connect_socket
def readline(self, eol=None, timeout=None):
......@@ -189,21 +218,36 @@ class BaseSocket:
def _readline(self, eol=None, timeout=None):
timeout_errmsg = "timeout on socket(%s, %d)" % (self._host, self._port)
with gevent.Timeout(timeout or self._timeout, SocketTimeout(timeout_errmsg)):
# local_timeout = timeout or self._timeout
local_eol = eol or self._eol
# start_time = time.time()
eol_pos = self._data.find(local_eol)
while eol_pos == -1:
self._event.wait()
self._event.clear()
if not self._connected:
raise socket.error(errno.EPIPE, "Broken pipe")
with capture_exceptions() as capture:
with gevent.Timeout(
timeout or self._timeout, SocketTimeout(timeout_errmsg)
):
local_eol = eol or self._eol
eol_pos = self._data.find(local_eol)
msg = self._data[:eol_pos]
self._data = self._data[eol_pos + len(local_eol) :]
return msg
while eol_pos == -1:
with capture():
self._event.wait()
self._event.clear()
eol_pos = self._data.find(local_eol)
if capture.failed:
other_exc = [
x
for _, x, _ in capture.failed
if not isinstance(x, gevent.Timeout)
]
if not other_exc:
if eol_pos == -1:
continue
else:
break
if not self._connected:
raise socket.error(errno.EPIPE, "Broken pipe")
msg = self._data[:eol_pos]
self._data = self._data[eol_pos + len(local_eol) :]
return msg
@try_connect_socket
def write(self, msg, timeout=None):
......@@ -301,15 +345,17 @@ class Socket(BaseSocket):
pass
finally:
try:
fd.close()
sock._raw_read_task = None
sock.close()
except socket.error:
pass
try:
sock._connected = False
sock._fd = None
sock._event.set()
except ReferenceError:
pass
finally:
try:
sock._event.set()
except ReferenceError:
pass
class CommandTimeout(CommunicationTimeout):
......@@ -332,7 +378,16 @@ def try_connect_command(fu):
self.connect(timeout=timeout)
kwarg.update({"timeout": prev_timeout})
with KillMask():
return fu(self, *args, **kwarg)
try:
return fu(self, *args, **kwarg)
except gevent.Timeout:
raise
except:
try:
self.close()
except:
pass
raise
return rfunc
......@@ -440,6 +495,7 @@ class Command:
)
self._connected = True
send(self, "connect", True)
return True
def close(self):
......@@ -449,11 +505,14 @@ class Command:
self._fd.shutdown(socket.SHUT_RDWR)
except: # probably closed one the server side
pass
self._fd.close()
if self._raw_read_task:
self._raw_read_task.kill()
self._raw_read_task = None
self._transaction_list = []
try:
self._fd.close()
finally:
if self._raw_read_task:
self._raw_read_task.kill()
self._raw_read_task = None
self._transaction_list = []
send(self, "connect", False)
@try_connect_command
def _read(self, transaction, size=1, timeout=None, clear_transaction=True):
......@@ -574,15 +633,17 @@ class Command:
pass
finally:
try:
fd.close()
command._raw_read_task = None
transaction_list = command._transaction_list
command.close()
except socket.error:
pass
except ReferenceError:
pass
try:
command._connected = False
command._fd = None
# inform all pending transaction that the socket is closed
with command._lock:
for trans in command._transaction_list:
for trans in transaction_list:
trans.put(socket.error(errno.EPIPE, "Broken pipe"))
except ReferenceError:
pass
......
This diff is collapsed.
......@@ -6,29 +6,13 @@
# Distributed under the GNU LGPLv3. See LICENSE for more info.
import sys
from gevent import GreenletExit
from louie import dispatcher
from louie import robustapply
from louie import saferef
if not hasattr(robustapply, "_robust_apply"):
# patch robustapply.robust_apply to display exceptions, but to ignore them
# this makes 'dispatcher.send' to continue on exceptions, which is
# the behaviour we want ; it's not because a receiver doesn't handle a
# signal properly that the whole chain should stop
robustapply._robust_apply = robustapply.robust_apply
def __my_robust_apply(*args, **kwargs):
try:
return robustapply._robust_apply(*args, **kwargs)
except:
sys.excepthook(*sys.exc_info())
robustapply.robust_apply = __my_robust_apply
del __my_robust_apply
def send(sender, signal, *args, **kwargs):
dispatcher.send(signal, sender, *args, **kwargs)
......
......@@ -8,8 +8,14 @@ MASKED_GREENLETS = dict()
class KillMask:
def __init__(self):
def __init__(self, masked_kill_nb=-1):
"""
masked_kill_nb: nb of masked kill
< 0 mean all kills are masked.
if > 0, at each kill attempt the counter decrements until 0, then the greenlet can be killed
"""
self.__greenlet = gevent.getcurrent()
self.__kill_counter = masked_kill_nb
def __enter__(self):
self.__func, self.__exception, self.__waiter = None, None, None
......@@ -29,9 +35,15 @@ class KillMask:
gevent.get_hub().loop.run_callback(self.__greenlet.throw, self.__exception)
def set_kill(self, func, exception, waiter):
self.__func = func
self.__exception = exception
self.__waiter = waiter
if self.__kill_counter:
self.__func = func
self.__exception = exception
self.__waiter = waiter
else: # reach 0
self.__func, self.__exception, self.__waiter = None, None, None
cnt = self.__kill_counter
self.__kill_counter -= 1
return not cnt
def set_hub_kill(self, exception):
self.__exception = exception
......@@ -45,6 +57,14 @@ def protect_from_kill(fu):
return func
def protect_from_one_kill(fu):
def func(*args, **kwargs):
with KillMask(masked_kill_nb=1):
return fu(*args, **kwargs)
return func
# gevent.greenlet module patch
saved_kill = greenlet._kill
......@@ -54,7 +74,9 @@ def _patched_kill(greenlet, exception, waiter):
masks = MASKED_GREENLETS.get(greenlet)
if masks:
for m in masks:
m.set_kill(saved_kill, exception, waiter)
if m.set_kill(saved_kill, exception, waiter):
saved_kill(greenlet, exception, waiter)
else:
saved_kill(greenlet, exception, waiter)
......
......@@ -415,7 +415,7 @@ class IntegratingCounter(Counter):
self,
name,
controller,
master_controller=None,
master_controller,
grouped_read_handler=None,
conversion_function=None,
):
......@@ -437,10 +437,7 @@ class IntegratingCounter(Counter):
name, grouped_read_handler, conversion_function, controller
)
if master_controller is None:
self._master_controller_ref = lambda: None
else:
self._master_controller_ref = weakref.ref(master_controller)
self._master_controller_ref = weakref.ref(master_controller)
def get_values(self, from_index=0):
"""
......
......@@ -22,6 +22,28 @@ def Group(*axes_list):
if not isinstance(axis, Axis):
raise ValueError("invalid axis %r" % axis)
axes[axis.name] = axis
# ensure a pseudo axis is not present with one of its corresponding real axes
def check_axes(*axes_to_check):
from bliss.controllers.motor import CalcController
grp_axes = axes.values()
for axis in axes_to_check:
if isinstance(axis.controller, CalcController):
names = [
grp_axis.name
for grp_axis in grp_axes
if grp_axis in axis.controller.reals
]
if names:
raise RuntimeError(
"Virtual axis '%s` cannot be present in group with any of its corresponding real axes: %r"
% (axis.name, names)
)
# also check reals, that can be calc axes themselves too
check_axes(*axis.controller.reals)
check_axes(*axes.values())
# always use the same group name for groups of same axes,
# this is to make sure master name will stay the same
# when doing step-by-step scans for example -- this is
......
......@@ -30,60 +30,61 @@ def floatOrNone(x):
def stateSetting(state):
from bliss.common import axis
try:
move_type = state.move_type
except Exception:
move_type = ""
s = axis.AxisState(state)
s.move_type = move_type
return s
class ControllerAxisSettings:
def __init__(self):
self.setting_names = [
"velocity",
"position",
"dial_position",
"_set_position",
"state",
"offset",
"acceleration",
"low_limit",
"high_limit",
]
self.convert_funcs = {
"velocity": float,
"position": float,
"dial_position": float,
"_set_position": float,
"state": stateSetting,
"offset": float,
"low_limit": floatOrNone,
"high_limit": floatOrNone,
"acceleration": float,
}
def add(self, setting_name, convert_func=str):
self.setting_names = []
self.disabled_settings = {}
self.convert_func = {}
self.persistent_setting = {}
self.config_setting = {}
self.add("velocity", float, config=True)
self.add("acceleration", float, config=True)
self.add("low_limit", floatOrNone)
self.add("high_limit", floatOrNone)
self.add("dial_position", float)
self.add("offset", float)
self.add("_set_position", float)
self.add("position", float)
self.add("state", stateSetting, persistent=False)
def config_settings(self):
return tuple(
[setting for setting, config in self.config_setting.iteritems() if config]
)
def add(self, setting_name, convert_func=str, persistent=True, config=False):
if setting_name not in self.setting_names:
self.setting_names.append(setting_name)
self.convert_funcs[setting_name] = convert_func
self.convert_func[setting_name] = convert_func
self.persistent_setting[setting_name] = persistent
self.config_setting[setting_name] = config
def get(self, axis, setting_name):
if setting_name not in self.setting_names:
raise ValueError(
"No setting '%s` for axis '%s`" % (setting_name, axis.name)
)
if setting_name not in ("state", "position"):
disabled_settings = self.disabled_settings.get(axis, set())
if setting_name in disabled_settings:
return None
if self.persistent_setting[setting_name]:
hash_setting = settings.HashSetting("axis.%s" % axis.name)
value = hash_setting.get(setting_name)
else:
value = None
if value is None:
chan = axis._beacon_channels[setting_name]
value = chan.value
chan = axis._beacon_channels.get(setting_name)
if chan:
value = chan.value
if value is not None:
convert_func = self.convert_funcs.get(setting_name)
convert_func = self.convert_func.get(setting_name)
if convert_func is not None:
value = convert_func(value)
return value
......@@ -98,11 +99,11 @@ class ControllerAxisSettings:
raise ValueError(
"No setting '%s` for axis '%s`" % (setting_name, axis.name)
)
convert_func = self.convert_funcs.get(setting_name)
convert_func = self.convert_func.get(setting_name)
if convert_func is not None:
value = convert_func(value)
if setting_name not in ("state", "position"):
if self.persistent_setting[setting_name]:
settings.HashSetting("axis.%s" % axis.name)[setting_name] = value
axis._beacon_channels[setting_name].value = value
......@@ -124,9 +125,30 @@ class AxisSettings:
self.__axis, setting_name, value
)
def convert_func(self, setting_name):
return self.__axis.controller.axis_settings.convert_func[setting_name]
def config_settings(self):
return self.__axis.controller.axis_settings.config_settings()
def get(self, setting_name):
return self.__axis.controller.axis_settings.get(self.__axis, setting_name)
def disable_cache(self, setting_name, flag=True):
"""
Remove the cache setting for the a setting_name.
"""
disabled_settings = self.__axis.controller.axis_settings.disabled_settings.setdefault(
self.__axis, set()
)
if flag:
disabled_settings.add(setting_name)
else:
try:
disabled_settings.remove(setting_name)
except KeyError:
pass
def __iter__(self):
for name in self.__axis.controller.axis_settings.setting_names:
yield name
......@@ -31,7 +31,7 @@ from bliss.common.motor_group import Group
from bliss.common.cleanup import cleanup, axis as cleanup_axis
from bliss.common.axis import estimate_duration
from bliss.scanning.default import DefaultAcquisitionChain
from bliss.scanning import scan as scan_module
from bliss.scanning.scan import Scan, StepScanDataWatch
from bliss.scanning.acquisition.motor import VariableStepTriggerMaster
from bliss.scanning.acquisition.motor import (
LinearStepTriggerMaster,
......@@ -43,22 +43,6 @@ _log = logging.getLogger("bliss.scans")
DEFAULT_CHAIN = DefaultAcquisitionChain()
def step_scan(chain, scan_info, name=None, save=True, save_images=True):
scan_data_watch = scan_module.StepScanDataWatch()
config = scan_module.ScanSaving().get()
writer = config.get("writer") if save else None
if writer:
writer._save_images = save_images
return scan_module.Scan(
chain,
name=name,
parent=config["parent"],
scan_info=scan_info,
writer=writer,
data_watch_callback=scan_data_watch,
)
def ascan(motor, start, stop, npoints, count_time, *counter_args, **kwargs):
"""
Absolute scan
......@@ -138,12 +122,13 @@ def ascan(motor, start, stop, npoints, count_time, *counter_args, **kwargs):
"Scanning %s from %f to %f in %d points", motor.name, start, stop, npoints
)
scan = step_scan(
scan = Scan(
chain,
scan_info,
scan_info=scan_info,
name=kwargs.setdefault("name", "ascan"),
save=scan_info["save"],
save_images=save_images,
data_watch_callback=StepScanDataWatch(),
)
if kwargs.get("run", True):
......@@ -189,8 +174,14 @@ def dscan(motor, start, stop, npoints, count_time, *counter_args, **kwargs):
"""
kwargs["type"] = "dscan"
kwargs.setdefault("name", "dscan")
args = kwargs.get("type", "dscan"), motor.name, start, stop, npoints, count_time
template = " ".join(["{{{0}}}".format(i) for i in range(len(args))])
title = template.format(*args)
kwargs.setdefault("title", title)
start += motor.position()
stop += motor.position()
with cleanup(motor, restore_list=(cleanup_axis.POS,)):
scan = ascan(motor, start, stop, npoints, count_time, *counter_args, **kwargs)
return scan
......@@ -329,12 +320,13 @@ def amesh(
npoints2,
)
scan = step_scan(
scan = Scan(
chain,
scan_info,
scan_info=scan_info,
name=kwargs.setdefault("name", "amesh"),
save=scan_info["save"],
save_images=save_images,
data_watch_callback=StepScanDataWatch(),
)
if kwargs.get("run", True):
......@@ -411,7 +403,7 @@ def a2scan(
**kwargs
):
"""
Absolute 2 motor scan
Absolute 2 motors scan
Scans two motors, as specified by *motor1* and *motor2*. The motors start
at the positions given by *start1* and *start2* and end at the positions
......@@ -518,12 +510,13 @@ def a2scan(
npoints,
)
scan = step_scan(
scan = Scan(
chain,
scan_info,
scan_info=scan_info,
name=kwargs.setdefault("name", "a2scan"),
save=scan_info["save"],
save_images=save_images,
data_watch_callback=StepScanDataWatch(),
)
if kwargs.get("run", True):
......@@ -546,7 +539,7 @@ def d2scan(
**kwargs
):
"""
Relative 2 motor scan
Relative 2 motors scan
Scans two motors, as specified by *motor1* and *motor2*. Each motor moves
the same number of points. If a motor is at position *X*
......@@ -555,8 +548,8 @@ def d2scan(
of intervals will be *npoints*-1. Count time is given by *count_time*
(seconds).
At the end of the scan (even in case of error) the motor will return to
its initial position
At the end of the scan (even in case of error) the motors will return to
their initial positions.
Use `d2scan(..., run=False)` to create a scan object and
its acquisition chain without executing the actual scan.
......@@ -584,28 +577,40 @@ def d2scan(
scan object and acquisition chain
return_scan (bool): True by default
"""
kwargs["type"] = "d2scan"
kwargs.setdefault("type", "d2scan")
args = (
kwargs.get("type"),
motor1.name,
start1,
stop1,
motor2.name,
start2,
stop2,
npoints,
count_time,
)
template = " ".join(["{{{0}}}".format(i) for i in range(len(args))])
title = template.format(*args)
kwargs.setdefault("title", title)
kwargs.setdefault("name", "d2scan")
oldpos1 = motor1.position()
oldpos2 = motor2.position()
kwargs.setdefault("name", "d2scan")
scan = a2scan(
motor1,
oldpos1 + start1,
oldpos1 + stop1,
motor2,
oldpos2 + start2,
oldpos2 + stop2,
npoints,
count_time,
*counter_args,
**kwargs
)
with cleanup(motor1, motor2, restore_list=(cleanup_axis.POS,)):
scan = a2scan(
motor1,
oldpos1 + start1,
oldpos1 + stop1,
motor2,
oldpos2 + start2,
oldpos2 + stop2,
npoints,
count_time,
*counter_args,
**kwargs
)
group = Group(motor1, motor2)
group.move(motor1, oldpos1, motor2, oldpos2)
return scan
......@@ -677,12 +682,13 @@ def timescan(count_time, *counter_args, **kwargs):
chain = DEFAULT_CHAIN.get(scan_info, counter_args)
scan = step_scan(
scan = Scan(
chain,
scan_info,
scan_info=scan_info,
name=kwargs.setdefault("name", "timescan"),
save=scan_info["save"],
save_images=save_images,
data_watch_callback=StepScanDataWatch(),
)
if kwargs.get("run", True):
......@@ -720,12 +726,17 @@ def loopscan(npoints, count_time, *counter_args, **kwargs):
"""
kwargs.setdefault("npoints", npoints)
kwargs.setdefault("name", "loopscan")
kwargs.setdefault("type", "loopscan")
args = kwargs.get("type", "loopscan"), npoints, count_time
template = " ".join(["{{{0}}}".format(i) for i in range(len(args))])
title = template.format(*args)
kwargs.setdefault("title", title)
return timescan(count_time, *counter_args, **kwargs)
def ct(count_time, *counter_args, **kwargs):
"""
Count for a specified time
Counts for a specified time
Use `ct(..., run=False)` to create a count object and
its acquisition chain without executing the actual count.
......@@ -760,9 +771,10 @@ def pointscan(motor, positions, count_time, *counter_args, **kwargs):
"""
Point scan
Scans one motor, as specified by *motor*. The motor starts at the position
given by the first value in *positions* and ends at the position given by last value *positions*.
Count time is given by *count_time* (seconds).
Scans one motor, as specified by *motor*. The motor starts at the
position given by the first value in *positions* and ends at the
position given by last value *positions*. Count time is given by
*count_time* (seconds).
Args:
motor (Axis): motor to scan
......@@ -822,12 +834,13 @@ def pointscan(motor, positions, count_time, *counter_args, **kwargs):
npoints,
)
scan = step_scan(
scan = Scan(
chain,
scan_info,
scan_info=scan_info,
name=kwargs.setdefault("name", "pointscan"),
save=scan_info["save"],
save_images=save_images,
data_watch_callback=StepScanDataWatch(),
)
scan.run()
......
......@@ -360,7 +360,7 @@ class Session(object):
from bliss.scanning.scan import ScanSaving, ScanDisplay, SCANS
env_dict["SCANS"] = SCANS
env_dict["SCAN_SAVING"] = ScanSaving()
env_dict["SCAN_SAVING"] = ScanSaving(self.name)
env_dict["SCAN_DISPLAY"] = ScanDisplay()
from bliss.common.measurementgroup import ACTIVE_MG
......@@ -389,17 +389,14 @@ class Session(object):
if self.setup_file is None:
return
try:
with get_file({"setup_file": self.setup_file}, "setup_file") as setup_file:
code = compile(setup_file.read(), self.setup_file, "exec")
exec (code, env_dict)
with get_file({"setup_file": self.setup_file}, "setup_file") as setup_file:
code = compile(setup_file.read(), self.setup_file, "exec")
exec (code, env_dict)
for obj_name, obj in env_dict.iteritems():
setattr(setup_globals, obj_name, obj)
for obj_name, obj in env_dict.iteritems():
setattr(setup_globals, obj_name, obj)
return True
except IOError:
raise ValueError("Session: setup-file %s cannot be found" % self.setup_file)
return True
def close(self):
if get_current() is self:
......
......@@ -81,9 +81,6 @@ class Shutter(object):
self.__settings = HashObjSetting("shutter:%s" % name)
self.__initialized_hw = Cache(self, "initialized", default_value=False)
self.__state = Cache(self, "state", default_value=Shutter.UNKNOWN)
self.__shutter_state = Channel(
name + ":shutter_state", default_value=Shutter.UNKNOWN
)
self._init_flag = False
self.__lock = lock.Semaphore()
......@@ -203,7 +200,7 @@ class Shutter(object):
return self.UNKNOWN
elif mode == self.CONFIGURATION:
return self.UNKNOWN
return self.__state.value
return self.__state.value
def _state(self):
raise NotImplementedError
......@@ -226,6 +223,20 @@ class Shutter(object):
def __exit__(self, exc_type, exc_value, traceback):
self.close()
@property
def is_open(self):
return self.state == self.OPEN
@property
def is_closed(self):
return self.state == self.CLOSED
def __enter__(self):
self.open()
def __exit__(self, exc_type, exc_value, traceback):
self.close()
@property
def external_control(self):
return self._external_ctrl
......@@ -301,7 +312,7 @@ class Shutter(object):
)
else:
ret = self._open()
self.__shutter_state.value = self.state
self.__state.value = self.OPEN
return ret
def _open(self):
......@@ -324,7 +335,7 @@ class Shutter(object):
)
else:
ret = self._close()
self.__shutter_state.value = self.state
self.__state.value = self.CLOSED
return ret
def _close(self):
......
# -*- coding: utf-8 -*-
#
# This file is part of the bliss project
#
# Copyright (c) 2016 Beamline Control Unit, ESRF
# Distributed under the GNU LGPLv3. See LICENSE for more info.
"""
Standard bliss macros (:func:`~bliss.common.standard.wa`, \
......@@ -35,6 +40,7 @@ import itertools
import inspect
import logging
import functools
import linecache
import gevent
from six import print_
......@@ -67,7 +73,7 @@ _FLOAT_FORMAT = ".05f"
_log = logging.getLogger("bliss.standard")
def __tabulate(data, **kwargs):
def _tabulate(data, **kwargs):
kwargs.setdefault("headers", "firstrow")
kwargs.setdefault("floatfmt", _FLOAT_FORMAT)
kwargs.setdefault("numalign", "right")
......@@ -107,17 +113,22 @@ def wa(**kwargs):
print_("Current Positions (user, dial)")
header, pos, dial = [], [], []
tables = [(header, pos, dial)]
for axis_name, position, dial_position in get_axes_positions_iter(on_error=_ERR):
for axis_name, position, dial_position, axis_unit in get_axes_positions_iter(
on_error=_ERR
):
if len(header) == max_cols:
header, pos, dial = [], [], []
tables.append((header, pos, dial))
header.append(axis_name)
axis_label = axis_name
if axis_unit:
axis_label += "[{0}]".format(axis_unit)
header.append(axis_label)
pos.append(position)
dial.append(dial_position)
for table in tables:
print_()
print_(__tabulate(table))
print_(_tabulate(table))
def wm(*axes, **kwargs):
......@@ -169,7 +180,11 @@ def wm(*axes, **kwargs):
low_dial,
)
)
header.append(axis.name)
unit = axis.config.get("unit", default=None)
axis_label = axis.name
if unit:
axis_label += "[{0}]".format(unit)
header.append(axis_label)
User.append(None)
high_user.append(high if high != None else _MISSING_VAL)
user.append(get(axis, "position"))
......@@ -181,7 +196,7 @@ def wm(*axes, **kwargs):
for table in tables:
print_()
print_(__tabulate(table))
print_(_tabulate(table))
def stm(*axes):
......@@ -206,7 +221,7 @@ def sta(read_hw=False):
)
for axis in get_axes_iter()
]
print_(__tabulate(table))
print_(_tabulate(table))
def mv(*args):
......@@ -348,6 +363,8 @@ def prdef(obj_or_name):
pass
fname = inspect.getfile(obj)
# make sure cache reloads changed file on disk
linecache.checkcache(fname)
lines, line_nb = inspect.getsourcelines(obj)
if name == real_name or is_arg_str:
......@@ -447,7 +464,7 @@ def edit_roi_counters(detector, acq_time=None):
else:
scan = setup_globals.SCANS[-1]
plot = scan.get_plot(detector, wait=True)
plot = scan.get_plot(detector.image, wait=True)
selections = []
for roi_name, roi in roi_counters.items():
......
......@@ -11,6 +11,7 @@ import gevent
import types
import itertools
import functools
import numpy
from bliss.common.event import saferef
try:
......@@ -384,7 +385,7 @@ class periodic_exec(object):
def __exit__(self, *args):
if self.__task is not None:
gevent.kill(self.__task)
self.__task.kill()
def _timer(self):
while True:
......@@ -440,6 +441,7 @@ def get_axes_positions_iter(on_error=None):
axis.name,
safe_get(axis, "position", on_error),
safe_get(axis, "dial", on_error),
axis.config.get("unit", default=None),
)
tasks = list()
......@@ -465,3 +467,65 @@ def closable(obj):
and inspect.ismethod(obj.close)
and obj.close.im_self is not None
)
def human_time_fmt(num, suffix="s"):
"""
format time second in human readable format
"""
for unit in ["", "m", "u", "p", "f"]:
if abs(num) < 1:
num *= 1000
continue
return "%3.3f%s%s" % (num, unit, suffix)
class Statistics(object):
"""
Calculate statistics from a profiling dictionary
key == function name
values == list of tuple (start_time,end_time)
"""
def __init__(self, profile):
self._profile = {
key: numpy.array(values, dtype=numpy.float)
for key, values in profile.items()
}
@property
def elapsed_time(self):
"""
elapsed time function
"""
return {
key: values[:, 1] - values[:, 0] for key, values in self._profile.items()
}
@property
def min_mean_max_std(self):
"""
dict with (min, mean, max, std) tuple
"""
return {
key: (values.min(), values.mean(), values.max(), values.std())
for key, values in self.elapsed_time.items()
}
def __repr__(self):
# due to recursion import standard here
from bliss.common import standard
data = [("func_name", "min", "mean", "max", "std")]
for key, values in sorted(self.min_mean_max_std.items()):
data.append(
(
key,
human_time_fmt(values[0]),
human_time_fmt(values[1]),
human_time_fmt(values[2]),
values[3],
)
)
return standard._tabulate(data)
......@@ -201,8 +201,8 @@ def _send_config_file(client_id, message):
file_path = file_path.replace("../", "") # prevent going up
full_path = os.path.join(_options.db_path, file_path)
try:
with codecs.open(full_path, "r", "utf-8") as f:
buffer = f.read().encode("utf-8")
with open(full_path, "rb") as f:
buffer = f.read()
client_id.sendall(
protocol.message(
protocol.CONFIG_GET_FILE_OK, "%s|%s" % (message_key, buffer)
......
......@@ -74,6 +74,8 @@ def _parse_list(config, value):
return_list = _parse_list(config, node)
if return_list:
object_list.append(return_list)
else:
object_list.append(node)
return object_list
......
......@@ -38,16 +38,16 @@ def boolify(s, **keys):
raise ValueError("Not Boolean Value!")
def auto_conversion(var):
"""guesses the str representation of the variables type"""
if var is None:
def auto_coerce_from_redis(s):
"""Convert variable to a new type from the str representation"""
if s is None:
return None
for caster in (boolify, int, float):
try:
return caster(var)
return caster(s)
except (ValueError, TypeError):
pass
return var
return s
def pickle_loads(var):
......@@ -161,8 +161,8 @@ class SimpleSetting(object):