unify name and module on Attached property
- setting the attribute using the name of an attached module - getting the attribute results in the module object + change names iodev to io, iodevClass to ioClass, sendRecv to communicate, HasIodev to HasIO Change-Id: I200b63a5a7dc1453bf6ac998782b065645201900 Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/27575 Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de> Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
@ -93,6 +93,7 @@ def main(argv=None):
|
||||
if args.relaxed:
|
||||
generalConfig.defaults['lazy_number_validation'] = True
|
||||
generalConfig.defaults['disable_value_range_check'] = True
|
||||
generalConfig.defaults['legacy_hasiodev'] = True
|
||||
generalConfig.init(args.gencfg)
|
||||
logger.init(loglevel)
|
||||
|
||||
|
@ -1,24 +1,23 @@
|
||||
[node LscSIM.psi.ch]
|
||||
[NODE]
|
||||
id = LscSIM.psi.ch
|
||||
description = Lsc Simulation at PSI
|
||||
|
||||
[interface tcp]
|
||||
type = tcp
|
||||
bindto = 0.0.0.0
|
||||
bindport = 5000
|
||||
[INTERFACE]
|
||||
uri = tcp://5000
|
||||
|
||||
[module res]
|
||||
[res]
|
||||
class = secop_psi.ls370res.ResChannel
|
||||
.channel = 3
|
||||
.description = resistivity
|
||||
.main = lsmain
|
||||
.iodev = lscom
|
||||
channel = 3
|
||||
description = resistivity
|
||||
main = lsmain
|
||||
io = lscom
|
||||
|
||||
[module lsmain]
|
||||
[lsmain]
|
||||
class = secop_psi.ls370res.Main
|
||||
.description = main control of Lsc controller
|
||||
.iodev = lscom
|
||||
description = main control of Lsc controller
|
||||
io = lscom
|
||||
|
||||
[module lscom]
|
||||
[lscom]
|
||||
class = secop_psi.ls370sim.Ls370Sim
|
||||
.description = simulated serial communicator to a LS 370
|
||||
.visibility = 3
|
||||
description = simulated serial communicator to a LS 370
|
||||
visibility = 3
|
||||
|
92
cfg/ppms.cfg
92
cfg/ppms.cfg
@ -8,117 +8,117 @@ uri = tcp://5000
|
||||
[tt]
|
||||
class = secop_psi.ppms.Temp
|
||||
description = main temperature
|
||||
iodev = ppms
|
||||
io = ppms
|
||||
|
||||
[mf]
|
||||
class = secop_psi.ppms.Field
|
||||
target.min = -9
|
||||
target.max = 9
|
||||
.description = magnetic field
|
||||
.iodev = ppms
|
||||
description = magnetic field
|
||||
io = ppms
|
||||
|
||||
[pos]
|
||||
class = secop_psi.ppms.Position
|
||||
.description = sample rotator
|
||||
.iodev = ppms
|
||||
description = sample rotator
|
||||
io = ppms
|
||||
|
||||
[lev]
|
||||
class = secop_psi.ppms.Level
|
||||
.description = helium level
|
||||
.iodev = ppms
|
||||
description = helium level
|
||||
io = ppms
|
||||
|
||||
[chamber]
|
||||
class = secop_psi.ppms.Chamber
|
||||
.description = chamber state
|
||||
.iodev = ppms
|
||||
description = chamber state
|
||||
io = ppms
|
||||
|
||||
[r1]
|
||||
class = secop_psi.ppms.BridgeChannel
|
||||
.description = resistivity channel 1
|
||||
.no = 1
|
||||
description = resistivity channel 1
|
||||
no = 1
|
||||
value.unit = Ohm
|
||||
.iodev = ppms
|
||||
io = ppms
|
||||
|
||||
[r2]
|
||||
class = secop_psi.ppms.BridgeChannel
|
||||
.description = resistivity channel 2
|
||||
.no = 2
|
||||
description = resistivity channel 2
|
||||
no = 2
|
||||
value.unit = Ohm
|
||||
.iodev = ppms
|
||||
io = ppms
|
||||
|
||||
[r3]
|
||||
class = secop_psi.ppms.BridgeChannel
|
||||
.description = resistivity channel 3
|
||||
.no = 3
|
||||
description = resistivity channel 3
|
||||
no = 3
|
||||
value.unit = Ohm
|
||||
.iodev = ppms
|
||||
io = ppms
|
||||
|
||||
[r4]
|
||||
class = secop_psi.ppms.BridgeChannel
|
||||
.description = resistivity channel 4
|
||||
.no = 4
|
||||
description = resistivity channel 4
|
||||
no = 4
|
||||
value.unit = Ohm
|
||||
.iodev = ppms
|
||||
io = ppms
|
||||
|
||||
[i1]
|
||||
class = secop_psi.ppms.Channel
|
||||
.description = current channel 1
|
||||
.no = 1
|
||||
description = current channel 1
|
||||
no = 1
|
||||
value.unit = uA
|
||||
.iodev = ppms
|
||||
io = ppms
|
||||
|
||||
[i2]
|
||||
class = secop_psi.ppms.Channel
|
||||
.description = current channel 2
|
||||
.no = 2
|
||||
description = current channel 2
|
||||
no = 2
|
||||
value.unit = uA
|
||||
.iodev = ppms
|
||||
io = ppms
|
||||
|
||||
[i3]
|
||||
class = secop_psi.ppms.Channel
|
||||
.description = current channel 3
|
||||
.no = 3
|
||||
description = current channel 3
|
||||
no = 3
|
||||
value.unit = uA
|
||||
.iodev = ppms
|
||||
io = ppms
|
||||
|
||||
[i4]
|
||||
class = secop_psi.ppms.Channel
|
||||
.description = current channel 4
|
||||
.no = 4
|
||||
description = current channel 4
|
||||
no = 4
|
||||
value.unit = uA
|
||||
.iodev = ppms
|
||||
io = ppms
|
||||
|
||||
[v1]
|
||||
class = secop_psi.ppms.DriverChannel
|
||||
.description = voltage channel 1
|
||||
.no = 1
|
||||
description = voltage channel 1
|
||||
no = 1
|
||||
value.unit = V
|
||||
.iodev = ppms
|
||||
io = ppms
|
||||
|
||||
[v2]
|
||||
class = secop_psi.ppms.DriverChannel
|
||||
.description = voltage channel 2
|
||||
.no = 2
|
||||
description = voltage channel 2
|
||||
no = 2
|
||||
value.unit = V
|
||||
.iodev = ppms
|
||||
io = ppms
|
||||
|
||||
[tv]
|
||||
class = secop_psi.ppms.UserChannel
|
||||
.description = VTI temperature
|
||||
description = VTI temperature
|
||||
enabled = 1
|
||||
value.unit = K
|
||||
.iodev = ppms
|
||||
io = ppms
|
||||
|
||||
[ts]
|
||||
class = secop_psi.ppms.UserChannel
|
||||
.description = sample temperature
|
||||
description = sample temperature
|
||||
enabled = 1
|
||||
value.unit = K
|
||||
.iodev = ppms
|
||||
io = ppms
|
||||
|
||||
[ppms]
|
||||
class = secop_psi.ppms.Main
|
||||
.description = the main and poller module
|
||||
.class_id = QD.MULTIVU.PPMS.1
|
||||
.visibility = 3
|
||||
description = the main and poller module
|
||||
class_id = QD.MULTIVU.PPMS.1
|
||||
visibility = 3
|
||||
pollinterval = 2
|
||||
|
@ -59,7 +59,7 @@ Communication
|
||||
:show-inheritance:
|
||||
:members: communicate, multicomm
|
||||
|
||||
.. autoclass:: secop.io.HasIodev
|
||||
.. autoclass:: secop.io.HasIO
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: secop.iohandler.IOHandlerBase
|
||||
|
@ -22,7 +22,7 @@ CCU4 luckily has a very simple and logical protocol:
|
||||
.. code:: python
|
||||
|
||||
# the most common Frappy classes can be imported from secop.core
|
||||
from secop.core import Readable, Parameter, FloatRange, BoolType, StringIO, HasIodev
|
||||
from secop.core import Readable, Parameter, FloatRange, BoolType, StringIO, HasIO
|
||||
|
||||
|
||||
class CCU4IO(StringIO):
|
||||
@ -34,14 +34,13 @@ CCU4 luckily has a very simple and logical protocol:
|
||||
identification = [('cid', r'CCU4.*')]
|
||||
|
||||
|
||||
# inheriting the HasIodev mixin creates us a private attribute *_iodev*
|
||||
# for talking with the hardware
|
||||
# inheriting HasIO allows us to use the communicate method for talking with the hardware
|
||||
# Readable as a base class defines the value and status parameters
|
||||
class HeLevel(HasIodev, Readable):
|
||||
class HeLevel(HasIO, Readable):
|
||||
"""He Level channel of CCU4"""
|
||||
|
||||
# define the communication class to create the IO module
|
||||
iodevClass = CCU4IO
|
||||
ioClass = CCU4IO
|
||||
|
||||
# define or alter the parameters
|
||||
# as Readable.value exists already, we give only the modified property 'unit'
|
||||
@ -49,7 +48,7 @@ CCU4 luckily has a very simple and logical protocol:
|
||||
|
||||
def read_value(self):
|
||||
# method for reading the main value
|
||||
reply = self._iodev.communicate('h') # send 'h\n' and get the reply 'h=<value>\n'
|
||||
reply = self.communicate('h') # send 'h\n' and get the reply 'h=<value>\n'
|
||||
name, txtvalue = reply.split('=')
|
||||
assert name == 'h' # check that we got a reply to our command
|
||||
return txtvalue # the framework will automatically convert the string to a float
|
||||
@ -115,17 +114,17 @@ the status codes from the hardware to the standard SECoP status codes.
|
||||
}
|
||||
|
||||
def read_status(self):
|
||||
name, txtvalue = self._iodev.communicate('hsf').split('=')
|
||||
name, txtvalue = self.communicate('hsf').split('=')
|
||||
assert name == 'hsf'
|
||||
return self.STATUS_MAP(int(txtvalue))
|
||||
|
||||
def read_empty_length(self):
|
||||
name, txtvalue = self._iodev.communicate('hem').split('=')
|
||||
name, txtvalue = self.communicate('hem').split('=')
|
||||
assert name == 'hem'
|
||||
return txtvalue
|
||||
|
||||
def write_empty_length(self, value):
|
||||
name, txtvalue = self._iodev.communicate('hem=%g' % value).split('=')
|
||||
name, txtvalue = self.communicate('hem=%g' % value).split('=')
|
||||
assert name == 'hem'
|
||||
return txtvalue
|
||||
|
||||
@ -152,7 +151,7 @@ which means it might be worth to create a *query* method, and then the
|
||||
for changing a parameter
|
||||
:returns: the (new) value of the parameter
|
||||
"""
|
||||
name, txtvalue = self._iodev.communicate(cmd).split('=')
|
||||
name, txtvalue = self.communicate(cmd).split('=')
|
||||
assert name == cmd.split('=')[0] # check that we got a reply to our command
|
||||
return txtvalue # Frappy will automatically convert the string to the needed data type
|
||||
|
||||
|
@ -36,5 +36,5 @@ from secop.params import Command, Parameter
|
||||
from secop.poller import AUTO, DYNAMIC, REGULAR, SLOW
|
||||
from secop.properties import Property
|
||||
from secop.proxy import Proxy, SecNode, proxy_class
|
||||
from secop.io import HasIodev, StringIO, BytesIO
|
||||
from secop.io import HasIO, StringIO, BytesIO, HasIodev # TODO: remove HasIodev (legacy stuff)
|
||||
from secop.persistent import PersistentMixin, PersistentParam
|
||||
|
63
secop/io.py
63
secop/io.py
@ -29,56 +29,79 @@ import time
|
||||
import threading
|
||||
|
||||
from secop.lib.asynconn import AsynConn, ConnectionClosed
|
||||
from secop.datatypes import ArrayOf, BLOBType, BoolType, FloatRange, IntRange, StringType, TupleOf, ValueType
|
||||
from secop.errors import CommunicationFailedError, CommunicationSilentError, ConfigError
|
||||
from secop.datatypes import ArrayOf, BLOBType, BoolType, FloatRange, IntRange, \
|
||||
StringType, TupleOf, ValueType
|
||||
from secop.errors import CommunicationFailedError, CommunicationSilentError, \
|
||||
ConfigError, ProgrammingError
|
||||
from secop.modules import Attached, Command, \
|
||||
Communicator, Done, Module, Parameter, Property
|
||||
from secop.poller import REGULAR
|
||||
from secop.lib import generalConfig
|
||||
|
||||
|
||||
generalConfig.defaults['legacy_hasiodev'] = False
|
||||
HEX_CODE = re.compile(r'[0-9a-fA-F][0-9a-fA-F]$')
|
||||
|
||||
|
||||
class HasIodev(Module):
|
||||
class HasIO(Module):
|
||||
"""Mixin for modules using a communicator"""
|
||||
iodev = Attached()
|
||||
io = Attached()
|
||||
uri = Property('uri for automatic creation of the attached communication module',
|
||||
StringType(), default='')
|
||||
|
||||
iodevDict = {}
|
||||
ioDict = {}
|
||||
ioClass = None
|
||||
|
||||
def __init__(self, name, logger, opts, srv):
|
||||
iodev = opts.get('iodev')
|
||||
io = opts.get('io')
|
||||
super().__init__(name, logger, opts, srv)
|
||||
if self.uri:
|
||||
opts = {'uri': self.uri, 'description': 'communication device for %s' % name,
|
||||
'export': False}
|
||||
ioname = self.iodevDict.get(self.uri)
|
||||
ioname = self.ioDict.get(self.uri)
|
||||
if not ioname:
|
||||
ioname = iodev or name + '_iodev'
|
||||
iodev = self.iodevClass(ioname, srv.log.getChild(ioname), opts, srv)
|
||||
iodev.callingModule = []
|
||||
srv.modules[ioname] = iodev
|
||||
self.iodevDict[self.uri] = ioname
|
||||
self.iodev = ioname
|
||||
elif not self.iodev:
|
||||
raise ConfigError("Module %s needs a value for either 'uri' or 'iodev'" % name)
|
||||
ioname = io or name + '_io'
|
||||
io = self.ioClass(ioname, srv.log.getChild(ioname), opts, srv) # pylint: disable=not-callable
|
||||
io.callingModule = []
|
||||
srv.modules[ioname] = io
|
||||
self.ioDict[self.uri] = ioname
|
||||
self.io = ioname
|
||||
elif not io:
|
||||
raise ConfigError("Module %s needs a value for either 'uri' or 'io'" % name)
|
||||
|
||||
def initModule(self):
|
||||
try:
|
||||
self._iodev.read_is_connected()
|
||||
self.io.read_is_connected()
|
||||
except (CommunicationFailedError, AttributeError):
|
||||
# AttributeError: for missing _iodev?
|
||||
# AttributeError: read_is_connected is not required for an io object
|
||||
pass
|
||||
super().initModule()
|
||||
|
||||
def communicate(self, *args):
|
||||
return self._iodev.communicate(*args)
|
||||
return self.io.communicate(*args)
|
||||
|
||||
def multicomm(self, *args):
|
||||
return self._iodev.multicomm(*args)
|
||||
return self.io.multicomm(*args)
|
||||
|
||||
sendRecv = communicate # TODO: remove legacy stuff
|
||||
|
||||
class HasIodev(HasIO):
|
||||
# TODO: remove this legacy mixin
|
||||
iodevClass = None
|
||||
|
||||
@property
|
||||
def _iodev(self):
|
||||
return self.io
|
||||
|
||||
def __init__(self, name, logger, opts, srv):
|
||||
self.ioClass = self.iodevClass
|
||||
super().__init__(name, logger, opts, srv)
|
||||
if generalConfig.legacy_hasiodev:
|
||||
self.log.warn('using the HasIodev mixin is deprecated - use HasIO instead')
|
||||
else:
|
||||
self.log.error('legacy HasIodev no longer supported')
|
||||
self.log.error('you may suppress this error message by running the server with --relaxed')
|
||||
raise ProgrammingError('legacy HasIodev no longer supported')
|
||||
self.sendRecv = self.communicate
|
||||
|
||||
|
||||
class IOBase(Communicator):
|
||||
|
@ -126,7 +126,7 @@ class CmdParser:
|
||||
try:
|
||||
argformat % ((0,) * len(casts)) # validate argformat
|
||||
except ValueError as e:
|
||||
raise ValueError("%s in %r" % (e, argformat))
|
||||
raise ValueError("%s in %r" % (e, argformat)) from None
|
||||
|
||||
def format(self, *values):
|
||||
return self.fmt % values
|
||||
@ -242,7 +242,7 @@ class IOHandler(IOHandlerBase):
|
||||
contain the command separator at the end.
|
||||
"""
|
||||
querycmd = self.make_query(module)
|
||||
reply = module.sendRecv(changecmd + querycmd)
|
||||
reply = module.communicate(changecmd + querycmd)
|
||||
return self.parse_reply(reply)
|
||||
|
||||
def send_change(self, module, *values):
|
||||
@ -253,7 +253,7 @@ class IOHandler(IOHandlerBase):
|
||||
"""
|
||||
changecmd = self.make_change(module, *values)
|
||||
if self.CMDSEPARATOR is None:
|
||||
module.sendRecv(changecmd) # ignore result
|
||||
module.communicate(changecmd) # ignore result
|
||||
return self.send_command(module)
|
||||
return self.send_command(module, changecmd + self.CMDSEPARATOR)
|
||||
|
||||
|
@ -771,19 +771,19 @@ class Communicator(HasComlog, Module):
|
||||
|
||||
|
||||
class Attached(Property):
|
||||
"""a special property, defining an attached modle
|
||||
"""a special property, defining an attached module
|
||||
|
||||
assign a module name to this property in the cfg file,
|
||||
and the server will create an attribute with this module
|
||||
|
||||
:param attrname: the name of the to be created attribute. if not given
|
||||
the attribute name is the property name prepended by an underscore.
|
||||
"""
|
||||
# we can not put this to properties.py, as it needs datatypes
|
||||
def __init__(self, attrname=None):
|
||||
self.attrname = attrname
|
||||
# we can not make it mandatory, as the check in Module.__init__ will be before auto-assign in HasIodev
|
||||
super().__init__('attached module', StringType(), mandatory=False)
|
||||
module = None
|
||||
|
||||
def __repr__(self):
|
||||
return 'Attached(%s)' % (repr(self.attrname) if self.attrname else '')
|
||||
def __init__(self, description='attached module'):
|
||||
super().__init__(description, StringType(), mandatory=False)
|
||||
|
||||
def __get__(self, obj, owner):
|
||||
if obj is None:
|
||||
return self
|
||||
if self.module is None:
|
||||
self.module = obj.DISPATCHER.get_module(super().__get__(obj, owner))
|
||||
return self.module
|
||||
|
@ -30,7 +30,7 @@ Usage examples:
|
||||
pollerClass = poller.Poller
|
||||
...
|
||||
|
||||
modules having a parameter 'iodev' with the same value will share the same poller
|
||||
modules having a parameter 'io' with the same value will share the same poller
|
||||
"""
|
||||
|
||||
import time
|
||||
@ -58,11 +58,11 @@ class PollerBase:
|
||||
|
||||
table is a dict, with (<pollerClass>, <name>) as the key, and the
|
||||
poller as value.
|
||||
<name> is module.iodev or module.name, if iodev is not present
|
||||
<name> is module.io.name or module.name, if io is not present
|
||||
"""
|
||||
# for modules with the same iodev, a common poller is used,
|
||||
# modules without iodev all get their own poller
|
||||
name = getattr(module, 'iodev', module.name)
|
||||
# for modules with the same io, a common poller is used,
|
||||
# modules without io all get their own poller
|
||||
name = getattr(module, 'io', module).name
|
||||
poller = table.get((cls, name), None)
|
||||
if poller is None:
|
||||
poller = cls(name)
|
||||
|
@ -29,17 +29,17 @@ from secop.lib import get_class
|
||||
from secop.modules import Drivable, Module, Readable, Writable
|
||||
from secop.params import Command, Parameter
|
||||
from secop.properties import Property
|
||||
from secop.io import HasIodev
|
||||
from secop.io import HasIO
|
||||
|
||||
|
||||
class ProxyModule(HasIodev, Module):
|
||||
class ProxyModule(HasIO, Module):
|
||||
module = Property('remote module name', datatype=StringType(), default='')
|
||||
|
||||
pollerClass = None
|
||||
_consistency_check_done = False
|
||||
_secnode = None
|
||||
|
||||
def iodevClass(self, name, logger, opts, srv):
|
||||
def ioClass(self, name, logger, opts, srv):
|
||||
opts['description'] = 'secnode %s on %s' % (opts.get('module', name), opts['uri'])
|
||||
return SecNode(name, logger, opts, srv)
|
||||
|
||||
@ -54,7 +54,7 @@ class ProxyModule(HasIodev, Module):
|
||||
def initModule(self):
|
||||
if not self.module:
|
||||
self.module = self.name
|
||||
self._secnode = self._iodev.secnode
|
||||
self._secnode = self.io.secnode
|
||||
self._secnode.register_callback(self.module, self.updateEvent,
|
||||
self.descriptiveDataChange, self.nodeStateChange)
|
||||
super().initModule()
|
||||
@ -227,5 +227,5 @@ def Proxy(name, logger, cfgdict, srv):
|
||||
remote_class = cfgdict.pop('remote_class')
|
||||
if 'description' not in cfgdict:
|
||||
cfgdict['description'] = 'remote module %s on %s' % (
|
||||
cfgdict.get('module', name), cfgdict.get('iodev', '?'))
|
||||
cfgdict.get('module', name), cfgdict.get('io', '?'))
|
||||
return proxy_class(remote_class)(name, logger, cfgdict, srv)
|
||||
|
@ -30,10 +30,9 @@ import sys
|
||||
import traceback
|
||||
from collections import OrderedDict
|
||||
|
||||
from secop.errors import ConfigError, SECoPError
|
||||
from secop.errors import ConfigError
|
||||
from secop.lib import formatException, get_class, generalConfig
|
||||
from secop.lib.multievent import MultiEvent
|
||||
from secop.modules import Attached
|
||||
from secop.params import PREDEFINED_ACCESSIBLES
|
||||
|
||||
try:
|
||||
@ -271,24 +270,17 @@ class Server:
|
||||
for modname, modobj in self.modules.items():
|
||||
self.log.info('registering module %r' % modname)
|
||||
self.dispatcher.register_module(modobj, modname, modobj.export)
|
||||
if modobj.pollerClass is not None:
|
||||
# a module might be explicitly excluded from polling by setting pollerClass to None
|
||||
modobj.pollerClass.add_to_table(poll_table, modobj)
|
||||
# also call earlyInit on the modules
|
||||
modobj.earlyInit()
|
||||
if not modobj.earlyInitDone:
|
||||
missing_super.add('%s was not called, probably missing super call'
|
||||
% modobj.earlyInit.__qualname__)
|
||||
|
||||
# handle attached modules
|
||||
# handle polling
|
||||
for modname, modobj in self.modules.items():
|
||||
for propname, propobj in modobj.propertyDict.items():
|
||||
if isinstance(propobj, Attached):
|
||||
try:
|
||||
setattr(modobj, propobj.attrname or '_' + propname,
|
||||
self.dispatcher.get_module(getattr(modobj, propname)))
|
||||
except SECoPError as e:
|
||||
errors.append('module %s, attached %s: %s' % (modname, propname, str(e)))
|
||||
if modobj.pollerClass is not None:
|
||||
# a module might be explicitly excluded from polling by setting pollerClass to None
|
||||
modobj.pollerClass.add_to_table(poll_table, modobj)
|
||||
|
||||
# call init on each module after registering all
|
||||
for modname, modobj in self.modules.items():
|
||||
|
@ -20,7 +20,7 @@
|
||||
# *****************************************************************************
|
||||
"""Andeen Hagerling capacitance bridge"""
|
||||
|
||||
from secop.core import Done, FloatRange, HasIodev, Parameter, Readable, StringIO
|
||||
from secop.core import Done, FloatRange, HasIO, Parameter, Readable, StringIO
|
||||
|
||||
|
||||
class Ah2700IO(StringIO):
|
||||
@ -28,19 +28,19 @@ class Ah2700IO(StringIO):
|
||||
timeout = 5
|
||||
|
||||
|
||||
class Capacitance(HasIodev, Readable):
|
||||
class Capacitance(HasIO, Readable):
|
||||
|
||||
value = Parameter('capacitance', FloatRange(unit='pF'), poll=True)
|
||||
freq = Parameter('frequency', FloatRange(unit='Hz'), readonly=False, default=0)
|
||||
voltage = Parameter('voltage', FloatRange(unit='V'), readonly=False, default=0)
|
||||
loss = Parameter('loss', FloatRange(unit='deg'), default=0)
|
||||
|
||||
iodevClass = Ah2700IO
|
||||
ioClass = Ah2700IO
|
||||
|
||||
def parse_reply(self, reply):
|
||||
if reply.startswith('SI'): # this is an echo
|
||||
self.sendRecv('SERIAL ECHO OFF')
|
||||
reply = self.sendRecv('SI')
|
||||
self.communicate('SERIAL ECHO OFF')
|
||||
reply = self.communicate('SI')
|
||||
if not reply.startswith('F='): # this is probably an error message like "LOSS TOO HIGH"
|
||||
self.status = [self.Status.ERROR, reply]
|
||||
return
|
||||
@ -59,14 +59,14 @@ class Capacitance(HasIodev, Readable):
|
||||
if lossunit == 'DS':
|
||||
self.loss = loss
|
||||
else: # the unit was wrong, we want DS = tan(delta), not NS = nanoSiemens
|
||||
reply = self.sendRecv('UN DS').split() # UN DS returns a reply similar to SI
|
||||
reply = self.communicate('UN DS').split() # UN DS returns a reply similar to SI
|
||||
try:
|
||||
self.loss = reply[7]
|
||||
except IndexError:
|
||||
pass # don't worry, loss will be updated next time
|
||||
|
||||
def read_value(self):
|
||||
self.parse_reply(self.sendRecv('SI')) # SI = single trigger
|
||||
self.parse_reply(self.communicate('SI')) # SI = single trigger
|
||||
return Done
|
||||
|
||||
def read_freq(self):
|
||||
@ -77,14 +77,14 @@ class Capacitance(HasIodev, Readable):
|
||||
self.read_value()
|
||||
return Done
|
||||
|
||||
def read_volt(self):
|
||||
def read_voltage(self):
|
||||
self.read_value()
|
||||
return Done
|
||||
|
||||
def write_freq(self, value):
|
||||
self.parse_reply(self.sendRecv('FR %g;SI' % value))
|
||||
self.parse_reply(self.communicate('FR %g;SI' % value))
|
||||
return Done
|
||||
|
||||
def write_volt(self, value):
|
||||
self.parse_reply(self.sendRecv('V %g;SI' % value))
|
||||
def write_voltage(self, value):
|
||||
self.parse_reply(self.communicate('V %g;SI' % value))
|
||||
return Done
|
||||
|
@ -23,7 +23,7 @@
|
||||
"""drivers for CCU4, the cryostat control unit at SINQ"""
|
||||
# the most common Frappy classes can be imported from secop.core
|
||||
from secop.core import EnumType, FloatRange, \
|
||||
HasIodev, Parameter, Readable, StringIO
|
||||
HasIO, Parameter, Readable, StringIO
|
||||
|
||||
|
||||
class CCU4IO(StringIO):
|
||||
@ -34,14 +34,13 @@ class CCU4IO(StringIO):
|
||||
identification = [('cid', r'CCU4.*')]
|
||||
|
||||
|
||||
# inheriting the HasIodev mixin creates us a private attribute *_iodev*
|
||||
# for talking with the hardware
|
||||
# inheriting HasIO allows us to use the communicate method for talking with the hardware
|
||||
# Readable as a base class defines the value and status parameters
|
||||
class HeLevel(HasIodev, Readable):
|
||||
class HeLevel(HasIO, Readable):
|
||||
"""He Level channel of CCU4"""
|
||||
|
||||
# define the communication class to create the IO module
|
||||
iodevClass = CCU4IO
|
||||
ioClass = CCU4IO
|
||||
|
||||
# define or alter the parameters
|
||||
# as Readable.value exists already, we give only the modified property 'unit'
|
||||
@ -71,7 +70,7 @@ class HeLevel(HasIodev, Readable):
|
||||
for changing a parameter
|
||||
:returns: the (new) value of the parameter
|
||||
"""
|
||||
name, txtvalue = self._iodev.communicate(cmd).split('=')
|
||||
name, txtvalue = self.communicate(cmd).split('=')
|
||||
assert name == cmd.split('=')[0] # check that we got a reply to our command
|
||||
return txtvalue # Frappy will automatically convert the string to the needed data type
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
not tested yet"""
|
||||
|
||||
from secop.core import Attached, BoolType, EnumType, FloatRange, \
|
||||
HasIodev, Module, Parameter, StringIO, Writable
|
||||
HasIO, Module, Parameter, StringIO, Writable
|
||||
|
||||
|
||||
class K2601bIO(StringIO):
|
||||
@ -41,7 +41,7 @@ SOURCECMDS = {
|
||||
}
|
||||
|
||||
|
||||
class SourceMeter(HasIodev, Module):
|
||||
class SourceMeter(HasIO, Module):
|
||||
|
||||
resistivity = Parameter('readback resistivity', FloatRange(unit='Ohm'), poll=True)
|
||||
power = Parameter('readback power', FloatRange(unit='W'), poll=True)
|
||||
@ -49,19 +49,19 @@ class SourceMeter(HasIodev, Module):
|
||||
readonly=False, default=0)
|
||||
active = Parameter('output enable', BoolType(), readonly=False, poll=True)
|
||||
|
||||
iodevClass = K2601bIO
|
||||
ioClass = K2601bIO
|
||||
|
||||
def read_resistivity(self):
|
||||
return self.sendRecv('print(smua.measure.r())')
|
||||
return self.communicate('print(smua.measure.r())')
|
||||
|
||||
def read_power(self):
|
||||
return self.sendRecv('print(smua.measure.p())')
|
||||
return self.communicate('print(smua.measure.p())')
|
||||
|
||||
def read_active(self):
|
||||
return self.sendRecv('print(smua.source.output)')
|
||||
return self.communicate('print(smua.source.output)')
|
||||
|
||||
def write_active(self, value):
|
||||
return self.sendRecv('smua.source.output = %d print(smua.source.output)' % value)
|
||||
return self.communicate('smua.source.output = %d print(smua.source.output)' % value)
|
||||
|
||||
# for now, mode will not be read from hardware
|
||||
|
||||
@ -69,11 +69,11 @@ class SourceMeter(HasIodev, Module):
|
||||
if value == 0:
|
||||
self.write_active(0)
|
||||
else:
|
||||
self.sendRecv(SOURCECMDS[value] + ' print(0)')
|
||||
self.communicate(SOURCECMDS[value] + ' print(0)')
|
||||
return value
|
||||
|
||||
|
||||
class Current(HasIodev, Writable):
|
||||
class Current(HasIO, Writable):
|
||||
sourcemeter = Attached()
|
||||
|
||||
value = Parameter('measured current', FloatRange(unit='A'), poll=True)
|
||||
@ -82,41 +82,41 @@ class Current(HasIodev, Writable):
|
||||
limit = Parameter('current limit', FloatRange(0, 2.0, unit='A'), default=2, poll=True)
|
||||
|
||||
def read_value(self):
|
||||
return self.sendRecv('print(smua.measure.i())')
|
||||
return self.communicate('print(smua.measure.i())')
|
||||
|
||||
def read_target(self):
|
||||
return self.sendRecv('print(smua.source.leveli)')
|
||||
return self.communicate('print(smua.source.leveli)')
|
||||
|
||||
def write_target(self, value):
|
||||
if not self.active:
|
||||
raise ValueError('current source is disabled')
|
||||
if value > self.limit:
|
||||
raise ValueError('current exceeds limit')
|
||||
return self.sendRecv('smua.source.leveli = %g print(smua.source.leveli)' % value)
|
||||
return self.communicate('smua.source.leveli = %g print(smua.source.leveli)' % value)
|
||||
|
||||
def read_limit(self):
|
||||
if self.active:
|
||||
return self.limit
|
||||
return self.sendRecv('print(smua.source.limiti)')
|
||||
return self.communicate('print(smua.source.limiti)')
|
||||
|
||||
def write_limit(self, value):
|
||||
if self.active:
|
||||
return value
|
||||
return self.sendRecv('smua.source.limiti = %g print(smua.source.limiti)' % value)
|
||||
return self.communicate('smua.source.limiti = %g print(smua.source.limiti)' % value)
|
||||
|
||||
def read_active(self):
|
||||
return self._sourcemeter.mode == 1 and self._sourcemeter.read_active()
|
||||
return self.sourcemeter.mode == 1 and self.sourcemeter.read_active()
|
||||
|
||||
def write_active(self, value):
|
||||
if self._sourcemeter.mode != 1:
|
||||
if self.sourcemeter.mode != 1:
|
||||
if value:
|
||||
self._sourcemeter.write_mode(1) # switch to current
|
||||
self.sourcemeter.write_mode(1) # switch to current
|
||||
else:
|
||||
return 0
|
||||
return self._sourcemeter.write_active(value)
|
||||
return self.sourcemeter.write_active(value)
|
||||
|
||||
|
||||
class Voltage(HasIodev, Writable):
|
||||
class Voltage(HasIO, Writable):
|
||||
sourcemeter = Attached()
|
||||
|
||||
value = Parameter('measured voltage', FloatRange(unit='V'), poll=True)
|
||||
@ -125,35 +125,35 @@ class Voltage(HasIodev, Writable):
|
||||
limit = Parameter('current limit', FloatRange(0, 2.0, unit='V'), default=2, poll=True)
|
||||
|
||||
def read_value(self):
|
||||
return self.sendRecv('print(smua.measure.v())')
|
||||
return self.communicate('print(smua.measure.v())')
|
||||
|
||||
def read_target(self):
|
||||
return self.sendRecv('print(smua.source.levelv)')
|
||||
return self.communicate('print(smua.source.levelv)')
|
||||
|
||||
def write_target(self, value):
|
||||
if not self.active:
|
||||
raise ValueError('voltage source is disabled')
|
||||
if value > self.limit:
|
||||
raise ValueError('voltage exceeds limit')
|
||||
return self.sendRecv('smua.source.levelv = %g print(smua.source.levelv)' % value)
|
||||
return self.communicate('smua.source.levelv = %g print(smua.source.levelv)' % value)
|
||||
|
||||
def read_limit(self):
|
||||
if self.active:
|
||||
return self.limit
|
||||
return self.sendRecv('print(smua.source.limitv)')
|
||||
return self.communicate('print(smua.source.limitv)')
|
||||
|
||||
def write_limit(self, value):
|
||||
if self.active:
|
||||
return value
|
||||
return self.sendRecv('smua.source.limitv = %g print(smua.source.limitv)' % value)
|
||||
return self.communicate('smua.source.limitv = %g print(smua.source.limitv)' % value)
|
||||
|
||||
def read_active(self):
|
||||
return self._sourcemeter.mode == 2 and self._sourcemeter.read_active()
|
||||
return self.sourcemeter.mode == 2 and self.sourcemeter.read_active()
|
||||
|
||||
def write_active(self, value):
|
||||
if self._sourcemeter.mode != 2:
|
||||
if self.sourcemeter.mode != 2:
|
||||
if value:
|
||||
self._sourcemeter.write_mode(2) # switch to voltage
|
||||
self.sourcemeter.write_mode(2) # switch to voltage
|
||||
else:
|
||||
return 0
|
||||
return self._sourcemeter.write_active(value)
|
||||
return self.sourcemeter.write_active(value)
|
||||
|
@ -28,7 +28,7 @@ from secop.lib import formatStatusBits
|
||||
from secop.modules import Attached, Done, \
|
||||
Drivable, Parameter, Property, Readable
|
||||
from secop.poller import REGULAR, Poller
|
||||
from secop.io import HasIodev
|
||||
from secop.io import HasIO
|
||||
|
||||
Status = Drivable.Status
|
||||
|
||||
@ -58,7 +58,7 @@ class StringIO(secop.io.StringIO):
|
||||
wait_before = 0.05
|
||||
|
||||
|
||||
class Main(HasIodev, Drivable):
|
||||
class Main(HasIO, Drivable):
|
||||
|
||||
value = Parameter('the current channel', poll=REGULAR, datatype=IntRange(0, 17))
|
||||
target = Parameter('channel to select', datatype=IntRange(0, 17))
|
||||
@ -66,7 +66,7 @@ class Main(HasIodev, Drivable):
|
||||
pollinterval = Parameter(default=1, export=False)
|
||||
|
||||
pollerClass = Poller
|
||||
iodevClass = StringIO
|
||||
ioClass = StringIO
|
||||
_channel_changed = 0 # time of last channel change
|
||||
_channels = None # dict <channel no> of <module object>
|
||||
|
||||
@ -81,7 +81,7 @@ class Main(HasIodev, Drivable):
|
||||
super().startModule(start_events)
|
||||
for ch in range(1, 16):
|
||||
if ch not in self._channels:
|
||||
self.sendRecv('INSET %d,0,0,0,0,0;INSET?%d' % (ch, ch))
|
||||
self.communicate('INSET %d,0,0,0,0,0;INSET?%d' % (ch, ch))
|
||||
|
||||
def read_value(self):
|
||||
channel, auto = scan.send_command(self)
|
||||
@ -114,7 +114,7 @@ class Main(HasIodev, Drivable):
|
||||
|
||||
def write_target(self, channel):
|
||||
scan.send_change(self, channel, self.autoscan)
|
||||
# self.sendRecv('SCAN %d,%d;SCAN?' % (channel, self.autoscan))
|
||||
# self.communicate('SCAN %d,%d;SCAN?' % (channel, self.autoscan))
|
||||
if channel != self.value:
|
||||
self.value = 0
|
||||
self._channel_changed = time.time()
|
||||
@ -123,11 +123,11 @@ class Main(HasIodev, Drivable):
|
||||
|
||||
def write_autoscan(self, value):
|
||||
scan.send_change(self, self.value, value)
|
||||
# self.sendRecv('SCAN %d,%d;SCAN?' % (channel, self.autoscan))
|
||||
# self.communicate('SCAN %d,%d;SCAN?' % (channel, self.autoscan))
|
||||
return value
|
||||
|
||||
|
||||
class ResChannel(HasIodev, Readable):
|
||||
class ResChannel(HasIO, Readable):
|
||||
"""temperature channel on Lakeshore 336"""
|
||||
|
||||
RES_RANGE = {key: i+1 for i, key in list(
|
||||
@ -142,7 +142,7 @@ class ResChannel(HasIodev, Readable):
|
||||
for val in [2, 6.32, 20, 63.2, 200, 632]))}
|
||||
|
||||
pollerClass = Poller
|
||||
iodevClass = StringIO
|
||||
ioClass = StringIO
|
||||
_main = None # main module
|
||||
_last_range_change = 0 # time of last range change
|
||||
|
||||
@ -183,7 +183,7 @@ class ResChannel(HasIodev, Readable):
|
||||
return Done
|
||||
# we got here, when we missed the idle state of self._main
|
||||
self._trigger_read = False
|
||||
result = self.sendRecv('RDGR?%d' % self.channel)
|
||||
result = self.communicate('RDGR?%d' % self.channel)
|
||||
result = float(result)
|
||||
if self.autorange == 'soft':
|
||||
now = time.time()
|
||||
@ -216,9 +216,9 @@ class ResChannel(HasIodev, Readable):
|
||||
def read_status(self):
|
||||
if not self.enabled:
|
||||
return [self.Status.DISABLED, 'disabled']
|
||||
if self.channel != self._main.value:
|
||||
if self.channel != self.main.value:
|
||||
return Done
|
||||
result = int(self.sendRecv('RDGST?%d' % self.channel))
|
||||
result = int(self.communicate('RDGST?%d' % self.channel))
|
||||
result &= 0x37 # mask T_OVER and T_UNDER (change this when implementing temperatures instead of resistivities)
|
||||
statustext = ' '.join(formatStatusBits(result, STATUS_BIT_LABELS))
|
||||
if statustext:
|
||||
@ -283,5 +283,5 @@ class ResChannel(HasIodev, Readable):
|
||||
def write_enabled(self, value):
|
||||
inset.write(self, 'enabled', value)
|
||||
if value:
|
||||
self._main.write_target(self.channel)
|
||||
self.main.write_target(self.channel)
|
||||
return Done
|
||||
|
@ -40,10 +40,10 @@ from secop.datatypes import BoolType, EnumType, \
|
||||
from secop.errors import HardwareError
|
||||
from secop.lib import clamp
|
||||
from secop.lib.enum import Enum
|
||||
from secop.modules import Attached, Communicator, Done, \
|
||||
from secop.modules import Communicator, Done, \
|
||||
Drivable, Parameter, Property, Readable
|
||||
from secop.poller import Poller
|
||||
from secop.io import HasIodev
|
||||
from secop.io import HasIO
|
||||
|
||||
try:
|
||||
import secop_psi.ppmswindows as ppmshw
|
||||
@ -130,10 +130,8 @@ class Main(Communicator):
|
||||
return data # return data as string
|
||||
|
||||
|
||||
class PpmsBase(HasIodev, Readable):
|
||||
class PpmsBase(HasIO, Readable):
|
||||
"""common base for all ppms modules"""
|
||||
iodev = Attached()
|
||||
|
||||
# polling is done by the main module
|
||||
# and PPMS does not deliver really more fresh values when polled more often
|
||||
value = Parameter(poll=False, needscfg=False)
|
||||
@ -150,7 +148,7 @@ class PpmsBase(HasIodev, Readable):
|
||||
|
||||
def initModule(self):
|
||||
super().initModule()
|
||||
self._iodev.register(self)
|
||||
self.io.register(self)
|
||||
|
||||
def update_value_status(self, value, packed_status):
|
||||
# update value and status
|
||||
@ -197,7 +195,7 @@ class UserChannel(Channel):
|
||||
datatype=StringType(), export=False, default='')
|
||||
|
||||
def write_enabled(self, enabled):
|
||||
other = self._iodev.modules.get(self.linkenable, None)
|
||||
other = self.io.modules.get(self.linkenable, None)
|
||||
if other:
|
||||
other.enabled = enabled
|
||||
return enabled
|
||||
|
@ -26,7 +26,7 @@ import time
|
||||
import struct
|
||||
|
||||
from secop.core import BoolType, Command, EnumType, FloatRange, IntRange, \
|
||||
HasIodev, Parameter, Property, Drivable, PersistentMixin, PersistentParam, Done
|
||||
HasIO, Parameter, Property, Drivable, PersistentMixin, PersistentParam, Done
|
||||
from secop.io import BytesIO
|
||||
from secop.errors import CommunicationFailedError, HardwareError, BadValueError, IsBusyError
|
||||
from secop.rwhandler import ReadHandler, WriteHandler
|
||||
@ -77,7 +77,7 @@ def writable(*args, **kwds):
|
||||
return PersistentParam(*args, readonly=False, poll=True, initwrite=True, **kwds)
|
||||
|
||||
|
||||
class Motor(PersistentMixin, HasIodev, Drivable):
|
||||
class Motor(PersistentMixin, HasIO, Drivable):
|
||||
address = Property('module address', IntRange(0, 255), default=1)
|
||||
|
||||
value = Parameter('motor position', FloatRange(unit='deg', fmtstr='%.3f'))
|
||||
@ -116,7 +116,7 @@ class Motor(PersistentMixin, HasIodev, Drivable):
|
||||
readonly=False, default=0, poll=True, visibility=3, group='more')
|
||||
pollinterval = Parameter(group='more')
|
||||
|
||||
iodevClass = BytesIO
|
||||
ioClass = BytesIO
|
||||
fast_pollfactor = 0.001 # poll as fast as possible when busy
|
||||
_started = 0
|
||||
_calcTimeout = True
|
||||
@ -131,20 +131,20 @@ class Motor(PersistentMixin, HasIodev, Drivable):
|
||||
:param value: if given, the parameter is written, else it is returned
|
||||
:return: the returned value
|
||||
"""
|
||||
if self._calcTimeout and self._iodev._conn:
|
||||
if self._calcTimeout and self.io._conn:
|
||||
self._calcTimeout = False
|
||||
baudrate = getattr(self._iodev._conn.connection, 'baudrate', None)
|
||||
baudrate = getattr(self.io._conn.connection, 'baudrate', None)
|
||||
if baudrate:
|
||||
if baudrate not in BAUDRATES:
|
||||
raise CommunicationFailedError('unsupported baud rate: %d' % baudrate)
|
||||
self._iodev.timeout = 0.03 + 200 / baudrate
|
||||
self.io.timeout = 0.03 + 200 / baudrate
|
||||
|
||||
exc = None
|
||||
byt = struct.pack('>BBBBi', self.address, cmd, adr, bank, round(value))
|
||||
byt += bytes([sum(byt) & 0xff])
|
||||
for itry in range(3,0,-1):
|
||||
try:
|
||||
reply = self._iodev.communicate(byt, 9)
|
||||
reply = self.communicate(byt, 9)
|
||||
if sum(reply[:-1]) & 0xff != reply[-1]:
|
||||
raise CommunicationFailedError('checksum error')
|
||||
# will try again
|
||||
|
78
test/test_attach.py
Normal file
78
test/test_attach.py
Normal file
@ -0,0 +1,78 @@
|
||||
# -*- 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>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from secop.modules import Module, Attached
|
||||
from secop.protocol.dispatcher import Dispatcher
|
||||
|
||||
|
||||
# class DispatcherStub:
|
||||
# # omit_unchanged_within = 0
|
||||
#
|
||||
# # def __init__(self, updates):
|
||||
# # self.updates = updates
|
||||
# #
|
||||
# # def announce_update(self, modulename, pname, pobj):
|
||||
# # self.updates.setdefault(modulename, {})
|
||||
# # if pobj.readerror:
|
||||
# # self.updates[modulename]['error', pname] = str(pobj.readerror)
|
||||
# # else:
|
||||
# # self.updates[modulename][pname] = pobj.value
|
||||
#
|
||||
# def __init__(self):
|
||||
# self.modules = {}
|
||||
#
|
||||
# def get_module(self, name):
|
||||
# return self.modules[name]
|
||||
#
|
||||
# def register_module(self, name, module):
|
||||
# self.modules[name] = module
|
||||
|
||||
|
||||
class LoggerStub:
|
||||
def debug(self, fmt, *args):
|
||||
print(fmt % args)
|
||||
info = warning = exception = debug
|
||||
handlers = []
|
||||
|
||||
|
||||
logger = LoggerStub()
|
||||
|
||||
|
||||
class ServerStub:
|
||||
restart = None
|
||||
shutdown = None
|
||||
|
||||
def __init__(self):
|
||||
self.dispatcher = Dispatcher('dispatcher', logger, {}, self)
|
||||
|
||||
|
||||
def test_attach():
|
||||
class Mod(Module):
|
||||
att = Attached()
|
||||
|
||||
srv = ServerStub()
|
||||
a = Module('a', logger, {'description': ''}, srv)
|
||||
m = Mod('m', logger, {'description': '', 'att': 'a'}, srv)
|
||||
assert m.propertyValues['att'] == 'a'
|
||||
srv.dispatcher.register_module(a, 'a')
|
||||
srv.dispatcher.register_module(m, 'm')
|
||||
assert m.att == a
|
@ -120,7 +120,7 @@ def test_IOHandler():
|
||||
real = Parameter('a float value', FloatRange(), default=12.3, handler=group2, readonly=False)
|
||||
text = Parameter('a string value', StringType(), default='x', handler=group2, readonly=False)
|
||||
|
||||
def sendRecv(self, command):
|
||||
def communicate(self, command):
|
||||
assert data.pop('command') == command
|
||||
return data.pop('reply')
|
||||
|
||||
@ -146,7 +146,7 @@ def test_IOHandler():
|
||||
print(updates)
|
||||
updates.clear() # get rid of updates from initialisation
|
||||
|
||||
# for sendRecv
|
||||
# for communicate
|
||||
data.push('command', 'SIMPLE?')
|
||||
data.push('reply', '4.51')
|
||||
# for analyze_group1
|
||||
@ -159,7 +159,7 @@ def test_IOHandler():
|
||||
assert updates.pop('simple') == 45.1
|
||||
assert not updates
|
||||
|
||||
# for sendRecv
|
||||
# for communicate
|
||||
data.push('command', 'CMD?3')
|
||||
data.push('reply', '1.23,text,5')
|
||||
# for analyze_group2
|
||||
@ -172,7 +172,7 @@ def test_IOHandler():
|
||||
assert data.empty()
|
||||
assert not updates
|
||||
|
||||
# for sendRecv
|
||||
# for communicate
|
||||
data.push('command', 'CMD?3')
|
||||
data.push('reply', '1.23,text,5')
|
||||
# for analyze_group2
|
||||
@ -183,7 +183,7 @@ def test_IOHandler():
|
||||
data.push('self', 12.3, 'string')
|
||||
data.push('new', 12.3, 'FOO')
|
||||
data.push('changed', 1.23, 'foo', 9)
|
||||
# for sendRecv
|
||||
# for communicate
|
||||
data.push('command', 'CMD 3,1.23,foo,9|CMD?3')
|
||||
data.push('reply', '1.23,foo,9')
|
||||
# for analyze_group2
|
||||
|
@ -117,7 +117,10 @@ class Parameter:
|
||||
class Module:
|
||||
properties = {}
|
||||
pollerClass = Poller
|
||||
iodev = 'common_iodev'
|
||||
|
||||
class io:
|
||||
name = 'common_io'
|
||||
|
||||
def __init__(self, name, pollinterval=5, fastfactor=0.25, slowfactor=4, busy=False,
|
||||
counts=(), auto=None):
|
||||
'''create a dummy module
|
||||
@ -203,7 +206,7 @@ def test_Poller(modules):
|
||||
count[pobj.polltype] += 1
|
||||
pobj.reset()
|
||||
assert len(pollTable) == 1
|
||||
poller = pollTable[(Poller, 'common_iodev')]
|
||||
poller = pollTable[(Poller, 'common_io')]
|
||||
artime.stop = poller.stop
|
||||
poller._event = Event() # patch Event.wait
|
||||
|
||||
|
Reference in New Issue
Block a user