move files in from repo git.psi.ch/sinqdev/nicos-sinq
This commit is contained in:
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
log/*
|
||||
html/*
|
||||
*.pyc
|
||||
pid/*
|
||||
__pycache__
|
||||
|
||||
# ide
|
||||
.idea
|
||||
|
||||
# Mac
|
||||
.DS_Store
|
||||
._*
|
||||
|
||||
# pytest
|
||||
.cache
|
||||
.coverage
|
||||
|
||||
# pyinstaller
|
||||
dist/
|
207
commands.py
Normal file
207
commands.py
Normal file
@ -0,0 +1,207 @@
|
||||
# -*- 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.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
|
||||
|
||||
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.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."""
|
||||
# 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
|
||||
for aliasname, targets in session.alias_config.items():
|
||||
if aliasname not in session.devices:
|
||||
# complain about this; setups should make sure that the device
|
||||
# exists when configuring it
|
||||
session.log.warning("alias device '%s' does not exist, cannot set"
|
||||
' its target', aliasname)
|
||||
continue
|
||||
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
|
||||
else:
|
||||
session.log.warning('none of the desired targets for alias %r '
|
||||
'actually exist', aliasname)
|
||||
|
||||
|
||||
def frappy_start(service, cfg=None):
|
||||
if service not in SERVICES:
|
||||
raise ValueError('unknown service %s' % service)
|
||||
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()
|
||||
applyAliasConfig()
|
||||
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)
|
||||
|
||||
|
150
devices.py
Normal file
150
devices.py
Normal file
@ -0,0 +1,150 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
# NICOS, the Networked Instrument Control System of the MLZ
|
||||
# Copyright (c) 2009-2018 by the NICOS contributors (see AUTHORS)
|
||||
#
|
||||
# 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>
|
||||
#
|
||||
# *****************************************************************************
|
||||
"""managing SECoP server and connections
|
||||
|
||||
SEC Node with added functionality for starting and stoping frappy servers
|
||||
connected to a SEC node
|
||||
"""
|
||||
|
||||
import os
|
||||
from os.path import expanduser
|
||||
|
||||
from nicos import config, session
|
||||
from nicos.core import Override, Param, Moveable, status
|
||||
from nicos.devices.secop import SecNodeDevice
|
||||
from nicos.utils.comparestrings import compare
|
||||
from servicemanager import FrappyManager
|
||||
|
||||
|
||||
def suggest(poi, allowed_keys):
|
||||
comp = {}
|
||||
for key in allowed_keys:
|
||||
comp[key] = compare(poi, key)
|
||||
comp = sorted(comp.items(), key=lambda t: t[1], reverse=True)
|
||||
return [m[0] for m in comp[:3] if m[1] > 2]
|
||||
|
||||
|
||||
class FrappyNode(SecNodeDevice, Moveable):
|
||||
"""SEC node device
|
||||
|
||||
with ability to start / restart / stop the frappy server
|
||||
"""
|
||||
|
||||
parameter_overrides = {
|
||||
'target': Override(description='configuration for the frappy server or host:port',
|
||||
type=str, default=''),
|
||||
}
|
||||
parameters = {
|
||||
'service': Param('frappy service name (main, stick or addons)', type=str, default=''),
|
||||
}
|
||||
|
||||
_service_manager = FrappyManager()
|
||||
_cfgvalue = None
|
||||
|
||||
def doStart(self, value):
|
||||
if value == 'None':
|
||||
value = None
|
||||
self.restart(value)
|
||||
|
||||
def doStop(self):
|
||||
"""never busy"""
|
||||
|
||||
def doRead(self, maxage=0):
|
||||
if self._cfgvalue is None and self._cache:
|
||||
self._cfgvalue = self._cache.get(self, 'value')
|
||||
return self._cfgvalue
|
||||
|
||||
@classmethod
|
||||
def config_dirs(cls, ins, service):
|
||||
sm = cls._service_manager
|
||||
sm.get_info()
|
||||
return sm.config_dirs(ins, service)
|
||||
|
||||
@classmethod
|
||||
def available_cfg(cls, service):
|
||||
ins = config.instrument
|
||||
available_cfg = set()
|
||||
for d in cls.config_dirs(ins, service):
|
||||
try:
|
||||
available_cfg |= set(c[:-4] for c in os.listdir(expanduser(d)) if c.endswith('.cfg'))
|
||||
except FileNotFoundError: # ignore missing directories
|
||||
pass
|
||||
return available_cfg
|
||||
|
||||
def disable(self):
|
||||
seaconn = session.devices.get('seaconn')
|
||||
if seaconn and seaconn._attached_secnode:
|
||||
seaconn.communicate('frappy_remove %s' % self.service)
|
||||
self._set_status(*self._status)
|
||||
|
||||
def _set_status(self, code, text):
|
||||
if self.uri == '':
|
||||
code, text = status.DISABLED, 'disabled'
|
||||
SecNodeDevice._set_status(self, code, text)
|
||||
|
||||
def restart(self, cfg=None):
|
||||
"""restart frappy server
|
||||
|
||||
if value is not given: restart with the same config
|
||||
else restart with given config
|
||||
"""
|
||||
was_cfg = self._cfgvalue and ':' not in self._cfgvalue
|
||||
if cfg is None: # do restart anyway
|
||||
cfg = self._cfgvalue
|
||||
elif cfg == self._cfgvalue: # probably called from doStart
|
||||
if cfg == '':
|
||||
self.disable()
|
||||
# do not restart in this case
|
||||
return
|
||||
is_cfg = cfg and ':' not in cfg
|
||||
ins = config.instrument
|
||||
info = self._service_manager.get_ins_info(ins)
|
||||
if is_cfg:
|
||||
available_cfg = self.available_cfg(self.service)
|
||||
failed = False
|
||||
for cfgitem in cfg.split(','):
|
||||
if cfgitem not in available_cfg:
|
||||
failed = True
|
||||
suggestions = suggest(cfgitem, available_cfg)
|
||||
if suggestions:
|
||||
session.log.error('%s unknown, did you mean: %s' % (cfgitem, ', '.join(suggestions)))
|
||||
if failed:
|
||||
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 was_cfg:
|
||||
self._service_manager.do_stop(ins, self.service)
|
||||
if uri:
|
||||
if is_cfg:
|
||||
self._service_manager.do_restart(ins, self.service, cfg, self.log)
|
||||
self.uri = uri # connect
|
||||
else:
|
||||
self.disable()
|
||||
self._cfgvalue = cfg
|
||||
if self._cache:
|
||||
self._cache.put(self, 'value', cfg)
|
||||
self._setROParam('target', cfg)
|
26
setups/frappy.py
Normal file
26
setups/frappy.py
Normal file
@ -0,0 +1,26 @@
|
||||
description = 'frappy'
|
||||
group = 'optional'
|
||||
|
||||
modules = ['nicos_sinq.frappy.commands']
|
||||
|
||||
devices = {
|
||||
'temperature': device('nicos.devices.generic.DeviceAlias'),
|
||||
'magfield': device('nicos.devices.generic.DeviceAlias'),
|
||||
}
|
||||
alias_config = {
|
||||
'temperature': {'se_ts': 110, 'se_tt': 100},
|
||||
'magfield': {'se_mf': 100},
|
||||
}
|
||||
|
||||
startupcode = '''
|
||||
printinfo("=======================================================================================")
|
||||
printinfo("Welcome to the NICOS frappy secnode setup!")
|
||||
printinfo(" ")
|
||||
printinfo("Usage:")
|
||||
printinfo(" frappy_main('<main cfg>') # change main SE configuration (e.g. cryostat)")
|
||||
printinfo(" frappy_stick('<stick cfg>') # change sample-stick configuration")
|
||||
printinfo(" frappy_stick('') # remove stick")
|
||||
printinfo(" frappy_main('') # remove main SE apparatus")
|
||||
printinfo(" frappy_main() # show the current SE configuration")
|
||||
printinfo("=======================================================================================")
|
||||
'''
|
11
setups/frappy_addons.py
Normal file
11
setups/frappy_addons.py
Normal file
@ -0,0 +1,11 @@
|
||||
from os import environ
|
||||
description = 'frappy addons'
|
||||
group = 'optional'
|
||||
|
||||
devices = {
|
||||
'se_addons':
|
||||
device('nicos_sinq.frappy.devices.FrappyNode',
|
||||
description='SEC node', unit='',
|
||||
prefix='se_', auto_create=True, service='addons',
|
||||
),
|
||||
}
|
11
setups/frappy_main.py
Normal file
11
setups/frappy_main.py
Normal file
@ -0,0 +1,11 @@
|
||||
from os import environ
|
||||
description = 'frappy main setup'
|
||||
group = 'optional'
|
||||
|
||||
devices = {
|
||||
'se_main':
|
||||
device('nicos_sinq.frappy.devices.FrappyNode',
|
||||
description='main SEC node', unit='',
|
||||
prefix='se_', auto_create=True, service='main',
|
||||
),
|
||||
}
|
11
setups/frappy_stick.py
Normal file
11
setups/frappy_stick.py
Normal file
@ -0,0 +1,11 @@
|
||||
from os import environ
|
||||
description = 'frappy stick setup'
|
||||
group = 'optional'
|
||||
|
||||
devices = {
|
||||
'se_stick':
|
||||
device('nicos_sinq.frappy.devices.FrappyNode',
|
||||
description='stick SEC node', unit='',
|
||||
prefix='se_', auto_create=True, service='stick',
|
||||
),
|
||||
}
|
Reference in New Issue
Block a user