...
 
Commits (536)
......@@ -3,6 +3,7 @@
*.pyc
*.pyo
*~
*.py,cover
*.bck
build
*.swp
......@@ -26,3 +27,4 @@ htmlcov/
*.so
bliss/release.py
prof
.mypy_cache/
......@@ -2,6 +2,7 @@ before_script:
# set pip cache to the Docker volume
- echo ${CI_PROJECT_DIR}
- export PIP_CACHE_DIR="/opt/cache/pip"
- /opt/conda/bin/conda init && source /root/.bashrc
- conda config --append channels conda-forge
- conda config --add channels defaults
- conda config --add channels http://bcu-ci.esrf.fr/stable
......@@ -44,7 +45,7 @@ run_tests:source:
only:
changes: # skip tests for doc changes
- "bin/**/*"
- "bliss/**/*"
- "**/*.{py}"
- "conda_recipe/**/*"
- "extensions/**/*"
- "scripts/**/*"
......@@ -59,9 +60,12 @@ package:
stage: build
image: continuumio/miniconda3:latest
script:
# create package env and install all requirements and conda-build
# create package env and install all requirements and conda-build, (gcc and g++ compiler required for flint)
- conda create --quiet --name buildenv --file requirements-conda.txt --file requirements-test-conda.txt conda-build
- source activate buildenv
# create links to reach prefixed compilers of conda
- ln -s /opt/conda/envs/buildenv/bin/x86_64-conda_cos6-linux-gnu-gcc /opt/conda/envs/buildenv/bin/gcc
- ln -s /opt/conda/envs/buildenv/bin/x86_64-conda_cos6-linux-gnu-g++ /opt/conda/envs/buildenv/bin/g++
# triggering the creation of bliss/release.py file
- python -c "from setup import generate_release_file;generate_release_file()"
# creating the meta.yaml file for conda packet generation
......@@ -131,6 +135,8 @@ run_tests:package:
pages:
stage: deploy
before_script:
- ''
tags:
- conda
- builder
......@@ -156,6 +162,8 @@ pages:
deploy_bliss:
stage: deploy
before_script:
- ''
tags:
- conda
- builder
......
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.2.0] - 2019-08-23
### Added
- Bliss Shell Interface
- New Bliss interface for visualize Scan results on a different window (pressing F5).
It uses Tmux under the hood allowing a bunch of new capabilities like remote assistance (replacing the need of `screen`)
Launching Bliss will use Tmux as a default, use the `--no-tmux` option to start a session without tmux
- New UserDialogs available to interact with users (ask questions and display messages)
- Bliss Shell
- Exception Management
- more friendly exception management hiding exception details under `last_error` global variable (#402)
- Introduced global variable ERROR_REPORT.expert_mode to allow full traceback for expert users
- added `history` tooltip for viewing last commands (globals)
- add shell autocomplexion for dynamic attributes
- **info(obj)** function will standardize the representation of an object inside Bliss shell,
it uses underline `__info__` method if it exists and eventually falling back to `repr`
- New controllers support added:
- Aerotech Soloist
- Elmo whistle
- Lakeshore 331/332/335/336/340
- MultiplePositions
- Mythen/Mythen2
- Counters
- Soft Timer provides also `epoch` time (before was only delta)
- Added simulation_counter for user testing purposes
- Calculation counter can now be defined over other counters
- Tango Attribute counter can now use `unit` and `display unit` from Tango config
- Sampling counter modes statistics
- Now is possible to iterate Counters and Motors
- Sessions
- A Bliss Session will connect to an existing one if they share the same name and host.
In fact the same session will have only one instance at a time. This assumes underline use of Tmux
- Aliases
- shortcut for bliss objects (Axis, Counters, Channels) at a session level
- used in hdf5
- global ALIASES
- Global Map
- New Session map where controllers/connections/aliases/counters are registered when they are
loaded. Provides convenient runtime access to all instances and allows building on
top other services.
- Comes with visualization capabilities useful for debug or other purposes.
- Logging
- Based on Global Map it allows to log each individual instance of the same class of
controller/connection.
- Rich set of shell commands: lslog, log_debug, debugon, ...
- GitLab continuous integration
- test is skip if only docs are changed
- allow graphical tests
- new coverage report
- package building for tagged version (releases)
- Beacon Config
- Config
- order of element in file is now keeped while saving (instead of sorted)
- new BeaconObject that groups Static Configs and Settings, to be used when planning to
use both Config and Settings
- Scan
- New `pointscan` that performs scan on a list of positions
- New ScanPreset argument to perform operation on prepare/start/stop
- Alligment with multiple motors can be done propertly with (cen, com, peak, ...)
- New possibility to add further metadata when launching a Scan using scan_meta
- hdf5
- Values of underline Calculation Motors are exported
- Documentation about external data writing script
- SamplingModes refactor with new modes: STATS, SAMPLES, SINGLE, LAST, INTEGRATE_STATS
- flint new possibility to select any (X,Y) axis combination for plot
- IntegratingCounter: master_controller can now be None
### Changed
- controllers: py2to3 porting, improvements, refactor and bugfix
- Add commands for keithley 6514 and 2000
- Icepap
- Change of names
- API change in linked axis
- New `show` command to check enabled/disabled axis
- New check of trajectory number of points in regard to Icepap memory
- Euro2400
- Keithley 485
- Lakeshore
- Musst
- Md2
- pi_c663
- pi_e712
- pi_e727
- pi_e871
- pi_hexa
- Prologix
- Scpi
- Shexapod v1 and v2
- Web interface py2to3 port
- Parameters renamed in ParametersWardrobe with improved functionalities:
- allow export to YAML file and beacon
- allow property_attributes (read-only) and not-removable parameters
- add purge, remove, copy, freeze, show_table
- SamplingModes refactor with renaming of SIMPLE_AVERAGE to MEAN
### Fixed
- musst: python3 data reading, synchronization
- "Add WagoAirHook hook" (#772 #110)
- Static config: simplify reparent process (#495)
- fuelcell: "import of fuelcell (moved to id31 dir.) (#817)
- hdf5: "writer try to create an existing scan entry" (#620)
- shell:
- "Line numbering does not increase in bliss shell" (#610)
- "Exit shell with <CTRL-d> + return"
- "Deprecation warning coming from jinja2" (#688)
- ".counters namespace not accesible from command line" (#625)
- "kwargs in signature display in shell" (#798)
- "typinghelper check callable " (#746)
- gevent: "gevent timeout"
- gitlab CI:
- "do not copy htmlcov dir to public/"
- "added missing libxi6"
- gpib: "ibwrt python3 tango" (#574)
- scan:
- "scan preset" (#561)
- "Scan display: does not work when multiple points are received at the same time" (#743)
- "Scan listener missing output lines on fast scan" (#747)
- "real motors of nested calc axis not published" (#714)
- "scan.run should raise an exception if re-started" (#771)
- "empty .children() list of data note after NEW_CHILD" (#826)
- web interface: "web page interface" (#627)
- louie: Remove louie from StepScanDataWatchCallback (#645)
- rpc: "uds connection when the socket is removed, return AttributError"
- fix on node __internal_walk
- serial: "use url if port is not set"
- redis:
- "fullnames containing : and ." (#615)
- "exception in TTL setter" (#630)
- "really use alias name in session" (#700)
- flint/silx:
- "axes colors" (#717)
- "rulers are not updated" (#718)
- "error in Flint interaction" (#829)
- "Throwing an error in channel notification breaks object" (#719)
- "apply_config() fails if wrong settings have been set at first init" (#751)
- "double definition of close" (#587)
- "Load_scripts does not return an object or function" (#722)
- lima:
- "fix missing .fullname in Lima data node object"
- "fix of lima bpm simulator working again" (#664)
- "YAML 1.1 specification says ON, OFF, and other values should be converted to boolean" (#781)
- "BaseShutter `repr` bug" (#783)
- "motor is not initialised with MOVING state in axis settings"
- "comm.tcp.Command.connect : undefined variables" (#824)
- "prdef does not print correctly the inspected function" (#777)
- "Cannot set unit on tango_attr_as_counter" (#833)
- "Saving/Editing configuration implemented in a controller" (#835)
- "lima: fixed timescan" (#844)
- "manage limits always in DIAL. convert limits only on user interaction" (#854)
### Removed
## [0.1.3] - 2019-03-07
......@@ -18,7 +18,7 @@ import gevent
import json
from bliss.common.utils import OrderedDict
from collections import OrderedDict
try:
import sps
......
......@@ -20,7 +20,6 @@
shell
tango
"""
from . import release
__version__ = release.version
......@@ -28,10 +27,46 @@ __author__ = release.author
__license__ = release.license
version_info = release.version_info
from gevent import monkey
from gevent import monkey as _monkey
_monkey.patch_all(thread=False)
from bliss.common.proxy import Proxy as _Proxy
def get_current_session():
from bliss.common import session
return session.get_current_session()
current_session = _Proxy(get_current_session)
from bliss.common.alias import MapWithAliases as _MapWithAliases
global_map = _MapWithAliases(current_session)
from bliss.common.logtools import Log as _Log
global_log = _Log(map=global_map)
def logging_startup(
log_level="WARNING", fmt="%(levelname)s %(asctime)-15s %(name)s: %(message)s"
):
"""
Provides basicConfig functionality to bliss activating at proper level the root loggers
"""
import logging # this is not to pollute the global namespace
monkey.patch_all(thread=False)
# save log messages format
global_log.set_log_format(fmt)
global_log._LOG_DEFAULT_LEVEL = log_level # to restore level of non-BlissLoggers
from redis import selector
# setting startup level for session and bliss logger
logging.getLogger("session").setLevel(log_level)
logging.getLogger("bliss").setLevel(log_level)
selector._DEFAULT_SELECTOR = selector.SelectSelector
# install an additional handler, only for debug messages
# (debugon / debugoff)
global_log.set_debug_handler(logging.StreamHandler())
......@@ -7,8 +7,8 @@
from warnings import warn
from .embl import ExporterClient
from bliss.common.logtools import LogMixin
from bliss.common import session
from bliss.common.logtools import *
from bliss import global_map
import gevent
from gevent.queue import Queue
......@@ -27,7 +27,7 @@ def start_exporter(address, port, timeout=3, retries=1):
return exporter_clients[(address, port)]
class Exporter(ExporterClient.ExporterClient, LogMixin):
class Exporter(ExporterClient.ExporterClient):
STATE_EVENT = "State"
STATUS_EVENT = "Status"
VALUE_EVENT = "Value"
......@@ -59,7 +59,7 @@ class Exporter(ExporterClient.ExporterClient, LogMixin):
self.events_queue = Queue()
self.events_processing_task = None
session.get_current().map.register(
global_map.register(
self, parents_list=["comms"], tag=f"exporter: {address}:{port}"
)
......@@ -145,8 +145,9 @@ class Exporter(ExporterClient.ExporterClient, LogMixin):
try:
cb(self._to_python_value(value))
except:
self._logger.exception(
"Exception while executing callback %s for event %s", cb, name
log_exception(
self,
f"Exception while executing callback {cb} for event {name}",
)
continue
......
......@@ -21,3 +21,5 @@ This module gathers different communication interfaces
tcp
util
"""
from bliss.comm.util import get_comm
This diff is collapsed.
......@@ -35,8 +35,8 @@
import socket, sys
from struct import *
from bliss.common import session
from bliss.common.logtools import LogMixin
from bliss.common.logtools import *
from bliss import global_map
# debug = ["io", "ignore_not_impl"] # "dummy_io", "rw"
debug = ["ignore_not_impl"]
......@@ -66,7 +66,7 @@ def _not_impl(name):
return _dbg(wrap, name)
class EnetSocket(LogMixin):
class EnetSocket:
def __init__(self, host, port=5000):
self._host = host
self._port = port
......@@ -75,12 +75,12 @@ class EnetSocket(LogMixin):
self.sta = self.err = self.cnt = 0
self.enet1000 = False
self._extra_socket = list()
session.get_current().map.register(self, parents_list=["comms"], tag=str(self))
global_map.register(self, parents_list=["comms"], tag=str(self))
def _open(self):
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._sock.connect((self._host, self._port))
session.get_current().map.register(
global_map.register(
self, parents_list=["comms"], children_list=[self._sock], tag=str(self)
)
......
......@@ -7,6 +7,7 @@
import struct
import weakref
from functools import wraps
from gevent import socket, select
from gevent import lock
from gevent import queue
......@@ -18,8 +19,8 @@ from .exceptions import CommunicationError, CommunicationTimeout
from ..common.greenlet_utils import KillMask, protect_from_kill
from . import serial
from bliss.common import session
from bliss.common.logtools import LogMixin
from bliss.common.logtools import *
from bliss import global_map
class ModbusError(CommunicationError):
......@@ -89,12 +90,12 @@ class Modbus_ASCII:
return lrc & 0xff
class Modbus_RTU(LogMixin):
class Modbus_RTU:
def __init__(self, node, *args, **kwargs):
self._serial = serial.Serial(*args, **kwargs)
self.node = node
self._lock = lock.RLock()
session.get_current().map.register(self, children_list=[self._serial])
global_map.register(self, children_list=[self._serial])
def __del__(self):
self._serial.close()
......@@ -284,6 +285,7 @@ class Modbus_RTU(LogMixin):
def try_connect_modbustcp(fu):
@wraps(fu)
def rfunc(self, *args, **kwargs):
timeout = kwargs.get("timeout")
if not self._connected:
......
......@@ -71,40 +71,6 @@ import psutil
MAX_BUFFER_SIZE = int(psutil.virtual_memory().total * 0.8)
# msgpack patch for numpy and pickle
import pickle
# Patch msgpack
import msgpack_numpy
# Add fallback pickle packing
_msgpack_numpy_encode = msgpack_numpy.encode
def _pickle_fallback_encoding(obj, chain=None):
robj = _msgpack_numpy_encode(obj, chain=chain)
if robj is obj: # try to pickle
return {b"<pickled>": True, b"data": pickle.dumps(obj)}
else:
return robj
_msgpack_numpy_decode = msgpack_numpy.decode
def _pickle_fallback_decode(obj, chain=None):
if obj.get(b"<pickled>") is True:
return pickle.loads(obj[b"data"])
else:
return _msgpack_numpy_decode(obj, chain=chain)
# replace patched encode decode
msgpack_numpy.encode = _pickle_fallback_encoding
msgpack_numpy.decode = _pickle_fallback_decode
msgpack_numpy.patch()
# END patch
import os
import re
import inspect
......@@ -120,7 +86,16 @@ from gevent import socket
from bliss.common.greenlet_utils import KillMask
from bliss.common.utils import StripIt
import msgpack
from bliss.common.logtools import *
from bliss import global_map
from bliss.common.msgpack_ext import MsgpackContext
msgpack = MsgpackContext()
# Registration order matter
msgpack.register_numpy()
msgpack.register_tb_exception()
msgpack.register_pickle()
SPECIAL_METHODS = set(
......@@ -440,6 +415,9 @@ class _cnx(object):
self._event.clear()
def __init__(self, address):
global_map.register(self, parents_list=["comms"], tag=f"rpc client:{address}")
if address.startswith("tcp"):
exp = re.compile("tcp://(.+?):([0-9]+)")
m = exp.match(address)
......@@ -451,6 +429,7 @@ class _cnx(object):
self.host = None
self.port = m.group(2)
self._address = address
self._socket = None
self._queues = dict()
self._reading_task = None
......@@ -505,6 +484,11 @@ class _cnx(object):
def _call__(self, code, args, kwargs):
timeout = kwargs.get("timeout", self._timeout)
log_debug(
self.proxy, f"rpc client ({self._address}): call code={code} args={args}"
)
with gevent.Timeout(timeout):
self.try_connect()
uniq_id = id(next(self._counter))
......@@ -514,7 +498,13 @@ class _cnx(object):
self._socket.sendall(msg)
value = w.get()
if isinstance(value, Exception):
raise value
# FIXME: checking the traceback is an approximation
# It would be better to know it was a raised exception
# from the server msg
if value.__traceback__ is None:
return value
else:
raise value
elif isinstance(value, self.Retry):
self.try_connect()
continue
......
......@@ -42,8 +42,8 @@ import numpy
from .util import get_interface
from .exceptions import CommunicationError, CommunicationTimeout
from bliss.common import session
from bliss.common.logtools import LogMixin
from bliss.common.logtools import *
from bliss import global_map
def decode_IDN(s):
......@@ -57,7 +57,7 @@ def __decode_Err(s):
def __decode_ErrArray(s):
msgs = map(str.strip, s.split(","))
msgs = list(map(str.strip, s.split(",")))
result = []
for i in range(0, len(msgs), 2):
code, desc = int(msgs[i]), msgs[i + 1][1:-1]
......@@ -324,7 +324,7 @@ COMMANDS = Commands(
"*SRE": IntCmdWO(doc="service request enable register"),
"*STB": StrCmdRO(doc="status byte register"),
"*TRG": FuncCmd(doc="bus trigger"),
"*TST": Cmd(get=lambda x: not decode_OnOff(x), doc="self-test query"),
"*TST": Cmd(get=lambda x: not __decode_OnOff(x), doc="self-test query"),
"*WAI": FuncCmd(doc="wait to continue"),
"SYSTem:ERRor[:NEXT]": ErrCmd(doc="return and clear oldest system error"),
}
......@@ -381,7 +381,7 @@ def sanitize_msgs(*msgs, **opts):
return commands, queries, eol.join(result) + eol
class SCPI(LogMixin):
class SCPI:
"""
:term:`SCPI` language helper.
......@@ -413,7 +413,7 @@ class SCPI(LogMixin):
def __init__(self, interface=None, commands=COMMANDS, **kwargs):
self.interface = interface
session.get_current().map.register(
global_map.register(
self, parents_list=["comms"], children_list=[self.interface], tag=str(self)
)
self._strict_query = kwargs.get("strict_query", True)
......@@ -557,7 +557,7 @@ class SCPI(LogMixin):
>>> (_, idn), (_, ese) = instrument.read('*ESE 1; *IDN?; *ESE?')
Args:
*msgs (str): raw message to be queried (ex: "\*IDN?")
*msgs (str): raw message to be queried (ex: '*IDN?')
**kwargs: supported kwargs: *raw* (default: False), *eol*,
*sep* (command separator)
Returns:
......@@ -576,10 +576,10 @@ class SCPI(LogMixin):
eol = kwargs.setdefault("eol", self._eol)
strict_query = kwargs.setdefault("strict_query", self._strict_query)
cmds, queries, msg = sanitize_msgs(*msgs, **kwargs)
self._logger.debug("[start] read %r", msg)
log_debug(self, f"[start] read {msg}")
raw_results = self.interface.write_readlines(msg.encode(), len(queries))
raw_results = [r.decode() for r in raw_results]
self._logger.debug("[ end ] read %r=%r", msg, raw_results)
log_debug(self, f"[ end ] read {msg}={raw_results}")
if raw:
return raw_results
if len(queries) != len(raw_results):
......@@ -595,9 +595,7 @@ class SCPI(LogMixin):
try:
result = getf(result)
except:
self._logger.debug(
"Failed to convert result. Details:", exc_info=1
)
log_exception(self, "Failed to convert result.")
results.append((query, result))
return results
......@@ -617,7 +615,7 @@ class SCPI(LogMixin):
instrumment.write('*RST')
Args:
*msgs (str): raw command (ex: "\*CLS")
*msgs (str): raw command (ex: '*CLS')
Raises:
CommunicationError: in case of device not accessible
......@@ -628,9 +626,9 @@ class SCPI(LogMixin):
context = self._contexts[-1]["commands"].extend(msgs)
return
msg = self.__to_write_commands(*msgs, **kwargs)
self._logger.debug("[start] write %r", msg)
log_debug(self, f"[start] write {msg}")
self.interface.write(msg.encode())
self._logger.debug("[ end ] write %r", msg)
log_debug(self, f"[ end ] write {msg}")
_MAX_ERR_STACK_SIZE = 20
......@@ -659,7 +657,7 @@ class SCPI(LogMixin):
return stack or None
class BaseSCPIDevice(LogMixin):
class BaseSCPIDevice:
"""Base SCPI device class"""
def __init__(self, *args, **kwargs):
......@@ -667,9 +665,7 @@ class BaseSCPIDevice(LogMixin):
commands = kwargs.pop("commands", {})
self.interface = interface
self.language = SCPI(interface=interface, commands=commands)
session.get_current().map.register(
self, children_list=[self.language], tag=str(self)
)
global_map.register(self, children_list=[self.language], tag=str(self))
def __str__(self):
return "{0}({1})".format(type(self).__name__, self.language)
......
......@@ -13,14 +13,14 @@ import re
import struct
import weakref
import binascii
from functools import wraps
import gevent
from gevent import socket, select, lock, event
from ..common.greenlet_utils import KillMask
from bliss.common.cleanup import capture_exceptions
from bliss.common import session
from bliss.common.logtools import LogMixin
from bliss.common.logtools import *
from bliss import global_map
import serial
......@@ -77,6 +77,7 @@ class SerialTimeout(CommunicationTimeout):
def try_open(fu):
@wraps(fu)
def rfunc(self, *args, **kwarg):
try:
with KillMask():
......@@ -151,7 +152,7 @@ class _BaseSerial:
msg = self._data[:eol_pos]
self._data = self._data[eol_pos + len(eol) :]
self._cnt()._logger.debug_data("Rx:", msg)
log_debug_data(self._cnt(), "Rx:", msg)
return msg
def read(self, size, timeout):
......@@ -177,7 +178,7 @@ class _BaseSerial:
break
msg = self._data[:size]
self._data = self._data[size:]
self._cnt()._logger.debug_data("Rx:", msg)
log_debug_data(self._cnt(), "Rx:", msg)
return msg
def write(self, msg, timeout):
......@@ -185,7 +186,7 @@ class _BaseSerial:
return self._write(msg)
def _write(self, msg):
self._cnt()._logger.debug_data("Tx:", msg)
log_debug_data(self._cnt(), "Tx:", msg)
while msg:
_, ready, _ = select.select([], [self.fd], [])
size_send = os.write(self.fd, msg)
......@@ -206,7 +207,7 @@ class _BaseSerial:
msg = self._data
self._data = b""
self._cnt()._logger.debug_data("Rx:", msg)
log_debug_data(self._cnt(), "Rx:", msg)
return msg
@staticmethod
......@@ -709,7 +710,7 @@ class TangoSerial(_BaseSerial):
self._device.DevSerFlush(self.FLUSH_INPUT)
class Serial(LogMixin):
class Serial:
LOCAL, RFC2217, SER2NET, TANGO = list(range(4))
def __init__(
......@@ -746,7 +747,7 @@ class Serial(LogMixin):
self._timeout = timeout
self._raw_handler = None
self._lock = lock.RLock()
session.get_current().map.register(self, parents_list=["comms"], tag=str(self))
global_map.register(self, parents_list=["comms"], tag=str(self))
def __del__(self):
self.close()
......@@ -761,8 +762,8 @@ class Serial(LogMixin):
def open(self):
if self._raw_handler is None:
serial_type = self._check_type()
self._logger.debug("open - serial_type=%s" % serial_type)
self._logger.debug(self._logger.log_format_dict(self._serial_kwargs))
log_debug(self, "open - serial_type=%s" % serial_type)
log_debug_data(self, "serial kwargs", self._serial_kwargs)
if serial_type == self.RFC2217:
self._raw_handler = RFC2217(self, **self._serial_kwargs)
elif serial_type == self.SER2NET:
......@@ -771,7 +772,7 @@ class Serial(LogMixin):
self._raw_handler = TangoSerial(self, **self._serial_kwargs)
else: # LOCAL
self._raw_handler = LocalSerial(self, **self._serial_kwargs)
session.get_current().map.register(
global_map.register(
self,
parents_list=["comms"],
children_list=[self._raw_handler],
......@@ -782,13 +783,13 @@ class Serial(LogMixin):
if self._raw_handler:
self._raw_handler.close()
self._raw_handler = None
self._logger.debug("close")
log_debug(self, "close")
@try_open
def raw_read(self, maxsize=None, timeout=None):
local_timeout = timeout or self._timeout
msg = self._raw_handler.raw_read(maxsize, local_timeout)
self._logger.debug_data("raw_read", msg)
log_debug_data(self, "raw_read", msg)
return msg
def read(self, size=1, timeout=None):
......@@ -799,7 +800,7 @@ class Serial(LogMixin):
def _read(self, size=1, timeout=None):
local_timeout = timeout or self._timeout
msg = self._raw_handler.read(size, local_timeout)
self._logger.debug_data("read", msg)
log_debug_data(self, "read", msg)
if len(msg) != size:
raise SerialError(
"read timeout on serial (%s)" % self._serial_kwargs.get(self._port, "")
......@@ -815,7 +816,7 @@ class Serial(LogMixin):
local_eol = eol or self._eol
local_timeout = timeout or self._timeout
msg = self._raw_handler.readline(local_eol, local_timeout)
self._logger.debug_data("readline", msg)
log_debug_data(self, "readline", msg)
return msg
def write(self, msg, timeout=None):
......@@ -827,7 +828,7 @@ class Serial(LogMixin):
@try_open
def _write(self, msg, timeout=None):
local_timeout = timeout or self._timeout
self._logger.debug_data("write", msg)
log_debug_data(self, "write", msg)
return self._raw_handler.write(msg, local_timeout)
@try_open
......@@ -869,7 +870,7 @@ class Serial(LogMixin):
@try_open
def flush(self):
self._logger.debug("flush")
log_debug(self, "flush")
self._raw_handler.flushInput()
def _check_type(self):
......
......@@ -17,9 +17,10 @@ import gevent
import gevent.socket
import socket
import weakref
from functools import wraps
from bliss.common import event
from bliss.common import session
from bliss.common.logtools import LogMixin
from bliss.common.logtools import *
from bliss import global_map
from .error import SpecClientNotConnectedError
from .channel import SpecChannel
from .message import *
......@@ -67,6 +68,7 @@ def makeConnection(conn):
def try_connect(fu):
@wraps(fu)
def rfunc(self, *args, **kwargs):
if self.connection_greenlet is None or self.connection_greenlet.ready():
self.connection_greenlet = makeConnection(self)
......@@ -119,8 +121,9 @@ def connectionHandler(conn, socket_to_spec):
try:
reply = conn.registeredReplies[replyID]
except BaseException:
conn._logger.exception(
"Unexpected error while receiving a message from server"
log_exception(
conn,
"Unexpected error while receiving a message from server",
)
else:
del conn.registeredReplies[replyID]
......@@ -143,7 +146,7 @@ def connectionHandler(conn, socket_to_spec):
receivedStrings = [s[offset:]]
class SpecConnection(LogMixin):
class SpecConnection:
"""SpecConnection class
Signals:
connected() -- emitted when the required Spec version gets connected
......@@ -184,7 +187,7 @@ class SpecConnection(LogMixin):
self.port = None
self.scanport = True
session.get_current().map.register(self, parents_list=["comms"], tag=str(self))
global_map.register(self, parents_list=["comms"], tag=str(self))
def __str__(self):
return "<connection to Spec, host=%s, port=%s>" % (
......@@ -229,9 +232,7 @@ class SpecConnection(LogMixin):
# we received a value, so emit an update signal
channel.update(channelValue, force=True)
except BaseException:
self._logger.exception(
"Uncaught exception in SpecConnection.registerChannel"
)
log_exception(self, "Uncaught exception in SpecConnection.registerChannel")
@try_connect
def unregisterChannel(self, chanName):
......@@ -263,7 +264,7 @@ class SpecConnection(LogMixin):
def error(self, error):
"""Emit the 'error' signal when the remote Spec version signals an error."""
self._logger.error("Error from Spec: %s", error)
log_error(self, f"Error from Spec: {error}")
event.send(self, "error", (error,))
......@@ -276,10 +277,9 @@ class SpecConnection(LogMixin):
old_state = self.state
self.state = CONNECTED
if old_state != CONNECTED:
self._logger.info(
"Connected to %s:%s",
self.host,
(self.scanport and self.scanname) or self.port,
log_info(
self,
f"Connected to {self.host}:{(self.scanport and self.scanname) or self.port}",
)
self.connected_event.set()
......@@ -293,10 +293,9 @@ class SpecConnection(LogMixin):
old_state = self.state
self.state = DISCONNECTED
if old_state == CONNECTED:
self._logger.info(
"Disconnected from %s:%s",
self.host,
(self.scanport and self.scanname) or self.port,
log_info(
self,
f"Disconnected from {self.host}:{(self.scanport and self.scanname) or self.port}",
)
event.send(self, "disconnected")
......@@ -339,7 +338,7 @@ class SpecConnection(LogMixin):
"""
return self.__send_msg_with_reply(
replyCallback=callback,
*msg_cmd_with_return(cmd, version=self.serverVersion)
*msg_cmd_with_return(cmd, version=self.serverVersion),
)
@try_connect
......@@ -350,8 +349,9 @@ class SpecConnection(LogMixin):
cmd -- command string
"""
if self.serverVersion < 3:
self._logger.error(
"Cannot execute command in Spec : feature is available since Spec server v3 only"
log_error(
self,
"Cannot execute command in Spec : feature is available since Spec server v3 only",
)
else:
message = msg_func_with_return(cmd, version=self.serverVersion)
......@@ -372,8 +372,9 @@ class SpecConnection(LogMixin):
cmd -- command string
"""
if self.serverVersion < 3:
self._logger.error(
"Cannot execute command in Spec : feature is available since Spec server v3 only"
log_error(
self,
"Cannot execute command in Spec : feature is available since Spec server v3 only",
)
else:
self.__send_msg_no_reply(msg_func(cmd, version=self.serverVersion))
......
This diff is collapsed.
......@@ -13,43 +13,44 @@ import re
from gevent import socket
from .tcp import BaseSocket
from .util import HexMsg
from bliss.common.logtools import *
class Socket(BaseSocket):
def _connect(self, host, port):
fd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
fd.setsockopt(socket.SOL_IP, socket.IP_TOS, 0x10)
fd.connect((host, port))
return fd
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_IP, socket.IP_TOS, 0x10)
sock.connect((host, port))
return sock
def _sendall(self, data):
self._logger.debug_data("Tx:", data)
return self._fd.send(data)
log_debug_data(self, "Tx:", data)
return self._socket.send(data)
@staticmethod
def _raw_read(sock, fd):
def _raw_read(bliss_socket, sock):
try:
while 1:
raw_data = fd.recv(16 * 1024)
sock._logger.debug_data("Rx:", raw_data)
raw_data = sock.recv(16 * 1024)
log_debug_data(bliss_socket, "Rx:", raw_data)
if raw_data:
sock._data += raw_data
sock._event.set()
bliss_socket._data += raw_data
bliss_socket._event.set()
else:
break
except:
pass
finally:
fd.close()
sock.close()
try:
sock._connected = False
sock._fd = None
sock._event.set()
bliss_socket._connected = False
bliss_socket._socket = None
bliss_socket._event.set()
except ReferenceError:
pass
class Udp(object):
class Udp:
def __new__(cls, url=None, **keys):
# for now only one udp class
# no need to test...
......
......@@ -42,10 +42,10 @@ def get_interface(*args, **kwargs):
Useful to use in an `__init__` to parse arguments and keyword arguments
with interface connection. Example of usage::
from ... import BaseDevice
?????? from ... import BaseDevice
from bliss.comm.util import get_interface
class Lecroy(BaseDevice):
class Lecroy(?????? BaseDevice):
def __init__(self, *args, **kwargs):
interface, args, kwargs = get_interface(*args, **kwargs)
......@@ -117,7 +117,7 @@ def get_comm_type(config):
raise ValueError("More than one communication channel found")
comm_type = UDP
if comm_type is None:
raise ValueError("No communication channel found in config")
raise ValueError("get_comm_type(): No communication channel found in config")
return comm_type
......@@ -192,7 +192,7 @@ def get_comm(config, ctype=None, **opts):
from .serial import Serial as klass
if klass is None:
# should not happen (get_comm_type should handle all errors)
raise ValueError("No communication channel found in config")
raise ValueError("get_comm(): No communication channel found in config")
if proxy_config is None:
return klass(**opts)
......
This diff is collapsed.
This diff is collapsed.
......@@ -7,6 +7,7 @@
from bliss.common.motor_config import StaticConfig
from bliss.common import event
from functools import wraps
import time
import gevent
import re
......@@ -15,6 +16,7 @@ import types
class Encoder(object):
def lazy_init(func):
@wraps(func)
def func_wrapper(self, *args, **kwargs):
self.controller._initialize_encoder(self)
return func(self, *args, **kwargs)
......
......@@ -9,19 +9,31 @@ import sys
from gevent import GreenletExit
from louie import dispatcher
from louie.dispatcher import get_receivers
from louie import robustapply
from louie import saferef
def _get_sender(sender):
try:
sender = sender.__wrapped__
except AttributeError:
pass
return sender
def send(sender, signal, *args, **kwargs):
sender = _get_sender(sender)
dispatcher.send(signal, sender, *args, **kwargs)
def connect(sender, signal, callback):
sender = _get_sender(sender)
dispatcher.connect(callback, signal, sender)
def disconnect(sender, signal, callback):
sender = _get_sender(sender)
try:
dispatcher.disconnect(callback, signal, sender)
except Exception:
......
import sys
from contextlib import contextmanager
from functools import wraps
from gevent import greenlet, timeout, getcurrent
from gevent.timeout import string_types
......@@ -63,6 +64,7 @@ def AllowKill():
def protect_from_kill(fu):
@wraps(fu)
def func(*args, **kwargs):
with KillMask():
return fu(*args, **kwargs)
......@@ -71,6 +73,7 @@ def protect_from_kill(fu):
def protect_from_one_kill(fu):
@wraps(fu)
def func(*args, **kwargs):
with KillMask(masked_kill_nb=1):
return fu(*args, **kwargs)
......@@ -79,6 +82,9 @@ def protect_from_one_kill(fu):
# gevent.greenlet module patch
_ori_timeout = gevent.timeout.Timeout
class Greenlet(greenlet.Greenlet):
def throw(self, exception):
if isinstance(exception, gevent.timeout.Timeout):
......@@ -92,6 +98,13 @@ class Greenlet(greenlet.Greenlet):
else:
super().throw(exception)
def get(self, *args, **keys):
try:
return super().get(*args, **keys)
except _ori_timeout as tmout:
t = Timeout(exception=tmout.exception)
raise t
gevent.spawn = Greenlet.spawn
gevent.spawn_later = Greenlet.spawn_later
......
......@@ -9,13 +9,13 @@
how to use motion hooks in your system"""
import weakref
from bliss.common import session
from bliss.common.logtools import LogMixin
from bliss.common.logtools import *
from bliss import global_map
__all__ = ["MotionHook"]
class MotionHook(LogMixin):
class MotionHook:
"""
Base motion hook. Executed before a motion starts and after motion ends.
"""
......@@ -31,7 +31,7 @@ class MotionHook(LogMixin):
axis (Axis): new axis to be added to the hook
"""
self.__axes[axis.name] = axis
session.get_current().map.register(self, children_list=list(self.axes.values()))