improve readability be renaming PARAMS,PROPS,CMDS
and others. Change-Id: Ie37768ed813acdf0cb0707c70ff63397ec8bfbf1 Reviewed-on: https://forge.frm2.tum.de/review/17320 Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de> Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
This commit is contained in:
parent
aba67dde7f
commit
f54e8ccb45
136
secop/modules.py
136
secop/modules.py
@ -39,14 +39,14 @@ from secop.datatypes import DataType, EnumType, TupleOf, StringType, FloatRange,
|
||||
|
||||
EVENT_ONLY_ON_CHANGED_VALUES = False
|
||||
|
||||
# storage for PARAMeter settings:
|
||||
# storage for Parameter settings:
|
||||
# if readonly is False, the value can be changed (by code, or remote)
|
||||
# if no default is given, the parameter MUST be specified in the configfile
|
||||
# during startup, value is initialized with the default value or
|
||||
# from the config file if specified there
|
||||
|
||||
|
||||
class PARAM(object):
|
||||
class Param(object):
|
||||
|
||||
def __init__(self,
|
||||
description,
|
||||
@ -85,7 +85,7 @@ class PARAM(object):
|
||||
|
||||
def copy(self):
|
||||
# return a copy of ourselfs
|
||||
return PARAM(description=self.description,
|
||||
return Param(description=self.description,
|
||||
datatype=self.datatype,
|
||||
default=self.default,
|
||||
unit=self.unit,
|
||||
@ -116,13 +116,13 @@ class PARAM(object):
|
||||
return self.datatype.export_value(self.value)
|
||||
|
||||
|
||||
class OVERRIDE(object):
|
||||
class Override(object):
|
||||
|
||||
def __init__(self, **kwds):
|
||||
self.kwds = kwds
|
||||
|
||||
def apply(self, paramobj):
|
||||
if isinstance(paramobj, PARAM):
|
||||
if isinstance(paramobj, Param):
|
||||
for k, v in self.kwds.iteritems():
|
||||
if hasattr(paramobj, k):
|
||||
setattr(paramobj, k, v)
|
||||
@ -133,12 +133,12 @@ class OVERRIDE(object):
|
||||
(k, v, paramobj))
|
||||
else:
|
||||
raise ProgrammingError(
|
||||
"Overrides can only be applied to PARAM's, %r is none!" %
|
||||
"Overrides can only be applied to Param's, %r is none!" %
|
||||
paramobj)
|
||||
|
||||
|
||||
# storage for CMDs settings (description + call signature...)
|
||||
class CMD(object):
|
||||
# storage for Commands settings (description + call signature...)
|
||||
class Command(object):
|
||||
|
||||
def __init__(self, description, arguments=None, result=None):
|
||||
# descriptive text for humans
|
||||
@ -171,8 +171,8 @@ class ModuleMeta(type):
|
||||
if '__constructed__' in attrs:
|
||||
return newtype
|
||||
|
||||
# merge PROPERTIES, PARAM and CMDS from all sub-classes
|
||||
for entry in ['PROPERTIES', 'PARAMS', 'CMDS']:
|
||||
# merge properties, Param and commands from all sub-classes
|
||||
for entry in ['properties', 'parameters', 'commands']:
|
||||
newentry = {}
|
||||
for base in reversed(bases):
|
||||
if hasattr(base, entry):
|
||||
@ -181,20 +181,20 @@ class ModuleMeta(type):
|
||||
setattr(newtype, entry, newentry)
|
||||
|
||||
# apply Overrides from all sub-classes
|
||||
newparams = getattr(newtype, 'PARAMS')
|
||||
newparams = getattr(newtype, 'parameters')
|
||||
for base in reversed(bases):
|
||||
overrides = getattr(base, 'OVERRIDES', {})
|
||||
overrides = getattr(base, 'overrides', {})
|
||||
for n, o in overrides.iteritems():
|
||||
newparams[n] = o.apply(newparams[n].copy())
|
||||
for n, o in attrs.get('OVERRIDES', {}).iteritems():
|
||||
for n, o in attrs.get('overrides', {}).iteritems():
|
||||
newparams[n] = o.apply(newparams[n].copy())
|
||||
|
||||
# check validity of PARAM entries
|
||||
for pname, pobj in newtype.PARAMS.items():
|
||||
# check validity of Param entries
|
||||
for pname, pobj in newtype.parameters.items():
|
||||
# XXX: allow dicts for overriding certain aspects only.
|
||||
if not isinstance(pobj, PARAM):
|
||||
raise ProgrammingError('%r: PARAMs entry %r should be a '
|
||||
'PARAM object!' % (name, pname))
|
||||
if not isinstance(pobj, Param):
|
||||
raise ProgrammingError('%r: Params entry %r should be a '
|
||||
'Param object!' % (name, pname))
|
||||
|
||||
# XXX: create getters for the units of params ??
|
||||
|
||||
@ -212,7 +212,7 @@ class ModuleMeta(type):
|
||||
else:
|
||||
# return cached value
|
||||
self.log.debug("rfunc(%s): return cached value" % pname)
|
||||
value = self.PARAMS[pname].value
|
||||
value = self.parameters[pname].value
|
||||
setattr(self, pname, value) # important! trigger the setter
|
||||
return value
|
||||
|
||||
@ -231,13 +231,13 @@ class ModuleMeta(type):
|
||||
|
||||
def wrapped_wfunc(self, value, pname=pname, wfunc=wfunc):
|
||||
self.log.debug("wfunc(%s): set %r" % (pname, value))
|
||||
pobj = self.PARAMS[pname]
|
||||
pobj = self.parameters[pname]
|
||||
value = pobj.datatype.validate(value)
|
||||
if wfunc:
|
||||
self.log.debug('calling %r(%r)' % (wfunc, value))
|
||||
value = wfunc(self, value) or value
|
||||
# XXX: use setattr or direct manipulation
|
||||
# of self.PARAMS[pname]?
|
||||
# of self.parameters[pname]?
|
||||
setattr(self, pname, value)
|
||||
return value
|
||||
|
||||
@ -248,33 +248,33 @@ class ModuleMeta(type):
|
||||
wrapped_wfunc.__wrapped__ = True
|
||||
|
||||
def getter(self, pname=pname):
|
||||
return self.PARAMS[pname].value
|
||||
return self.parameters[pname].value
|
||||
|
||||
def setter(self, value, pname=pname):
|
||||
pobj = self.PARAMS[pname]
|
||||
pobj = self.parameters[pname]
|
||||
value = pobj.datatype.validate(value)
|
||||
pobj.timestamp = time.time()
|
||||
if not EVENT_ONLY_ON_CHANGED_VALUES or (value != pobj.value):
|
||||
pobj.value = value
|
||||
# also send notification
|
||||
if self.PARAMS[pname].export:
|
||||
if self.parameters[pname].export:
|
||||
self.log.debug('%s is now %r' % (pname, value))
|
||||
self.DISPATCHER.announce_update(self, pname, pobj)
|
||||
|
||||
setattr(newtype, pname, property(getter, setter))
|
||||
|
||||
# also collect/update information about CMD's
|
||||
setattr(newtype, 'CMDS', getattr(newtype, 'CMDS', {}))
|
||||
# also collect/update information about Command's
|
||||
setattr(newtype, 'commands', getattr(newtype, 'commands', {}))
|
||||
for attrname in attrs:
|
||||
if attrname.startswith('do_'):
|
||||
if attrname[3:] in newtype.CMDS:
|
||||
if attrname[3:] in newtype.commands:
|
||||
continue
|
||||
value = getattr(newtype, attrname)
|
||||
if isinstance(value, types.MethodType):
|
||||
argspec = inspect.getargspec(value)
|
||||
if argspec[0] and argspec[0][0] == 'self':
|
||||
del argspec[0][0]
|
||||
newtype.CMDS[attrname[3:]] = CMD(
|
||||
newtype.commands[attrname[3:]] = Command(
|
||||
getattr(value, '__doc__'), argspec.args,
|
||||
None) # XXX: how to find resulttype?
|
||||
attrs['__constructed__'] = True
|
||||
@ -294,9 +294,9 @@ class ModuleMeta(type):
|
||||
class Module(object):
|
||||
"""Basic Module, doesn't do much"""
|
||||
__metaclass__ = ModuleMeta
|
||||
# static PROPERTIES, definitions in derived classes should overwrite earlier ones.
|
||||
# static properties, definitions in derived classes should overwrite earlier ones.
|
||||
# how to configure some stuff which makes sense to take from configfile???
|
||||
PROPERTIES = {
|
||||
properties = {
|
||||
'group': None, # some Modules may be grouped together
|
||||
'meaning': None, # XXX: ???
|
||||
'priority': None, # XXX: ???
|
||||
@ -304,11 +304,11 @@ class Module(object):
|
||||
'description': "The manufacturer forgot to set a meaningful description. please nag him!",
|
||||
# what else?
|
||||
}
|
||||
# PARAMS and CMDS are auto-merged upon subclassing
|
||||
# PARAMS = {
|
||||
# 'description': PARAM('short description of this module and its function', datatype=StringType(), default='no specified'),
|
||||
# parameter and commands are auto-merged upon subclassing
|
||||
# parameters = {
|
||||
# 'description': Param('short description of this module and its function', datatype=StringType(), default='no specified'),
|
||||
# }
|
||||
CMDS = {}
|
||||
commands = {}
|
||||
DISPATCHER = None
|
||||
|
||||
def __init__(self, logger, cfgdict, devname, dispatcher):
|
||||
@ -316,48 +316,48 @@ class Module(object):
|
||||
self.DISPATCHER = dispatcher
|
||||
self.log = logger
|
||||
self.name = devname
|
||||
# make local copies of PARAMS
|
||||
# make local copies of parameter
|
||||
params = {}
|
||||
for k, v in self.PARAMS.items()[:]:
|
||||
for k, v in self.parameters.items()[:]:
|
||||
params[k] = v.copy()
|
||||
|
||||
self.PARAMS = params
|
||||
# make local copies of PROPERTIES
|
||||
self.parameters = params
|
||||
# make local copies of properties
|
||||
props = {}
|
||||
for k, v in self.PROPERTIES.items()[:]:
|
||||
for k, v in self.properties.items()[:]:
|
||||
props[k] = v
|
||||
|
||||
self.PROPERTIES = props
|
||||
self.properties = props
|
||||
|
||||
# check and apply properties specified in cfgdict
|
||||
# moduleproperties are to be specified as
|
||||
# '.<propertyname>=<propertyvalue>'
|
||||
for k, v in cfgdict.items():
|
||||
if k[0] == '.':
|
||||
if k[1:] in self.PROPERTIES:
|
||||
self.PROPERTIES[k[1:]] = v
|
||||
if k[1:] in self.properties:
|
||||
self.properties[k[1:]] = v
|
||||
del cfgdict[k]
|
||||
|
||||
# derive automatic properties
|
||||
mycls = self.__class__
|
||||
myclassname = '%s.%s' % (mycls.__module__, mycls.__name__)
|
||||
self.PROPERTIES['_implementation'] = myclassname
|
||||
self.PROPERTIES['interface_class'] = [
|
||||
self.properties['_implementation'] = myclassname
|
||||
self.properties['interface_class'] = [
|
||||
b.__name__ for b in mycls.__mro__ if b.__module__.startswith('secop.modules')]
|
||||
#self.PROPERTIES['interface'] = self.PROPERTIES['interfaces'][0]
|
||||
#self.properties['interface'] = self.properties['interfaces'][0]
|
||||
|
||||
# remove unset (default) module properties
|
||||
for k, v in self.PROPERTIES.items():
|
||||
for k, v in self.properties.items():
|
||||
if v is None:
|
||||
del self.PROPERTIES[k]
|
||||
del self.properties[k]
|
||||
|
||||
# check and apply parameter_properties
|
||||
# specified as '<paramname>.<propertyname> = <propertyvalue>'
|
||||
for k, v in cfgdict.items()[:]:
|
||||
if '.' in k[1:]:
|
||||
paramname, propname = k.split('.', 1)
|
||||
if paramname in self.PARAMS:
|
||||
paramobj = self.PARAMS[paramname]
|
||||
if paramname in self.parameters:
|
||||
paramobj = self.parameters[paramname]
|
||||
if propname == 'datatype':
|
||||
paramobj.datatype = get_datatype(cfgdict.pop(k))
|
||||
elif hasattr(paramobj, propname):
|
||||
@ -365,17 +365,17 @@ class Module(object):
|
||||
del cfgdict[k]
|
||||
|
||||
# check config for problems
|
||||
# only accept config items specified in PARAMS
|
||||
# only accept config items specified in parameters
|
||||
for k, v in cfgdict.items():
|
||||
if k not in self.PARAMS:
|
||||
if k not in self.parameters:
|
||||
raise ConfigError(
|
||||
'Module %s:config Parameter %r '
|
||||
'not unterstood! (use on of %r)' %
|
||||
(self.name, k, self.PARAMS.keys()))
|
||||
(self.name, k, self.parameters.keys()))
|
||||
|
||||
# complain if a PARAM entry has no default value and
|
||||
# complain if a Param entry has no default value and
|
||||
# is not specified in cfgdict
|
||||
for k, v in self.PARAMS.items():
|
||||
for k, v in self.parameters.items():
|
||||
if k not in cfgdict:
|
||||
if v.default is Ellipsis and k != 'value':
|
||||
# Ellipsis is the one single value you can not specify....
|
||||
@ -385,8 +385,8 @@ class Module(object):
|
||||
# assume default value was given
|
||||
cfgdict[k] = v.default
|
||||
|
||||
# replace CLASS level PARAM objects with INSTANCE level ones
|
||||
self.PARAMS[k] = self.PARAMS[k].copy()
|
||||
# replace CLASS level Param objects with INSTANCE level ones
|
||||
self.parameters[k] = self.parameters[k].copy()
|
||||
|
||||
# now 'apply' config:
|
||||
# pass values through the datatypes and store as attributes
|
||||
@ -394,7 +394,7 @@ class Module(object):
|
||||
if k == 'value':
|
||||
continue
|
||||
# apply datatype, complain if type does not fit
|
||||
datatype = self.PARAMS[k].datatype
|
||||
datatype = self.parameters[k].datatype
|
||||
if datatype is not None:
|
||||
# only check if datatype given
|
||||
try:
|
||||
@ -420,12 +420,12 @@ class Readable(Module):
|
||||
|
||||
providing the readonly parameter 'value' and 'status'
|
||||
"""
|
||||
PARAMS = {
|
||||
'value': PARAM('current value of the Module', readonly=True, default=0.,
|
||||
parameters = {
|
||||
'value': Param('current value of the Module', readonly=True, default=0.,
|
||||
datatype=FloatRange(), unit='', poll=True),
|
||||
'pollinterval': PARAM('sleeptime between polls', default=5,
|
||||
'pollinterval': Param('sleeptime between polls', default=5,
|
||||
readonly=False, datatype=FloatRange(0.1, 120), ),
|
||||
'status': PARAM('current status of the Module', default=(status.OK, ''),
|
||||
'status': Param('current status of the Module', default=(status.OK, ''),
|
||||
datatype=TupleOf(
|
||||
EnumType(**{
|
||||
'IDLE': status.OK,
|
||||
@ -458,13 +458,13 @@ class Readable(Module):
|
||||
def poll(self, nr):
|
||||
# poll status first
|
||||
fastpoll = False
|
||||
if 'status' in self.PARAMS:
|
||||
if 'status' in self.parameters:
|
||||
stat = self.read_status(0)
|
||||
# self.log.info('polling read_status -> %r' % (stat,))
|
||||
fastpoll = stat[0] == status.BUSY
|
||||
# if fastpoll:
|
||||
# self.log.info('fastpoll!')
|
||||
for pname, pobj in self.PARAMS.iteritems():
|
||||
for pname, pobj in self.parameters.iteritems():
|
||||
if not pobj.poll:
|
||||
continue
|
||||
if pname == 'status':
|
||||
@ -489,15 +489,15 @@ class Writable(Readable):
|
||||
|
||||
providing a settable 'target' parameter to those of a Readable
|
||||
"""
|
||||
PARAMS = {
|
||||
'target': PARAM(
|
||||
parameters = {
|
||||
'target': Param(
|
||||
'target value of the Module',
|
||||
default=0.,
|
||||
readonly=False,
|
||||
datatype=FloatRange(),
|
||||
),
|
||||
}
|
||||
# XXX: CMDS ???? auto deriving working well enough?
|
||||
# XXX: commands ???? auto deriving working well enough?
|
||||
|
||||
|
||||
class Drivable(Writable):
|
||||
@ -519,8 +519,8 @@ class Communicator(Module):
|
||||
providing no parameters, but a 'communicate' command.
|
||||
"""
|
||||
|
||||
CMDS = {
|
||||
"communicate" : CMD("provides the simplest mean to communication",
|
||||
commands = {
|
||||
"communicate" : Command("provides the simplest mean to communication",
|
||||
arguments=[StringType()],
|
||||
result=StringType()
|
||||
),
|
||||
|
@ -194,7 +194,7 @@ class Dispatcher(object):
|
||||
if modulename in self._export:
|
||||
# omit export=False params!
|
||||
res = {}
|
||||
for paramname, param in self.get_module(modulename).PARAMS.items():
|
||||
for paramname, param in self.get_module(modulename).parameters.items():
|
||||
if param.export:
|
||||
res[paramname] = param.as_dict(only_static)
|
||||
self.log.debug('list params for module %s -> %r' %
|
||||
@ -208,7 +208,7 @@ class Dispatcher(object):
|
||||
if modulename in self._export:
|
||||
# omit export=False params!
|
||||
res = {}
|
||||
for cmdname, cmdobj in self.get_module(modulename).CMDS.items():
|
||||
for cmdname, cmdobj in self.get_module(modulename).commands.items():
|
||||
res[cmdname] = cmdobj.as_dict()
|
||||
self.log.debug('list cmds for module %s -> %r' % (modulename, res))
|
||||
return res
|
||||
@ -229,7 +229,7 @@ class Dispatcher(object):
|
||||
mod_desc['parameters'].extend([pname, param])
|
||||
for cname, cmd in self.list_module_cmds(modulename).items():
|
||||
mod_desc['commands'].extend([cname, cmd])
|
||||
for propname, prop in module.PROPERTIES.items():
|
||||
for propname, prop in module.properties.items():
|
||||
mod_desc[propname] = prop
|
||||
result['modules'].extend([modulename, mod_desc])
|
||||
result['equipment_id'] = self.equipment_id
|
||||
@ -250,7 +250,7 @@ class Dispatcher(object):
|
||||
modulename,
|
||||
only_static=True),
|
||||
'commands': self.list_module_cmds(modulename),
|
||||
'properties': module.PROPERTIES,
|
||||
'properties': module.properties,
|
||||
}
|
||||
result['modules'][modulename] = dd
|
||||
result['equipment_id'] = self.equipment_id
|
||||
@ -267,7 +267,7 @@ class Dispatcher(object):
|
||||
if moduleobj is None:
|
||||
raise NoSuchModuleError(module=modulename)
|
||||
|
||||
cmdspec = moduleobj.CMDS.get(command, None)
|
||||
cmdspec = moduleobj.commands.get(command, None)
|
||||
if cmdspec is None:
|
||||
raise NoSuchCommandError(module=modulename, command=command)
|
||||
if len(cmdspec.arguments) != len(arguments):
|
||||
@ -293,7 +293,7 @@ class Dispatcher(object):
|
||||
if moduleobj is None:
|
||||
raise NoSuchModuleError(module=modulename)
|
||||
|
||||
pobj = moduleobj.PARAMS.get(pname, None)
|
||||
pobj = moduleobj.parameters.get(pname, None)
|
||||
if pobj is None:
|
||||
raise NoSuchParamError(module=modulename, parameter=pname)
|
||||
if pobj.readonly:
|
||||
@ -318,7 +318,7 @@ class Dispatcher(object):
|
||||
if moduleobj is None:
|
||||
raise NoSuchModuleError(module=modulename)
|
||||
|
||||
pobj = moduleobj.PARAMS.get(pname, None)
|
||||
pobj = moduleobj.parameters.get(pname, None)
|
||||
if pobj is None:
|
||||
raise NoSuchParamError(module=modulename, parameter=pname)
|
||||
|
||||
@ -368,7 +368,7 @@ class Dispatcher(object):
|
||||
res = self._setParamValue(msg.module, msg.parameter, msg.value)
|
||||
else:
|
||||
# first check if module has a target
|
||||
if 'target' not in self.get_module(msg.module).PARAMS:
|
||||
if 'target' not in self.get_module(msg.module).parameters:
|
||||
raise ReadonlyError(module=msg.module, parameter=None)
|
||||
res = self._setParamValue(msg.module, 'target', msg.value)
|
||||
res.parameter = 'target'
|
||||
@ -398,10 +398,10 @@ class Dispatcher(object):
|
||||
self.activate_connection(conn)
|
||||
# easy approach: poll all values...
|
||||
for modulename, moduleobj in self._modules.items():
|
||||
for pname, pobj in moduleobj.PARAMS.items():
|
||||
for pname, pobj in moduleobj.parameters.items():
|
||||
if not pobj.export:
|
||||
continue
|
||||
# WARNING: THIS READS ALL PARAMS FROM HW!
|
||||
# WARNING: THIS READS ALL parameters FROM HW!
|
||||
# XXX: should we send the cached values instead? (pbj.value)
|
||||
# also: ignore errors here.
|
||||
try:
|
||||
|
@ -24,7 +24,7 @@
|
||||
import random
|
||||
from time import sleep
|
||||
|
||||
from secop.modules import Module, Readable, Writable, Drivable, PARAM
|
||||
from secop.modules import Module, Readable, Writable, Drivable, Param
|
||||
from secop.lib import mkthread
|
||||
from secop.protocol import status
|
||||
from secop.datatypes import FloatRange
|
||||
@ -32,20 +32,30 @@ from secop.datatypes import FloatRange
|
||||
|
||||
class SimBase(object):
|
||||
def __init__(self, cfgdict):
|
||||
# spice up PARAMS if requested by extra property
|
||||
# spice up parameters if requested by extra property
|
||||
# hint: us a comma-separated list if mor than one extra_param
|
||||
# BIG FAT WARNING: changing extra params will NOT generate events!
|
||||
# XXX: implement default read_* and write_* methods to handle
|
||||
# read and change messages correctly
|
||||
if '.extra_params' in cfgdict:
|
||||
extra_params = cfgdict.pop('.extra_params')
|
||||
# make a copy of self.PARAMS
|
||||
self.PARAMS=dict((k,v.copy()) for k,v in self.PARAMS.items())
|
||||
# make a copy of self.parameter
|
||||
self.parameters = dict((k,v.copy()) for k,v in self.parameters.items())
|
||||
for k in extra_params.split(','):
|
||||
k = k.strip()
|
||||
self.PARAMS[k] = PARAM('extra_param: %s' % k.strip(),
|
||||
self.parameters[k] = Param('extra_param: %s' % k.strip(),
|
||||
datatype=FloatRange(),
|
||||
default=0.0)
|
||||
def reader(maxage=0, pname=k):
|
||||
self.log.debug('simulated reading %s' % pname)
|
||||
return self.parameters[pname].value
|
||||
setattr(self, 'read_' + k, reader)
|
||||
def writer(newval, pname=k):
|
||||
self.log.debug('simulated writing %r to %s' % (newval, pname))
|
||||
self.parameters[pname].value = newval
|
||||
return newval
|
||||
setattr(self, 'write_' + k, writer)
|
||||
|
||||
def late_init(self):
|
||||
self._sim_thread = mkthread(self._sim)
|
||||
|
||||
@ -61,7 +71,7 @@ class SimBase(object):
|
||||
return True
|
||||
|
||||
def read_value(self, maxage=0):
|
||||
if 'jitter' in self.PARAMS:
|
||||
if 'jitter' in self.parameters:
|
||||
return self._value + self.jitter*(0.5-random.random())
|
||||
return self._value
|
||||
|
||||
@ -76,14 +86,14 @@ class SimReadable(SimBase, Readable):
|
||||
def __init__(self, logger, cfgdict, devname, dispatcher):
|
||||
SimBase.__init__(self, cfgdict)
|
||||
Readable.__init__(self, logger, cfgdict, devname, dispatcher)
|
||||
self._value = self.PARAMS['value'].default
|
||||
self._value = self.parameters['value'].default
|
||||
|
||||
|
||||
class SimWritable(SimBase, Writable):
|
||||
def __init__(self, logger, cfgdict, devname, dispatcher):
|
||||
SimBase.__init__(self, cfgdict)
|
||||
Writable.__init__(self, logger, cfgdict, devname, dispatcher)
|
||||
self._value = self.PARAMS['value'].default
|
||||
self._value = self.parameters['value'].default
|
||||
def read_value(self, maxage=0):
|
||||
return self.target
|
||||
def write_target(self, value):
|
||||
@ -94,16 +104,16 @@ class SimDrivable(SimBase, Drivable):
|
||||
def __init__(self, logger, cfgdict, devname, dispatcher):
|
||||
SimBase.__init__(self, cfgdict)
|
||||
Drivable.__init__(self, logger, cfgdict, devname, dispatcher)
|
||||
self._value = self.PARAMS['value'].default
|
||||
self._value = self.parameters['value'].default
|
||||
|
||||
def sim(self):
|
||||
while self._value == self.target:
|
||||
sleep(0.3)
|
||||
self.status = status.BUSY, 'MOVING'
|
||||
speed = 0
|
||||
if 'ramp' in self.PARAMS:
|
||||
if 'ramp' in self.parameters:
|
||||
speed = self.ramp / 60. # ramp is per minute!
|
||||
elif 'speed' in self.PARAMS:
|
||||
elif 'speed' in self.parameters:
|
||||
speed = self.speed
|
||||
if speed == 0:
|
||||
self._value = self.target
|
||||
|
@ -23,9 +23,8 @@
|
||||
from math import atan
|
||||
import time
|
||||
import random
|
||||
import threading
|
||||
|
||||
from secop.modules import Drivable, CMD, PARAM
|
||||
from secop.modules import Drivable, Command, Param
|
||||
from secop.protocol import status
|
||||
from secop.datatypes import FloatRange, EnumType, TupleOf
|
||||
from secop.lib import clamp, mkthread
|
||||
@ -42,90 +41,90 @@ class Cryostat(CryoBase):
|
||||
- cooling power
|
||||
- thermal transfer between regulation and samplen
|
||||
"""
|
||||
PARAMS = dict(
|
||||
jitter=PARAM("amount of random noise on readout values",
|
||||
parameters = dict(
|
||||
jitter=Param("amount of random noise on readout values",
|
||||
datatype=FloatRange(0, 1), unit="K",
|
||||
default=0.1, readonly=False, export=False,
|
||||
),
|
||||
T_start=PARAM("starting temperature for simulation",
|
||||
T_start=Param("starting temperature for simulation",
|
||||
datatype=FloatRange(0), default=10,
|
||||
export=False,
|
||||
),
|
||||
looptime=PARAM("timestep for simulation",
|
||||
looptime=Param("timestep for simulation",
|
||||
datatype=FloatRange(0.01, 10), unit="s", default=1,
|
||||
readonly=False, export=False,
|
||||
),
|
||||
ramp=PARAM("ramping speed of the setpoint",
|
||||
ramp=Param("ramping speed of the setpoint",
|
||||
datatype=FloatRange(0, 1e3), unit="K/min", default=1,
|
||||
readonly=False,
|
||||
),
|
||||
setpoint=PARAM("current setpoint during ramping else target",
|
||||
setpoint=Param("current setpoint during ramping else target",
|
||||
datatype=FloatRange(), default=1, unit='K',
|
||||
),
|
||||
maxpower=PARAM("Maximum heater power",
|
||||
maxpower=Param("Maximum heater power",
|
||||
datatype=FloatRange(0), default=1, unit="W",
|
||||
readonly=False,
|
||||
group='heater_settings',
|
||||
),
|
||||
heater=PARAM("current heater setting",
|
||||
heater=Param("current heater setting",
|
||||
datatype=FloatRange(0, 100), default=0, unit="%",
|
||||
group='heater_settings',
|
||||
),
|
||||
heaterpower=PARAM("current heater power",
|
||||
heaterpower=Param("current heater power",
|
||||
datatype=FloatRange(0), default=0, unit="W",
|
||||
group='heater_settings',
|
||||
),
|
||||
target=PARAM("target temperature",
|
||||
target=Param("target temperature",
|
||||
datatype=FloatRange(0), default=0, unit="K",
|
||||
readonly=False,
|
||||
),
|
||||
value=PARAM("regulation temperature",
|
||||
value=Param("regulation temperature",
|
||||
datatype=FloatRange(0), default=0, unit="K",
|
||||
),
|
||||
pid=PARAM("regulation coefficients",
|
||||
pid=Param("regulation coefficients",
|
||||
datatype=TupleOf(FloatRange(0), FloatRange(0, 100),
|
||||
FloatRange(0, 100)),
|
||||
default=(40, 10, 2), readonly=False,
|
||||
group='pid',
|
||||
),
|
||||
p=PARAM("regulation coefficient 'p'",
|
||||
p=Param("regulation coefficient 'p'",
|
||||
datatype=FloatRange(0), default=40, unit="%/K", readonly=False,
|
||||
group='pid',
|
||||
),
|
||||
i=PARAM("regulation coefficient 'i'",
|
||||
i=Param("regulation coefficient 'i'",
|
||||
datatype=FloatRange(0, 100), default=10, readonly=False,
|
||||
group='pid',
|
||||
),
|
||||
d=PARAM("regulation coefficient 'd'",
|
||||
d=Param("regulation coefficient 'd'",
|
||||
datatype=FloatRange(0, 100), default=2, readonly=False,
|
||||
group='pid',
|
||||
),
|
||||
mode=PARAM("mode of regulation",
|
||||
mode=Param("mode of regulation",
|
||||
datatype=EnumType('ramp', 'pid', 'openloop'),
|
||||
default='ramp',
|
||||
readonly=False,
|
||||
),
|
||||
pollinterval=PARAM("polling interval",
|
||||
pollinterval=Param("polling interval",
|
||||
datatype=FloatRange(0), default=5,
|
||||
),
|
||||
tolerance=PARAM("temperature range for stability checking",
|
||||
tolerance=Param("temperature range for stability checking",
|
||||
datatype=FloatRange(0, 100), default=0.1, unit='K',
|
||||
readonly=False,
|
||||
group='stability',
|
||||
),
|
||||
window=PARAM("time window for stability checking",
|
||||
window=Param("time window for stability checking",
|
||||
datatype=FloatRange(1, 900), default=30, unit='s',
|
||||
readonly=False,
|
||||
group='stability',
|
||||
),
|
||||
timeout=PARAM("max waiting time for stabilisation check",
|
||||
timeout=Param("max waiting time for stabilisation check",
|
||||
datatype=FloatRange(1, 36000), default=900, unit='s',
|
||||
readonly=False,
|
||||
group='stability',
|
||||
),
|
||||
)
|
||||
CMDS = dict(
|
||||
stop=CMD(
|
||||
commands = dict(
|
||||
stop=Command(
|
||||
"Stop ramping the setpoint\n\nby setting the current setpoint as new target",
|
||||
[],
|
||||
None),
|
||||
@ -236,7 +235,7 @@ class Cryostat(CryoBase):
|
||||
lastflow = 0
|
||||
last_heaters = (0, 0)
|
||||
delta = 0
|
||||
I = D = 0
|
||||
_I = _D = 0
|
||||
lastD = 0
|
||||
damper = 1
|
||||
lastmode = self.mode
|
||||
@ -283,32 +282,32 @@ class Cryostat(CryoBase):
|
||||
kp = self.p / 10. # LakeShore P = 10*k_p
|
||||
ki = kp * abs(self.i) / 500. # LakeShore I = 500/T_i
|
||||
kd = kp * abs(self.d) / 2. # LakeShore D = 2*T_d
|
||||
P = kp * error
|
||||
I += ki * error * h
|
||||
D = kd * delta / h
|
||||
_P = kp * error
|
||||
_I += ki * error * h
|
||||
_D = kd * delta / h
|
||||
|
||||
# avoid reset windup
|
||||
I = clamp(I, 0., 100.) # I is in %
|
||||
_I = clamp(_I, 0., 100.) # _I is in %
|
||||
|
||||
# avoid jumping heaterpower if switching back to pid mode
|
||||
if lastmode != self.mode:
|
||||
# adjust some values upon switching back on
|
||||
I = self.heater - P - D
|
||||
_I = self.heater - _P - _D
|
||||
|
||||
v = P + I + D
|
||||
v = _P + _I + _D
|
||||
# in damping mode, use a weighted sum of old + new heaterpower
|
||||
if damper > 1:
|
||||
v = ((damper**2 - 1) * self.heater + v) / damper**2
|
||||
|
||||
# damp oscillations due to D switching signs
|
||||
if D * lastD < -0.2:
|
||||
if _D * lastD < -0.2:
|
||||
v = (v + heater) / 2.
|
||||
# clamp new heater power to 0..100%
|
||||
heater = clamp(v, 0., 100.)
|
||||
lastD = D
|
||||
lastD = _D
|
||||
|
||||
self.log.debug('PID: P = %.2f, I = %.2f, D = %.2f, '
|
||||
'heater = %.2f' % (P, I, D, heater))
|
||||
'heater = %.2f' % (_P, _I, _D, heater))
|
||||
|
||||
# check for turn-around points to detect oscillations ->
|
||||
# increase damper
|
||||
@ -351,9 +350,9 @@ class Cryostat(CryoBase):
|
||||
window.pop(0)
|
||||
# obtain min/max
|
||||
deviation = 0
|
||||
for _, T in window:
|
||||
if abs(T - self.target) > deviation:
|
||||
deviation = abs(T - self.target)
|
||||
for _, _T in window:
|
||||
if abs(_T - self.target) > deviation:
|
||||
deviation = abs(_T - self.target)
|
||||
if (len(window) < 3) or deviation > self.tolerance:
|
||||
self.status = status.BUSY, 'unstable'
|
||||
elif self.setpoint == self.target:
|
||||
|
@ -24,7 +24,7 @@ import time
|
||||
import random
|
||||
import threading
|
||||
|
||||
from secop.modules import Readable, Drivable, PARAM
|
||||
from secop.modules import Readable, Drivable, Param
|
||||
from secop.datatypes import EnumType, FloatRange, IntRange, ArrayOf, StringType, TupleOf, StructOf, BoolType
|
||||
from secop.protocol import status
|
||||
|
||||
@ -32,19 +32,19 @@ from secop.protocol import status
|
||||
class Switch(Drivable):
|
||||
"""switch it on or off....
|
||||
"""
|
||||
PARAMS = {
|
||||
'value': PARAM('current state (on or off)',
|
||||
parameters = {
|
||||
'value': Param('current state (on or off)',
|
||||
datatype=EnumType(on=1, off=0), default=0,
|
||||
),
|
||||
'target': PARAM('wanted state (on or off)',
|
||||
'target': Param('wanted state (on or off)',
|
||||
datatype=EnumType(on=1, off=0), default=0,
|
||||
readonly=False,
|
||||
),
|
||||
'switch_on_time': PARAM('seconds to wait after activating the switch',
|
||||
'switch_on_time': Param('seconds to wait after activating the switch',
|
||||
datatype=FloatRange(0, 60), unit='s',
|
||||
default=10, export=False,
|
||||
),
|
||||
'switch_off_time': PARAM('cool-down time in seconds',
|
||||
'switch_off_time': Param('cool-down time in seconds',
|
||||
datatype=FloatRange(0, 60), unit='s',
|
||||
default=10, export=False,
|
||||
),
|
||||
@ -77,7 +77,7 @@ class Switch(Drivable):
|
||||
return status.BUSY, info
|
||||
|
||||
def _update(self):
|
||||
started = self.PARAMS['target'].timestamp
|
||||
started = self.parameters['target'].timestamp
|
||||
info = ''
|
||||
if self.target > self.value:
|
||||
info = 'waiting for ON'
|
||||
@ -97,23 +97,23 @@ class Switch(Drivable):
|
||||
class MagneticField(Drivable):
|
||||
"""a liquid magnet
|
||||
"""
|
||||
PARAMS = {
|
||||
'value': PARAM('current field in T',
|
||||
parameters = {
|
||||
'value': Param('current field in T',
|
||||
unit='T', datatype=FloatRange(-15, 15), default=0,
|
||||
),
|
||||
'target': PARAM('target field in T',
|
||||
'target': Param('target field in T',
|
||||
unit='T', datatype=FloatRange(-15, 15), default=0,
|
||||
readonly=False,
|
||||
),
|
||||
'ramp': PARAM('ramping speed',
|
||||
'ramp': Param('ramping speed',
|
||||
unit='T/min', datatype=FloatRange(0, 1), default=0.1,
|
||||
readonly=False,
|
||||
),
|
||||
'mode': PARAM('what to do after changing field',
|
||||
'mode': Param('what to do after changing field',
|
||||
default=1, datatype=EnumType(persistent=1, hold=0),
|
||||
readonly=False,
|
||||
),
|
||||
'heatswitch': PARAM('name of heat switch device',
|
||||
'heatswitch': Param('name of heat switch device',
|
||||
datatype=StringType(), export=False,
|
||||
),
|
||||
}
|
||||
@ -184,11 +184,11 @@ class MagneticField(Drivable):
|
||||
class CoilTemp(Readable):
|
||||
"""a coil temperature
|
||||
"""
|
||||
PARAMS = {
|
||||
'value': PARAM('Coil temperatur',
|
||||
parameters = {
|
||||
'value': Param('Coil temperatur',
|
||||
unit='K', datatype=FloatRange(), default=0,
|
||||
),
|
||||
'sensor': PARAM("Sensor number or calibration id",
|
||||
'sensor': Param("Sensor number or calibration id",
|
||||
datatype=StringType(), readonly=True,
|
||||
),
|
||||
}
|
||||
@ -200,14 +200,14 @@ class CoilTemp(Readable):
|
||||
class SampleTemp(Drivable):
|
||||
"""a sample temperature
|
||||
"""
|
||||
PARAMS = {
|
||||
'value': PARAM('Sample temperature',
|
||||
parameters = {
|
||||
'value': Param('Sample temperature',
|
||||
unit='K', datatype=FloatRange(), default=10,
|
||||
),
|
||||
'sensor': PARAM("Sensor number or calibration id",
|
||||
'sensor': Param("Sensor number or calibration id",
|
||||
datatype=StringType(), readonly=True,
|
||||
),
|
||||
'ramp': PARAM('moving speed in K/min',
|
||||
'ramp': Param('moving speed in K/min',
|
||||
datatype=FloatRange(0, 100), unit='K/min', default=0.1,
|
||||
readonly=False,
|
||||
),
|
||||
@ -241,20 +241,23 @@ class SampleTemp(Drivable):
|
||||
|
||||
|
||||
class Label(Readable):
|
||||
"""
|
||||
"""Displays the status of a cryomagnet
|
||||
|
||||
by composing its (stringtype) value from the status/value
|
||||
of several subdevices. used for demoing connections between
|
||||
modules.
|
||||
"""
|
||||
PARAMS = {
|
||||
'system': PARAM("Name of the magnet system",
|
||||
parameters = {
|
||||
'system': Param("Name of the magnet system",
|
||||
datatype=StringType, export=False,
|
||||
),
|
||||
'subdev_mf': PARAM("name of subdevice for magnet status",
|
||||
'subdev_mf': Param("name of subdevice for magnet status",
|
||||
datatype=StringType, export=False,
|
||||
),
|
||||
'subdev_ts': PARAM("name of subdevice for sample temp",
|
||||
'subdev_ts': Param("name of subdevice for sample temp",
|
||||
datatype=StringType, export=False,
|
||||
),
|
||||
'value': PARAM("final value of label string",
|
||||
'value': Param("final value of label string",
|
||||
datatype=StringType,
|
||||
),
|
||||
}
|
||||
@ -265,7 +268,7 @@ class Label(Readable):
|
||||
dev_ts = self.DISPATCHER.get_module(self.subdev_ts)
|
||||
if dev_ts:
|
||||
strings.append('at %.3f %s' %
|
||||
(dev_ts.read_value(), dev_ts.PARAMS['value'].unit))
|
||||
(dev_ts.read_value(), dev_ts.parameters['value'].unit))
|
||||
else:
|
||||
strings.append('No connection to sample temp!')
|
||||
|
||||
@ -274,7 +277,7 @@ class Label(Readable):
|
||||
mf_stat = dev_mf.read_status()
|
||||
mf_mode = dev_mf.mode
|
||||
mf_val = dev_mf.value
|
||||
mf_unit = dev_mf.PARAMS['value'].unit
|
||||
mf_unit = dev_mf.parameters['value'].unit
|
||||
if mf_stat[0] == status.OK:
|
||||
state = 'Persistent' if mf_mode else 'Non-persistent'
|
||||
else:
|
||||
@ -287,21 +290,28 @@ class Label(Readable):
|
||||
|
||||
|
||||
class DatatypesTest(Readable):
|
||||
"""for demoing all datatypes
|
||||
"""
|
||||
"""
|
||||
PARAMS = {
|
||||
'enum': PARAM(
|
||||
parameters = {
|
||||
'enum': Param(
|
||||
'enum', datatype=EnumType(
|
||||
'boo', 'faar', z=9), readonly=False, default=1), 'tupleof': PARAM(
|
||||
'boo', 'faar', z=9), readonly=False, default=1), 'tupleof': Param(
|
||||
'tuple of int, float and str', datatype=TupleOf(
|
||||
IntRange(), FloatRange(), StringType()), readonly=False, default=(
|
||||
1, 2.3, 'a')), 'arrayof': PARAM(
|
||||
1, 2.3, 'a')), 'arrayof': Param(
|
||||
'array: 2..3 times bool', datatype=ArrayOf(
|
||||
BoolType(), 2, 3), readonly=False, default=[
|
||||
1, 0, 1]), 'intrange': PARAM(
|
||||
1, 0, 1]), 'intrange': Param(
|
||||
'intrange', datatype=IntRange(
|
||||
2, 9), readonly=False, default=4), 'floatrange': PARAM(
|
||||
2, 9), readonly=False, default=4), 'floatrange': Param(
|
||||
'floatrange', datatype=FloatRange(
|
||||
-1, 1), readonly=False, default=0, ), 'struct': PARAM(
|
||||
-1, 1), readonly=False, default=0, ), 'struct': Param(
|
||||
'struct(a=str, b=int, c=bool)', datatype=StructOf(
|
||||
a=StringType(), b=IntRange(), c=BoolType()), ), }
|
||||
|
||||
|
||||
class ArrayTest(Readable):
|
||||
parameters = {
|
||||
"x" : Param('value', datatype=ArrayOf(FloatRange(),100000,100000),
|
||||
default = 100000 * [0]),
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
import random
|
||||
|
||||
from secop.modules import Readable, Drivable, Communicator, PARAM
|
||||
from secop.modules import Readable, Drivable, Communicator, Param
|
||||
from secop.datatypes import FloatRange, StringType
|
||||
|
||||
|
||||
@ -43,8 +43,8 @@ class Heater(Drivable):
|
||||
class name indicates it to be some heating element,
|
||||
but the implementation may do anything
|
||||
"""
|
||||
PARAMS = {
|
||||
'maxheaterpower': PARAM('maximum allowed heater power',
|
||||
parameters = {
|
||||
'maxheaterpower': Param('maximum allowed heater power',
|
||||
datatype=FloatRange(0, 100), unit='W',
|
||||
),
|
||||
}
|
||||
@ -62,15 +62,15 @@ class Temp(Drivable):
|
||||
class name indicates it to be some temperature controller,
|
||||
but the implementation may do anything
|
||||
"""
|
||||
PARAMS = {
|
||||
'sensor': PARAM(
|
||||
parameters = {
|
||||
'sensor': Param(
|
||||
"Sensor number or calibration id",
|
||||
datatype=StringType(
|
||||
8,
|
||||
16),
|
||||
readonly=True,
|
||||
),
|
||||
'target': PARAM(
|
||||
'target': Param(
|
||||
"Target temperature",
|
||||
default=300.0,
|
||||
datatype=FloatRange(0),
|
||||
|
@ -20,10 +20,8 @@
|
||||
# Erik Dahlbäck <erik.dahlback@esss.se>
|
||||
# *****************************************************************************
|
||||
|
||||
import random
|
||||
|
||||
from secop.datatypes import EnumType, TupleOf, FloatRange, get_datatype, StringType
|
||||
from secop.modules import Readable, Module, Drivable, PARAM
|
||||
from secop.datatypes import EnumType, FloatRange, StringType
|
||||
from secop.modules import Readable, Drivable, Param
|
||||
from secop.protocol import status
|
||||
|
||||
try:
|
||||
@ -59,18 +57,18 @@ except ImportError:
|
||||
|
||||
class EpicsReadable(Readable):
|
||||
"""EpicsDrivable handles a Drivable interfacing to EPICS v4"""
|
||||
# Commmon PARAMS for all EPICS devices
|
||||
PARAMS = {
|
||||
'value': PARAM('EPICS generic value',
|
||||
# Commmon parameter for all EPICS devices
|
||||
parameters = {
|
||||
'value': Param('EPICS generic value',
|
||||
datatype=FloatRange(),
|
||||
default=300.0,),
|
||||
'epics_version': PARAM("EPICS version used, v3 or v4",
|
||||
'epics_version': Param("EPICS version used, v3 or v4",
|
||||
datatype=EnumType(v3=3, v4=4),),
|
||||
# 'private' parameters: not remotely accessible
|
||||
'value_pv': PARAM('EPICS pv_name of value',
|
||||
'value_pv': Param('EPICS pv_name of value',
|
||||
datatype=StringType(),
|
||||
default="unset", export=False),
|
||||
'status_pv': PARAM('EPICS pv_name of status',
|
||||
'status_pv': Param('EPICS pv_name of status',
|
||||
datatype=StringType(),
|
||||
default="unset", export=False),
|
||||
}
|
||||
@ -119,20 +117,20 @@ class EpicsReadable(Readable):
|
||||
|
||||
class EpicsDrivable(Drivable):
|
||||
"""EpicsDrivable handles a Drivable interfacing to EPICS v4"""
|
||||
# Commmon PARAMS for all EPICS devices
|
||||
PARAMS = {
|
||||
'target': PARAM('EPICS generic target', datatype=FloatRange(),
|
||||
# Commmon parameter for all EPICS devices
|
||||
parameters = {
|
||||
'target': Param('EPICS generic target', datatype=FloatRange(),
|
||||
default=300.0, readonly=False),
|
||||
'value': PARAM('EPICS generic value', datatype=FloatRange(),
|
||||
'value': Param('EPICS generic value', datatype=FloatRange(),
|
||||
default=300.0,),
|
||||
'epics_version': PARAM("EPICS version used, v3 or v4",
|
||||
'epics_version': Param("EPICS version used, v3 or v4",
|
||||
datatype=StringType(),),
|
||||
# 'private' parameters: not remotely accessible
|
||||
'target_pv': PARAM('EPICS pv_name of target', datatype=StringType(),
|
||||
'target_pv': Param('EPICS pv_name of target', datatype=StringType(),
|
||||
default="unset", export=False),
|
||||
'value_pv': PARAM('EPICS pv_name of value', datatype=StringType(),
|
||||
'value_pv': Param('EPICS pv_name of value', datatype=StringType(),
|
||||
default="unset", export=False),
|
||||
'status_pv': PARAM('EPICS pv_name of status', datatype=StringType(),
|
||||
'status_pv': Param('EPICS pv_name of status', datatype=StringType(),
|
||||
default="unset", export=False),
|
||||
}
|
||||
|
||||
@ -188,22 +186,22 @@ class EpicsDrivable(Drivable):
|
||||
'Moving')
|
||||
|
||||
|
||||
"""Temperature control loop"""
|
||||
# """Temperature control loop"""
|
||||
# should also derive from secop.core.temperaturecontroller, once its
|
||||
# features are agreed upon
|
||||
|
||||
|
||||
class EpicsTempCtrl(EpicsDrivable):
|
||||
|
||||
PARAMS = {
|
||||
parameters = {
|
||||
# TODO: restrict possible values with oneof datatype
|
||||
'heaterrange': PARAM('Heater range', datatype=StringType(),
|
||||
'heaterrange': Param('Heater range', datatype=StringType(),
|
||||
default='Off', readonly=False,),
|
||||
'tolerance': PARAM('allowed deviation between value and target',
|
||||
'tolerance': Param('allowed deviation between value and target',
|
||||
datatype=FloatRange(1e-6, 1e6), default=0.1,
|
||||
readonly=False,),
|
||||
# 'private' parameters: not remotely accessible
|
||||
'heaterrange_pv': PARAM('EPICS pv_name of heater range',
|
||||
'heaterrange_pv': Param('EPICS pv_name of heater range',
|
||||
datatype=StringType(), default="unset", export=False,),
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ from secop.lib.sequence import SequencerMixin, Step
|
||||
from secop.protocol import status
|
||||
from secop.datatypes import StringType, TupleOf, FloatRange, ArrayOf, StructOf
|
||||
from secop.errors import DisabledError, ConfigError
|
||||
from secop.modules import PARAM, Drivable
|
||||
from secop.modules import Param, Drivable
|
||||
|
||||
|
||||
class GarfieldMagnet(SequencerMixin, Drivable):
|
||||
@ -48,31 +48,31 @@ class GarfieldMagnet(SequencerMixin, Drivable):
|
||||
the symmetry setting selects which.
|
||||
"""
|
||||
|
||||
PARAMS = {
|
||||
'subdev_currentsource': PARAM('(bipolar) Powersupply', datatype=StringType(), readonly=True, export=False),
|
||||
'subdev_enable': PARAM('Switch to set for on/off', datatype=StringType(), readonly=True, export=False),
|
||||
'subdev_polswitch': PARAM('Switch to set for polarity', datatype=StringType(), readonly=True, export=False),
|
||||
'subdev_symmetry': PARAM('Switch to read for symmetry', datatype=StringType(), readonly=True, export=False),
|
||||
'userlimits': PARAM('User defined limits of device value',
|
||||
parameters = {
|
||||
'subdev_currentsource': Param('(bipolar) Powersupply', datatype=StringType(), readonly=True, export=False),
|
||||
'subdev_enable': Param('Switch to set for on/off', datatype=StringType(), readonly=True, export=False),
|
||||
'subdev_polswitch': Param('Switch to set for polarity', datatype=StringType(), readonly=True, export=False),
|
||||
'subdev_symmetry': Param('Switch to read for symmetry', datatype=StringType(), readonly=True, export=False),
|
||||
'userlimits': Param('User defined limits of device value',
|
||||
unit='main', datatype=TupleOf(FloatRange(), FloatRange()),
|
||||
default=(float('-Inf'), float('+Inf')), readonly=False, poll=10),
|
||||
'abslimits': PARAM('Absolute limits of device value',
|
||||
'abslimits': Param('Absolute limits of device value',
|
||||
unit='main', datatype=TupleOf(FloatRange(), FloatRange()),
|
||||
default=(-0.5, 0.5), poll=True,
|
||||
),
|
||||
'precision': PARAM('Precision of the device value (allowed deviation '
|
||||
'precision': Param('Precision of the device value (allowed deviation '
|
||||
'of stable values from target)',
|
||||
unit='main', datatype=FloatRange(0.001), default=0.001, readonly=False,
|
||||
),
|
||||
'ramp': PARAM('Target rate of field change per minute', readonly=False,
|
||||
'ramp': Param('Target rate of field change per minute', readonly=False,
|
||||
unit='main/min', datatype=FloatRange(), default=1.0),
|
||||
'calibration': PARAM('Coefficients for calibration '
|
||||
'calibration': Param('Coefficients for calibration '
|
||||
'function: [c0, c1, c2, c3, c4] calculates '
|
||||
'B(I) = c0*I + c1*erf(c2*I) + c3*atan(c4*I)'
|
||||
' in T', poll=1,
|
||||
datatype=ArrayOf(FloatRange(), 5, 5),
|
||||
default=(1.0, 0.0, 0.0, 0.0, 0.0)),
|
||||
'calibrationtable': PARAM('Map of Coefficients for calibration per symmetry setting',
|
||||
'calibrationtable': Param('Map of Coefficients for calibration per symmetry setting',
|
||||
datatype=StructOf(symmetric=ArrayOf(FloatRange(), 5, 5),
|
||||
short=ArrayOf(
|
||||
FloatRange(), 5, 5),
|
||||
@ -175,7 +175,7 @@ class GarfieldMagnet(SequencerMixin, Drivable):
|
||||
def read_abslimits(self, maxage=0):
|
||||
maxfield = self._current2field(self._currentsource.abslimits[1])
|
||||
# limit to configured value (if any)
|
||||
maxfield = min(maxfield, max(self.PARAMS['abslimits'].default))
|
||||
maxfield = min(maxfield, max(self.parameters['abslimits'].default))
|
||||
return -maxfield, maxfield
|
||||
|
||||
def read_ramp(self, maxage=0):
|
||||
@ -203,10 +203,10 @@ class GarfieldMagnet(SequencerMixin, Drivable):
|
||||
# safe to switch
|
||||
self._polswitch.write_target(
|
||||
'+1' if polarity > 0 else str(polarity))
|
||||
return 0
|
||||
return
|
||||
if self._currentsource.value < 0.1:
|
||||
self._polswitch.write_target('0')
|
||||
return current_pol
|
||||
return
|
||||
# unsafe to switch, go to safe state first
|
||||
self._currentsource.write_target(0)
|
||||
|
||||
|
@ -22,26 +22,25 @@
|
||||
# *****************************************************************************
|
||||
|
||||
# This is based upon the entangle-nicos integration
|
||||
"""
|
||||
This module contains the MLZ SECoP - TANGO integration.
|
||||
"""This module contains the MLZ SECoP - TANGO integration.
|
||||
|
||||
Here we support devices which fulfill the official
|
||||
MLZ TANGO interface for the respective device classes.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from time import sleep, time as currenttime
|
||||
import threading
|
||||
|
||||
import PyTango
|
||||
import numpy
|
||||
|
||||
from secop.lib import lazy_property, mkthread
|
||||
from secop.lib import lazy_property
|
||||
from secop.protocol import status
|
||||
from secop.datatypes import *
|
||||
from secop.errors import SECoPServerError, ConfigError, ProgrammingError, CommunicationError, HardwareError
|
||||
from secop.modules import PARAM, CMD, OVERRIDE, Module, Readable, Drivable
|
||||
from secop.parse import Parser
|
||||
from secop.datatypes import IntRange, FloatRange, StringType, TupleOf, \
|
||||
ArrayOf, EnumType
|
||||
from secop.errors import ConfigError, ProgrammingError, CommunicationError, HardwareError
|
||||
from secop.modules import Param, Command, Override, Module, Readable, Drivable
|
||||
|
||||
#####
|
||||
|
||||
@ -158,13 +157,13 @@ class PyTangoDevice(Module):
|
||||
execution and attribute operations with logging and exception mapping.
|
||||
"""
|
||||
|
||||
PARAMS = {
|
||||
'comtries': PARAM('Maximum retries for communication',
|
||||
parameters = {
|
||||
'comtries': Param('Maximum retries for communication',
|
||||
datatype=IntRange(1, 100), default=3, readonly=False, group='communication'),
|
||||
'comdelay': PARAM('Delay between retries', datatype=FloatRange(0), unit='s', default=0.1,
|
||||
'comdelay': Param('Delay between retries', datatype=FloatRange(0), unit='s', default=0.1,
|
||||
readonly=False, group='communication'),
|
||||
|
||||
'tangodevice': PARAM('Tango device name',
|
||||
'tangodevice': Param('Tango device name',
|
||||
datatype=StringType(), readonly=True,
|
||||
# export=True, # for testing only
|
||||
export=False,
|
||||
@ -186,7 +185,7 @@ class PyTangoDevice(Module):
|
||||
def _com_retry(self, info, function, *args, **kwds):
|
||||
"""Try communicating with the hardware/device.
|
||||
|
||||
PARAMeter "info" is passed to _com_return and _com_raise methods that
|
||||
Parameter "info" is passed to _com_return and _com_raise methods that
|
||||
process the return value or exception raised after maximum tries.
|
||||
"""
|
||||
tries = self.comtries
|
||||
@ -380,7 +379,7 @@ class AnalogInput(PyTangoDevice, Readable):
|
||||
# prefer configured unit if nothing is set on the Tango device, else
|
||||
# update
|
||||
if attrInfo.unit != 'No unit':
|
||||
self.PARAMS['value'].unit = attrInfo.unit
|
||||
self.parameters['value'].unit = attrInfo.unit
|
||||
|
||||
def read_value(self, maxage=0):
|
||||
return self._dev.value
|
||||
@ -397,8 +396,8 @@ class Sensor(AnalogInput):
|
||||
# note: we don't transport the formula to secop....
|
||||
# we support the adjust method
|
||||
|
||||
CMDS = {
|
||||
'setposition' : CMD('Set the position to the given value.',
|
||||
commands = {
|
||||
'setposition' : Command('Set the position to the given value.',
|
||||
arguments=[FloatRange()],
|
||||
result=None
|
||||
),
|
||||
@ -409,8 +408,7 @@ class Sensor(AnalogInput):
|
||||
|
||||
|
||||
class AnalogOutput(PyTangoDevice, Drivable):
|
||||
"""
|
||||
The AnalogOutput handles all devices which set an analogue value.
|
||||
"""The AnalogOutput handles all devices which set an analogue value.
|
||||
|
||||
The main application field is the output of any signal which may be
|
||||
considered as continously in a range. The values may have nearly any
|
||||
@ -421,41 +419,25 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
||||
controllers, ...
|
||||
"""
|
||||
|
||||
PARAMS = {
|
||||
'userlimits': PARAM(
|
||||
'User defined limits of device value',
|
||||
unit='main',
|
||||
datatype=TupleOf(
|
||||
FloatRange(),
|
||||
FloatRange()),
|
||||
default=(
|
||||
float('-Inf'),
|
||||
float('+Inf')),
|
||||
readonly=False,
|
||||
poll=10),
|
||||
'abslimits': PARAM(
|
||||
'Absolute limits of device value',
|
||||
unit='main',
|
||||
datatype=TupleOf(
|
||||
FloatRange(),
|
||||
FloatRange()),
|
||||
),
|
||||
'precision': PARAM(
|
||||
'Precision of the device value (allowed deviation '
|
||||
'of stable values from target)',
|
||||
unit='main',
|
||||
datatype=FloatRange(1e-38),
|
||||
readonly=False,
|
||||
),
|
||||
'window': PARAM(
|
||||
'Time window for checking stabilization if > 0',
|
||||
unit='s',
|
||||
default=60.0,
|
||||
datatype=FloatRange(
|
||||
0,
|
||||
900),
|
||||
readonly=False,
|
||||
),
|
||||
parameters = {
|
||||
'userlimits': Param('User defined limits of device value',
|
||||
datatype=TupleOf(FloatRange(), FloatRange()),
|
||||
default=(float('-Inf'),float('+Inf')),
|
||||
unit='main', readonly=False, poll=10,
|
||||
),
|
||||
'abslimits': Param('Absolute limits of device value',
|
||||
datatype=TupleOf(FloatRange(), FloatRange()),
|
||||
unit='main',
|
||||
),
|
||||
'precision': Param('Precision of the device value (allowed deviation '
|
||||
'of stable values from target)',
|
||||
unit='main', datatype=FloatRange(1e-38),
|
||||
readonly=False,
|
||||
),
|
||||
'window': Param('Time window for checking stabilization if > 0',
|
||||
unit='s', default=60.0, readonly=False,
|
||||
datatype=FloatRange(0, 900),
|
||||
),
|
||||
}
|
||||
|
||||
def init(self):
|
||||
@ -470,7 +452,7 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
||||
# prefer configured unit if nothing is set on the Tango device, else
|
||||
# update
|
||||
if attrInfo.unit != 'No unit':
|
||||
self.PARAMS['value'].unit = attrInfo.unit
|
||||
self.parameters['value'].unit = attrInfo.unit
|
||||
|
||||
def poll(self, nr):
|
||||
super(AnalogOutput, self).poll(nr)
|
||||
@ -563,9 +545,8 @@ class AnalogOutput(PyTangoDevice, Drivable):
|
||||
|
||||
|
||||
class Actuator(AnalogOutput):
|
||||
"""
|
||||
The actuator interface describes all analog devices which DO something in a
|
||||
defined way.
|
||||
"""The aAtuator interface describes all analog devices which DO something
|
||||
in a defined way.
|
||||
|
||||
The difference to AnalogOutput is that there is a speed attribute, and the
|
||||
value attribute is converted from the “raw value” with a formula and
|
||||
@ -573,22 +554,18 @@ class Actuator(AnalogOutput):
|
||||
"""
|
||||
# for secop: support the speed and ramp parameters
|
||||
|
||||
PARAMS = {
|
||||
'speed': PARAM(
|
||||
'The speed of changing the value',
|
||||
unit='main/s',
|
||||
readonly=False,
|
||||
datatype=FloatRange(0)),
|
||||
'ramp': PARAM(
|
||||
'The speed of changing the value',
|
||||
unit='main/min',
|
||||
readonly=False,
|
||||
datatype=FloatRange(0),
|
||||
poll=30),
|
||||
parameters = {
|
||||
'speed': Param('The speed of changing the value',
|
||||
unit='main/s', readonly=False, datatype=FloatRange(0),
|
||||
),
|
||||
'ramp': Param('The speed of changing the value',
|
||||
unit='main/min', readonly=False, datatype=FloatRange(0),
|
||||
poll=30,
|
||||
),
|
||||
}
|
||||
|
||||
CMDS = {
|
||||
'setposition' : CMD('Set the position to the given value.',
|
||||
commands = {
|
||||
'setposition' : Command('Set the position to the given value.',
|
||||
arguments=[FloatRange()],
|
||||
result=None
|
||||
),
|
||||
@ -612,28 +589,22 @@ class Actuator(AnalogOutput):
|
||||
|
||||
|
||||
class Motor(Actuator):
|
||||
"""
|
||||
This class implements a motor device (in a sense of a real motor
|
||||
"""This class implements a motor device (in a sense of a real motor
|
||||
(stepper motor, servo motor, ...)).
|
||||
|
||||
It has the ability to move a real object from one place to another place.
|
||||
"""
|
||||
|
||||
PARAMS = {
|
||||
'refpos': PARAM(
|
||||
'Reference position',
|
||||
datatype=FloatRange(),
|
||||
unit='main'),
|
||||
'accel': PARAM(
|
||||
'Acceleration',
|
||||
datatype=FloatRange(),
|
||||
readonly=False,
|
||||
unit='main/s^2'),
|
||||
'decel': PARAM(
|
||||
'Deceleration',
|
||||
datatype=FloatRange(),
|
||||
readonly=False,
|
||||
unit='main/s^2'),
|
||||
parameters = {
|
||||
'refpos': Param('Reference position',
|
||||
datatype=FloatRange(), unit='main',
|
||||
),
|
||||
'accel': Param('Acceleration',
|
||||
datatype=FloatRange(), readonly=False, unit='main/s^2',
|
||||
),
|
||||
'decel': Param('Deceleration',
|
||||
datatype=FloatRange(), readonly=False, unit='main/s^2',
|
||||
),
|
||||
}
|
||||
|
||||
def read_refpos(self, maxage=0):
|
||||
@ -657,35 +628,35 @@ class Motor(Actuator):
|
||||
|
||||
|
||||
class TemperatureController(Actuator):
|
||||
"""
|
||||
A temperature control loop device.
|
||||
"""A temperature control loop device.
|
||||
"""
|
||||
|
||||
PARAMS = {
|
||||
'p': PARAM('Proportional control PARAMeter', datatype=FloatRange(),
|
||||
parameters = {
|
||||
'p': Param('Proportional control Parameter', datatype=FloatRange(),
|
||||
readonly=False, group='pid',
|
||||
),
|
||||
'i': PARAM('Integral control PARAMeter', datatype=FloatRange(),
|
||||
'i': Param('Integral control Parameter', datatype=FloatRange(),
|
||||
readonly=False, group='pid',
|
||||
),
|
||||
'd': PARAM('Derivative control PARAMeter', datatype=FloatRange(),
|
||||
'd': Param('Derivative control Parameter', datatype=FloatRange(),
|
||||
readonly=False, group='pid',
|
||||
),
|
||||
'pid': PARAM('pid control PARAMeters', datatype=TupleOf(FloatRange(), FloatRange(), FloatRange()),
|
||||
'pid': Param('pid control Parameters',
|
||||
datatype=TupleOf(FloatRange(), FloatRange(), FloatRange()),
|
||||
readonly=False, group='pid', poll=30,
|
||||
),
|
||||
'setpoint': PARAM('Current setpoint', datatype=FloatRange(), poll=1,
|
||||
'setpoint': Param('Current setpoint', datatype=FloatRange(), poll=1,
|
||||
),
|
||||
'heateroutput': PARAM('Heater output', datatype=FloatRange(), poll=1,
|
||||
'heateroutput': Param('Heater output', datatype=FloatRange(), poll=1,
|
||||
),
|
||||
'ramp': PARAM('Temperature ramp', unit='main/min',
|
||||
'ramp': Param('Temperature ramp', unit='main/min',
|
||||
datatype=FloatRange(), readonly=False, poll=30),
|
||||
}
|
||||
|
||||
OVERRIDES = {
|
||||
overrides = {
|
||||
# We want this to be freely user-settable, and not produce a warning
|
||||
# on startup, so select a usually sensible default.
|
||||
'precision': OVERRIDE(default=0.1),
|
||||
'precision': Override(default=0.1),
|
||||
}
|
||||
|
||||
def read_ramp(self, maxage=0):
|
||||
@ -732,16 +703,15 @@ class TemperatureController(Actuator):
|
||||
|
||||
|
||||
class PowerSupply(Actuator):
|
||||
"""
|
||||
A power supply (voltage and current) device.
|
||||
"""A power supply (voltage and current) device.
|
||||
"""
|
||||
|
||||
PARAMS = {
|
||||
'ramp': PARAM('Current/voltage ramp', unit='main/min',
|
||||
parameters = {
|
||||
'ramp': Param('Current/voltage ramp', unit='main/min',
|
||||
datatype=FloatRange(), readonly=False, poll=30,),
|
||||
'voltage': PARAM('Actual voltage', unit='V',
|
||||
'voltage': Param('Actual voltage', unit='V',
|
||||
datatype=FloatRange(), poll=-5),
|
||||
'current': PARAM('Actual current', unit='A',
|
||||
'current': Param('Actual current', unit='A',
|
||||
datatype=FloatRange(), poll=-5),
|
||||
}
|
||||
|
||||
@ -759,12 +729,11 @@ class PowerSupply(Actuator):
|
||||
|
||||
|
||||
class DigitalInput(PyTangoDevice, Readable):
|
||||
"""
|
||||
A device reading a bitfield.
|
||||
"""A device reading a bitfield.
|
||||
"""
|
||||
|
||||
OVERRIDES = {
|
||||
'value': OVERRIDE(datatype=IntRange()),
|
||||
overrides = {
|
||||
'value': Override(datatype=IntRange()),
|
||||
}
|
||||
|
||||
def read_value(self, maxage=0):
|
||||
@ -772,19 +741,22 @@ class DigitalInput(PyTangoDevice, Readable):
|
||||
|
||||
|
||||
class NamedDigitalInput(DigitalInput):
|
||||
"""
|
||||
A DigitalInput with numeric values mapped to names.
|
||||
"""A DigitalInput with numeric values mapped to names.
|
||||
"""
|
||||
|
||||
PARAMS = {
|
||||
'mapping': PARAM('A dictionary mapping state names to integers',
|
||||
parameters = {
|
||||
'mapping': Param('A dictionary mapping state names to integers',
|
||||
datatype=StringType(), export=False), # XXX:!!!
|
||||
}
|
||||
|
||||
def init(self):
|
||||
super(NamedDigitalInput, self).init()
|
||||
try:
|
||||
self.PARAMS['value'].datatype = EnumType(**eval(self.mapping))
|
||||
mapping, rem = Parser().parse(self.mapping)
|
||||
if rem:
|
||||
raise ValueError('Illegal Value for mapping, '
|
||||
'trailing garbage: %r' % rem)
|
||||
self.parameters['value'].datatype = EnumType(**mapping)
|
||||
except Exception as e:
|
||||
raise ValueError('Illegal Value for mapping: %r' % e)
|
||||
|
||||
@ -794,26 +766,21 @@ class NamedDigitalInput(DigitalInput):
|
||||
|
||||
|
||||
class PartialDigitalInput(NamedDigitalInput):
|
||||
"""
|
||||
Base class for a TANGO DigitalInput with only a part of the full
|
||||
"""Base class for a TANGO DigitalInput with only a part of the full
|
||||
bit width accessed.
|
||||
"""
|
||||
|
||||
PARAMS = {
|
||||
'startbit': PARAM(
|
||||
'Number of the first bit',
|
||||
datatype=IntRange(0),
|
||||
default=0),
|
||||
'bitwidth': PARAM(
|
||||
'Number of bits',
|
||||
datatype=IntRange(0),
|
||||
default=1),
|
||||
parameters = {
|
||||
'startbit': Param('Number of the first bit',
|
||||
datatype=IntRange(0), default=0),
|
||||
'bitwidth': Param('Number of bits',
|
||||
datatype=IntRange(0), default=1),
|
||||
}
|
||||
|
||||
def init(self):
|
||||
super(PartialDigitalInput, self).init()
|
||||
self._mask = (1 << self.bitwidth) - 1
|
||||
#self.PARAMS['value'].datatype = IntRange(0, self._mask)
|
||||
#self.parameters['value'].datatype = IntRange(0, self._mask)
|
||||
|
||||
def read_value(self, maxage=0):
|
||||
raw_value = self._dev.value
|
||||
@ -822,14 +789,13 @@ class PartialDigitalInput(NamedDigitalInput):
|
||||
|
||||
|
||||
class DigitalOutput(PyTangoDevice, Drivable):
|
||||
"""
|
||||
A devices that can set and read a digital value corresponding to a
|
||||
"""A device that can set and read a digital value corresponding to a
|
||||
bitfield.
|
||||
"""
|
||||
|
||||
OVERRIDES = {
|
||||
'value': OVERRIDE(datatype=IntRange()),
|
||||
'target': OVERRIDE(datatype=IntRange()),
|
||||
overrides = {
|
||||
'value': Override(datatype=IntRange()),
|
||||
'target': Override(datatype=IntRange()),
|
||||
}
|
||||
|
||||
def read_value(self, maxage=0):
|
||||
@ -845,88 +811,74 @@ class DigitalOutput(PyTangoDevice, Drivable):
|
||||
|
||||
|
||||
class NamedDigitalOutput(DigitalOutput):
|
||||
"""
|
||||
A DigitalOutput with numeric values mapped to names.
|
||||
"""A DigitalOutput with numeric values mapped to names.
|
||||
"""
|
||||
|
||||
# PARAMS = {
|
||||
# 'mapping': PARAM('A dictionary mapping state names to integers',
|
||||
# parameters = {
|
||||
# 'mapping': Param('A dictionary mapping state names to integers',
|
||||
# datatype=EnumType(), export=False), # XXX: !!!
|
||||
# }
|
||||
#
|
||||
# def init(self):
|
||||
# super(NamedDigitalOutput, self).init()
|
||||
# try: # XXX: !!!
|
||||
# self.PARAMS['value'].datatype = EnumType(**eval(self.mapping))
|
||||
# self.parameters['value'].datatype = EnumType(**eval(self.mapping))
|
||||
# except Exception as e:
|
||||
# raise ValueError('Illegal Value for mapping: %r' % e)
|
||||
|
||||
def write_target(self, target):
|
||||
def write_target(self, value):
|
||||
# map from enum-str to integer value
|
||||
self._dev.value = self.PARAMS[
|
||||
'target'].datatype.reversed.get(target, target)
|
||||
self._dev.value = self.parameters[
|
||||
'target'].datatype.reversed.get(value, value)
|
||||
self.read_value()
|
||||
|
||||
|
||||
class PartialDigitalOutput(NamedDigitalOutput):
|
||||
"""
|
||||
Base class for a TANGO DigitalOutput with only a part of the full
|
||||
"""Base class for a TANGO DigitalOutput with only a part of the full
|
||||
bit width accessed.
|
||||
"""
|
||||
|
||||
PARAMS = {
|
||||
'startbit': PARAM(
|
||||
'Number of the first bit',
|
||||
datatype=IntRange(0),
|
||||
default=0),
|
||||
'bitwidth': PARAM(
|
||||
'Number of bits',
|
||||
datatype=IntRange(0),
|
||||
default=1),
|
||||
parameters = {
|
||||
'startbit': Param('Number of the first bit',
|
||||
datatype=IntRange(0), default=0),
|
||||
'bitwidth': Param('Number of bits',
|
||||
datatype=IntRange(0), default=1),
|
||||
}
|
||||
|
||||
def init(self, mode):
|
||||
def init(self):
|
||||
super(PartialDigitalOutput, self).init()
|
||||
self._mask = (1 << self.bitwidth) - 1
|
||||
#self.PARAMS['value'].datatype = IntRange(0, self._mask)
|
||||
#self.PARAMS['target'].datatype = IntRange(0, self._mask)
|
||||
#self.parameters['value'].datatype = IntRange(0, self._mask)
|
||||
#self.parameters['target'].datatype = IntRange(0, self._mask)
|
||||
|
||||
def read_value(self, maxage=0):
|
||||
raw_value = self._dev.value
|
||||
value = (raw_value >> self.startbit) & self._mask
|
||||
return value # mapping is done by datatype upon export()
|
||||
|
||||
def write_target(self, target):
|
||||
def write_target(self, value):
|
||||
curvalue = self._dev.value
|
||||
newvalue = (curvalue & ~(self._mask << self.startbit)) | \
|
||||
(target << self.startbit)
|
||||
(value << self.startbit)
|
||||
self._dev.value = newvalue
|
||||
self.read_value()
|
||||
|
||||
|
||||
class StringIO(PyTangoDevice, Module):
|
||||
"""
|
||||
StringIO abstracts communication over a hardware bus that sends and
|
||||
"""StringIO abstracts communication over a hardware bus that sends and
|
||||
receives strings.
|
||||
"""
|
||||
|
||||
PARAMS = {
|
||||
'bustimeout': PARAM(
|
||||
'Communication timeout',
|
||||
datatype=FloatRange(),
|
||||
readonly=False,
|
||||
unit='s',
|
||||
group='communication'),
|
||||
'endofline': PARAM(
|
||||
'End of line',
|
||||
datatype=StringType(),
|
||||
readonly=False,
|
||||
group='communication'),
|
||||
'startofline': PARAM(
|
||||
'Start of line',
|
||||
datatype=StringType(),
|
||||
readonly=False,
|
||||
group='communication'),
|
||||
parameters = {
|
||||
'bustimeout': Param('Communication timeout',
|
||||
datatype=FloatRange(), readonly=False,
|
||||
unit='s', group='communication'),
|
||||
'endofline': Param('End of line',
|
||||
datatype=StringType(), readonly=False,
|
||||
group='communication'),
|
||||
'startofline': Param('Start of line',
|
||||
datatype=StringType(), readonly=False,
|
||||
group='communication'),
|
||||
}
|
||||
|
||||
def read_bustimeout(self, maxage=0):
|
||||
@ -947,52 +899,28 @@ class StringIO(PyTangoDevice, Module):
|
||||
def write_startofline(self, value):
|
||||
self._dev.startOfLine = value
|
||||
|
||||
CMDS = {
|
||||
'communicate': CMD(
|
||||
'Send a string and return the reply',
|
||||
arguments=[
|
||||
StringType()],
|
||||
result=StringType()),
|
||||
'flush': CMD(
|
||||
'Flush output buffer',
|
||||
arguments=[],
|
||||
result=None),
|
||||
'read': CMD(
|
||||
'read some characters from input buffer',
|
||||
arguments=[
|
||||
IntRange()],
|
||||
result=StringType()),
|
||||
'write': CMD(
|
||||
'write some chars to output',
|
||||
arguments=[
|
||||
StringType()],
|
||||
result=None),
|
||||
'readLine': CMD(
|
||||
'Read sol - a whole line - eol',
|
||||
arguments=[],
|
||||
result=StringType()),
|
||||
'writeLine': CMD(
|
||||
'write sol + a whole line + eol',
|
||||
arguments=[
|
||||
StringType()],
|
||||
result=None),
|
||||
'availablechars': CMD(
|
||||
'return number of chars in input buffer',
|
||||
arguments=[],
|
||||
result=IntRange(0)),
|
||||
'availablelines': CMD(
|
||||
'return number of lines in input buffer',
|
||||
arguments=[],
|
||||
result=IntRange(0)),
|
||||
'multicommunicate': CMD(
|
||||
'perform a sequence of communications',
|
||||
arguments=[
|
||||
ArrayOf(
|
||||
TupleOf(
|
||||
StringType(),
|
||||
IntRange()),100)],
|
||||
result=ArrayOf(
|
||||
StringType(),100)),
|
||||
commands = {
|
||||
'communicate': Command('Send a string and return the reply',
|
||||
arguments=[StringType()],
|
||||
result=StringType()),
|
||||
'flush': Command('Flush output buffer',
|
||||
arguments=[], result=None),
|
||||
'read': Command('read some characters from input buffer',
|
||||
arguments=[IntRange()], result=StringType()),
|
||||
'write': Command('write some chars to output',
|
||||
arguments=[StringType()], result=None),
|
||||
'readLine': Command('Read sol - a whole line - eol',
|
||||
arguments=[], result=StringType()),
|
||||
'writeLine': Command('write sol + a whole line + eol',
|
||||
arguments=[StringType()], result=None),
|
||||
'availablechars': Command('return number of chars in input buffer',
|
||||
arguments=[], result=IntRange(0)),
|
||||
'availablelines': Command('return number of lines in input buffer',
|
||||
arguments=[], result=IntRange(0)),
|
||||
'multicommunicate': Command('perform a sequence of communications',
|
||||
arguments=[ArrayOf(
|
||||
TupleOf(StringType(), IntRange()),100)],
|
||||
result=ArrayOf(StringType(),100)),
|
||||
}
|
||||
|
||||
def do_communicate(self, value=StringType()):
|
||||
|
Loading…
x
Reference in New Issue
Block a user