Exemple #1
0
class SAMPManager():

    __client = None
    __received = False
    __params = {}
    __hub = None
    __server_name = "frastro server"
    __server_description = "frastro Web client"
    __server_description_html = "<h1>frastro Web client</h1>"
    __author = "Camilo E. Jimenez-Angel"
    __instritution = "IAC"
    __email_contact = "*****@*****.**"
    __url_icon = "http://localhost:8000/static/img/frastro_icon.jpg"

    def __init__(self, server_name=""):
        # Instantiate the client and connect to the hub
        self.__server_name = server_name if server_name != "" else self.__server_name
        self.connect()

    def starHubServer(self, web_profile=True):
        if self.__hub is None:
            self.__hub = SAMPHubServer(web_profile=web_profile,
                                       label=self.__server_name)
        self.__hub.start()

    def receive_call(self, private_key, sender_id, msg_id, mtype, params,
                     extra):
        self.__params = params
        self.__received = True
        self.__client.reply(msg_id, {
            "samp.status": "samp.ok",
            "samp.result": {}
        })

    def receive_notification(self, private_key, sender_id, mtype, params,
                             extra):
        self.__params = params
        self.__received = True

    def bind_to_server(self):
        # Listen for any instructions to load a table
        self.__client.bind_receive_call("table.load.votable",
                                        self.receive_call)
        self.__client.bind_receive_notification("table.load.votable",
                                                self.receive_notification)

    def sampMetadata(self):
        meta = {
            "samp.name": self.__server_name,
            "samp.description.text": self.__server_description,
            "samp.description.html": self.__server_description_html,
            "samp.icon.url": self.__url_icon,
            "author.affiliation": self.__instritution,
            "author.name": self.__author,
            "author.email": self.__email_contact
        }
        return meta

    def getRegisteredClients(self):
        clients = self.__client.get_registered_clients()
        run_clients = {}
        for client in clients:
            client_data = self.getMetadata(client)
            run_clients[client] = client_data
        return run_clients

    def getMetadata(self, client_id):
        return self.__client.get_metadata(client_id)

    def sendTable(self, message, recipient="all"):
        if recipient == "all":
            self.__client.notify_all(message)
        else:
            self.__client.notify(recipient, message)

    def connect(self):
        self.__client = SAMPIntegratedClient(name=self.__server_name,
                                             metadata=self.sampMetadata())
        self.__client.connect()
        self.bind_to_server()

    def sendImage(self, params, id=""):
        message = {}
        message["samp.mtype"] = "image.load.fits"
        message["samp.params"] = params
        if id == "" or id == "all":
            self.__client.notify_all(message)
        else:
            self.__client.notify(recipient_id=id, message=message)

    def sendMessage(self, params, id=""):
        message = {}
        message["samp.mtype"] = "table.load.votable"
        message["samp.params"] = params
        if id == "" or id == "all":
            self.__client.notify_all(message)
        else:
            self.__client.notify(recipient_id=id, message=message)

    def disconnect(self):
        self.__client.disconnect()

    def __del__(self):
        self.disconnect()
