[wip] auto sample environment change, basic version

This commit is contained in:
2024-04-30 14:41:56 +02:00
parent 09237e4118
commit b10102e052
2 changed files with 298 additions and 213 deletions

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# ***************************************************************************** # *****************************************************************************
# NICOS, the Networked Instrument Control System of the MLZ # NICOS, the Networked Instrument Control System of the MLZ
# Copyright (c) 2009-2018 by the NICOS contributors (see AUTHORS) # Copyright (c) 2009-2018 by the NICOS contributors (see AUTHORS)
@ -30,14 +29,14 @@ connected to a SEC node
import threading import threading
from nicos import config, session from nicos import config, session
from nicos.core import Override, Param, Moveable, status, POLLER, SIMULATION, DeviceAlias, \ from nicos.core import Override, Param, Moveable, status, POLLER, SIMULATION, DeviceAlias, \
Device, anytype, listof Device, anytype, listof, MASTER
from nicos.devices.secop.devices import SecNodeDevice, NicosSecopClient from nicos.devices.secop.devices import SecNodeDevice
from nicos.core.utils import USER, User, createThread from nicos.core.utils import createThread
from nicos.services.daemon.script import RequestError, ScriptRequest
from nicos.utils.comparestrings import compare from nicos.utils.comparestrings import compare
from nicos.devices.secop.devices import get_attaching_devices from nicos.devices.secop.devices import get_attaching_devices
from nicos.commands.basic import AddSetup, CreateAllDevices, CreateDevice from nicos.commands.basic import AddSetup, CreateAllDevices, CreateDevice
from servicemanager import FrappyManager, SeaManager from nicos.utils import loggers
from servicemanager import FrappyManager, SeaManager, Reconnect, Keep
SERVICES = FrappyManager.services SERVICES = FrappyManager.services
@ -57,7 +56,7 @@ def applyAliasConfig():
""" """
# reimplemented from Session.applyAliasConfig # reimplemented from Session.applyAliasConfig
# apply also when target dev name does not change, as the target device might have # apply also when target dev name does not change, as the target device might have
# be exchanged in the mean time # be exchanged in the meantime
for aliasname, targets in session.alias_config.items(): for aliasname, targets in session.alias_config.items():
if aliasname not in session.devices: if aliasname not in session.devices:
continue # silently ignore continue # silently ignore
@ -92,6 +91,8 @@ def all_info(all_cfg, prefix='currently configured: '):
if cfglist is None: if cfglist is None:
addkwd = True addkwd = True
else: else:
# if cfglist is True:
# cfglist = ['reconnect']
if isinstance(cfglist, str): if isinstance(cfglist, str):
cfglist = [cfglist] cfglist = [cfglist]
cfginfo = ','.join(c if isinstance(c, str) cfginfo = ','.join(c if isinstance(c, str)
@ -108,7 +109,7 @@ def get_frappy_config():
try: try:
return session.devices['frappy'] return session.devices['frappy']
except KeyError: except KeyError:
session.log.error("the frappy device is not available - 'frappy' setup is not loaded") session.log.exception("the frappy device is not available - 'frappy' setup is not loaded")
return None return None
@ -135,71 +136,124 @@ class FrappyConfig(Device):
type=listof(str), default=[]), type=listof(str), default=[]),
} }
meanings = list(parameters) meanings = [n for n, p in parameters.items() if p.type is anytype]
meanings.remove('nodes') _update_setup = 'on_frappy'
_trigger_change = None
_previous_shown = None _previous_shown = None
_previous_state = None _previous_state = None
_initial_config = None _initial_info = None
_servers_loaded = False _shutdown_event = None
_restarting = False
_within_update_setup = False
def doInit(self, mode): def doInit(self, mode):
if mode != SIMULATION and session.sessiontype != POLLER: if mode != SIMULATION and session.sessiontype != POLLER:
self._trigger_change = threading.Event() self._shutdown_event = threading.Event()
for name in self.nodes: for name in self.nodes:
secnode = session.devices.get(name) secnode = session.devices.get(name)
if secnode: if secnode:
secnode.uri = '' secnode.uri = ''
createThread('frappy change notification', self.handle_notifications) createThread('check frappy and sea servers', self.detect_changes)
def handle_notifications(self): def doShutdown(self):
controller = session.daemon_device._controller self._shutdown_event.set()
while True:
# we do not wait for ever here, because there might be changes
# on an unconnected service
self._trigger_change.wait(15)
self._trigger_change.clear()
while self._trigger_change.wait(2): # triggered again within 2 sec
self._trigger_change.clear()
try:
cfgs = self.check_services()
changes, state, remarks = self.to_consider(cfgs)
if state != self._previous_state and changes:
self._previous_state = state
cmd = 'frappy.has_changed() # inserted automatically when frappy or sea servers changed'
controller.new_request(ScriptRequest(cmd, None, User('guest', USER)))
except RequestError as e:
session.log.error(f'can not queue request {e!r}')
def to_consider(self, cfgs): def detect_changes(self):
"""return info from sea and frappy servers before_check = before_change = prev_shown = None
cnt = 0
for a potential "please consider calling frappy(...)" message 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
cnt = 0
need_change, changes, fm = self.to_consider()
if fm.state == before_change or not need_change:
continue
if fm.state != before_check:
# must be twice the same
before_check = fm.state
continue
if busy or fm.state == prev_shown:
continue
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 = []
for service, cfg in changes.items():
arg = str(cfg)
if cfg:
if isinstance(cfg, Keep):
arg = f'={cfg}'
session.log.info('keep %s: %s', service, cfg)
else:
session.log.warning('change %s to %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)
def to_consider(self, cfgs=None):
"""return info about a proposed changes
:param cfgs: previous configuration
:return: <'need change' flag>, <change dict>, <frappy manager instance>
the information takes into account running frappy and sea servers
and their configuration
""" """
error, proposed, state, remarks = FrappyManager().get_server_state(config.instrument, cfgs) 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) changes = dict(proposed)
for service, guess in proposed.items(): for service, cfg in proposed.items():
if guess is True: if cfg == 'reconnect': # means: server is running, no restart needed
changes.pop(service) if service in current_cfgs:
disconnected = set() changes.pop(service)
for service, info in cfgs.items(): need_change = False
if info == '<disconnected>': for service in SERVICES:
disconnected.add(service) cfg = changes.get(service) # proposed cfg
info = cfgs.get(service) # running cfg
if not info:
if cfg == '':
changes.pop(service)
cfg = None
if target_cfgs.get(service):
need_change = True
elif info == '<disconnected>':
if not changes.get(service): if not changes.get(service):
changes[service] = '' changes[service] = ''
return changes, (proposed,) + state, remarks need_change = True
cfg = ''
if cfg and not isinstance(cfg, Keep):
need_change = True
# elif target_cfgs.get(service) != cfgs.get(service):
# need_change = True
return need_change, changes, fm
def check_services(self): def check_services(self):
cfgs = {} cfgs = {}
targets = {}
for secnodename in self.nodes: for secnodename in self.nodes:
secnode = session.devices.get(secnodename) secnode = session.devices.get(secnodename)
if secnode: if secnode:
cfgs[secnode.service] = secnode.get_info() cfgs[secnode.service] = secnode.get_info()
return cfgs targets[secnode.service] = secnode.target
return cfgs, targets
def start_services(self, main=None, stick=None, addons=None): def start_services(self, main=None, stick=None, addons=None,):
"""start/stop frappy servers """start/stop frappy servers
:param main, stick, addons: cfg for frappy servers, '' to stop, None to keep
for example: start_services(main='xy', stick='') for example: start_services(main='xy', stick='')
- restart main server with cfg='xy' - restart main server with cfg='xy'
- stop stick server - stop stick server
@ -208,67 +262,71 @@ class FrappyConfig(Device):
in addition, if a newly given cfg is already used on a running server, in addition, if a newly given cfg is already used on a running server,
this cfg is removed from the server (remark: cfg might be a comma separated list) this cfg is removed from the server (remark: cfg might be a comma separated list)
""" """
services = {'main': main, 'stick': stick, 'addons': addons} self._restarting = True
for service, cfg in services.items(): try:
if cfg == '': services = {'main': main, 'stick': stick, 'addons': addons}
seaconn = session.devices.get(f'se_sea_{service}') to_reconnect = {}
if seaconn and seaconn._attached_secnode: for service, cfg in services.items():
try: if cfg == '':
seaconn.communicate('frappy_remove %s' % service) seaconn = session.devices.get(f'se_sea_{service}')
except Exception: if seaconn and seaconn._attached_secnode:
pass try:
used_cfg = {} seaconn.communicate('frappy_remove %s' % service)
all_cfg = {} except Exception:
new_cfg = {} pass
secnodes = {} used_cfg = {}
remove_cfg = [] all_cfg = {}
for service, cfginfo in services.items(): new_cfg = {}
secnodes[service] = secnode = session.devices.get('se_' + service) secnodes = {}
chkinfo = '' remove_cfg = []
if secnode: for service, cfginfo in services.items():
all_cfg[service] = chkinfo = secnode.get_info() secnodes[service] = secnode = session.devices.get('se_' + service)
if cfginfo is not None: chkinfo = ''
new_cfg[service] = chkinfo = cfginfo
# check cfg is not used twice
for cfg in chkinfo.split(','):
cfg = cfg.strip()
if cfg and cfg != 'restart':
prev = used_cfg.get(cfg)
if prev:
raise ValueError('%r can not be used in both %s and %s' % (cfg, prev, service))
used_cfg[cfg] = service
for service, cfginfo in reversed(list(new_cfg.items())):
if cfginfo != all_cfg.get(service, ''):
secnode = secnodes[service]
if secnode: if secnode:
secnode('') # stop previous frappy server 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
if new_cfg: # check cfg is not used twice
for service, cfginfo in new_cfg.items(): for cfg in chkinfo.split(','):
nodename = 'se_' + service cfg = cfg.strip()
secnode = secnodes[service] if cfg and cfg != 'restart':
prev = all_cfg.get(service) prev = used_cfg.get(cfg)
if cfginfo != prev: if prev:
if cfginfo == 'restart': raise ValueError('%r can not be used in both %s and %s' % (cfg, prev, service))
cfginfo = prev used_cfg[cfg] = service
if not cfginfo:
continue for service, cfginfo in reversed(list(new_cfg.items())):
if cfginfo != all_cfg.get(service, ''):
secnode = secnodes[service]
if secnode:
secnode('') # stop previous frappy server
if new_cfg:
for service, cfginfo in new_cfg.items():
nodename = 'se_' + service
secnode = secnodes[service]
prev = all_cfg.get(service)
if not secnode: if not secnode:
if not cfginfo: if not cfginfo:
continue continue
AddSetup('frappy_' + service) AddSetup('frappy_' + service)
secnode = session.devices[nodename] secnode = session.devices[nodename]
secnode(cfginfo) secnode(cfginfo)
to_reconnect.pop(service, None)
all_cfg[service] = secnode.get_info() all_cfg[service] = secnode.get_info()
CreateDevice(nodename) CreateDevice(nodename)
cleanup_defunct() cleanup_defunct()
CreateAllDevices() CreateAllDevices()
self.set_envlist() for secnode in to_reconnect.values():
for secnode in remove_cfg: secnode._secnode.connect()
secnode.disable() self.set_envlist()
self._cache.put(self, 'config', all_cfg) for secnode in remove_cfg:
secnode.disable()
finally:
self._restarting = False
return all_cfg return all_cfg
def __call__(self, *args, main=None, stick=None, addons=None, force=False): def __call__(self, *args, main=None, stick=None, addons=None, force=False):
@ -280,24 +338,29 @@ class FrappyConfig(Device):
- addons are not changed when not given - addons are not changed when not given
- frappy(main='<cfg>') # main cfg is changed, but stick is kept - frappy(main='<cfg>') # main cfg is changed, but stick is kept
- frappy('restart') # restart all frappy servers - frappy('restart') # restart all frappy servers
- frappy(stick='restart') # restart stick frappy server - frappy('reconnect') # reconnect to running frappy servers
""" """
stickarg = stick stickarg = stick
confirmed = SeaManager().get_cfg(config.instrument, 'sea', True).split('/', 1)[0]
_, changes, fm = to_consider = self.to_consider()
confirmed = fm.sea.get_cfg(config.instrument, 'sea', True).split('/', 1)[0]
if args: if args:
if main is not None: if main is not None:
raise TypeError('got multiple values for main') raise TypeError('got multiple values for main')
main = args[0] main = args[0]
if len(args) == 1: # special case: main given as single argument if len(args) == 1: # special case: main given as single argument
if main == 'restart': if main == 'restart':
stick = 'restart' main = self._current_cfgs.get('main')
addons = 'restart' stick = self._current_cfgs.get('stick')
addons = self._current_cfgs.get('addons')
elif main == 'reconnect':
main = None
elif stick is None: # auto stick elif stick is None: # auto stick
if main == '': if main == '':
stick = '' # remove stick with main stick = '' # remove stick with main
else: else:
stickcfg = main + 'stick' stickcfg = main + 'stick'
if FrappyManager().is_cfg(config.instrument, 'stick', stickcfg): if fm.is_cfg(config.instrument, 'stick', stickcfg):
# if a default stick is available, start this also # if a default stick is available, start this also
stick = stickcfg stick = stickcfg
else: else:
@ -311,7 +374,7 @@ class FrappyConfig(Device):
raise TypeError('got multiple values for addons') raise TypeError('got multiple values for addons')
addons = ','.join(alist) addons = ','.join(alist)
elif main is None and stick is None and addons is None: # bare frappy() command elif main is None and stick is None and addons is None: # bare frappy() command
self.show_config(self.check_services(), True) self.show_config(None, True, to_consider=to_consider)
return return
if confirmed and confirmed != main and main not in (None, 'restart') and not force: if confirmed and confirmed != main and main not in (None, 'restart') and not force:
session.log.warning('%r is plugged to the cryostat control rack', confirmed) session.log.warning('%r is plugged to the cryostat control rack', confirmed)
@ -320,80 +383,53 @@ class FrappyConfig(Device):
raise TypeError('refuse to override plugged device') raise TypeError('refuse to override plugged device')
self.show_config(self.start_services(main, stick, addons)) self.show_config(self.start_services(main, stick, addons))
def show_config(self, allcfg, show_server_state=False): def show_config(self, allcfg, show_server_state=False, to_consider=None):
changes, state, remarks = self.to_consider(allcfg) need_change, changes, fm = to_consider or self.to_consider(allcfg)
if show_server_state == 'auto': if show_server_state == 'auto':
show_server_state = state != self._previous_shown show_server_state = fm.state != self._previous_shown and need_change
if show_server_state: if show_server_state:
proposed, frappycfgs, seacfgs = state
rows = [['server', 'frappy', 'sea', '']] rows = [['server', 'frappy', 'sea', '']]
for key, remark in remarks.items(): for key, remark in fm.remarks.items():
rows.append([key if key in ('main', 'stick') else 'addons', rows.append([key if key in ('main', 'stick') else 'addons',
frappycfgs.get(key, ''), seacfgs.get(key, ''), remark]) fm.frappy_cfgs.get(key, ''), fm.sea_cfgs.get(key, ''), remark])
wid = [max(len(v) for v in column) for column in zip(*rows)] wid = [max(len(v) for v in column) for column in zip(*rows)]
# insert title underlines # insert title underlines
rows.insert(1, ['-' * w for w in wid[:-1]] + ['']) rows.insert(1, ['-' * w for w in wid[:-1]] + [''])
for row in rows: for row in rows:
session.log.info('%s', ' '.join(v.ljust(w) for w, v in zip(wid, row))) session.log.info('%s', ' '.join(v.ljust(w) for w, v in zip(wid, row)))
session.log.info('') session.log.info('')
# remove 'frappy.has_changed()' commands in script queue self._previous_state = self._previous_shown = fm.state
controller = session.daemon_device._controller session.log.info(all_info(self._current_cfgs))
controller.block_requests(r['reqid'] for r in controller.get_queue()
if r['script'].startswith('frappy.has_changed()'))
self._previous_state = self._previous_shown = state
session.log.info(all_info(allcfg))
if changes: if need_change:
info = all_info(changes, 'proposed cfg changes: ') self.show_changes(changes)
session.log.info(info)
session.log.warning('please consider to call: frappy.update() for doing above changes')
if '?' in info:
session.log.warning("but create cfg files first for items marked with '?'")
def update(self): def update(self, *args):
changes = self.to_consider(self.check_services())[0] 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:
changes = self.to_consider()[1]
self.show_config(self.start_services(**changes)) self.show_config(self.start_services(**changes))
def initial_restart_cfg(self, service): def get_init_info(self, service):
"""get cfg for (re)start of the service """check whether a connect of this service is required"""
if self._initial_info is None:
returns:
cfg, when the server has to (re)started with a new cfg
True, when the server is running and does not need a restart
None, when the server is not running, but does not need a restart
"""
if self._servers_loaded:
return None
if self._initial_config is None:
# we do this only once for all services # we do this only once for all services
fm = FrappyManager() fm = FrappyManager()
running = fm.get_cfg(config.instrument, None) running = fm.get_cfg(config.instrument, None)
cache = self._getCache() cache = self._getCache()
cfgs = {} cfgs = {}
for serv, secnode in zip(fm.services, self.nodes): for serv, secnode in zip(fm.services, self.nodes):
cfg = running.get(serv) if cache:
if not cfg and cache: cfg = cache.get(secnode, 'previous_config', '')
cfg = cache.get(secnode, 'value') if cfg:
if cfg: cfgs[serv] = cfg
cfgs[serv] = cfg self._initial_info = {s: (cfgs.get(s), running.get(s)) for s in fm.services}
running_main = running.get('main') fm.get_server_state(config.instrument, cfgs)
if running_main and running_main != cfgs.get('main'): if not fm.error:
# new main device: clear old stick
running_stick = running.get('stick')
if running_stick:
cfgs['stick'] = running_stick
else:
cfgs.pop('stick', None)
error, proposed, state, remarks = fm.get_server_state(config.instrument, cfgs)
self._initial_config = proposed
if not error:
# do not show server state on startup # do not show server state on startup
self._previous_state = self._previous_shown = state self._previous_state = self._previous_shown = fm.state
return self._initial_config.get(service) return self._initial_info[service]
def has_changed(self, show_server_state='auto'):
self._servers_loaded = True
self.show_config(self.check_services(), show_server_state)
def remove_aliases(self): def remove_aliases(self):
for meaning in self.meanings: for meaning in self.meanings:
@ -485,6 +521,9 @@ class FrappyConfig(Device):
session.log.debug('change alias %r -> %r', aliasname, devname) session.log.debug('change alias %r -> %r', aliasname, devname)
else: else:
session.log.debug('create alias %r -> %r', aliasname, devname) session.log.debug('create alias %r -> %r', aliasname, devname)
session.cache.put(aliasname, 'visibility', dev.visibility)
session.cache.put(aliasname, 'loglevel', dev.loglevel)
session.cache.put(aliasname, 'description', dev.description)
aliasdev = session.createDevice(aliasname, recreate=True, explicit=True) aliasdev = session.createDevice(aliasname, recreate=True, explicit=True)
aliasdev.alias = devname aliasdev.alias = devname
if aliasnames: if aliasnames:
@ -532,10 +571,13 @@ class FrappyNode(SecNodeDevice, Moveable):
'param_category': Param("category of parameters\n\n" 'param_category': Param("category of parameters\n\n"
"set to 'general' if all parameters should appear in the datafile header", "set to 'general' if all parameters should appear in the datafile header",
type=str, default='', settable=True), type=str, default='', settable=True),
'quiet_init': Param('flag to set loglevel to error while initializing',
type=bool, default=True, settable=True)
} }
_cfgvalue = None _cfgvalue = None
_lastcfg = None _lastcfg = None
__log_recording = ()
def doStart(self, value): def doStart(self, value):
if value == 'None': if value == 'None':
@ -548,16 +590,17 @@ class FrappyNode(SecNodeDevice, Moveable):
else: else:
fc = session.devices.get('frappy') fc = session.devices.get('frappy')
if fc: if fc:
cfg = fc.initial_restart_cfg(self.service) cfg, running = fc.get_init_info(self.service)
if isinstance(cfg, str): # may also be None or True self._cfgvalue = running
self.restart(cfg) self._setROParam('target', cfg)
if cfg is None: # None means: server is not running, and does not need to be restarted if cfg and (':' not in cfg and cfg != running):
self._disconnect() self._set_status(status.ERROR, 'cfg changed')
return return
try: if self.uri:
self._connect() try:
except Exception: self._connect()
pass except Exception:
pass
def doStop(self): def doStop(self):
"""never busy""" """never busy"""
@ -568,10 +611,6 @@ class FrappyNode(SecNodeDevice, Moveable):
def createDevices(self): def createDevices(self):
cfg = self.read() cfg = self.read()
super().createDevices() super().createDevices()
if cfg != self._lastcfg:
fc = get_frappy_config()
if fc:
fc._trigger_change.set()
if self.param_category: if self.param_category:
for devname, (_, devcfg) in self.setup_info.items(): for devname, (_, devcfg) in self.setup_info.items():
params_cfg = devcfg['params_cfg'] params_cfg = devcfg['params_cfg']
@ -581,21 +620,59 @@ class FrappyNode(SecNodeDevice, Moveable):
if not pinfo.category: if not pinfo.category:
pinfo.category = self.param_category pinfo.category = self.param_category
def makeDynamicDevices(self, setup_info):
patched_loggers = []
if self.quiet_init:
for devname, (_, devcfg) in setup_info.items():
log = session.getLogger(devname)
if log not in patched_loggers:
result = [loggers.INFO] # default level
patched_loggers.append((log, result))
log.setLevel(loggers.ERROR)
# avoid level change when the loglevel parameter is treated
# store level instead in result
log.__dict__['setLevel'] = result.append
try:
super().makeDynamicDevices(setup_info)
finally:
for log, result in patched_loggers:
log.__dict__.pop('setLevel', None) # re-enable setLevel
log.setLevel(result[-1]) # set to stored or default value
def showInitLog(self):
for devname, record in self.__log_recording:
session.getLogger(devname).handle(record)
self.__log_recording = ()
def nodeStateChange(self, online, state): def nodeStateChange(self, online, state):
super().nodeStateChange(online, state) print(f'NODE {self.service} {online} {state}')
# self.log.info('NODE %r %r', online, state)
if online: if online:
super().nodeStateChange(online, state)
if self._cfgvalue is None: if self._cfgvalue is None:
self._cfgvalue = FrappyManager().get_cfg(config.instrument, self.service) running_cfg = FrappyManager().get_cfg(config.instrument, self.service)
if not self._cfgvalue: if running_cfg:
if running_cfg != self.target:
self.log.warning(f'server info {running_cfg!r} does not match target cfg {self.target!r}')
self._cfgvalue = running_cfg
else:
self._cfgvalue = self.uri self._cfgvalue = self.uri
else: else:
self._cfgvalue = None if self.target == self._cfgvalue:
cfg = self.read() # SecNodeDevice.nodeStateChange will change status to 'reconnecting'
if self._lastcfg != cfg: super().nodeStateChange(online, state)
self._lastcfg = cfg else:
fc = get_frappy_config() # do not reconnect
if fc: self._cfgvalue = None
fc._trigger_change.set()
def descriptiveDataChange(self, module, description):
running_cfg = FrappyManager().get_cfg(config.instrument, self.service)
if not running_cfg or running_cfg == self.target:
super().descriptiveDataChange(module, description)
else:
self._disconnect(True)
self._cfgvalue = running_cfg
self._set_status(status.ERROR, 'cfg changed')
def disable(self): def disable(self):
seaconn = session.devices.get('sea_%s' % self.service) seaconn = session.devices.get('sea_%s' % self.service)
@ -615,47 +692,56 @@ class FrappyNode(SecNodeDevice, Moveable):
""" """
if cfg is None: if cfg is None:
cfg = self._cfgvalue cfg = self._cfgvalue
if cfg is None:
self.log.error('can not restart - previous cfg unknown')
return
ins = config.instrument ins = config.instrument
fm = FrappyManager() fm = FrappyManager()
info = fm.get_ins_info(ins) info = fm.get_ins_info(ins)
running_cfg = fm.get_cfg(ins, self.service) or '' running_cfg = fm.get_cfg(ins, self.service) or ''
if cfg is None:
self.log.error('can not restart - previous cfg unknown')
return
if cfg != running_cfg: if cfg != running_cfg:
self.disable() self.disable()
if running_cfg: if running_cfg:
self._disconnect() self._disconnect()
session.log.info('stop frappy_%s %r %r', self.service, running_cfg, cfg)
fm.do_stop(ins, self.service) fm.do_stop(ins, self.service)
is_cfg = cfg and ':' not in cfg try:
if is_cfg: is_cfg = cfg and ':' not in cfg
available_cfg = None
for cfgitem in cfg.split(','):
if not fm.is_cfg(config.instrument, self.service, cfgitem):
if available_cfg is None:
available_cfg = fm.all_cfg(config.instrument, self.service)
suggestions = suggest(cfgitem, available_cfg)
if suggestions:
session.log.error('%s unknown, did you mean: %s' % (cfgitem, ', '.join(suggestions)))
if available_cfg is not None:
raise ValueError('use "frappy_list()" to get a list of valid frappy configurations')
uri = 'localhost:%d' % info[self.service]
else:
uri = cfg
if uri != self.uri:
self.uri = '' # disconnect
if uri:
if is_cfg: if is_cfg:
fm.do_start(ins, self.service, cfg, logger=self.log) if cfg == 'reconnect':
self.uri = uri # connect is_cfg = False
self._cfgvalue = cfg else:
if self._cache: available_cfg = None
self._cache.put(self, 'value', cfg) for cfgitem in cfg.split(','):
self._setROParam('target', cfg) if not fm.is_cfg(config.instrument, self.service, cfgitem):
if available_cfg is None:
available_cfg = fm.all_cfg(config.instrument, self.service)
suggestions = suggest(cfgitem, available_cfg)
if suggestions:
session.log.error('%s unknown, did you mean: %s' % (cfgitem, ', '.join(suggestions)))
if available_cfg is not None:
raise ValueError('use "frappy_list()" to get a list of valid frappy configurations')
uri = 'localhost:%d' % info[self.service]
else:
uri = cfg
if uri != self.uri:
self.uri = '' # disconnect
if uri:
if is_cfg:
session.log.info('start frappy_%s', self.service)
fm.do_start(ins, self.service, cfg, logger=self.log)
self.uri = uri # connect
self._cfgvalue = cfg
if self._cache:
self._cache.put(self, 'value', cfg)
self._setROParam('target', cfg)
finally:
self._cache.put(self, 'previous_config', self._cfgvalue or self.uri)
def _disconnect(self): def _disconnect(self, keeptarget=False):
super()._disconnect() super()._disconnect()
self._setROParam('target', '') if not keeptarget:
self._setROParam('target', '')
def get_info(self): def get_info(self):
result = self.doRead() or '' result = self.doRead() or ''

