updates from mlz repo
- asynconn: raise * from * - asynconn: correct handling for timeout in AsynSerial - add new py35compat - target unit $ Change-Id: I052185ad3ebb3e3d1e3374f7ece9c7df06223951
This commit is contained in:
parent
41ce909172
commit
a7b741eaa4
@ -48,6 +48,7 @@ class ConnectionClosed(ConnectionError):
|
|||||||
|
|
||||||
class AsynConn:
|
class AsynConn:
|
||||||
timeout = 1 # inter byte timeout
|
timeout = 1 # inter byte timeout
|
||||||
|
scheme = None
|
||||||
SCHEME_MAP = {}
|
SCHEME_MAP = {}
|
||||||
connection = None # is not None, if connected
|
connection = None # is not None, if connected
|
||||||
defaultport = None
|
defaultport = None
|
||||||
@ -62,11 +63,11 @@ class AsynConn:
|
|||||||
except (ValueError, TypeError, AssertionError):
|
except (ValueError, TypeError, AssertionError):
|
||||||
if 'COM' in uri:
|
if 'COM' in uri:
|
||||||
raise ValueError("the correct uri for a COM port is: "
|
raise ValueError("the correct uri for a COM port is: "
|
||||||
"'serial://COM<i>[?<option>=<value>[+<option>=value ...]]'")
|
"'serial://COM<i>[?<option>=<value>[+<option>=value ...]]'") from None
|
||||||
if '/dev' in uri:
|
if '/dev' in uri:
|
||||||
raise ValueError("the correct uri for a serial port is: "
|
raise ValueError("the correct uri for a serial port is: "
|
||||||
"'serial:///dev/<tty>[?<option>=<value>[+<option>=value ...]]'")
|
"'serial:///dev/<tty>[?<option>=<value>[+<option>=value ...]]'") from None
|
||||||
raise ValueError('invalid uri: %s' % uri)
|
raise ValueError('invalid uri: %s' % uri) from None
|
||||||
iocls = cls.SCHEME_MAP['tcp']
|
iocls = cls.SCHEME_MAP['tcp']
|
||||||
uri = 'tcp://%s:%d' % host_port
|
uri = 'tcp://%s:%d' % host_port
|
||||||
return object.__new__(iocls)
|
return object.__new__(iocls)
|
||||||
@ -80,7 +81,9 @@ class AsynConn:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __init_subclass__(cls):
|
def __init_subclass__(cls):
|
||||||
cls.SCHEME_MAP[cls.scheme] = cls
|
"""register subclass to scheme, if available"""
|
||||||
|
if cls.scheme:
|
||||||
|
cls.SCHEME_MAP[cls.scheme] = cls
|
||||||
|
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@ -166,7 +169,7 @@ class AsynTcp(AsynConn):
|
|||||||
self.connection = tcpSocket(uri, self.defaultport, self.timeout)
|
self.connection = tcpSocket(uri, self.defaultport, self.timeout)
|
||||||
except (ConnectionRefusedError, socket.gaierror) as e:
|
except (ConnectionRefusedError, socket.gaierror) as e:
|
||||||
# indicate that retrying might make sense
|
# indicate that retrying might make sense
|
||||||
raise CommunicationFailedError(str(e))
|
raise CommunicationFailedError(str(e)) from None
|
||||||
|
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
if self.connection:
|
if self.connection:
|
||||||
@ -237,8 +240,8 @@ class AsynSerial(AsynConn):
|
|||||||
options = dict((kv.split('=') for kv in uri[1].split('+')))
|
options = dict((kv.split('=') for kv in uri[1].split('+')))
|
||||||
except IndexError: # no uri[1], no options
|
except IndexError: # no uri[1], no options
|
||||||
options = {}
|
options = {}
|
||||||
except ValueError:
|
except ValueError as e:
|
||||||
raise ConfigError('illegal serial options')
|
raise ConfigError('illegal serial options') from e
|
||||||
parity = options.pop('parity', None) # only parity is to be treated as text
|
parity = options.pop('parity', None) # only parity is to be treated as text
|
||||||
for k, v in options.items():
|
for k, v in options.items():
|
||||||
try:
|
try:
|
||||||
@ -251,14 +254,12 @@ class AsynSerial(AsynConn):
|
|||||||
if not fullname.startswith(name):
|
if not fullname.startswith(name):
|
||||||
raise ConfigError('illegal parity: %s' % parity)
|
raise ConfigError('illegal parity: %s' % parity)
|
||||||
options['parity'] = name[0]
|
options['parity'] = name[0]
|
||||||
if 'timeout' in options:
|
if 'timeout' not in options:
|
||||||
options['timeout'] = float(self.timeout)
|
|
||||||
else:
|
|
||||||
options['timeout'] = self.timeout
|
options['timeout'] = self.timeout
|
||||||
try:
|
try:
|
||||||
self.connection = Serial(dev, **options)
|
self.connection = Serial(dev, **options)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise ConfigError(e)
|
raise ConfigError(e) from None
|
||||||
# TODO: turn exceptions into ConnectionFailedError, where a retry makes sense
|
# TODO: turn exceptions into ConnectionFailedError, where a retry makes sense
|
||||||
|
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
|
58
secop/lib/py35compat.py
Normal file
58
secop/lib/py35compat.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# *****************************************************************************
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under
|
||||||
|
# the terms of the GNU General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 2 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# This program 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 General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# Module authors:
|
||||||
|
# Markus Zolliker <markus.zolliker@psi.ch>
|
||||||
|
#
|
||||||
|
# *****************************************************************************
|
||||||
|
"""workaround for python versions older than 3.6
|
||||||
|
|
||||||
|
``Object`` must be inherited for classes needing support for
|
||||||
|
__init_subclass__ and __set_name__
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
if hasattr(object, '__init_subclass__'):
|
||||||
|
class Object:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
class PEP487Metaclass(type):
|
||||||
|
# support for __set_name__ and __init_subclass__ for older python versions
|
||||||
|
# slightly modified from PEP487 doc
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
if len(args) != 3:
|
||||||
|
return super().__new__(cls, *args)
|
||||||
|
name, bases, ns = args
|
||||||
|
init = ns.get('__init_subclass__')
|
||||||
|
if callable(init):
|
||||||
|
ns['__init_subclass__'] = classmethod(init)
|
||||||
|
newtype = super().__new__(cls, name, bases, ns)
|
||||||
|
for k, v in newtype.__dict__.items():
|
||||||
|
func = getattr(v, '__set_name__', None)
|
||||||
|
if func is not None:
|
||||||
|
func(newtype, k)
|
||||||
|
if bases:
|
||||||
|
super(newtype, newtype).__init_subclass__(**kwargs) # pylint: disable=bad-super-call
|
||||||
|
return newtype
|
||||||
|
|
||||||
|
def __init__(cls, name, bases, ns, **kwargs):
|
||||||
|
super().__init__(name, bases, ns)
|
||||||
|
|
||||||
|
class Object(metaclass=PEP487Metaclass):
|
||||||
|
@classmethod
|
||||||
|
def __init_subclass__(cls, *args, **kwargs):
|
||||||
|
pass
|
@ -29,7 +29,7 @@ from collections import OrderedDict
|
|||||||
|
|
||||||
from secop.datatypes import ArrayOf, BoolType, EnumType, FloatRange, \
|
from secop.datatypes import ArrayOf, BoolType, EnumType, FloatRange, \
|
||||||
IntRange, StatusType, StringType, TextType, TupleOf
|
IntRange, StatusType, StringType, TextType, TupleOf
|
||||||
from secop.errors import BadValueError, ConfigError, InternalError, \
|
from secop.errors import BadValueError, ConfigError, \
|
||||||
ProgrammingError, SECoPError, SilentError, secop_error
|
ProgrammingError, SECoPError, SilentError, secop_error
|
||||||
from secop.lib import formatException, mkthread
|
from secop.lib import formatException, mkthread
|
||||||
from secop.lib.enum import Enum
|
from secop.lib.enum import Enum
|
||||||
@ -57,6 +57,7 @@ class HasAccessibles(HasProperties):
|
|||||||
merged_properties = {} # dict of dict of merged properties
|
merged_properties = {} # dict of dict of merged properties
|
||||||
new_names = [] # list of names of new accessibles
|
new_names = [] # list of names of new accessibles
|
||||||
override_values = {} # bare values overriding a parameter and methods overriding a command
|
override_values = {} # bare values overriding a parameter and methods overriding a command
|
||||||
|
|
||||||
for base in reversed(cls.__mro__):
|
for base in reversed(cls.__mro__):
|
||||||
for key, value in base.__dict__.items():
|
for key, value in base.__dict__.items():
|
||||||
if isinstance(value, Accessible):
|
if isinstance(value, Accessible):
|
||||||
@ -66,8 +67,6 @@ class HasAccessibles(HasProperties):
|
|||||||
accessibles[key] = value
|
accessibles[key] = value
|
||||||
override_values.pop(key, None)
|
override_values.pop(key, None)
|
||||||
elif key in accessibles:
|
elif key in accessibles:
|
||||||
# either a bare value overriding a parameter
|
|
||||||
# or a method overriding a command
|
|
||||||
override_values[key] = value
|
override_values[key] = value
|
||||||
for aname, aobj in accessibles.items():
|
for aname, aobj in accessibles.items():
|
||||||
if aname in override_values:
|
if aname in override_values:
|
||||||
@ -244,12 +243,12 @@ class Module(HasAccessibles):
|
|||||||
|
|
||||||
# reference to the dispatcher (used for sending async updates)
|
# reference to the dispatcher (used for sending async updates)
|
||||||
DISPATCHER = None
|
DISPATCHER = None
|
||||||
|
|
||||||
pollerClass = Poller #: default poller used
|
pollerClass = Poller #: default poller used
|
||||||
|
|
||||||
def __init__(self, name, logger, cfgdict, srv):
|
def __init__(self, name, logger, cfgdict, srv):
|
||||||
# remember the dispatcher object (for the async callbacks)
|
# remember the dispatcher object (for the async callbacks)
|
||||||
self.DISPATCHER = srv.dispatcher
|
self.DISPATCHER = srv.dispatcher
|
||||||
|
self.omit_unchanged_within = getattr(self.DISPATCHER, 'omit_unchanged_within', 0.1)
|
||||||
self.log = logger
|
self.log = logger
|
||||||
self.name = name
|
self.name = name
|
||||||
self.valueCallbacks = {}
|
self.valueCallbacks = {}
|
||||||
@ -444,23 +443,20 @@ class Module(HasAccessibles):
|
|||||||
pobj = self.parameters[pname]
|
pobj = self.parameters[pname]
|
||||||
timestamp = timestamp or time.time()
|
timestamp = timestamp or time.time()
|
||||||
changed = pobj.value != value
|
changed = pobj.value != value
|
||||||
if value is not None:
|
try:
|
||||||
pobj.value = value # store the value even in case of error
|
# store the value even in case of error
|
||||||
|
pobj.value = pobj.datatype(value)
|
||||||
|
except Exception as e:
|
||||||
|
if not err: # do not overwrite given error
|
||||||
|
err = e
|
||||||
if err:
|
if err:
|
||||||
if not isinstance(err, SECoPError):
|
err = secop_error(err)
|
||||||
err = InternalError(err)
|
|
||||||
if str(err) == str(pobj.readerror):
|
if str(err) == str(pobj.readerror):
|
||||||
return # do call updates for repeated errors
|
return # do call updates for repeated errors
|
||||||
else:
|
elif not changed and timestamp < (pobj.timestamp or 0) + self.omit_unchanged_within:
|
||||||
try:
|
# no change within short time -> omit
|
||||||
pobj.value = pobj.datatype(value)
|
return
|
||||||
except Exception as e:
|
pobj.timestamp = timestamp or time.time()
|
||||||
err = secop_error(e)
|
|
||||||
if not changed and timestamp < ((pobj.timestamp or 0)
|
|
||||||
+ self.DISPATCHER.OMIT_UNCHANGED_WITHIN):
|
|
||||||
# no change within short time -> omit
|
|
||||||
return
|
|
||||||
pobj.timestamp = timestamp
|
|
||||||
pobj.readerror = err
|
pobj.readerror = err
|
||||||
if pobj.export:
|
if pobj.export:
|
||||||
self.DISPATCHER.announce_update(self.name, pname, pobj)
|
self.DISPATCHER.announce_update(self.name, pname, pobj)
|
||||||
@ -495,15 +491,15 @@ class Module(HasAccessibles):
|
|||||||
for pname in self.parameters:
|
for pname in self.parameters:
|
||||||
errfunc = getattr(modobj, 'error_update_' + pname, None)
|
errfunc = getattr(modobj, 'error_update_' + pname, None)
|
||||||
if errfunc:
|
if errfunc:
|
||||||
def errcb(err, p=pname, m=modobj, efunc=errfunc):
|
def errcb(err, p=pname, efunc=errfunc):
|
||||||
try:
|
try:
|
||||||
efunc(err)
|
efunc(err)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
m.announceUpdate(p, err=e)
|
modobj.announceUpdate(p, err=e)
|
||||||
self.errorCallbacks[pname].append(errcb)
|
self.errorCallbacks[pname].append(errcb)
|
||||||
else:
|
else:
|
||||||
def errcb(err, p=pname, m=modobj):
|
def errcb(err, p=pname):
|
||||||
m.announceUpdate(p, err=err)
|
modobj.announceUpdate(p, err=err)
|
||||||
if pname in autoupdate:
|
if pname in autoupdate:
|
||||||
self.errorCallbacks[pname].append(errcb)
|
self.errorCallbacks[pname].append(errcb)
|
||||||
|
|
||||||
@ -516,8 +512,8 @@ class Module(HasAccessibles):
|
|||||||
efunc(e)
|
efunc(e)
|
||||||
self.valueCallbacks[pname].append(cb)
|
self.valueCallbacks[pname].append(cb)
|
||||||
elif pname in autoupdate:
|
elif pname in autoupdate:
|
||||||
def cb(value, p=pname, m=modobj):
|
def cb(value, p=pname):
|
||||||
m.announceUpdate(p, value)
|
modobj.announceUpdate(p, value)
|
||||||
self.valueCallbacks[pname].append(cb)
|
self.valueCallbacks[pname].append(cb)
|
||||||
|
|
||||||
def isBusy(self, status=None):
|
def isBusy(self, status=None):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user