- add rotation_x to the list - allow to configure items creating an alias without entry in envlist
291 lines
11 KiB
Python
291 lines
11 KiB
Python
# -*- 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>
|
|
#
|
|
# *****************************************************************************
|
|
|
|
import sys
|
|
from os.path import expanduser, basename, join
|
|
from glob import glob
|
|
from configparser import ConfigParser
|
|
|
|
from nicos import session, config
|
|
from nicos.core import DeviceAlias, Moveable
|
|
from nicos.utils import printTable
|
|
from nicos.commands import helparglist, usercommand
|
|
from nicos.commands.basic import AddSetup, CreateAllDevices, CreateDevice
|
|
from nicos.devices.secop import get_attaching_devices, SecopDevice
|
|
|
|
home = expanduser('~')
|
|
if home not in sys.path:
|
|
# the first Frappy installations have /home/nicos in the PYTHONPATH in nicos.conf
|
|
# for newer Frappy installations this should be home (= /home/<instrument>)
|
|
# the following line fixes this in case nicos.conf is not yet updated
|
|
sys.path.append(home)
|
|
from nicos_sinq.frappy_sinq.devices import FrappyNode
|
|
from servicemanager import FrappyManager
|
|
|
|
|
|
def cleanup_defunct():
|
|
for devname, setupname in list(session.dynamic_devices.items()):
|
|
dev = session.devices.get(devname)
|
|
if dev and dev._defunct:
|
|
devnames = [d.name for d, _ in get_attaching_devices(dev)]
|
|
if devnames:
|
|
session.log.warning('can not remove device %r due to dependencies on %s'
|
|
% (devname, ', '.join(devnames)))
|
|
else:
|
|
session.destroyDevice(devname)
|
|
session.dynamic_devices.pop(devname, None)
|
|
|
|
|
|
SERVICES = FrappyManager.services
|
|
|
|
|
|
def all_info(all_cfg):
|
|
info = []
|
|
for srv, cfginfo in all_cfg.items():
|
|
if cfginfo is not None:
|
|
info.append('frappy_%s(%r)' % (srv, cfginfo))
|
|
return 'currently configured: %s' % ', '.join(info)
|
|
|
|
|
|
def applyAliasConfig():
|
|
"""Apply the desired aliases from session.alias_config.
|
|
|
|
be more quiet than original
|
|
"""
|
|
# reimplemented from Session.applyAliasConfig
|
|
# apply also when target dev name does not change, as the target device might have
|
|
# be exchanged in the mean time
|
|
unused = set()
|
|
for aliasname, targets in session.alias_config.items():
|
|
if aliasname not in session.devices:
|
|
continue # silently ignore
|
|
aliasdev = session.getDevice(aliasname)
|
|
for target, _ in sorted(targets, key=lambda t: -t[1]):
|
|
if target in session.devices:
|
|
try:
|
|
aliasdev.alias = target
|
|
except Exception:
|
|
session.log.exception("could not set '%s' alias", aliasdev)
|
|
break
|
|
|
|
|
|
def remove_se_aliases():
|
|
frappy_config = session.getDevice('frappy_config')
|
|
configured = {}
|
|
for meaning in frappy_config.meanings:
|
|
info = getattr(frappy_config, meaning)
|
|
aliasname = info['alias']
|
|
aliasdev = session.devices.get(aliasname)
|
|
if aliasdev:
|
|
session.destroyDevice(aliasname)
|
|
session.configured_devices.pop(aliasname, None)
|
|
session.dynamic_devices.pop(aliasname, None)
|
|
|
|
@usercommand
|
|
def set_se_list():
|
|
"""create aliases for SECoP devices
|
|
|
|
depending on their meaning
|
|
"""
|
|
remove_se_aliases()
|
|
|
|
frappy_config = session.getDevice('frappy_config')
|
|
|
|
nodedevs = filter(None, [session.devices.get(devname) for devname in frappy_config.nodes])
|
|
sample_devices = {}
|
|
for nodedev in nodedevs:
|
|
secnode = nodedev._secnode
|
|
if not secnode:
|
|
continue
|
|
for devname, (_, desc) in nodedev.setup_info.items():
|
|
secop_module = desc['secop_module']
|
|
meaning = secnode.modules[secop_module]['properties'].get('meaning')
|
|
if meaning:
|
|
meaning_name, importance = meaning
|
|
sample_devices.setdefault(meaning_name, []).append((importance, devname))
|
|
|
|
devset = set()
|
|
newenv = {e: None for e in session.experiment.envlist} # use dict instead of set because of order
|
|
drivables = {}
|
|
for meaning in frappy_config.meanings:
|
|
info = getattr(frappy_config, meaning)
|
|
aliasname = info['alias']
|
|
envlistflag = info.get('envlist', True)
|
|
aliascfg = info.get('targets', {})
|
|
importance_list = sample_devices.get(meaning, [])
|
|
importance_list.extend([(nr, nam) for nam, nr in aliascfg.items() if nam in session.devices])
|
|
importance_list = sorted(importance_list, reverse=True)
|
|
session.log.debug('%s: %r', meaning, importance_list)
|
|
prefix, _, postfix = meaning.partition('_')
|
|
if postfix == 'drivable':
|
|
# append the previously collected drivables in the group to the importance_list
|
|
group = drivables.get(prefix, {})
|
|
for key in 'regulation', '':
|
|
if key in group:
|
|
importance_list.append((None, group[key]))
|
|
for _, devname, in importance_list:
|
|
dev = session.devices.get(devname)
|
|
if isinstance(dev, Moveable):
|
|
drivables.setdefault(prefix, {})[postfix] = devname
|
|
elif postfix == 'drivable':
|
|
# marked as xxx_drivable, but not really drivable: skip
|
|
continue
|
|
if dev:
|
|
session.log.debug('create alias %r pointing to %r', aliasname, devname)
|
|
devcfg = ('nicos.core.DeviceAlias', {})
|
|
session.configured_devices[aliasname] = devcfg
|
|
session.dynamic_devices[aliasname] = 'frappy'
|
|
aliasdev = session.createDevice(aliasname, recreate=True, explicit=True)
|
|
aliasdev.alias = devname
|
|
if devname not in devset and envlistflag:
|
|
# take only the first one
|
|
devset.add(devname)
|
|
newenv[aliasname] = None
|
|
else:
|
|
newenv.pop(aliasname, None)
|
|
break
|
|
else:
|
|
newenv.pop(aliasname, None)
|
|
|
|
applyAliasConfig() # for other aliases
|
|
if set(newenv) != set(session.experiment.envlist):
|
|
session.experiment.setEnvironment(list(newenv))
|
|
session.log.info('changed environment to: %s', ', '.join(session.experiment.envlist))
|
|
|
|
|
|
def frappy_start(service, cfg=None):
|
|
if service not in SERVICES:
|
|
raise ValueError('unknown service %s' % service)
|
|
remove_se_aliases()
|
|
if cfg == '':
|
|
seaconn = session.devices.get('seaconn')
|
|
if seaconn and seaconn._attached_secnode:
|
|
seaconn.communicate('frappy_remove %s' % service)
|
|
startnode = None
|
|
all_cfg = {}
|
|
for srv in SERVICES:
|
|
nodename = 'se_' + srv
|
|
secnode = session.devices.get(nodename)
|
|
|
|
cfginfo = None if secnode is None else secnode()
|
|
if srv == service:
|
|
if cfg is not None:
|
|
if cfg and not secnode:
|
|
AddSetup('frappy_' + service)
|
|
secnode = session.devices[nodename]
|
|
if cfginfo:
|
|
secnode('')
|
|
startnode = secnode
|
|
cfginfo = cfg
|
|
elif cfg and cfginfo:
|
|
# remark: cfg might be a comma separated list of configurations
|
|
ourcfg = set(cfg.split(','))
|
|
other = set(cfginfo.split(','))
|
|
both = other & ourcfg
|
|
if both:
|
|
# remove ourcfg from other
|
|
cfginfo = ','.join(other - ourcfg)
|
|
# stop other server with the same cfg
|
|
# or restart with remaining cfgs
|
|
secnode(cfginfo)
|
|
all_cfg[srv] = cfginfo
|
|
if startnode and cfg:
|
|
startnode(cfg)
|
|
CreateDevice(str(startnode))
|
|
if cfg is not None:
|
|
cleanup_defunct()
|
|
CreateAllDevices()
|
|
set_se_list()
|
|
if startnode and cfg == '':
|
|
startnode.disable()
|
|
return all_cfg
|
|
|
|
|
|
@usercommand
|
|
@helparglist('cfg')
|
|
def frappy_main(cfg=None):
|
|
"""(re)start frappy_main server with given cfg and load setup if needed
|
|
|
|
- without argument: list running frappy servers
|
|
- cfg = "": stop frappy_main server
|
|
"""
|
|
all_cfg = frappy_start('main', cfg)
|
|
if cfg:
|
|
stickcfg = cfg + 'stick'
|
|
if stickcfg in FrappyNode.available_cfg('stick'):
|
|
# if a default stick is available, start this also
|
|
all_cfg = frappy_start('stick', stickcfg)
|
|
session.log.info(all_info(all_cfg))
|
|
|
|
|
|
@usercommand
|
|
@helparglist('cfg')
|
|
def frappy_stick(cfg=None):
|
|
"""(re)start frappy_stick server with given cfg and load setup if needed
|
|
|
|
- without argument: list running frappy servers
|
|
- cfg = "": stop frappy_stick server
|
|
"""
|
|
session.log.info(all_info(frappy_start('stick', cfg)))
|
|
|
|
|
|
@usercommand
|
|
@helparglist('cfg,...')
|
|
def frappy_addons(cfg=None):
|
|
"""(re)start frappy_addons server with given cfg and load setup if needed
|
|
|
|
- without argument: list running frappy servers
|
|
- cfg = "": stop frappy_addons server
|
|
"""
|
|
session.log.info(all_info(frappy_start('addons', cfg)))
|
|
|
|
|
|
@usercommand
|
|
@helparglist('')
|
|
def frappy_list(service=None):
|
|
"""list available configuration files"""
|
|
table = []
|
|
bases = list(dict.fromkeys(expanduser(p) for p in FrappyNode.config_dirs(config.instrument, service or 'main')))
|
|
if service is None:
|
|
session.log.info('Available configuration files')
|
|
session.log.info(' ')
|
|
session.log.info('Hint: if no config file can be found which matches your needs exactly')
|
|
session.log.info('make a copy of an existing one, and change the description accordingly')
|
|
session.log.info(' ')
|
|
session.log.info('Usage (default argument "main"):')
|
|
session.log.info(' ')
|
|
printTable(['command'], [['frappy_list(%r)' % s] for s in SERVICES], session.log.info)
|
|
session.log.info(' ')
|
|
for cfgdir in bases:
|
|
for cfgfile in glob(join(cfgdir, '*.cfg')):
|
|
parser = ConfigParser()
|
|
parser.read(cfgfile)
|
|
desc = ''
|
|
for s in parser.sections():
|
|
if s == 'NODE' or s.startswith('node '):
|
|
desc = parser[s].get('description', '').split('\n')[0]
|
|
break
|
|
table.append([basename(cfgfile)[:-4], desc])
|
|
printTable(['cfg file', 'description'], table, session.log.info)
|
|
|
|
|