improve detect_changes

check also if envlist and aliases have to be rebuilt
This commit is contained in:
2024-05-07 08:51:36 +02:00
parent b10102e052
commit ead54b14a5

View File

@ -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,12 +345,12 @@ 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():
for service, secnode in secnodes.items():
if services.get(service):
secnode._secnode.connect()
self.set_envlist()
for secnode in remove_cfg:
@ -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,6 +586,11 @@ class FrappyConfig(Device):
else:
to_remove.update(aliasnames)
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)
@ -547,6 +599,12 @@ class FrappyConfig(Device):
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)