improve detect_changes
check also if envlist and aliases have to be rebuilt
This commit is contained in:
143
devices.py
143
devices.py
@ -144,6 +144,9 @@ class FrappyConfig(Device):
|
||||
_shutdown_event = None
|
||||
_restarting = False
|
||||
_within_update_setup = False
|
||||
_current_cfgs = None
|
||||
_target_cfgs = None
|
||||
_rebuild_env = None
|
||||
|
||||
def doInit(self, mode):
|
||||
if mode != SIMULATION and session.sessiontype != POLLER:
|
||||
@ -160,15 +163,23 @@ class FrappyConfig(Device):
|
||||
def detect_changes(self):
|
||||
before_check = before_change = prev_shown = None
|
||||
cnt = 0
|
||||
normal_state = None
|
||||
while not self._shutdown_event.wait(1):
|
||||
busy = bool(session.experiment.scripts or session.daemon_device._controller.queue.scripts)
|
||||
if busy and cnt < 10:
|
||||
# check only every 10 sec when busy
|
||||
cnt += 1
|
||||
continue
|
||||
if not busy and normal_state == 'back_to_normal':
|
||||
session.log.warning(all_info(self._current_cfgs, 'servers match configuration: '))
|
||||
normal_state = None
|
||||
cnt = 0
|
||||
need_change, changes, fm = self.to_consider()
|
||||
if fm.state == before_change or not need_change:
|
||||
if fm.state == before_change:
|
||||
continue
|
||||
if not need_change:
|
||||
if normal_state == 'changed':
|
||||
normal_state = 'back_to_normal'
|
||||
continue
|
||||
if fm.state != before_check:
|
||||
# must be twice the same
|
||||
@ -176,27 +187,36 @@ class FrappyConfig(Device):
|
||||
continue
|
||||
if busy or fm.state == prev_shown:
|
||||
continue
|
||||
normal_state = 'changed'
|
||||
self.show_changes(changes)
|
||||
prev_shown = fm.state
|
||||
|
||||
def show_changes(self, changes):
|
||||
if changes is None:
|
||||
need_change, changes, fm = self.to_consider()
|
||||
session.log.warning('sample environment has changed:')
|
||||
args = []
|
||||
session.log.info(' ')
|
||||
session.log.warning('sample environment configuration should be changed:')
|
||||
session.log.info(' ')
|
||||
kwargs = {}
|
||||
for service, cfg in changes.items():
|
||||
arg = str(cfg)
|
||||
kwargs[service] = str(cfg)
|
||||
prev = self._target_cfgs.get(service)
|
||||
if cfg:
|
||||
if isinstance(cfg, Keep):
|
||||
arg = f'={cfg}'
|
||||
session.log.info('keep %s: %s', service, cfg)
|
||||
kwargs.pop(service, None)
|
||||
session.log.info('%s: keep %s', service, cfg)
|
||||
else:
|
||||
session.log.warning('change %s to %s', service, cfg)
|
||||
if prev:
|
||||
session.log.info('%s: change from %s to %s', service, prev, cfg)
|
||||
else:
|
||||
session.log.info('%s: start as %s', service, cfg)
|
||||
elif cfg == '':
|
||||
session.log.warning('remove %s', service)
|
||||
args.append(arg)
|
||||
args = tuple(args)
|
||||
session.log.warning('use frappy%r or frappy.update() to activate', args)
|
||||
session.log.info('%s: remove %s', service, prev)
|
||||
if self._rebuild_env:
|
||||
session.log.info('%s', self._rebuild_env)
|
||||
alternative = f" or {all_info(kwargs, '')}" if kwargs else ''
|
||||
session.log.info(' ')
|
||||
session.log.info('use frappy.update()%s to activate', alternative)
|
||||
|
||||
def to_consider(self, cfgs=None):
|
||||
"""return info about a proposed changes
|
||||
@ -206,37 +226,48 @@ class FrappyConfig(Device):
|
||||
|
||||
the information takes into account running frappy and sea servers
|
||||
and their configuration
|
||||
|
||||
side effect: try to reconnect a frappy server which should run with
|
||||
the target cfg
|
||||
"""
|
||||
current_cfgs, target_cfgs = self.check_services()
|
||||
if cfgs is None:
|
||||
cfgs = current_cfgs
|
||||
self._current_cfgs = cfgs
|
||||
fm = FrappyManager()
|
||||
proposed = fm.get_server_state(config.instrument, cfgs)
|
||||
changes = dict(proposed)
|
||||
for service, cfg in proposed.items():
|
||||
if cfg == 'reconnect': # means: server is running, no restart needed
|
||||
if service in current_cfgs:
|
||||
changes.pop(service)
|
||||
need_change = False
|
||||
for service in SERVICES:
|
||||
cfg = changes.get(service) # proposed cfg
|
||||
info = cfgs.get(service) # running cfg
|
||||
if not info:
|
||||
running = fm.frappy_cfgs.get(service) # running cfg
|
||||
if running:
|
||||
prop = changes.get(service)
|
||||
if prop:
|
||||
if prop == target_cfgs.get(service) == running:
|
||||
secnode = session.devices.get('se_' + service)
|
||||
if secnode:
|
||||
if secnode._secnode:
|
||||
secnode._secnode.connect()
|
||||
else:
|
||||
secnode._connect()
|
||||
else:
|
||||
changes[service] = ''
|
||||
need_change = True
|
||||
cfg = ''
|
||||
else:
|
||||
if cfg == '':
|
||||
changes.pop(service)
|
||||
cfg = None
|
||||
if target_cfgs.get(service):
|
||||
need_change = True
|
||||
elif info == '<disconnected>':
|
||||
if not changes.get(service):
|
||||
changes[service] = ''
|
||||
need_change = True
|
||||
cfg = ''
|
||||
need_change = 1
|
||||
if cfg and not isinstance(cfg, Keep):
|
||||
need_change = cfg
|
||||
if not need_change and all(isinstance(v, Keep) for v in changes.values()):
|
||||
self.set_envlist(checkonly=True)
|
||||
if self._rebuild_env:
|
||||
need_change = True
|
||||
# elif target_cfgs.get(service) != cfgs.get(service):
|
||||
# need_change = True
|
||||
else:
|
||||
self._rebuild_env = None
|
||||
return need_change, changes, fm
|
||||
|
||||
def check_services(self):
|
||||
@ -247,6 +278,8 @@ class FrappyConfig(Device):
|
||||
if secnode:
|
||||
cfgs[secnode.service] = secnode.get_info()
|
||||
targets[secnode.service] = secnode.target
|
||||
self._current_cfgs = cfgs
|
||||
self._target_cfgs = targets
|
||||
return cfgs, targets
|
||||
|
||||
def start_services(self, main=None, stick=None, addons=None,):
|
||||
@ -265,7 +298,6 @@ class FrappyConfig(Device):
|
||||
self._restarting = True
|
||||
try:
|
||||
services = {'main': main, 'stick': stick, 'addons': addons}
|
||||
to_reconnect = {}
|
||||
for service, cfg in services.items():
|
||||
if cfg == '':
|
||||
seaconn = session.devices.get(f'se_sea_{service}')
|
||||
@ -286,8 +318,6 @@ class FrappyConfig(Device):
|
||||
all_cfg[service] = chkinfo = secnode.get_info()
|
||||
if cfginfo is not None and (cfginfo != chkinfo or not isinstance(cfginfo, Reconnect)):
|
||||
new_cfg[service] = chkinfo = cfginfo
|
||||
if secnode:
|
||||
to_reconnect[service] = secnode
|
||||
|
||||
# check cfg is not used twice
|
||||
for cfg in chkinfo.split(','):
|
||||
@ -315,13 +345,13 @@ class FrappyConfig(Device):
|
||||
AddSetup('frappy_' + service)
|
||||
secnode = session.devices[nodename]
|
||||
secnode(cfginfo)
|
||||
to_reconnect.pop(service, None)
|
||||
all_cfg[service] = secnode.get_info()
|
||||
CreateDevice(nodename)
|
||||
cleanup_defunct()
|
||||
CreateAllDevices()
|
||||
for secnode in to_reconnect.values():
|
||||
secnode._secnode.connect()
|
||||
for service, secnode in secnodes.items():
|
||||
if services.get(service):
|
||||
secnode._secnode.connect()
|
||||
self.set_envlist()
|
||||
for secnode in remove_cfg:
|
||||
secnode.disable()
|
||||
@ -404,11 +434,12 @@ class FrappyConfig(Device):
|
||||
if need_change:
|
||||
self.show_changes(changes)
|
||||
|
||||
def update(self, *args):
|
||||
if args:
|
||||
changes = {k: (Keep(v[1:]) if v.startswith('=') else v) for k, v in zip(SERVICES, args) if v is not None}
|
||||
else:
|
||||
def update(self, main=None, stick=None, addons=None):
|
||||
if main is None and stick is None and addons is None:
|
||||
changes = self.to_consider()[1]
|
||||
else:
|
||||
changes = {k: Keep(v) for k, v in zip(SERVICES, (main, stick, addons))
|
||||
if v is not None}
|
||||
self.show_config(self.start_services(**changes))
|
||||
|
||||
def get_init_info(self, service):
|
||||
@ -457,7 +488,7 @@ class FrappyConfig(Device):
|
||||
result[aliasname] = aliasdev
|
||||
return result
|
||||
|
||||
def set_envlist(self):
|
||||
def set_envlist(self, checkonly=False):
|
||||
"""create aliases and envlist for SECoP devices
|
||||
|
||||
depending on their meaning
|
||||
@ -511,6 +542,18 @@ class FrappyConfig(Device):
|
||||
dev = session.devices.get(devname)
|
||||
if dev is None or info.get('drivable_only', False) and not isinstance(dev, Moveable):
|
||||
continue
|
||||
if checkonly:
|
||||
if aliasnames and devname not in newenv:
|
||||
newenv[devname] = aliasnames[0]
|
||||
for aliasname in aliasnames:
|
||||
adev = previous_aliases.pop(aliasname, None)
|
||||
if not adev:
|
||||
self._rebuild_env = f'need alias {aliasname} for {devname}'
|
||||
return
|
||||
if adev.alias != devname:
|
||||
self._rebuild_env = f'alias {aliasname} must point to {devname}'
|
||||
return
|
||||
continue
|
||||
for aliasname in aliasnames:
|
||||
devcfg = ('nicos.core.DeviceAlias', {})
|
||||
session.configured_devices[aliasname] = devcfg
|
||||
@ -526,6 +569,10 @@ class FrappyConfig(Device):
|
||||
session.cache.put(aliasname, 'description', dev.description)
|
||||
aliasdev = session.createDevice(aliasname, recreate=True, explicit=True)
|
||||
aliasdev.alias = devname
|
||||
# make sure device panel is updated
|
||||
dev = aliasdev._obj
|
||||
dev._cache.put(dev, 'status', dev.status())
|
||||
dev._cache.put(dev, 'value', dev.read())
|
||||
if aliasnames:
|
||||
# only the first item of aliasnames is added to the envlist
|
||||
aliasname = aliasnames[0]
|
||||
@ -539,14 +586,25 @@ class FrappyConfig(Device):
|
||||
else:
|
||||
to_remove.update(aliasnames)
|
||||
|
||||
for aliasname in previous_aliases:
|
||||
session.destroyDevice(aliasname)
|
||||
session.configured_devices.pop(aliasname, None)
|
||||
session.dynamic_devices.pop(aliasname, None)
|
||||
if checkonly:
|
||||
if previous_aliases:
|
||||
self._rebuild_env = f'need to remove aliases {", ".join(previous_aliases)}'
|
||||
return
|
||||
else:
|
||||
for aliasname in previous_aliases:
|
||||
session.destroyDevice(aliasname)
|
||||
session.configured_devices.pop(aliasname, None)
|
||||
session.dynamic_devices.pop(aliasname, None)
|
||||
|
||||
applyAliasConfig() # for other aliases
|
||||
applyAliasConfig() # for other aliases
|
||||
|
||||
envlist = [k for k in session.experiment.envlist if k not in to_remove] + list(newenv.values())
|
||||
if checkonly:
|
||||
if set(envlist) != set(session.experiment.envlist):
|
||||
self._rebuild_env = f'need to update envlist to {envlist}'
|
||||
else:
|
||||
self._rebuild_env = None
|
||||
return
|
||||
if envlist != session.experiment.envlist:
|
||||
removed = set(session.experiment.envlist).difference(envlist)
|
||||
session.experiment.setEnvironment(envlist)
|
||||
@ -645,7 +703,6 @@ class FrappyNode(SecNodeDevice, Moveable):
|
||||
self.__log_recording = ()
|
||||
|
||||
def nodeStateChange(self, online, state):
|
||||
print(f'NODE {self.service} {online} {state}')
|
||||
# self.log.info('NODE %r %r', online, state)
|
||||
if online:
|
||||
super().nodeStateChange(online, state)
|
||||
|
Reference in New Issue
Block a user