[server.py] rename initialisation steps for better clarity

initialisation occurs in this order:
 - object creeation (via __init__ which should consume the cfg values it knows about)
 - registering each object with the dispatcher
 - calling init_module() on each module (for connecting to other modules, checking hw, creating threads....)
 - calling start_module(cb) on each module. after the module finished startup it should call cb(self) once.
   This is the right place to do initialisation of hw which is not needed to read from the hw.
   (uploading curves, polling/re-setting all parameters, etc.)

Change-Id: Ieaf9df5876e764634836861241f58ab986027f44
Reviewed-on: https://forge.frm2.tum.de/review/18566
Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
This commit is contained in:
Enrico Faulhaber
2018-07-31 16:37:36 +02:00
parent acb8e6d0a1
commit 5f640ce299
9 changed files with 52 additions and 41 deletions

View File

@ -189,21 +189,21 @@ class Module(object):
v.unit = v.unit.replace('$', self.accessibles['value'].unit) v.unit = v.unit.replace('$', self.accessibles['value'].unit)
def init(self): def early_init(self):
# may be overriden in derived classes to init stuff # may be overriden in derived classes to init stuff
self.log.debug('empty init()') self.log.debug('empty early_init()')
def postinit(self): def init_module(self):
self.log.debug('empty postinit()') self.log.debug('empty init_module()')
def late_init(self, started_callback): def start_module(self, started_callback):
'''runs after postinit of all modules '''runs after init of all modules
started_callback to be called when thread spawned by late_init started_callback to be called when thread spawned by late_init
or, if not implmemented, immediately or, if not implmemented, immediately
''' '''
self.log.debug('empty late init()') self.log.debug('empty start_module()')
started_callback(self) started_callback(self)
@ -238,10 +238,7 @@ class Readable(Module):
), ),
} }
def init(self): def start_module(self, started_callback):
Module.init(self)
def late_init(self, started_callback):
'''start polling thread''' '''start polling thread'''
mkthread(self.__pollThread, started_callback) mkthread(self.__pollThread, started_callback)

View File

@ -214,16 +214,16 @@ class Server(object):
for devname, devobj, export in devs: for devname, devobj, export in devs:
self.log.info(u'registering module %r' % devname) self.log.info(u'registering module %r' % devname)
self._dispatcher.register_module(devobj, devname, export) self._dispatcher.register_module(devobj, devname, export)
# also call init on the modules # also call early_init on the modules
devobj.init() devobj.early_init()
# call postinit on each module after registering all # call init on each module after registering all
for _devname, devobj, _export in devs: for _devname, devobj, _export in devs:
devobj.postinit() devobj.init_module()
starting_modules = set() starting_modules = set()
finished_modules = Queue() finished_modules = Queue()
for _devname, devobj, _export in devs: for _devname, devobj, _export in devs:
starting_modules.add(devobj) starting_modules.add(devobj)
devobj.late_init(started_callback=finished_modules.put) devobj.start_module(started_callback=finished_modules.put)
# remark: it is the module implementors responsibility to call started_callback # remark: it is the module implementors responsibility to call started_callback
# within reasonable time (using timeouts). If we find later, that this is not # within reasonable time (using timeouts). If we find later, that this is not
# enough, we might insert checking for a timeout here, and somehow set the remaining # enough, we might insert checking for a timeout here, and somehow set the remaining
@ -233,6 +233,7 @@ class Server(object):
self.log.info(u'%s has started' % finished.name) self.log.info(u'%s has started' % finished.name)
# use discard instead of remove here, catching the case when started_callback is called twice # use discard instead of remove here, catching the case when started_callback is called twice
starting_modules.discard(finished) starting_modules.discard(finished)
finished_modules.task_done()
def _processInterfaceOptions(self, interfaceopts): def _processInterfaceOptions(self, interfaceopts):
# eval interfaces # eval interfaces

View File

@ -55,7 +55,7 @@ class SimBase(object):
return newval return newval
setattr(self, 'write_' + k, writer) setattr(self, 'write_' + k, writer)
def late_init(self): def init_module(self):
self._sim_thread = mkthread(self._sim) self._sim_thread = mkthread(self._sim)
def _sim(self): def _sim(self):

