Beispiel #1
0
    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)
Beispiel #3
0
    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)
Beispiel #4
0
 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
Beispiel #5
0
    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
Beispiel #6
0
    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)
Beispiel #11
0
    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
Beispiel #12
0
 def __init__(self, parent=None, *args, **kwargs):
     super(SynopticWidget, self).__init__(parent)
     self.attribute_name_validator = AttributeNameValidator()
Beispiel #13
0
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)