Files
seweb/base.py
T
zolliker 1201801170 break busy loop blocking the server
- Use gevent.sleep with a tiny value after a message has been
  received instead of no call to sleep at all. This was the
  reason for the webserver to block when used with prep8
  and the leiden dil with a lot of data
- move gevent.monkey.patch_all() to secop-webserver
- properly close clients
2026-05-04 13:19:33 +02:00

132 lines
4.5 KiB
Python

import sys
import time
import uuid
ONEYEAR = 366 * 24 * 3600
def get_abs_time(times):
"""Gets the absolute times for the given potential relative times.
If a given timestamp is less than one year, then the value is
relative (to now, rounded up to a full second) and converted
into an absolute timestamp
Parameters :
times([(float)]) : an array of unix timestamps or relative duration (< 1 year) as floats
Returns :
[(float)] : an array of absolute unix timestamps as floats
"""
now = int(time.time() + 0.999)
return [t + now if t < ONEYEAR else t for t in times]
class Logger(object):
def __init__(self, logpath):
self.terminal = sys.stdout
self.log = open(logpath, "a")
def write(self, message):
self.terminal.write(message)
self.log.write(message)
def flush(self):
pass
class HandlerBase:
def __init__(self):
self.handlers = {k[2:]: getattr(self, k) for k in dir(type(self)) if k.startswith('w_')}
class Client(HandlerBase):
"""a generic parameter client"""
dictionary = {} # dict (kind, uri) of node
def __init__(self, server, streams, instrument_name, device_name):
super().__init__()
self.id = uuid.uuid4().hex[0:15]
self.nodes = {}
self.node_map = {}
self.update_filter = set() # modules to be updated
self.cache = {} # a dict to store the current state of all values
if streams:
for uri in streams:
urisplit = uri.rsplit('://')
kind = urisplit[0] if len(urisplit) == 2 else 'secop'
node = self.dictionary.get((kind, uri))
if node is None:
def change_callback(dictionary=self.dictionary, kind_uri=(kind, uri)):
# do not keep changed nodes in dict
dictionary.pop(kind_uri, None)
node = server.interactor_classes[kind](uri, self.node_map, change_callback)
self.dictionary[kind, uri] = node
else:
node.update_node_map(self.node_map)
self.nodes[uri] = node
self.server = server
self.instrument_name = instrument_name
self.device_name = device_name # do not know if this is needed
def close(self):
"""remove unused nodes from dictionary
The last one turns off the lights
"""
for cl in self.server.clients.values():
if cl is not self and isinstance(cl, Client):
for uri in cl.nodes:
# this node is still used
self.nodes.pop(uri, None)
# remaining in self.nodes are to be removed from dictionary
for uri, node in self.nodes.items():
print(f'disconnect {uri}')
self.dictionary.pop(uri, None)
def poll(self):
updates = sum((n.get_updates(self.cache, self.update_filter) for n in self.nodes.values()), start=[])
result = [dict(type='update', updates=updates)] if updates else []
graph_updates = self.handlers.get('graphpoll', type(None))()
if graph_updates:
result.append(graph_updates)
return result
def w_getblock(self, path):
path = path.split(',')[-1] # TODO: why this?
if path == "main": # TODO: change to "-main-"?
components = []
for node in self.nodes.values():
node.add_main_components(components)
return dict(type='draw', path='main', title='modules', components=components)
try:
node = self.node_map[path]
except KeyError:
raise RuntimeError(f'ERROR path={path}')
return dict(type='draw', path=path, title=path, components=node.get_components(path))
def w_updateblock(self, path):
if path == 'main': # TODO: change to "-main-"?
self.update_filter.add('') # ready to accept updates for main block
else:
self.update_filter.add(path)
return dict(type='accept-block')
def w_console(self): # TODO: check if still used
return dict(type='accept-console')
def w_sendcommand(self, command):
result = None
for node in self.nodes.values():
result = node.handle_command(command)
if result is not None:
break
if isinstance(result, dict):
return dict(type='accept-command', result=result)
return dict(type='accept-command')
def info(self):
return ["na"]