moved some code from iohandler.py to metaclass.py
- code for calling write_<param> before write function from handler is moved to the metaclass - moved some methods from IOBaseHandler to IOHandler Change-Id: I733c7fe8d3d59d9013e7b5a33e170c4b3e386921 Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/22098 Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de> Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
parent
f0a3306f9c
commit
859bf5e1a2
@ -20,7 +20,8 @@
|
|||||||
# *****************************************************************************
|
# *****************************************************************************
|
||||||
"""IO handler
|
"""IO handler
|
||||||
|
|
||||||
Utility class for cases, where multiple parameters are treated with a common command.
|
Utility class for cases, where multiple parameters are treated with a common command,
|
||||||
|
or in cases, where IO can be parametrized.
|
||||||
The support for LakeShore and similar protocols is already included.
|
The support for LakeShore and similar protocols is already included.
|
||||||
|
|
||||||
For read, instead of the methods read_<parameter> we write one method analyze_<group>
|
For read, instead of the methods read_<parameter> we write one method analyze_<group>
|
||||||
@ -62,7 +63,7 @@ class CmdParser:
|
|||||||
"""helper for parsing replies
|
"""helper for parsing replies
|
||||||
|
|
||||||
using a subset of old style python formatting.
|
using a subset of old style python formatting.
|
||||||
The same format can be used or formatting command arguments
|
The same format can be used for formatting command arguments
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# make a map of cast functions
|
# make a map of cast functions
|
||||||
@ -74,14 +75,12 @@ class CmdParser:
|
|||||||
('xX', lambda x: int(x, 16)),
|
('xX', lambda x: int(x, 16)),
|
||||||
('eEfFgG', float),
|
('eEfFgG', float),
|
||||||
) for letter in letters}
|
) for letter in letters}
|
||||||
# pattern for chacaters to be escaped
|
# pattern for characters to be escaped
|
||||||
ESC_PAT = re.compile('([\\%s])' % '\\'.join('|^$-.+*?()[]{}<>'))
|
ESC_PAT = re.compile(r'([\|\^\$\-\.\+\*\?\(\)\[\]\{\}\<\>])')
|
||||||
# format pattern
|
# format pattern
|
||||||
FMT_PAT = re.compile('(%%|%[^diouxXfFgGeEcrsa]*(?:.|$))')
|
FMT_PAT = re.compile('(%%|%[^diouxXfFgGeEcrsa]*(?:.|$))')
|
||||||
|
|
||||||
def __init__(self, argformat):
|
def __init__(self, argformat):
|
||||||
# replace named patterns
|
|
||||||
|
|
||||||
self.fmt = argformat
|
self.fmt = argformat
|
||||||
spl = self.FMT_PAT.split(argformat)
|
spl = self.FMT_PAT.split(argformat)
|
||||||
spl_iter = iter(spl)
|
spl_iter = iter(spl)
|
||||||
@ -178,30 +177,66 @@ class Change:
|
|||||||
|
|
||||||
|
|
||||||
class IOHandlerBase:
|
class IOHandlerBase:
|
||||||
"""generic command handler"""
|
"""abstract IO handler
|
||||||
|
|
||||||
def __init__(self, group):
|
IO handlers for parametrized access should inherit from this
|
||||||
# group is used for calling the proper analyze_<group> and change_<group> methods
|
"""
|
||||||
|
|
||||||
|
def get_read_func(self, modclass, pname):
|
||||||
|
"""get the read function for parameter pname"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_write_func(self, pname):
|
||||||
|
"""get the write function for parameter pname"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class IOHandler(IOHandlerBase):
|
||||||
|
"""IO handler for cases, where multiple parameters are treated with a common command
|
||||||
|
|
||||||
|
This IO handler works for a syntax, where the reply of a query command has
|
||||||
|
the same format as the arguments for the change command.
|
||||||
|
Examples: devices from LakeShore, PPMS
|
||||||
|
|
||||||
|
implementing classes may override the following class variables
|
||||||
|
"""
|
||||||
|
CMDARGS = [] # list of properties or parameters to be used for building some of the the query and change commands
|
||||||
|
CMDSEPARATOR = None # if not None, it is possible to join a command and a query with the given separator
|
||||||
|
|
||||||
|
def __init__(self, group, querycmd, replyfmt, changecmd=None):
|
||||||
|
"""initialize the IO handler
|
||||||
|
|
||||||
|
group: the handler group (used for analyze_<group> and change_<group>)
|
||||||
|
querycmd: the command for a query, may contain named formats for cmdargs
|
||||||
|
replyfmt: the format for reading the reply with some scanf like behaviour
|
||||||
|
changecmd: the first part of the change command (without values), may be
|
||||||
|
omitted if no write happens
|
||||||
|
"""
|
||||||
self.group = group
|
self.group = group
|
||||||
self.parameters = set()
|
self.parameters = set()
|
||||||
self._module_class = None
|
self._module_class = None
|
||||||
|
self.querycmd = querycmd
|
||||||
|
self.replyfmt = CmdParser(replyfmt)
|
||||||
|
self.changecmd = changecmd
|
||||||
|
|
||||||
def parse_reply(self, reply):
|
def parse_reply(self, reply):
|
||||||
"""return values from a raw reply"""
|
"""return values from a raw reply"""
|
||||||
raise NotImplementedError
|
return self.replyfmt.parse(reply)
|
||||||
|
|
||||||
def make_query(self, module):
|
def make_query(self, module):
|
||||||
"""make a query"""
|
"""make a query"""
|
||||||
raise NotImplementedError
|
return self.querycmd % {k: getattr(module, k, None) for k in self.CMDARGS}
|
||||||
|
|
||||||
def make_change(self, module, *values):
|
def make_change(self, module, *values):
|
||||||
"""make a change command from values"""
|
"""make a change command"""
|
||||||
raise NotImplementedError
|
changecmd = self.changecmd % {k: getattr(module, k, None) for k in self.CMDARGS}
|
||||||
|
return changecmd + self.replyfmt.format(*values)
|
||||||
|
|
||||||
def send_command(self, module, changecmd=''):
|
def send_command(self, module, changecmd=''):
|
||||||
"""send a command (query or change+query) and parse the reply into a list
|
"""send a command (query or change+query) and parse the reply into a list
|
||||||
|
|
||||||
If changecmd is given, it is prepended before the query.
|
If changecmd is given, it is prepended before the query. changecmd must
|
||||||
|
contain the command separator at the end.
|
||||||
"""
|
"""
|
||||||
querycmd = self.make_query(module)
|
querycmd = self.make_query(module)
|
||||||
reply = module.sendRecv(changecmd + querycmd)
|
reply = module.sendRecv(changecmd + querycmd)
|
||||||
@ -210,13 +245,14 @@ class IOHandlerBase:
|
|||||||
def send_change(self, module, *values):
|
def send_change(self, module, *values):
|
||||||
"""compose and send a command from values
|
"""compose and send a command from values
|
||||||
|
|
||||||
and send a query afterwards. This method might be overriden, if the change command
|
and send a query afterwards, or combine with a query command.
|
||||||
can be combined with a query command, or if the change command already includes
|
Override this method, if the change command already includes a reply.
|
||||||
a reply.
|
|
||||||
"""
|
"""
|
||||||
changecmd = self.make_change(module, *values)
|
changecmd = self.make_change(module, *values)
|
||||||
|
if self.CMDSEPARATOR is None:
|
||||||
module.sendRecv(changecmd) # ignore result
|
module.sendRecv(changecmd) # ignore result
|
||||||
return self.send_command(module)
|
return self.send_command(module)
|
||||||
|
return self.send_command(module, changecmd + self.CMDSEPARATOR)
|
||||||
|
|
||||||
def get_read_func(self, modclass, pname):
|
def get_read_func(self, modclass, pname):
|
||||||
"""returns the read function passed to the metaclass
|
"""returns the read function passed to the metaclass
|
||||||
@ -253,7 +289,7 @@ class IOHandlerBase:
|
|||||||
raise
|
raise
|
||||||
return Done
|
return Done
|
||||||
|
|
||||||
def get_write_func(self, pname, pre_wfunc=None):
|
def get_write_func(self, pname):
|
||||||
"""returns the write function passed to the metaclass
|
"""returns the write function passed to the metaclass
|
||||||
|
|
||||||
If pre_wfunc is given, it is to be called before change_<group>.
|
If pre_wfunc is given, it is to be called before change_<group>.
|
||||||
@ -261,17 +297,6 @@ class IOHandlerBase:
|
|||||||
"""
|
"""
|
||||||
self.change = getattr(self._module_class, 'change_' + self.group)
|
self.change = getattr(self._module_class, 'change_' + self.group)
|
||||||
|
|
||||||
if pre_wfunc:
|
|
||||||
|
|
||||||
def wfunc(module, value, hdl=self, pname=pname, wfunc=pre_wfunc):
|
|
||||||
value = wfunc(module, value)
|
|
||||||
if value is None or value is Done:
|
|
||||||
return value
|
|
||||||
hdl.write(module, pname, value)
|
|
||||||
return Done
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
def wfunc(module, value, hdl=self, pname=pname):
|
def wfunc(module, value, hdl=self, pname=pname):
|
||||||
hdl.write(module, pname, value)
|
hdl.write(module, pname, value)
|
||||||
return Done
|
return Done
|
||||||
@ -300,49 +325,3 @@ class IOHandlerBase:
|
|||||||
result = self.analyze(module, *reply)
|
result = self.analyze(module, *reply)
|
||||||
for k, v in result.items():
|
for k, v in result.items():
|
||||||
setattr(module, k, v)
|
setattr(module, k, v)
|
||||||
|
|
||||||
|
|
||||||
class IOHandler(IOHandlerBase):
|
|
||||||
"""more evolved command handler
|
|
||||||
|
|
||||||
This command handler works for a syntax, where the reply of a query command has
|
|
||||||
the same format as the arguments for the change command.
|
|
||||||
Examples: devices from LakeShore, PPMS
|
|
||||||
|
|
||||||
implementing classes have to define/override the following:
|
|
||||||
"""
|
|
||||||
CMDARGS = [] # list of properties or parameters to be used for building some of the the query and change commands
|
|
||||||
CMDSEPARATOR = ';' # if given, it is valid to join a command a a query with the given separator
|
|
||||||
|
|
||||||
def __init__(self, group, querycmd, replyfmt, changecmd=None):
|
|
||||||
"""initialize the command handler
|
|
||||||
|
|
||||||
group: the handler group (used for analyze_<group> and change_<group>)
|
|
||||||
querycmd: the command for a query, may contain named formats for cmdargs
|
|
||||||
replyfmt: the format for reading the reply with some scanf like behaviour
|
|
||||||
changecmd: the first part of the change command (without values), may be
|
|
||||||
omitted if no write happens
|
|
||||||
"""
|
|
||||||
super().__init__(group)
|
|
||||||
self.querycmd = querycmd
|
|
||||||
self.replyfmt = CmdParser(replyfmt)
|
|
||||||
self.changecmd = changecmd
|
|
||||||
|
|
||||||
def parse_reply(self, reply):
|
|
||||||
"""return values from a raw reply"""
|
|
||||||
return self.replyfmt.parse(reply)
|
|
||||||
|
|
||||||
def make_query(self, module):
|
|
||||||
"""make a query"""
|
|
||||||
return self.querycmd % {k: getattr(module, k, None) for k in self.CMDARGS}
|
|
||||||
|
|
||||||
def make_change(self, module, *values):
|
|
||||||
"""make a change command"""
|
|
||||||
changecmd = self.changecmd % {k: getattr(module, k, None) for k in self.CMDARGS}
|
|
||||||
return changecmd + self.replyfmt.format(*values)
|
|
||||||
|
|
||||||
def send_change(self, module, *values):
|
|
||||||
"""join change and query commands"""
|
|
||||||
if self.CMDSEPARATOR is None:
|
|
||||||
return super().send_change(module, *values)
|
|
||||||
return self.send_command(module, self.make_change(module, *values) + self.CMDSEPARATOR)
|
|
||||||
|
@ -136,6 +136,7 @@ class ModuleMeta(PropertyMeta):
|
|||||||
break
|
break
|
||||||
rfunc = getattr(base, 'read_' + pname, None)
|
rfunc = getattr(base, 'read_' + pname, None)
|
||||||
|
|
||||||
|
# create wrapper except when read function is already wrapped
|
||||||
if rfunc is None or getattr(rfunc, '__wrapped__', False) is False:
|
if rfunc is None or getattr(rfunc, '__wrapped__', False) is False:
|
||||||
|
|
||||||
def wrapped_rfunc(self, pname=pname, rfunc=rfunc):
|
def wrapped_rfunc(self, pname=pname, rfunc=rfunc):
|
||||||
@ -157,36 +158,40 @@ class ModuleMeta(PropertyMeta):
|
|||||||
setattr(self, pname, value) # important! trigger the setter
|
setattr(self, pname, value) # important! trigger the setter
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
if rfunc:
|
||||||
wrapped_rfunc.__doc__ = rfunc.__doc__
|
wrapped_rfunc.__doc__ = rfunc.__doc__
|
||||||
setattr(newtype, 'read_' + pname, wrapped_rfunc)
|
setattr(newtype, 'read_' + pname, wrapped_rfunc)
|
||||||
wrapped_rfunc.__wrapped__ = True
|
wrapped_rfunc.__wrapped__ = True
|
||||||
|
|
||||||
if not pobj.readonly:
|
if not pobj.readonly:
|
||||||
wfunc = attrs.get('write_' + pname, None)
|
wfunc = attrs.get('write_' + pname, None)
|
||||||
# if a handler and write_<param> is present, wfunc will be called
|
|
||||||
# by the handler first
|
|
||||||
wfunc = pobj.handler.get_write_func(pname, wfunc) if pobj.handler else wfunc
|
|
||||||
for base in bases:
|
for base in bases:
|
||||||
if wfunc is not None:
|
if wfunc is not None:
|
||||||
break
|
break
|
||||||
wfunc = getattr(base, 'write_' + pname, None)
|
wfunc = getattr(base, 'write_' + pname, None)
|
||||||
|
|
||||||
|
# create wrapper except when write function is already wrapped
|
||||||
if wfunc is None or getattr(wfunc, '__wrapped__', False) is False:
|
if wfunc is None or getattr(wfunc, '__wrapped__', False) is False:
|
||||||
|
|
||||||
def wrapped_wfunc(self, value, pname=pname, wfunc=wfunc):
|
# append write function from handler, to be called after wfunc
|
||||||
|
wfuncs = (wfunc, pobj.handler.get_write_func(pname) if pobj.handler else None)
|
||||||
|
|
||||||
|
def wrapped_wfunc(self, value, pname=pname, wfuncs=wfuncs):
|
||||||
self.log.debug("check validity of %s = %r" % (pname, value))
|
self.log.debug("check validity of %s = %r" % (pname, value))
|
||||||
pobj = self.accessibles[pname]
|
pobj = self.accessibles[pname]
|
||||||
value = pobj.datatype(value)
|
value = pobj.datatype(value)
|
||||||
if wfunc:
|
for wfunc in filter(None, wfuncs):
|
||||||
self.log.debug('calling %r(%r)' % (wfunc, value))
|
self.log.debug('calling %s %r(%r)' % (wfunc.__name__, wfunc, value))
|
||||||
returned_value = wfunc(self, value)
|
returned_value = wfunc(self, value)
|
||||||
if returned_value is Done: # the setter is already triggered
|
if returned_value is Done: # the setter is already triggered
|
||||||
return getattr(self, pname)
|
return getattr(self, pname)
|
||||||
if returned_value is not None:
|
if returned_value is None: # goodie: accept missing return value
|
||||||
|
break # handler is not called in this case
|
||||||
value = returned_value
|
value = returned_value
|
||||||
setattr(self, pname, value)
|
setattr(self, pname, value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
if wfunc:
|
||||||
wrapped_wfunc.__doc__ = wfunc.__doc__
|
wrapped_wfunc.__doc__ = wfunc.__doc__
|
||||||
setattr(newtype, 'write_' + pname, wrapped_wfunc)
|
setattr(newtype, 'write_' + pname, wrapped_wfunc)
|
||||||
wrapped_wfunc.__wrapped__ = True
|
wrapped_wfunc.__wrapped__ = True
|
||||||
|
Loading…
x
Reference in New Issue
Block a user