diff --git a/client/jsFiles/SEAWebClientMain.js b/client/jsFiles/SEAWebClientMain.js index 0a464d6..ca65929 100644 --- a/client/jsFiles/SEAWebClientMain.js +++ b/client/jsFiles/SEAWebClientMain.js @@ -73,10 +73,10 @@ new Settings() .treat("debugGraphics", "dg", to_bool, false) .treat("hostPort", "hp", 0, location.hostname + ":" + location.port) .treat("showMain", "sm", to_bool, true) - .treat("showConsole", "sc", to_bool, true) + .treat("showConsole", "sc", to_bool, false) .treat("showOverview", "so", to_bool, false) .treat("showGraphics", "sg", to_bool, true) // false) - .treat("hideRightPart", "hr", to_bool, true) //used to completely disable the right part + .treat("hideRightPart", "hr", to_bool, false) //used to completely disable the right part .treat("wideGraphs", "wg", to_bool, false) //used to toggle the size of the graphs part .treat("showAsync", "sa", to_bool, false) diff --git a/client/jsFiles/SEAWebClientResponsivity.js b/client/jsFiles/SEAWebClientResponsivity.js index 6f008a2..bdf0671 100644 --- a/client/jsFiles/SEAWebClientResponsivity.js +++ b/client/jsFiles/SEAWebClientResponsivity.js @@ -94,7 +94,7 @@ function adjustGrid() { case 2: rightWidth = Math.min(50, MINWIDTH / width * 100); leftWidth = 100 - rightWidth; - if (nRows == 1) { + if (nRows == 1 || !window['showConsole']) { style(0,leftWidth + "vw","100vh"); style(1,rightWidth + "vw","100vh"); style(2); // hide @@ -109,7 +109,7 @@ function adjustGrid() { case 3: rightWidth = MINWIDTH / width * 100; leftWidth = 100 - rightWidth; - if (nRows == 1) { + if (nRows == 1 || !window['showConsole']) { style(0,leftWidth + "vw","100vh"); style(1,rightWidth + "vw","100vh"); style(2); // hide @@ -124,11 +124,16 @@ function adjustGrid() { case 4: rightWidth = MINWIDTH / width * 100; leftWidth = 100 - 2 * rightWidth; - if (nRows == 1) { + if (nRows == 1 || !window['showConsole']) { style(0,leftWidth + "vw","100vh"); style(1,rightWidth + "vw","100vh"); - style(2); // hide - style(3,rightWidth + "vw","100vh"); + if (window['showConsole']) { + style(2); // hide + style(3,rightWidth + "vw","100vh"); + } else { + style(2,rightWidth + "vw","100vh"); + style(3); // hide + } } else { style(0,leftWidth + "vw","100vh"); style(1,rightWidth + "vw","50vh"); diff --git a/seaweb.py b/seaweb.py index 9fd37cf..aa6023d 100755 --- a/seaweb.py +++ b/seaweb.py @@ -133,13 +133,12 @@ def export(): id = kwargs.pop('id') client = instrument.clients[id] bytes = client.w_export(**kwargs) - return flask.send_file( bytes, as_attachment=True, download_name='export.tsv', mimetype='text/tab-separated-values' - ) + ) except Exception as e: logging.error('%s', traceback.format_exc()) @@ -213,7 +212,11 @@ def default(): @app.route('/') def general_file(file): subdir = "client/" - resp = flask.send_file(subdir+file, mimetype=guess_mimetype(file)) + try: + resp = flask.send_file(subdir+file, mimetype=guess_mimetype(file)) + except FileNotFoundError: + logging.warning('file %s not found', file) + return 'file not found' #resp.headers['Content-Security-Policy'] = "sandbox; script-src 'unsafe-inline';" return resp @@ -920,21 +923,18 @@ def convert_event(messages): for msg in messages: if msg.type == 'update': updates.append(dict(name=msg.par, value=msg.value[0])) + elif msg.type == 'error_update': + updates.append(dict(name=msg.par, value=f'{msg.value[0]} - {msg.value[1]}')) # updates.append(dict(name=msg.par, value=str(msg.value[0]))) return [dict(type='update', updates=updates)] -class SecopClient: - prio_par = ["value", "status", "target"] - hide_par = ["baseclass", "class", "pollinterval"] - skip_par = ["status2"] - +class SecNodeClient: def __init__(self, host_port): - self.linesocket = tcp_lineserver.LineClient(host_port) - self.id = uuid.uuid4().hex[0:15] + self.linesocket = tcp_lineserver.LineClient(hostport_split(host_port)) + self.consolequeue = [] self.queue = [] self.syncreply = [] - self.consolequeue = [] #self.out = open("debug.txt", "w") #self.out = sys.stdout self.out = None @@ -948,45 +948,83 @@ class SecopClient: self.consolequeue.append(dict(type='command',line=command,origin='self')) self.linesocket.send_line(command) t = 0 + delta = 0.001 while True: if self.syncreply: msg = self.syncreply.pop(0) break line = self.linesocket.get_line() - if line != None: - if not line.startswith('update'): + if line is not None: + if not line.startswith(('update', 'error_update')): self.consolequeue.append(dict(type='reply',line=line,origin='other')) if self.out: self.out.write("<"+line+"\n") msg = SecopMsg(line) - #print '<', msg.type, msg.par if msg.asynch: # and replytype != msg['type'] + "=" + msg.par: t = 0 self.queue.append(msg) else: break if t >= tmo: - #print 'TIMEOUT' raise Exception("timeout") - gevent.sleep(0.001) - t += 0.001 - #print 'REPLY', msg['type'], msg.par, json.dumps(msg.value)[0:50] + gevent.sleep(delta) + t += delta + delta += 0.001 if not replytype.startswith(msg.type): logging.error('REPLY MISMATCH %s <> %s', replytype, repr(msg)) self.replytype = "" return msg + def poll(self): + if self.consolequeue: + messages = self.consolequeue + self.consolequeue = [] + return messages + if self.queue: + messages = convert_event(self.queue) + self.queue = [] + return messages + line = self.linesocket.get_line() + if line: + if not line.startswith(('update', 'error_update')): + self.consolequeue.append(dict(type='reply',line=line,origin='other')) + if self.out: self.out.write("<"+line+"\n") + msg = SecopMsg(line) + if msg.asynch: # and self.replytype != msg['type'] + "=" + msg.par: + return convert_event(SecopMsg(line)) + self.syncreply.append(msg) + return [] + + + +class SecopClient: + prio_par = ["value", "status", "target"] + hide_par = ["baseclass", "class", "pollinterval"] + skip_par = ["status2"] + + def __init__(self, host_ports): + self.nodes = [] + self.node_map = {} + for host_port in host_ports.split(','): + node = SecNodeClient(host_port) + self.nodes.append(node) + for name, mod in node.description["modules"].items(): + self.node_map[name] = node + self.id = uuid.uuid4().hex[0:15] + def w_getblock(self, path): path = path.split(',')[-1] if path == "main": components = [] - for name, m in self.description["modules"].items(): - #components.append(convert_par(name, 'value', m['parameters']['value'])) - components.append(dict(type='rdlink', name=name+':value', title=name)) + for node in self.nodes: + for name, m in node.description["modules"].items(): + #components.append(convert_par(name, 'value', m['parameters']['value'])) + components.append(dict(type='rdlink', name=name+':value', title=name)) #print components return dict(type='draw', path='main', title='modules', components=components) else: - module = self.description['modules'][path] - logging.info('MP %r %r', path, module) + node = self.node_map[path] + module = node.description['modules'][path] + logging.info('MP %r %r', path) parameters = dict(module["accessibles"]) components = [] for name in SecopClient.skip_par: @@ -1007,48 +1045,40 @@ class SecopClient: def w_updateblock(self, path): if path == 'main': path = '' - self.cmd_reply(f"activate {path}", "active") + for node in self.nodes: + node.cmd_reply(f"activate", "active") + else: + node = self.node_map[path] + node.cmd_reply(f"activate {path}", "active") return dict(type='accept-block') def w_console(self): return dict(type='accept-console') def w_sendcommand(self, command): + # TODO: if not obsolete: add node to command. now its always sending to the first node logging.info('SENDCOMMAND %r', command) if not command.strip(): return dict(type='accept-command') # cmd = "change " + command cmd = command - return self.cmd_reply(cmd, 'event ' + command.split(' ')[0]) + return self.nodes[0].cmd_reply(cmd, 'event ' + command.split(' ')[0]) def poll(self): - if self.consolequeue: - messages = self.consolequeue - self.consolequeue = [] - return messages - if self.queue: - messages = convert_event(self.queue) - self.queue = [] - return messages - line = self.linesocket.get_line() - if line: - if not line.startswith('update'): - self.consolequeue.append(dict(type='reply',line=line,origin='other')) - if self.out: self.out.write("<"+line+"\n") - msg = SecopMsg(line) - if msg.asynch: # and self.replytype != msg['type'] + "=" + msg.par: - return convert_event(SecopMsg(line)) - self.syncreply.append(msg) - return [] + messages = [] + for node in self.nodes: + messages.extend(node.poll()) + return messages def info(self): return ["na"] + class SecopInstrument(Instrument): def __init__(self, inst_name, instrument_config): self.instrument_config = instrument_config - self.host_port = hostport_split(instrument_config['hostport']) + self.host_ports = instrument_config['hostport'] self.logger_dir = instrument_config.get('logger_dir', '') #test_day = instrument_config.get('test_day', None) #self.test_day = [int(x) for x in test_day.split('-')] if test_day else None @@ -1056,14 +1086,21 @@ class SecopInstrument(Instrument): self.clients = {} def newClient(self): - return self.register(SecopClient(self.host_port)) + return self.register(SecopClient(self.host_ports)) class SecopInfluxClient(SecopClient, InfluxGraph): def __init__(self): - SecopClient.__init__(self, instrument.host_port) + SecopClient.__init__(self, instrument.host_ports) InfluxGraph.__init__(self, instrument.influx_data_getter, instrument.title) + def poll(self): + messages = super().poll() + msg = self.graphpoll() + if msg: + messages.append(msg) + return messages + class SecopInfluxInstrument(SecopInstrument):