def _build_elements(self): self._user_elements = [] self._physical_elements = {} self._physical_elements_set = set() pool = self._get_pool() for user_element_id in self._user_element_ids: # an internal element internal = type(user_element_id) is int if internal: try: user_element = pool.get_element(id=user_element_id) except KeyError: self._pending = True self._user_elements = None self._physical_elements = None self._physical_elements_set = None raise internal = self._is_managed_element(user_element) if not internal: user_element_id = user_element.get_source() # a tango channel or non internal element (ex: ioregister or motor # in measurement group) if not internal: validator = TangoAttributeNameValidator() params = validator.getParams(user_element_id) params['pool'] = self._get_pool() user_element = PoolExternalObject(**params) self.add_user_element(user_element) self._pending = False
def set_configuration_from_user(self, cfg, propagate=1): config = {} user_elements = self.get_user_elements() pool = self.pool timer_name = cfg.get('timer', user_elements[0].full_name) monitor_name = cfg.get('monitor', user_elements[0].full_name) config['timer'] = pool.get_element_by_full_name(timer_name) config['monitor'] = pool.get_element_by_full_name(monitor_name) config['controllers'] = controllers = {} for c_name, c_data in cfg['controllers'].items(): # discard controllers which don't have items (garbage) ch_count = 0 for u_data in c_data['units'].values(): ch_count += len(u_data['channels']) if ch_count == 0: continue external = c_name.startswith('__') if external: ctrl = c_name else: ctrl = pool.get_element_by_full_name(c_name) assert ctrl.get_type() == ElementType.Controller controllers[ctrl] = ctrl_data = {} ctrl_data['units'] = units = {} for u_id, u_data in c_data['units'].items(): # discard units which don't have items (garbage) if len(u_data['channels']) == 0: continue units[u_id] = unit_data = dict(u_data) unit_data['id'] = u_data.get('id', u_id) if not external and ctrl.is_timerable(): unit_data['timer'] = pool.get_element_by_full_name( u_data['timer']) unit_data['monitor'] = pool.get_element_by_full_name( u_data['monitor']) unit_data['trigger_type'] = u_data['trigger_type'] unit_data['channels'] = channels = {} for ch_name, ch_data in u_data['channels'].items(): if external: validator = TangoAttributeNameValidator() params = validator.getParams(ch_data['full_name']) params['pool'] = self.pool channel = PoolExternalObject(**params) else: channel = pool.get_element_by_full_name(ch_name) channels[channel] = dict(ch_data) config['label'] = cfg.get('label', self.name) config['description'] = cfg.get('description', self.DFT_DESC) self.set_configuration(config, propagate=propagate)
def set_configuration_from_user(self, cfg, propagate=1): config = {} user_elements = self.get_user_elements() pool = self.pool timer_name = cfg.get('timer', user_elements[0].full_name) monitor_name = cfg.get('monitor', user_elements[0].full_name) config['timer'] = pool.get_element_by_full_name(timer_name) config['monitor'] = pool.get_element_by_full_name(monitor_name) config['controllers'] = controllers = {} for c_name, c_data in cfg['controllers'].items(): # discard controllers which don't have items (garbage) ch_count = 0 for u_data in c_data['units'].values(): ch_count += len(u_data['channels']) if ch_count == 0: continue external = c_name.startswith('__') if external: ctrl = c_name else: ctrl = pool.get_element_by_full_name(c_name) assert ctrl.get_type() == ElementType.Controller controllers[ctrl] = ctrl_data = {} ctrl_data['units'] = units = {} for u_id, u_data in c_data['units'].items(): # discard units which don't have items (garbage) if len(u_data['channels']) == 0: continue units[u_id] = unit_data = dict(u_data) unit_data['id'] = u_data.get('id', u_id) if not external and ctrl.is_timerable(): unit_data['timer'] = pool.get_element_by_full_name(u_data['timer']) unit_data['monitor'] = pool.get_element_by_full_name(u_data['monitor']) unit_data['trigger_type'] = u_data['trigger_type'] unit_data['channels'] = channels = {} for ch_name, ch_data in u_data['channels'].items(): if external: validator = TangoAttributeNameValidator() params = validator.getParams(ch_data['full_name']) params['pool'] = self.pool channel = PoolExternalObject(**params) else: channel = pool.get_element_by_full_name(ch_name) channels[channel] = dict(ch_data) config['label'] = cfg.get('label', self.name) config['description'] = cfg.get('description', self.DFT_DESC) self.set_configuration(config, propagate=propagate)
def getModelMimeData(self): """ Used for drag events """ model, mimeData = '', None try: #model = getattr(self.scene().itemAt(*self.mousePos),'_name','') selected = self.scene()._selectedItems if not selected: self.debug('jdrawView.getModelMimeData(%s): nothing to drag' % model) return model = getattr(([ s for s in selected if s.isUnderMouse() and getattr(s, '_name', '') ] or [selected])[0], '_name', '') self.debug('getModelMimeData(%s)' % model) mimeData = Qt.QMimeData() if model: if DeviceNameValidator().getParams(model): self.debug('getMimeData(): DeviceModel at %s: %s', self.mousePos, model) mimeData.setData(TAURUS_DEV_MIME_TYPE, model) elif AttributeNameValidator().getParams(model): self.debug('getMimeData(): AttributeModel at %s: %s', self.mousePos, model) mimeData.setData(TAURUS_ATTR_MIME_TYPE, model) else: self.debug('getMimeData(): UnknownModel at %s: %s', self.mousePos, model) mimeData.setData(TAURUS_MODEL_MIME_TYPE, model) except: self.debug( 'jdrawView.getModelMimeData(%s): unable to get MimeData' % model) self.debug(traceback.format_exc()) return mimeData
def create_measurement_group(self, **kwargs): name = kwargs['name'] elem_ids = kwargs["user_elements"] kwargs['pool'] = self kwargs["pool_name"] = self.name td = TYPE_MAP_OBJ[ElementType.MeasurementGroup] klass = td.klass auto_full_name = td.auto_full_name full_name = kwargs.get("full_name", auto_full_name.format(**kwargs)) kwargs.pop('pool_name') self.check_element(name, full_name) for elem_id in elem_ids: if isinstance(elem_id, int): self.pool.get_element(id=elem_id) else: tg_attr_validator = TangoAttributeNameValidator() params = tg_attr_validator.getParams(elem_id) if params is None: raise Exception("Invalid channel name %s" % elem_id) eid = kwargs.get('id') if eid is None: kwargs['id'] = eid = self.get_new_id() else: self.reserve_id(eid) elem = klass(**kwargs) ret = self.add_element(elem) self.fire_event(EventType("ElementCreated"), elem) return ret
def set_configuration_from_user(self, cfg, to_fqdn=True): """Load measurement configuration from serializable data structure.""" user_elements = self._parent.get_user_elements() if len(user_elements) == 0: # All channels were disabled raise ValueError('The configuration has all the channels disabled') pool = self._parent.pool label = cfg.get('label', self._parent.name) description = cfg.get('description', self.DFT_DESC) timerable_ctrls = {AcqSynch.HardwareGate: [], AcqSynch.HardwareStart: [], AcqSynch.HardwareTrigger: [], AcqSynch.SoftwareStart: [], AcqSynch.SoftwareTrigger: [], AcqSynch.SoftwareGate: []} zerod_ctrls = [] synch_ctrls = [] other_ctrls = [] master_timer_sw = None master_monitor_sw = None master_timer_sw_start = None master_monitor_sw_start = None master_timer_idx_sw = float("+inf") master_monitor_idx_sw = float("+inf") master_timer_idx_sw_start = float("+inf") master_monitor_idx_sw_start = float("+inf") user_elem_ids = {} channel_acq_synch = {} ctrl_acq_synch = {} user_config = {} user_config['controllers'] = {} user_config['label'] = label user_config['description'] = description for ctrl_name, ctrl_data in cfg['controllers'].items(): # backwards compatibility for measurement groups created before # implementing feature-372: # https://sourceforge.net/p/sardana/tickets/372/ # WARNING: this is one direction backwards compatibility - it just # reads channels from the units, but does not write channels to the # units back if 'units' in ctrl_data: ctrl_data = ctrl_data['units']['0'] # discard controllers which don't have items (garbage) ch_count = len(ctrl_data['channels']) if ch_count == 0: continue external = ctrl_name.startswith('__') if external: ctrl = ctrl_name else: if to_fqdn: ctrl_name = _to_fqdn(ctrl_name, logger=self._parent) ctrl = pool.get_element_by_full_name(ctrl_name) assert ctrl.get_type() == ElementType.Controller user_config['controllers'][ctrl_name] = user_config_ctrl = {} ctrl_conf = {} synchronizer = ctrl_data.get('synchronizer', 'software') conf_synch = None if synchronizer is None or synchronizer == 'software': ctrl_conf['synchronizer'] = 'software' user_config_ctrl['synchronizer'] = 'software' else: if to_fqdn: synchronizer = _to_fqdn(synchronizer, logger=self._parent) user_config_ctrl['synchronizer'] = synchronizer pool_synch = pool.get_element_by_full_name(synchronizer) pool_synch_ctrl = pool_synch.controller conf_synch = SynchronizerConfiguration(pool_synch) conf_synch_ctrl = None if len(synch_ctrls) > 0: conf_synch_ctrl = None for conf_ctrl in synch_ctrls: if pool_synch_ctrl == conf_ctrl.element: conf_synch_ctrl = conf_ctrl if conf_synch_ctrl is None: conf_synch_ctrl = ControllerConfiguration(pool_synch_ctrl) conf_synch_ctrl.add_channel(conf_synch) synch_ctrls.append(conf_synch_ctrl) ctrl_conf['synchronizer'] = conf_synch try: synchronization = ctrl_data['synchronization'] except KeyError: # backwards compatibility for configurations before SEP6 try: synchronization = ctrl_data['trigger_type'] msg = ("trigger_type configuration parameter is deprecated" " in favor of synchronization. Re-apply " "configuration in order to upgrade.") self._parent.warning(msg) except KeyError: synchronization = AcqSynchType.Trigger ctrl_conf['synchronization'] = synchronization user_config_ctrl['synchronization'] = synchronization acq_synch = None if external: ctrl_item = ExternalControllerConfiguration(ctrl) elif ctrl.is_timerable(): is_software = synchronizer == 'software' acq_synch = AcqSynch.from_synch_type(is_software, synchronization) ctrl_acq_synch[ctrl] = acq_synch ctrl_item = TimerableControllerConfiguration(ctrl, ctrl_conf) else: ctrl_item = ControllerConfiguration(ctrl, ctrl_conf) ctrl_enabled = False if 'channels' in ctrl_data: user_config_ctrl['channels'] = user_config_channel = {} for ch_name, ch_data in ctrl_data['channels'].items(): if external: validator = TangoAttributeNameValidator() full_name = ch_data.get('full_name', ch_name) params = validator.getParams(full_name) params['pool'] = pool channel = PoolExternalObject(**params) else: if to_fqdn: ch_name = _to_fqdn(ch_name, logger=self._parent) channel = pool.get_element_by_full_name(ch_name) ch_data = self._fill_channel_data(channel, ch_data) user_config_channel[ch_name] = ch_data ch_item = ChannelConfiguration(channel, ch_data) ch_item.controller = ctrl_item ctrl_item.add_channel(ch_item) if ch_item.enabled: if external: id_ = channel.full_name else: id_ = channel.id user_elem_ids[ch_item.index] = id_ if ch_item.enabled: ctrl_enabled = True if acq_synch is not None: channel_acq_synch[channel] = acq_synch if not external and ctrl.is_timerable(): ctrl_item.update_timer() ctrl_item.update_monitor() user_config_ctrl['timer'] = ctrl_item.timer.full_name user_config_ctrl['monitor'] = ctrl_item.monitor.full_name # Update synchronizer state if conf_synch is not None: conf_synch.enabled = ctrl_enabled ctrl_item.validate() if external: other_ctrls.append(ctrl_item) elif ctrl.is_timerable(): timerable_ctrls[acq_synch].append(ctrl_item) # Find master timer/monitor the system take the channel with # less index if acq_synch in (AcqSynch.SoftwareTrigger, AcqSynch.SoftwareGate): if ctrl_item.timer.index < master_timer_idx_sw: master_timer_sw = ctrl_item.timer master_timer_idx_sw = ctrl_item.timer.index if ctrl_item.monitor.index < master_monitor_idx_sw: master_monitor_sw = ctrl_item.monitor master_monitor_idx_sw = ctrl_item.monitor.index elif acq_synch == AcqSynch.SoftwareStart: if ctrl_item.timer.index < master_timer_idx_sw_start: master_timer_sw_start = ctrl_item.timer master_timer_idx_sw_start = ctrl_item.timer.index if ctrl_item.monitor.index < master_monitor_idx_sw_start: master_monitor_sw_start = ctrl_item.monitor master_monitor_idx_sw_start = ctrl_item.monitor.index elif ctrl.get_ctrl_types()[0] == ElementType.ZeroDExpChannel: zerod_ctrls.append(ctrl_item) # Update synchronizer controller states for conf_synch_ctrl in synch_ctrls: conf_synch_ctrl.update_state() # Fill user configuration with measurement group's timer & monitor # This is a backwards compatibility cause the measurement group's # timer & monitor are not used if master_timer_sw is not None: user_config['timer'] = master_timer_sw.full_name elif master_timer_sw_start is not None: user_config['timer'] = master_timer_sw_start.full_name else: user_config['timer'] = cfg['timer'] if master_monitor_sw is not None: user_config['monitor'] = master_monitor_sw.full_name elif master_monitor_sw_start is not None: user_config['monitor'] = master_monitor_sw_start.full_name else: user_config['monitor'] = cfg['monitor'] # Update internals values self._label = label self._description = description self._timerable_ctrls = timerable_ctrls self._zerod_ctrls = zerod_ctrls self._synch_ctrls = synch_ctrls self._other_ctrls = other_ctrls self._master_timer_sw = master_timer_sw self._master_monitor_sw = master_monitor_sw self._master_timer_sw_start = master_timer_sw_start self._master_monitor_sw_start = master_monitor_sw_start self._user_confg = user_config self._channel_acq_synch = channel_acq_synch self._ctrl_acq_synch = ctrl_acq_synch # sorted ids may not be consecutive (if a channel is disabled) indexes = sorted(user_elem_ids.keys()) user_elem_ids_list = [user_elem_ids[idx] for idx in indexes] for conf_synch_ctrl in synch_ctrls: for conf_synch in conf_synch_ctrl.get_channels(enabled=True): user_elem_ids_list.append(conf_synch.id) self._parent.set_user_element_ids(user_elem_ids_list) self.changed = True
def __init__(self, parent=None, *args, **kwargs): super(SynopticWidget, self).__init__(parent) self.attribute_name_validator = AttributeNameValidator()
class SynopticWidget(TaurusWidget): """ A Qt widget displaying a SVG synoptic in a webview. Basically all interaction is handled by JS on the webview side, here we just connect the JS and Tango sides up. """ def __init__(self, parent=None, *args, **kwargs): super(SynopticWidget, self).__init__(parent) self.attribute_name_validator = AttributeNameValidator() def setModel(self, svg, section=None): self._svg = svg self.registry = Registry() self.registry.start() self._setup_ui(svg, section) def getModel(self): return self._url def listen(self, name, active=True): "" if self.attribute_name_validator.isValid(name): if active: self.registry.subscribe_attribute(name, self._attribute_listener) else: self.registry.unsubscribe_attribute(name) def on_click(self, kind, name): """The default behavior is to mark a clicked device and to zoom to a clicked section. Override this function if you need something else.""" if kind == "device": self.select_devices([name]) self.emit(Qt.SIGNAL("graphicItemSelected(QString)"), name) elif kind == "section": self.zoom_to_section(name) def on_rightclick(synoptic, kind, name): pass def _setup_ui(self, url, section=None): hbox = QtGui.QHBoxLayout(self) hbox.setContentsMargins(0, 0, 0, 0) hbox.layout().setContentsMargins(0, 0, 0, 0) hbox.addWidget(self._create_view(url, section)) self.setLayout(hbox) self.js.leftclicked.connect(self.on_click) self.js.rightclicked.connect(self.on_rightclick) def _create_view(self, svg, section=None): view = QWebView(self) view.setRenderHint(QtGui.QPainter.TextAntialiasing, False) view.setPage(LoggingWebPage()) view.setContextMenuPolicy(QtCore.Qt.PreventContextMenu) # the HTML page that will contain the SVG path = os.path.dirname(os.path.realpath(__file__)) html = QUrl(os.path.join(path, "index.html")) # make file configurable # setup the JS interface frame = view.page().mainFrame() self.js = JSInterface(frame) #self.js.registered.connect(self.register) self.js.visibility.connect(self.listen) # mouse interaction signals self.clicked = self.js.leftclicked self.rightClicked = self.js.rightclicked # Inject JSInterface into the JS global namespace as "Widget" frame.addToJavaScriptWindowObject('Widget', self.js) # confusing? # when the page (and all the JS) has loaded, load the SVG def load_svg(): print "blorrt", svg self.js.load(svg, section) view.loadFinished.connect(load_svg) # load the page # print "url", QUrl(url) view.load(html) return view def _set_sub_alarms(self, basename): """Find all devices that 'belong' to an alarm and update their alarm status. This is a bit hacky as it depends on alarm names; find a better way.""" subalarms = self.registry.panic.get(basename + "*") for alarm in subalarms: devname = alarm.tag.replace("__", "/").replace("_", "-").upper() active = alarm.get_active() if active is not None: print "subalarm on", devname, active self.js.evaljs.emit( "Synoptic.setSubAlarm(%r, %r, %s)" % ("device", devname, str(bool(active)).lower())) ### Listener callbacks ### def _device_listener(self, evt_src, evt_type, evt_value): if evt_type in (PyTango.EventType.PERIODIC_EVENT, PyTango.EventType.CHANGE_EVENT): name = evt_src.getNormalName() if name: state = evt_value.value device = str(name).rsplit("/", 1)[0] self.js.evaljs.emit("Synoptic.setDeviceStatus('%s', '%s')" % (str(device), str(state))) else: print "***" print "No name for", evt_value print "***" def _attribute_listener(self, event): # TODO: seems like multiline strings need more escaping, else # evaljs complains about "SyntaxError: Expected token ')'" self.js.evaljs.emit("Tango.onmessage(%r)" % json.dumps([event])) def __attribute_listener(self, evt_src, evt_type, evt_value): if evt_type in (PyTango.EventType.PERIODIC_EVENT, PyTango.EventType.CHANGE_EVENT): name = evt_src.getNormalName() if name: print "attribute_listener", name attr = Attribute(name) fmt = attr.getFormat() unit = attr.getUnit() value = evt_value.value if evt_value.type is PyTango._PyTango.CmdArgType.DevState: value_str = str(value) # e.g. "ON" else: value_str = fmt % value # e.g. "2.40e-5" attr_type = str(evt_value.type) self.js.evaljs.emit("Synoptic.setAttribute('%s', '%s', '%s', '%s')" % (name, value_str, attr_type, unit)) def _alarm_listener(self, evt_src, evt_type, evt_value): if evt_type in (PyTango.EventType.PERIODIC_EVENT, PyTango.EventType.CHANGE_EVENT): name = evt_src.getNormalName() if name: print "alarm_listener", name alarmname = str(name).rsplit("/", 1)[-1] value = evt_value.value self.js.evaljs.emit("Synoptic.setAlarm(%r, %s)" % ( alarmname, str(value).lower())) self._set_sub_alarms(alarmname) ### 'Public' API ### def zoom_to_section(self, secname): print "zoom_to_section", secname self.js.evaljs.emit("Synoptic.view.zoomTo('section', %r)" % str(secname)) def zoom_to_device(self, devname): self.js.evaljs.emit("Synoptic.view.zoomTo('device', %r, 10)" % str(devname)) def select_devices(self, devices): self.js.select('device', devices) def closeEvent(self, event): "Clean things up" self.registry.running = False self.registry.join() del self.registry def sendDebugMessage(self, msg): self.js.evaljs.emit("Tango.debugMessage(%r)" % msg)
def set_configuration_from_user(self, cfg, propagate=1, to_fqdn=True): config = {} user_elements = self.get_user_elements() pool = self.pool timer_name = cfg.get('timer', user_elements[0].full_name) monitor_name = cfg.get('monitor', user_elements[0].full_name) if to_fqdn: timer_name = _to_fqdn(timer_name, logger=self) config['timer'] = pool.get_element_by_full_name(timer_name) if to_fqdn: monitor_name = _to_fqdn(monitor_name, logger=self) config['monitor'] = pool.get_element_by_full_name(monitor_name) config['controllers'] = controllers = {} for c_name, c_data in cfg['controllers'].items(): # backwards compatibility for measurement groups created before # implementing feature-372: # https://sourceforge.net/p/sardana/tickets/372/ # WARNING: this is one direction backwards compatibility - it just # reads channels from the units, but does not write channels to the # units back if 'units' in c_data: c_data = c_data['units']['0'] # discard controllers which don't have items (garbage) ch_count = len(c_data['channels']) if ch_count == 0: continue external = c_name.startswith('__') if external: ctrl = c_name else: if to_fqdn: c_name = _to_fqdn(c_name, logger=self) ctrl = pool.get_element_by_full_name(c_name) assert ctrl.get_type() == ElementType.Controller controllers[ctrl] = ctrl_data = {} # exclude external and not timerable elements if not external and ctrl.is_timerable(): timer_name = c_data['timer'] if to_fqdn: timer_name = _to_fqdn(timer_name, logger=self) timer = pool.get_element_by_full_name(timer_name) ctrl_data['timer'] = timer monitor_name = c_data['monitor'] if to_fqdn: monitor_name = _to_fqdn(monitor_name, logger=self) monitor = pool.get_element_by_full_name(monitor_name) ctrl_data['monitor'] = monitor synchronizer = c_data.get('synchronizer') # for backwards compatibility purposes # protect measurement groups without synchronizer defined if synchronizer is None: synchronizer = 'software' elif synchronizer != 'software': if to_fqdn: synchronizer = _to_fqdn(synchronizer, logger=self) synchronizer = pool.get_element_by_full_name(synchronizer) ctrl_data['synchronizer'] = synchronizer try: synchronization = c_data['synchronization'] except KeyError: # backwards compatibility for configurations before SEP6 synchronization = c_data['trigger_type'] msg = ("trigger_type configuration parameter is deprecated" " in favor of synchronization. Re-apply " "configuration in order to upgrade.") self.warning(msg) ctrl_data['synchronization'] = synchronization ctrl_data['channels'] = channels = {} for ch_name, ch_data in c_data['channels'].items(): if external: validator = TangoAttributeNameValidator() params = validator.getParams(ch_data['full_name']) params['pool'] = self.pool channel = PoolExternalObject(**params) else: if to_fqdn: ch_name = _to_fqdn(ch_name, logger=self) channel = pool.get_element_by_full_name(ch_name) channels[channel] = dict(ch_data) config['label'] = cfg.get('label', self.name) config['description'] = cfg.get('description', self.DFT_DESC) self.set_configuration(config, propagate=propagate, to_fqdn=to_fqdn)
def set_configuration_from_user(self, cfg, to_fqdn=True): """Load measurement configuration from serializable data structure.""" user_elements = self._parent.get_user_elements() if len(user_elements) == 0: # All channels were disabled raise ValueError('The configuration has all the channels disabled') pool = self._parent.pool label = cfg.get('label', self._parent.name) description = cfg.get('description', self.DFT_DESC) timerable_ctrls = { AcqSynch.HardwareGate: [], AcqSynch.HardwareStart: [], AcqSynch.HardwareTrigger: [], AcqSynch.SoftwareStart: [], AcqSynch.SoftwareTrigger: [], AcqSynch.SoftwareGate: [] } zerod_ctrls = [] synch_ctrls = [] other_ctrls = [] master_timer_sw = None master_monitor_sw = None master_timer_sw_start = None master_monitor_sw_start = None master_timer_idx_sw = float("+inf") master_monitor_idx_sw = float("+inf") master_timer_idx_sw_start = float("+inf") master_monitor_idx_sw_start = float("+inf") user_elem_ids = {} channel_acq_synch = {} ctrl_acq_synch = {} user_config = {} user_config['controllers'] = {} user_config['label'] = label user_config['description'] = description for ctrl_name, ctrl_data in cfg['controllers'].items(): # backwards compatibility for measurement groups created before # implementing feature-372: # https://sourceforge.net/p/sardana/tickets/372/ # WARNING: this is one direction backwards compatibility - it just # reads channels from the units, but does not write channels to the # units back if 'units' in ctrl_data: ctrl_data = ctrl_data['units']['0'] # discard controllers which don't have items (garbage) ch_count = len(ctrl_data['channels']) if ch_count == 0: continue external = ctrl_name.startswith('__') if external: ctrl = ctrl_name else: if to_fqdn: ctrl_name = _to_fqdn(ctrl_name, logger=self._parent) ctrl = pool.get_element_by_full_name(ctrl_name) assert ctrl.get_type() == ElementType.Controller user_config['controllers'][ctrl_name] = user_config_ctrl = {} ctrl_conf = {} synchronizer = ctrl_data.get('synchronizer', 'software') conf_synch = None if synchronizer is None or synchronizer == 'software': ctrl_conf['synchronizer'] = 'software' user_config_ctrl['synchronizer'] = 'software' else: if to_fqdn: synchronizer = _to_fqdn(synchronizer, logger=self._parent) user_config_ctrl['synchronizer'] = synchronizer pool_synch = pool.get_element_by_full_name(synchronizer) pool_synch_ctrl = pool_synch.controller conf_synch = SynchronizerConfiguration(pool_synch) conf_synch_ctrl = None if len(synch_ctrls) > 0: conf_synch_ctrl = None for conf_ctrl in synch_ctrls: if pool_synch_ctrl == conf_ctrl.element: conf_synch_ctrl = conf_ctrl if conf_synch_ctrl is None: conf_synch_ctrl = ControllerConfiguration(pool_synch_ctrl) conf_synch_ctrl.add_channel(conf_synch) synch_ctrls.append(conf_synch_ctrl) ctrl_conf['synchronizer'] = conf_synch try: synchronization = ctrl_data['synchronization'] except KeyError: # backwards compatibility for configurations before SEP6 try: synchronization = ctrl_data['trigger_type'] msg = ("trigger_type configuration parameter is deprecated" " in favor of synchronization. Re-apply " "configuration in order to upgrade.") self._parent.warning(msg) except KeyError: synchronization = AcqSynchType.Trigger ctrl_conf['synchronization'] = synchronization user_config_ctrl['synchronization'] = synchronization acq_synch = None if external: ctrl_item = ExternalControllerConfiguration(ctrl) elif ctrl.is_timerable(): is_software = synchronizer == 'software' acq_synch = AcqSynch.from_synch_type(is_software, synchronization) ctrl_acq_synch[ctrl] = acq_synch ctrl_item = TimerableControllerConfiguration(ctrl, ctrl_conf) else: ctrl_item = ControllerConfiguration(ctrl, ctrl_conf) ctrl_enabled = False if 'channels' in ctrl_data: user_config_ctrl['channels'] = user_config_channel = {} for ch_name, ch_data in ctrl_data['channels'].items(): if external: validator = TangoAttributeNameValidator() full_name = ch_data.get('full_name', ch_name) params = validator.getParams(full_name) params['pool'] = pool channel = PoolExternalObject(**params) else: if to_fqdn: ch_name = _to_fqdn(ch_name, logger=self._parent) channel = pool.get_element_by_full_name(ch_name) ch_data = self._fill_channel_data(channel, ch_data) user_config_channel[ch_name] = ch_data ch_item = ChannelConfiguration(channel, ch_data) ch_item.controller = ctrl_item ctrl_item.add_channel(ch_item) if ch_item.enabled: if external: id_ = channel.full_name else: id_ = channel.id user_elem_ids[ch_item.index] = id_ if ch_item.enabled: ctrl_enabled = True if acq_synch is not None: channel_acq_synch[channel] = acq_synch if not external and ctrl.is_timerable(): ctrl_item.update_timer() ctrl_item.update_monitor() user_config_ctrl['timer'] = ctrl_item.timer.full_name user_config_ctrl['monitor'] = ctrl_item.monitor.full_name # Update synchronizer state if conf_synch is not None: conf_synch.enabled = ctrl_enabled ctrl_item.validate() if external: other_ctrls.append(ctrl_item) elif ctrl.is_timerable(): timerable_ctrls[acq_synch].append(ctrl_item) # Find master timer/monitor the system take the channel with # less index if acq_synch in (AcqSynch.SoftwareTrigger, AcqSynch.SoftwareGate): if ctrl_item.timer.index < master_timer_idx_sw: master_timer_sw = ctrl_item.timer master_timer_idx_sw = ctrl_item.timer.index if ctrl_item.monitor.index < master_monitor_idx_sw: master_monitor_sw = ctrl_item.monitor master_monitor_idx_sw = ctrl_item.monitor.index elif acq_synch == AcqSynch.SoftwareStart: if ctrl_item.timer.index < master_timer_idx_sw_start: master_timer_sw_start = ctrl_item.timer master_timer_idx_sw_start = ctrl_item.timer.index if ctrl_item.monitor.index < master_monitor_idx_sw_start: master_monitor_sw_start = ctrl_item.monitor master_monitor_idx_sw_start = ctrl_item.monitor.index elif ctrl.get_ctrl_types()[0] == ElementType.ZeroDExpChannel: zerod_ctrls.append(ctrl_item) # Update synchronizer controller states for conf_synch_ctrl in synch_ctrls: conf_synch_ctrl.update_state() # Fill user configuration with measurement group's timer & monitor # This is a backwards compatibility cause the measurement group's # timer & monitor are not used if master_timer_sw is not None: user_config['timer'] = master_timer_sw.full_name elif master_timer_sw_start is not None: user_config['timer'] = master_timer_sw_start.full_name else: user_config['timer'] = cfg['timer'] if master_monitor_sw is not None: user_config['monitor'] = master_monitor_sw.full_name elif master_monitor_sw_start is not None: user_config['monitor'] = master_monitor_sw_start.full_name else: user_config['monitor'] = cfg['monitor'] # Update internals values self._label = label self._description = description self._timerable_ctrls = timerable_ctrls self._zerod_ctrls = zerod_ctrls self._synch_ctrls = synch_ctrls self._other_ctrls = other_ctrls self._master_timer_sw = master_timer_sw self._master_monitor_sw = master_monitor_sw self._master_timer_sw_start = master_timer_sw_start self._master_monitor_sw_start = master_monitor_sw_start self._user_confg = user_config self._channel_acq_synch = channel_acq_synch self._ctrl_acq_synch = ctrl_acq_synch # sorted ids may not be consecutive (if a channel is disabled) indexes = sorted(user_elem_ids.keys()) user_elem_ids_list = [user_elem_ids[idx] for idx in indexes] for conf_synch_ctrl in synch_ctrls: for conf_synch in conf_synch_ctrl.get_channels(enabled=True): user_elem_ids_list.append(conf_synch.id) self._parent.set_user_element_ids(user_elem_ids_list) self.changed = True
class SynopticWidget(TaurusWidget): """ A Qt widget displaying a SVG synoptic in a webview. Basically all interaction is handled by JS on the webview side, here we just connect the JS and Tango sides up. """ def __init__(self, parent=None, *args, **kwargs): super(SynopticWidget, self).__init__(parent) self.attribute_name_validator = AttributeNameValidator() def setModel(self, svg, section=None): self._svg = svg self.registry = Registry() self.registry.start() self._setup_ui(svg, section) def getModel(self): return self._url def listen(self, name, active=True): "" if self.attribute_name_validator.isValid(name): if active: self.registry.subscribe_attribute(name, self._attribute_listener) else: self.registry.unsubscribe_attribute(name) def on_click(self, kind, name): """The default behavior is to mark a clicked device and to zoom to a clicked section. Override this function if you need something else.""" if kind == "device": self.select_devices([name]) self.emit(Qt.SIGNAL("graphicItemSelected(QString)"), name) elif kind == "section": self.zoom_to_section(name) def on_rightclick(synoptic, kind, name): pass def _setup_ui(self, url, section=None): hbox = QtGui.QHBoxLayout(self) hbox.setContentsMargins(0, 0, 0, 0) hbox.layout().setContentsMargins(0, 0, 0, 0) hbox.addWidget(self._create_view(url, section)) self.setLayout(hbox) self.js.leftclicked.connect(self.on_click) self.js.rightclicked.connect(self.on_rightclick) def _create_view(self, svg, section=None): view = QWebView(self) view.setRenderHint(QtGui.QPainter.TextAntialiasing, False) view.setPage(LoggingWebPage()) view.setContextMenuPolicy(QtCore.Qt.PreventContextMenu) # the HTML page that will contain the SVG path = os.path.dirname(os.path.realpath(__file__)) html = QUrl(os.path.join(path, "index.html")) # make file configurable # setup the JS interface frame = view.page().mainFrame() self.js = JSInterface(frame) #self.js.registered.connect(self.register) self.js.visibility.connect(self.listen) # mouse interaction signals self.clicked = self.js.leftclicked self.rightClicked = self.js.rightclicked # Inject JSInterface into the JS global namespace as "Widget" frame.addToJavaScriptWindowObject('Widget', self.js) # confusing? # when the page (and all the JS) has loaded, load the SVG def load_svg(): print "blorrt", svg self.js.load(svg, section) view.loadFinished.connect(load_svg) # load the page # print "url", QUrl(url) view.load(html) return view def _set_sub_alarms(self, basename): """Find all devices that 'belong' to an alarm and update their alarm status. This is a bit hacky as it depends on alarm names; find a better way.""" subalarms = self.registry.panic.get(basename + "*") for alarm in subalarms: devname = alarm.tag.replace("__", "/").replace("_", "-").upper() active = alarm.get_active() if active is not None: print "subalarm on", devname, active self.js.evaljs.emit( "Synoptic.setSubAlarm(%r, %r, %s)" % ("device", devname, str(bool(active)).lower())) ### Listener callbacks ### def _device_listener(self, evt_src, evt_type, evt_value): if evt_type in (PyTango.EventType.PERIODIC_EVENT, PyTango.EventType.CHANGE_EVENT): name = evt_src.getNormalName() if name: state = evt_value.value device = str(name).rsplit("/", 1)[0] self.js.evaljs.emit("Synoptic.setDeviceStatus('%s', '%s')" % (str(device), str(state))) else: print "***" print "No name for", evt_value print "***" def _attribute_listener(self, event): # TODO: seems like multiline strings need more escaping, else # evaljs complains about "SyntaxError: Expected token ')'" self.js.evaljs.emit("Tango.onmessage(%r)" % json.dumps([event])) def __attribute_listener(self, evt_src, evt_type, evt_value): if evt_type in (PyTango.EventType.PERIODIC_EVENT, PyTango.EventType.CHANGE_EVENT): name = evt_src.getNormalName() if name: print "attribute_listener", name attr = Attribute(name) fmt = attr.getFormat() unit = attr.getUnit() value = evt_value.value if evt_value.type is PyTango._PyTango.CmdArgType.DevState: value_str = str(value) # e.g. "ON" else: value_str = fmt % value # e.g. "2.40e-5" attr_type = str(evt_value.type) self.js.evaljs.emit( "Synoptic.setAttribute('%s', '%s', '%s', '%s')" % (name, value_str, attr_type, unit)) def _alarm_listener(self, evt_src, evt_type, evt_value): if evt_type in (PyTango.EventType.PERIODIC_EVENT, PyTango.EventType.CHANGE_EVENT): name = evt_src.getNormalName() if name: print "alarm_listener", name alarmname = str(name).rsplit("/", 1)[-1] value = evt_value.value self.js.evaljs.emit("Synoptic.setAlarm(%r, %s)" % (alarmname, str(value).lower())) self._set_sub_alarms(alarmname) ### 'Public' API ### def zoom_to_section(self, secname): print "zoom_to_section", secname self.js.evaljs.emit("Synoptic.view.zoomTo('section', %r)" % str(secname)) def zoom_to_device(self, devname): self.js.evaljs.emit("Synoptic.view.zoomTo('device', %r, 10)" % str(devname)) def select_devices(self, devices): self.js.select('device', devices) def closeEvent(self, event): "Clean things up" self.registry.running = False self.registry.join() del self.registry def sendDebugMessage(self, msg): self.js.evaljs.emit("Tango.debugMessage(%r)" % msg)