View File

@ -18,7 +18,7 @@ devices = dict(
# device, using the given importance number, with similar values as # device, using the given importance number, with similar values as
# given by the SECoP standard (10: instrument, 20: cryostat, 30: insert) # given by the SECoP standard (10: instrument, 20: cryostat, 30: insert)
temperature = { # the SECoP meaning temperature = { # the SECoP meaning
'alias': ['Ts', 'temperature'], # the name(s) to be given to the alias 'alias': 'Ts', # the name(s) to be given to the alias
'targets': # possible devices in addition with importance 'targets': # possible devices in addition with importance
{'se_ts': 20, 'se_tt': 19, 'se_tm': 18}, {'se_ts': 20, 'se_tt': 19, 'se_tm': 18},
}, },
@ -28,7 +28,7 @@ devices = dict(
'drivable_only': True, 'drivable_only': True,
}, },
magneticfield = { magneticfield = {
'alias': ['B', 'magfield'], 'alias': 'B',
'targets': {'se_mf': 20}, 'targets': {'se_mf': 20},
}, },
pressure = { pressure = {
@ -62,5 +62,4 @@ printinfo(" frappy('') # remove main SE apparatus")
printinfo(" frappy() # show the current SE configuration") printinfo(" frappy() # show the current SE configuration")
printinfo("=======================================================================================") printinfo("=======================================================================================")
set_se_list() set_se_list()
frappy.has_changed()
''' '''