Exemple #2
0
class SAMPManager(object):
    def __init__(self, callback, check_connection_period=1):
        self.callback = callback
        self.dirname = os.path.dirname(os.path.abspath(__file__))
        self.timer = None
        self.samp_hub = None
        self.samp_client = None
        self.metadata = {
            'samp.name': 'iSpec',
            'samp.description.text': 'iSpec',
            'samp.icon.url': 'file://' + self.dirname + '/images/iSpec.png',
        }
        self.samp_client = SAMPIntegratedClient(metadata=self.metadata,
                                                addr='localhost')
        self.check_connection_period = check_connection_period  # seconds
        self.__check_connection()
        signal.signal(signal.SIGINT, self.__signal_handler)

    def is_connected(self):
        working_connection = False
        if self.samp_client is not None and self.samp_client.is_connected():
            try:
                self.samp_client.ping()
                working_connection = True
            except:
                working_connection = False
        return working_connection

    def __check_connection(self):
        status = "Status: "
        if not self.is_connected():
            if self.samp_client is not None and self.samp_client.is_connected(
            ):
                # Destroy old client to completely reset the connection status
                del self.samp_client
                self.samp_client = SAMPIntegratedClient(metadata=self.metadata,
                                                        addr='localhost')
                logging.info("SAMP Connection lost")
            status += "Disconnected"
            self.__connect()
        else:
            status += "Connected"
            pass
        logging.debug(status)

        self.timer = threading.Timer(self.check_connection_period,
                                     self.__check_connection)
        self.timer.start()

    def __connect(self):
        # Before we start, let's kill off any zombies
        try:
            self.shutdown()
        except:
            pass

        # sampy seems to fall over sometimes if 'localhost' isn't specified, even though it shouldn't
        #self.samp_hub = sampy.SAMPHubServer(addr='localhost')
        #self.samp_hub.start()

        try:
            self.samp_client.connect()
            logging.info("SAMP Connection established")
        except Exception:
            pass
        else:
            #samp_client.bindReceiveNotification("*", self.__samp_receive_notification)
            #samp_client.bindReceiveCall("*", self.__samp_receive_call)
            self.samp_client.bind_receive_notification(
                "samp.hub.event.register", self.__samp_receive_notification)
            self.samp_client.bind_receive_notification(
                "samp.hub.event.metadata", self.__samp_receive_notification)
            self.samp_client.bind_receive_notification(
                "samp.hub.event.subscriptions",
                self.__samp_receive_notification)
            self.samp_client.bind_receive_notification(
                "samp.hub.event.unregister", self.__samp_receive_notification)
            self.samp_client.bind_receive_notification(
                "table.load.votable", self.__samp_receive_notification)
            self.samp_client.bind_receive_notification(
                "spectrum.load.ssa-generic", self.__samp_receive_notification)
            self.samp_client.bind_receive_call("table.load.votable",
                                               self.__samp_receive_call)
            self.samp_client.bind_receive_call("spectrum.load.ssa-generic",
                                               self.__samp_receive_call)

    def __samp_receive_notification(self, private_key, sender_id, mtype,
                                    params, extra):
        #print "Notification:", private_key, sender_id, mtype, params, extra
        if mtype == 'samp.hub.event.register':
            #print "Registered:", params['id']
            pass
        elif mtype == 'samp.hub.event.metadata':
            #print "Metadata:", params['id'], "=", params['metadata']['samp.name']
            pass
        elif mtype == 'samp.hub.event.subscriptions':
            if 'spectrum.load.ssa-generic' in list(
                    params['subscriptions'].keys()):
                #print params['id'], "supports spectrum"
                pass
            if 'table.load.votable' in list(params['subscriptions'].keys()):
                #print params['id'], "supports votable"
                pass
            #print params
        elif mtype == 'samp.hub.event.unregister':
            #print params['id'], "unregistered"
            pass
        else:
            # For instance, VOSpec sends load votable/spectrum in form of notification
            # so we should try to process them also
            msg_id = None
            spectrum = self.__samp_receive_and_transform_spectrum(
                mtype, params)
            logging.info("Spectrum received via SAMP")
            self.callback(spectrum, "Received_spectrum")

    # Function called when a call is received
    def __samp_receive_call(self, private_key, sender_id, msg_id, mtype,
                            params, extra):
        #print "Call:", private_key, sender_id, msg_id, mtype, params, extra
        spectrum = self.__samp_receive_and_transform_spectrum(mtype, params)
        self.samp_client.ereply(msg_id,
                                sampy.SAMP_STATUS_OK,
                                result={"txt": "printed"})
        logging.info("Spectrum received via SAMP")
        self.callback(spectrum, "Received_spectrum")

    def __samp_receive_and_transform_spectrum(self, mtype, params):
        if mtype == 'table.load.votable':
            #print "Received votable", params['url']
            votable = self.__read_votable(params['url'])
            spectrum = self.__votable_to_spectrum(votable)
            #print "Spectrum success"
        elif mtype == 'spectrum.load.ssa-generic':
            #print "Received spectrum", params['url'], "in format", params['meta']['Access.Format']
            if params['meta']['Access.Format'] == 'application/votable':
                #print "- Votable"
                votable = self.__read_votable(params['url'])
                spectrum = self.__votable_to_spectrum(votable)
                #print "Spectrum success"
            elif params['meta']['Access.Format'] == 'application/fits':
                #print "- FITS"
                spectrum = self.__read_vofits(params['url'])
                #print "Spectrum success"
            else:
                raise Exception("Unknown format")
        else:
            raise Exception("Unknown action")
        return spectrum

    def __signal_handler(self, signal, frame):
        print('SIGTERM received (ctrl+c)')
        self.shutdown()

    def shutdown(self):
        if self.timer is not None: self.timer.cancel()
        if self.samp_client.is_connected():
            try:
                self.samp_client.disconnect()
            except Exception:
                pass
        if self.samp_hub is not None and self.samp_hub._is_running:
            self.samp_hub.stop()
        sys.exit(0)

    def __get_target_client(self, target_client_name):
        neighbours = samp_client.get_registered_clients()
        for neighbour in neighbours:
            metadata = self.samp_client.get_metadata(neighbour)
            try:
                if (metadata['samp.name'] == target_client_name):
                    return neighbour
            except KeyError:
                continue
        return None

    ########### [start] File operations
    def __votable_to_spectrum(self, votable):
        spectrum = None
        # Search for what it is required
        has_waveobs = False
        has_flux = False
        target_table = None
        waveobs_units = None
        for resource in votable.resources:
            for table in resource.tables:
                if len(table.array) > 0 and len(table.fields) >= 2:
                    for field in table.fields:
                        if field.name in ["wave", "waveobs"]:
                            has_waveobs = True
                            waveobs_units = field.unit
                        elif field.name == "flux":
                            has_flux = True
                        if has_waveobs and has_flux:
                            break
                if has_waveobs and has_flux:
                    target_table = table
                    break
            if has_waveobs and has_flux:
                break

        if target_table is not None:
            spectrum = np.recarray((len(target_table.array), ),
                                   dtype=[('waveobs', float), ('flux', float),
                                          ('err', float)])
            if 'waveobs' in list(target_table.array.dtype.fields.keys()):
                spectrum['waveobs'] = target_table.array['waveobs']
            elif 'wave' in list(target_table.array.dtype.fields.keys()):
                spectrum['waveobs'] = target_table.array['wave']
            else:
                # the first column
                spectrum['waveobs'] = target_table.array[
                    target_table.array.dtype.names[0]]
            if 'flux' in list(target_table.array.dtype.fields.keys()):
                spectrum['flux'] = target_table.array['flux']
            else:
                # the second column
                spectrum['flux'] = target_table.array[
                    target_table.array.dtype.names[1]]
            if 'err' in list(target_table.array.dtype.fields.keys()):
                spectrum['err'] = target_table.array['err']
            elif 'error' in list(target_table.array.dtype.fields.keys()):
                spectrum['err'] = target_table.array['error']
            elif 'errors' in list(target_table.array.dtype.fields.keys()):
                spectrum['err'] = target_table.array['errors']
            if 'sigma' in list(target_table.array.dtype.fields.keys()):
                spectrum['err'] = target_table.array['sigma']
            elif len(table.fields) >= 3:
                # the third column if exists
                spectrum['err'] = target_table.array[
                    target_table.array.dtype.names[2]]
            else:
                spectrum['err'] = 0.0
        if target_table is None or spectrum is None:
            raise Exception("Table not compatible")

        return spectrum

    def __read_votable(self, url):
        votable = None
        if url.startswith('http://'):
            u = urllib.request.urlopen(url)
            tmp = tempfile.NamedTemporaryFile(mode="wt",
                                              suffix=".xml",
                                              delete=False,
                                              encoding='utf-8')
            tmp.write(u.read())
            tmp.close()
            #print tmp.name
            votable = parse(tmp.name, pedantic=False)
            os.remove(tmp.name)
        elif url.startswith('file://localhost/'):
            filename = url[17:]
            if filename[0] != "/":
                filename = "/" + filename
            votable = parse(filename, pedantic=False)
        else:
            raise Exception("Unkown URL")
        return votable

    def __read_vofits(self, url):
        spectrum = None
        #print url
        if url.startswith('http://localhost/'):
            u = urllib.request.urlopen(url)
            tmp = tempfile.NamedTemporaryFile(mode="wt",
                                              suffix=".xml",
                                              delete=False,
                                              encoding='utf-8')
            tmp.write(u.read())
            tmp.close()
            #print tmp.name
            hdulist = pyfits.open(tmp.name)
            os.remove(tmp)
        elif url.startswith('file://localhost/'):
            filename = url[17:]
            if filename[0] != "/":
                filename = "/" + filename
            hdulist = pyfits.open(filename)
        else:
            raise Exception("Unkown URL")
        # Find hdu containing data
        target_hdu = None
        for hdu in hdulist:
            if hdu.data is not None and len(hdu.data) > 0 and len(
                    list(hdu.data.dtype.fields.keys())) >= 2:
                target_hdu = hdu
        spectrum = np.recarray((len(target_hdu.data), ),
                               dtype=[('waveobs', float), ('flux', float),
                                      ('err', float)])
        # the first column
        spectrum['waveobs'] = target_hdu.data[target_hdu.data.dtype.names[0]]
        # the second column
        spectrum['flux'] = target_hdu.data[target_hdu.data.dtype.names[1]]
        if len(list(hdu.data.dtype.fields.keys())) >= 3:
            # the third column if exists
            spectrum['err'] = target_hdu.data[target_hdu.data.dtype.names[2]]
        else:
            spectrum['err'] = 0.0
        if target_hdu is None or spectrum is None:
            raise Exception("FITS not compatible")
        return spectrum

    def __spectrum_to_vofits(self, spectrum):
        t = pyfits.new_table(spectrum)
        t.header['TTYPE1'] = "WAVELENGTH"
        t.header['TTYPE2'] = "FLUX"
        t.header['TTYPE3'] = "SIGMA"
        fits = pyfits.HDUList(pyfits.PrimaryHDU())
        fits.append(t)
        return fits

    def __spectrum_to_votable(self, spectrum):
        # Create a new VOTable file...
        votable = VOTableFile()

        # ...with one resource...
        resource = Resource()
        votable.resources.append(resource)

        # ... with one table
        table = Table(votable)
        resource.tables.append(table)

        # Define some fields
        waveobs = Field(votable,
                        name="waveobs",
                        datatype="double",
                        unit="nm",
                        ucd="em.wl")
        flux = Field(votable,
                     name="flux",
                     datatype="double",
                     unit="Jy",
                     ucd="phot.flux")
        err = Field(votable,
                    name="err",
                    datatype="double",
                    ucd="stat.error;phot.flux")
        table.fields.extend([waveobs, flux, err])
        table.groups.extend([Group([flux, err])])
        #import ipdb
        #ipdb.set_trace()
        # Now, use those field definitions to create the numpy record arrays, with
        # the given number of rows
        table.create_arrays(len(spectrum))

        # Now table.array can be filled with data
        table.array['waveobs'] = spectrum['waveobs']
        table.array['flux'] = spectrum['flux']
        table.array['err'] = spectrum['err']

        #votable.set_all_tables_format('binary') # VOSpec does not understand binary format
        return votable

    ########### [end]   File operations

    def get_subscribers(self):
        ids = []
        names = []
        as_tables = []

        try:
            if self.samp_client.is_connected():
                subscribers1 = self.samp_client.get_subscribed_clients(
                    "table.load.votable")
                subscribers2 = self.samp_client.get_subscribed_clients(
                    "spectrum.load.ssa-generic")
                subscribers = dict(
                    list(subscribers1.items()) + list(subscribers2.items()))
                for subscriber in list(subscribers.keys()):
                    metadata = self.samp_client.get_metadata(subscriber)
                    name = ""
                    if 'samp.name' in metadata:
                        name = metadata['samp.name']
                    ids.append(subscriber)
                    names.append(name)
                    # Prefered form: "spectrum.load.ssa-generic"
                    if subscriber in list(subscribers2.keys()):
                        as_tables.append(False)
                    else:
                        as_tables.append(True)

                # If there are repeated names, add the identifier as a prefix
                uniq_names, uniq_names_index = np.unique(names,
                                                         return_index=True)
                if len(names) != len(uniq_names):
                    for i in np.arange(len(names)):
                        # Only add a preffix to the repeated application names
                        if i not in uniq_names_index:
                            names[i] += " [" + ids[i] + "]"
        except Exception:
            pass

        return (ids, names, as_tables)

    # Broadcast a table file to TOPCAT
    def broadcast_spectrum(self,
                           spectrum,
                           spectrum_name,
                           target_client,
                           target_client_is_name=False,
                           as_table=True,
                           as_fits=False):
        if as_fits:
            suffix = ".fits"
        else:
            suffix = ".xml"
        tmp = tempfile.NamedTemporaryFile(mode="wt",
                                          suffix=suffix,
                                          delete=False,
                                          encoding='utf-8')
        tmp.close()

        if not as_fits:
            votable = self.__spectrum_to_votable(spectrum)
            votable.to_xml(tmp.name)
        else:
            fits = self.__spectrum_to_vofits(spectrum)
            fits.writeto(tmp.name, clobber=True)

        if as_table:
            mtype = 'table.load.votable'
        else:
            mtype = 'spectrum.load.ssa-generic'

        metadata = {
            'samp.mtype': mtype,
            'samp.params': {
                'name': spectrum_name,
                'table-id': spectrum_name,
                'url': 'file://localhost/' + tmp.name
            }
        }

        if target_client_is_name:
            target_client_name = target_client
            target_client = self.__get_target_client(target_client_name)
        response = False
        if target_client is not None:
            try:
                response = self.samp_client.notify(target_client, metadata)
                logging.info("Spectrum sent via SAMP")
            except Exception:
                pass
        # Do not delete or the target client will not have time to read it
        #os.remove(tmp.name)
        return response