View File

@ -129,7 +129,7 @@ class Cryostat(CryoBase):
None), None),
) )
def init(self): def init_module(self):
self._stopflag = False self._stopflag = False
self._thread = mkthread(self.thread) self._thread = mkthread(self.thread)

View File

@ -115,7 +115,7 @@ class MagneticField(Drivable):
), ),
} }
def init(self): def init_module(self):
self._state = Enum('state', idle=1, switch_on=2, switch_off=3, ramp=4).idle self._state = Enum('state', idle=1, switch_on=2, switch_off=3, ramp=4).idle
self._heatswitch = self.DISPATCHER.get_module(self.heatswitch) self._heatswitch = self.DISPATCHER.get_module(self.heatswitch)
_thread = threading.Thread(target=self._thread) _thread = threading.Thread(target=self._thread)
@ -211,7 +211,7 @@ class SampleTemp(Drivable):
), ),
} }
def init(self): def init_module(self):
_thread = threading.Thread(target=self._thread) _thread = threading.Thread(target=self._thread)
_thread.daemon = True _thread.daemon = True
_thread.start() _thread.start()

View File

@ -132,8 +132,8 @@ class GarfieldMagnet(SequencerMixin, Drivable):
raise ConfigError(self, raise ConfigError(self,
'_current2field polynome not monotonic!') '_current2field polynome not monotonic!')
def init(self): def init_module(self):
super(GarfieldMagnet, self).init() super(GarfieldMagnet, self).init_module()
self._enable = self.DISPATCHER.get_module(self.subdev_enable) self._enable = self.DISPATCHER.get_module(self.subdev_enable)
self._symmetry = self.DISPATCHER.get_module(self.subdev_symmetry) self._symmetry = self.DISPATCHER.get_module(self.subdev_symmetry)
self._polswitch = self.DISPATCHER.get_module(self.subdev_polswitch) self._polswitch = self.DISPATCHER.get_module(self.subdev_polswitch)

View File

