add various changes to superfrappy

not 100 % sure about the best state ...
This commit is contained in:
2026-04-23 16:26:49 +02:00
parent 51667171fe
commit 19f5f84848
2 changed files with 42 additions and 35 deletions
+34 -32
View File
@@ -111,17 +111,17 @@ class SecNode:
self.cfg = cfg
self.description = cfg
self.online = False
self._status = 'created'
self.status = 'created'
self.trigger = True
self.announce_pnp = True # None: do not announce, True: announce new, False: announce removal
self.setup_was_loaded = False
def connect(self, complete_callback=None, log=None):
self._status = 'connecting'
self.status = 'connecting'
self.log = log
self.complete_callback = complete_callback
if self.log:
self.log.debug('spawn connect')
self.log.info('spawn connect')
self.client.spawn_connect(self.complete)
def get_setup(self):
@@ -130,10 +130,10 @@ class SecNode:
def complete(self):
try:
self.online = True
self._status = 'completing'
self.status = 'completing'
self.nodename = self.client.nodename
if self.log:
self.log.debug('connected to %r', self.nodename)
self.log.info('connected to %r', self.nodename)
if self.complete_callback:
if not self.cfg:
if self.nodename == self.client.uri:
@@ -147,19 +147,16 @@ class SecNode:
self.complete_callback = None
desc = self.client.properties.get('description') or self.nodename
self.description = desc.split('\n')[0]
self._status = 'connected'
self.status = 'connected'
except Exception as e:
self.log.exception('connect failed')
self._status = f'disconnected {e!r}'
self.status = f'disconnected {e!r}'
def disconnect(self):
self.online = False
self._status = 'disconnecting'
self.status = 'disconnecting'
self.client.disconnect()
self._status = 'disconnected'
def status(self):
return self._status
self.status = 'disconnected'
def noop(*args):
@@ -184,7 +181,6 @@ class NicosCache(Communicator):
_sock = None
def doPoll(self):
self.log.info('doPoll')
if self._error:
self.log.error('%r', self._error)
self._error = None
@@ -213,7 +209,6 @@ class NicosCache(Communicator):
while True:
try:
self._connect(handler_lookup)
self._error = 'connected'
except Exception:
self._error = formatExtendedTraceback()
time.sleep(10)
@@ -260,6 +255,7 @@ class SuperFrappy(HasIO, Readable):
is_main_instrument = Property('this is the main instrument', BoolType(), default=True)
value = Parameter('running servers', ArrayOf(secnodetype), default=())
instance = Parameter('"this" or <instrument>', StringType())
plugplay = Parameter('enable plug and play', BoolType(), readonly=False, default=False)
nicos_setups = Parameter('active nicos se setups', ArrayOf(StringType()))
_marche = None
_secnodes = None # dict <host_post> of SecNode
@@ -292,7 +288,7 @@ class SuperFrappy(HasIO, Readable):
self.io.communicate(key + op + value)
def _handle_scripts(self, key, op, value):
self.log.debug('scripts %r', value)
self.log.info('scripts %r', value)
if value:
self._nicos_idle_since = None
self.setFastPoll(False)
@@ -303,19 +299,23 @@ class SuperFrappy(HasIO, Readable):
def _handle_setups(self, key, op, value):
self._setups = set(value)
self.log.debug('setups %r', value)
self.log.info('setups %r', value)
for secnode in self._secnodes.values():
setup = secnode.get_setup()
loaded = setup in self._setups
pnp = self._current_plugplay.get(secnode.nodename)
if loaded:
if pnp is True:
self._send_pnp_message(secnode.nodename, None, True)
if self.plugplay:
self._send_pnp_message(secnode.nodename, None, True)
secnode.status = 'loaded'
secnode.setup_was_loaded = True
else:
if pnp is False:
self._send_pnp_message(secnode.nodename, None, False)
if secnode.setup_was_loaded:
if self.plugplay:
self._send_pnp_message(secnode.nodename, None, False)
if secnode.setup_was_loaded and secnode.host == 'localhost' or not secnode.client.online:
secnode.status = 'to_close'
self._to_close.add(secnode.host_port)
def _handle_plugplay(self, key, op, value):
@@ -324,7 +324,7 @@ class SuperFrappy(HasIO, Readable):
self._current_plugplay.pop(key, None)
else:
self._current_plugplay[key] = value
self.log.debug('pnp %r', self._current_plugplay)
self.log.info('pnp %r', self._current_plugplay)
def _write_setup_file(self, secnode):
setup_file = Path(self.setupdir) / f'{secnode.get_setup()}.py'
@@ -359,24 +359,26 @@ class SuperFrappy(HasIO, Readable):
if secnode.announce_pnp is False:
if self._nicos_idle_since:
secnode.announce_pnp = None
self._send_pnp_message(nodename, setup, False)
if self.plugplay:
self._send_pnp_message(nodename, setup, False)
elif secnode.host_port not in self._to_close:
secnode.announce_pnp = False
else:
if secnode.online:
if not setup_file.is_file():
self.log.debug('write_setup %r', secnode.cfg)
self.log.info('write_setup %r', secnode.cfg)
self._write_setup_file(secnode)
if secnode.announce_pnp is True and self._nicos_idle_since:
secnode.announce_pnp = None
self._send_pnp_message(nodename, setup, True)
if self.plugplay:
self._send_pnp_message(nodename, setup, True)
self._to_close.discard(secnode.host_port)
for setup in self._setups:
# do not delete active setups
superfluous_setup_files.discard(Path(self.setupdir) / f'{setup}.py')
reload_marche = bool(superfluous_cfg_files)
for file in superfluous_setup_files | superfluous_cfg_files:
self.log.debug('remove %s', file)
self.log.info('remove %s', file)
try:
file.unlink()
except FileNotFoundError:
@@ -391,9 +393,9 @@ class SuperFrappy(HasIO, Readable):
if msg:
uri = msg['uri']
if uri in self._secnodes:
self.log.debug('%r is already known', msg)
self.log.info('%r is already known', msg)
else:
self.log.debug('%r appeared', msg)
self.log.info('%r appeared', msg)
cfg = msg.get('device', '')
if uri.startswith('localhost:') and cfg != 'superfrappy':
self.connect(uri)
@@ -411,7 +413,7 @@ class SuperFrappy(HasIO, Readable):
def read_value(self):
value = []
for secnode in self._secnodes.values():
info = {'cfg': secnode.cfg, 'uri': secnode.host_port, 'status': secnode.status()}
info = {'cfg': secnode.cfg, 'uri': secnode.host_port, 'status': secnode.status}
value.append(info)
return value
@@ -442,9 +444,9 @@ class SuperFrappy(HasIO, Readable):
host_port = normalizeuri(host_port, True)
secnode = self._secnodes.get(host_port)
if secnode:
self.log.debug('already connected %r', host_port)
self.log.info('already connected %r', host_port)
else:
self.log.debug('connect secnode %r', host_port)
self.log.info('connect secnode %r', host_port)
secnode = SecNode(host_port, self._marche.cfg_info.get(host_port, ''))
self._secnodes[host_port] = secnode
secnode.connect(log=self.log)
@@ -474,13 +476,13 @@ class SuperFrappy(HasIO, Readable):
:param service: 'stick', 'main', '' or a stringified port number
:param cfg: config file or equipment id
"""
self.log.debug('add and start %r', cfg)
self.log.info('add and start %r', cfg)
port = self._marche.get_port(service)
host_port = f'localhost:{port}'
secnode = SecNode(host_port, cfg)
self._secnodes[host_port] = secnode
self._marche.add_frappy_service(service, secnode.cfg, secnode.port, self.log)
self.log.debug('start %r at %r', secnode.cfg, host_port)
self.log.info('start %r at %r', secnode.cfg, host_port)
self._marche.start(secnode.cfg)
secnode.connect(log=self.log)
self.read_value()
@@ -488,7 +490,7 @@ class SuperFrappy(HasIO, Readable):
def _remove_secnode(self, secnode):
secnode.announce = False
secnode.disconnect()
self.log.debug('secnode.host %r', secnode)
self.log.info('secnode.host %r', secnode)
if secnode.host == 'localhost':
self._marche.stop(secnode.cfg)
self._update()
+8 -3
View File
@@ -27,7 +27,7 @@ import logging
MARCHESRC = ['/home/software/marche']
CFGDIRS = ['/home/linse/config', '/home/l_samenv/linse_config']
# CFGDIRS = ['/home/linse/config', '/home/l_samenv/linse_config']
def get_logger(previous=[]):
@@ -144,8 +144,8 @@ class FrappyMarche(MarcheControl):
def __init__(self, instance, host='localhost', port=None, user=None):
parser = ConfigParser()
parser.optionxform = str
gencfg = f'~/.config/frappy/{instance}.cfg'
parser.read([str(Path(gencfg).expanduser())])
gencfg = '/sq_sw/linse/frappycfg/generalConfig.cfg'
parser.read([gencfg])
try:
section = dict(parser['superfrappy'])
except KeyError:
@@ -208,6 +208,11 @@ class FrappyMarche(MarcheControl):
return {sorted(v)[-1][-1]: k for k, v in result.items()}
def get_port(self, service):
"""get a port number for service
return a predefined port number for 'main' and 'stick'
or a free port number for 'addons'
"""
if service not in {'main', 'stick', 'addons', 'addon'}:
raise ValueError('illegal service argument')
ports = self.get_std_port(service)