Exemple #3
0
class SAMPState(State):

    status = CallbackProperty('Not connected to SAMP Hub')
    connected = CallbackProperty(False)
    clients = CallbackProperty([])
    highlight_is_selection = CallbackProperty(False)

    def __init__(self):
        super(SAMPState, self).__init__()
        self.hub = SAMPHubServer()
        self.client = SAMPIntegratedClient()
        self.add_callback('connected', self.on_connected)

    def start_samp(self):
        if not self.client.is_connected:
            try:
                self.client.connect()
            except SAMPHubError:
                try:
                    self.hub.start()
                    self.client.connect()
                except Exception:
                    self.connected = False
                    self.status = 'Could not connect to Hub'
                else:
                    self.connected = True
                    self.status = 'Connected to (glue) SAMP Hub'
            except:
                self.connected = False
                self.status = 'Could not connect to Hub'
            else:
                self.connected = True
                self.status = 'Connected to SAMP Hub'

    def stop_samp(self):
        if self.client.is_connected:
            self.client.disconnect()
        if self.hub.is_running:
            self.hub.stop()
        self.connected = False
        self.status = 'Not connected to SAMP Hub'

    def on_connected(self, *args):
        if self.connected:
            metadata = {
                'author.email': '*****@*****.**',
                'author.name': 'Thomas Robitaille',
                'home.page': 'http://www.glueviz.org',
                'samp.description.text':
                'Multi-dimensional linked data exploration',
                'samp.documentation.url': 'http://www.glueviz.org',
                'samp.icon.url': 'file://' + ICON_PATH,
                'samp.name': 'glueviz',
                'glue.version': glue_version
            }
            self.client.declare_metadata(metadata)
            self.on_client_change()

    def on_client_change(self):
        clients = []
        for client in self.client.get_registered_clients():
            metadata = self.client.get_metadata(client)
            clients.append((client, metadata.get('samp.name', client)))
        self.clients = clients

    def send_data(self, layer=None, client=None):

        filename = tempfile.mktemp()

        message = {}
        message["samp.params"] = {}

        if isinstance(layer, Data):

            if layer.ndim == 1:
                table = data_to_astropy_table(layer)
                table.write(filename, format='votable')
                message["samp.mtype"] = "table.load.votable"
                if 'samp-table-id' not in layer.meta:
                    layer.meta['samp-table-id'] = layer.label
                message["samp.params"]['table-id'] = layer.meta[
                    'samp-table-id']
            elif layer.ndim == 2:
                fits_writer(filename, layer)
                message["samp.mtype"] = "image.load.fits"
                if 'samp-image-id' not in layer.meta:
                    layer.meta['samp-image-id'] = layer.label
                message["samp.params"]['image-id'] = layer.meta[
                    'samp-image-id']
            else:
                return

            message["samp.params"]['name'] = layer.label
            message["samp.params"]['url'] = 'file://' + os.path.abspath(
                filename)

        else:

            message['samp.mtype'] = 'table.select.rowList'

            if layer.ndim == 1:
                message["samp.params"]['table-id'] = layer.data.meta[
                    'samp-table-id']
                message["samp.params"]['row-list'] = np.nonzero(
                    layer.to_mask())[0].astype(str).tolist()
            else:
                return

        if client is None:
            self.client.notify_all(message)
        else:
            # Make sure client is subscribed otherwise an exception is raised
            subscriptions = self.client.get_subscriptions(client)
            for mtype in subscriptions:
                if fnmatch(message['samp.mtype'], mtype):
                    self.client.notify(client, message)
                    return
            else:
                return