@ -210,12 +210,12 @@ class PyTangoDevice(Module):
self._com_warn(tries, name, err, info) self._com_warn(tries, name, err, info)
sleep(self.comdelay) sleep(self.comdelay)
def init(self): def early_init(self):
# Wrap PyTango client creation (so even for the ctor, logging and # Wrap PyTango client creation (so even for the ctor, logging and
# exception mapping is enabled). # exception mapping is enabled).
self._createPyTangoDevice = self._applyGuardToFunc( self._createPyTangoDevice = self._applyGuardToFunc(
self._createPyTangoDevice, 'constructor') self._createPyTangoDevice, 'constructor')
super(PyTangoDevice, self).init() super(PyTangoDevice, self).early_init()
@lazy_property @lazy_property
def _dev(self): def _dev(self):
@ -379,8 +379,8 @@ class AnalogInput(PyTangoDevice, Readable):
The AnalogInput handles all devices only delivering an analogue value. The AnalogInput handles all devices only delivering an analogue value.
""" """
def late_init(self, started_callback): def start_module(self, started_callback):
super(AnalogInput, self).late_init(started_callback) super(AnalogInput, self).start_module(started_callback)
# query unit from tango and update value property # query unit from tango and update value property
attrInfo = self._dev.attribute_query('value') attrInfo = self._dev.attribute_query('value')
# prefer configured unit if nothing is set on the Tango device, else # prefer configured unit if nothing is set on the Tango device, else
@ -456,14 +456,14 @@ class AnalogOutput(PyTangoDevice, Drivable):
_timeout = None _timeout = None
_moving = False _moving = False
def init(self): def init_module(self):
super(AnalogOutput, self).init() super(AnalogOutput, self).init_module()
# init history # init history
self._history = [] # will keep (timestamp, value) tuple self._history = [] # will keep (timestamp, value) tuple
self._timeout = None # keeps the time at which we will timeout, or None self._timeout = None # keeps the time at which we will timeout, or None
def late_init(self, started_callback): def start_module(self, started_callback):
super(AnalogOutput, self).late_init(started_callback) super(AnalogOutput, self).start_module(started_callback)
# query unit from tango and update value property # query unit from tango and update value property
attrInfo = self._dev.attribute_query('value') attrInfo = self._dev.attribute_query('value')
# prefer configured unit if nothing is set on the Tango device, else # prefer configured unit if nothing is set on the Tango device, else
@ -801,8 +801,8 @@ class NamedDigitalInput(DigitalInput):
datatype=StringType(), export=False), # XXX:!!! datatype=StringType(), export=False), # XXX:!!!
} }
def init(self): def init_module(self):
super(NamedDigitalInput, self).init() super(NamedDigitalInput, self).init_module()
try: try:
# pylint: disable=eval-used # pylint: disable=eval-used
self.accessibles['value'].datatype = EnumType('value', **eval(self.mapping)) self.accessibles['value'].datatype = EnumType('value', **eval(self.mapping))
@ -826,8 +826,8 @@ class PartialDigitalInput(NamedDigitalInput):
datatype=IntRange(0), default=1), datatype=IntRange(0), default=1),
} }
def init(self): def init_module(self):
super(PartialDigitalInput, self).init() super(PartialDigitalInput, self).init_module()
self._mask = (1 << self.bitwidth) - 1 self._mask = (1 << self.bitwidth) - 1
# self.accessibles['value'].datatype = IntRange(0, self._mask) # self.accessibles['value'].datatype = IntRange(0, self._mask)
@ -868,8 +868,8 @@ class NamedDigitalOutput(DigitalOutput):
datatype=StringType(), export=False), datatype=StringType(), export=False),
} }
def init(self): def init_module(self):
super(NamedDigitalOutput, self).init() super(NamedDigitalOutput, self).init_module()
try: try:
# pylint: disable=eval-used # pylint: disable=eval-used
self.accessibles['value'].datatype = EnumType('value', **eval(self.mapping)) self.accessibles['value'].datatype = EnumType('value', **eval(self.mapping))
@ -896,8 +896,8 @@ class PartialDigitalOutput(NamedDigitalOutput):
datatype=IntRange(0), default=1), datatype=IntRange(0), default=1),
} }
def init(self): def init_module(self):
super(PartialDigitalOutput, self).init() super(PartialDigitalOutput, self).init_module()
self._mask = (1 << self.bitwidth) - 1 self._mask = (1 << self.bitwidth) - 1
# self.accessibles['value'].datatype = IntRange(0, self._mask) # self.accessibles['value'].datatype = IntRange(0, self._mask)
# self.accessibles['target'].datatype = IntRange(0, self._mask) # self.accessibles['target'].datatype = IntRange(0, self._mask)

View File

@ -28,6 +28,10 @@ sys.path.insert(0, sys.path[0] + '/..')
# no fixtures needed # no fixtures needed
import pytest import pytest
try:
import Queue as queue
except ImportError:
import queue as queue
from secop.datatypes import BoolType, EnumType from secop.datatypes import BoolType, EnumType
@ -47,7 +51,11 @@ def test_Communicator():
))() ))()
o = Communicator(logger, {}, 'o1', dispatcher) o = Communicator(logger, {}, 'o1', dispatcher)
o.init() o.early_init()
o.init_module()
q = queue.Queue()
o.start_module(q.put)
q.get()
def test_ModuleMeta(): def test_ModuleMeta():
newclass = ModuleMeta.__new__(ModuleMeta, 'TestReadable', (Drivable, Writable, Readable, Module), { newclass = ModuleMeta.__new__(ModuleMeta, 'TestReadable', (Drivable, Writable, Readable, Module), {
@ -100,5 +108,10 @@ def test_ModuleMeta():
params_found.add(o) params_found.add(o)
assert o.ctr not in ctr_found assert o.ctr not in ctr_found
ctr_found.add(o.ctr) ctr_found.add(o.ctr)
o1.init() o1.early_init()
o2.init() o2.early_init()
o1.init_module()
o2.init_module()
q = queue.Queue()
o1.start_module(q.put)
o2.start_module(q.put)