import logging import uuid from base import Instrument, get_abs_time from frappy.client import SecopClient as SecNodeClient from frappy.lib.enum import EnumMember from frappy.datatypes import get_datatype def convert_par(module, name, par): result = dict(type='input', name=module+":"+name, title=name) if par.get('readonly', True): result['type'] = 'rdonly' else: result['command'] = 'change %s:%s' % (module, name) if par['datainfo']['type'] == 'enum': result['enum_names'] = [dict(title=k, value=v) for k, v in par['datainfo']['members'].items()] result['type'] = 'enum' elif par['datainfo']['type'] == 'bool': result['type'] = 'checkbox' return result class SecopClient: prio_par = ["value", "status", "target"] hide_par = ["baseclass", "class", "pollinterval"] skip_par = ["status2"] def __init__(self, instrument): self.instrument = instrument self.id = uuid.uuid4().hex[0:15] self.module_updates = set() self.param_updates = set() self.updates = {} def w_getblock(self, path): path = path.split(',')[-1] # TODO: why this? if path == "main": components = [dict(type='rdlink', name=name+':value', title=name) for node in self.instrument.nodes for name in node.modules] self.param_updates = {'value'} return dict(type='draw', path='main', title='modules', components=components) self.module_updates.add(path) # TODO: remove others? node = self.instrument.node_map[path] module = node.modules[path] # logging.info('MP %r', path) parameters = dict(module["parameters"]) components = [] for name in SecopClient.skip_par: if name in parameters: parameters.pop(name) for name in SecopClient.prio_par: if name in parameters: components.append(convert_par(path, name, parameters.pop(name))) components1 = [] for name in SecopClient.hide_par: if name in parameters: components1.append(convert_par(path, name, parameters.pop(name))) for name, p in parameters.items(): components.append(convert_par(path, name, parameters[name])) components.extend(components1) return dict(type='draw', path=path, title=path, components=components) def updateItem(self, module, parameter, entry): key = module, parameter # print(key, entry) if module in self.module_updates or parameter in self.param_updates: name = f'{module}:{parameter}' if entry.readerror: item = {'name': name, 'error': str(entry.readerror)} else: item = {'name': name, 'value': str(entry), 'formatted': entry.formatted()} # print(item) self.updates[key] = item def w_updateblock(self, path): if path == 'main': path = '' for node in self.instrument.nodes: for modname in node.modules: key = modname, 'value' if key in node.cache: self.updateItem(*key, node.cache[key]) else: node = self.instrument.node_map[path] for param in node.modules[path]['parameters']: key = path, param if key in node.cache: self.updateItem(*key, node.cache[key]) return dict(type='accept-block') def w_console(self): return dict(type='accept-console') def w_sendcommand(self, command): logging.info('SENDCOMMAND %r', command) if not command.strip(): return dict(type='accept-command') if command.startswith('change '): command = command[7:] modpar, _, strvalue = command.partition(' ') module, _, parameter = modpar.partition(':') if not parameter: parameter = 'target' node = self.instrument.node_map[module] try: node.setParameterFromString(module, parameter, strvalue) except Exception as e: print(f"{e!r} converting {strvalue} to {node.modules[module]['parameters'][parameter]['datatype']}") return dict(type='accept-command') def w_gettime(self, time): """parse time (using server time) time: comma separated time range (beg,end) values < 1 year are treated as relative to the current time """ time = [float(t) for t in time.split(',')] return dict(type='time', time=get_abs_time(time)) def poll(self): updates, self.updates = self.updates, {} if not updates: return [] messages = [dict(type='update', updates=list(updates.values()))] return messages def info(self): return ["na"] class SecopInstrument(Instrument): def __init__(self, inst_name, instrument_config): super().__init__() self.instrument_config = instrument_config 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 self.title = inst_name self.device = 'UNDEFINED' self.nodes = [] self.node_map = {} for host_port in host_ports.split(','): node = SecNodeClient(host_port) node.connect() self.nodes.append(node) for name, mod in node.modules.items(): self.node_map[name] = node def register(self, client): print('OPEN') for node in self.nodes: node.register_callback(None, client.updateItem) return super().register(client) def remove(self, client): print('REMOVE') for node in self.nodes: node.unregister_callback(None, client.updateItem) super().remove(client) def new_client(self): return self.register(SecopClient(self))