def main():
    # Change URL to point to a valid portal node.
    # If you are not interested in any subarray specific information
    # (e.g. schedule blocks), then the number can be omitted, as below.
    portal_client = KATPortalClient('http://{}/api/client'.format(args.host),
                                    on_update_callback,
                                    logger=logger)

    # First connect to the websocket, before subscribing.
    yield portal_client.connect()

    # Use a namespace with a unique name when subscribing to avoid a
    # clash with existing namespaces.
    namespace = 'namespace_' + str(uuid.uuid4())

    # Subscribe to the namespace (async call) - no messages will be received yet,
    # as this is a new namespace.
    result = yield portal_client.subscribe(namespace)
    print "Subscription result: {} identifier(s).".format(result)

    # Set the sampling strategies for the sensors of interest, on our custom
    # namespace.  In this example, we are interested in a number of patterns,
    # e.g. any sensor with "mode" in the name.  The response messages will
    # be published to our namespace every 5 seconds.
    result = yield portal_client.set_sampling_strategies(
        namespace, args.sensors, 'period 5.0')
    print "\nSet sampling strategies result: {}.\n".format(result)
Exemple #2
0
class KatportalClientWrapper(object):
    def __init__(self, host):
        self._client = KATPortalClient(host,
            on_update_callback=None, logger=logging.getLogger('katcp'))

    @coroutine
    def _query(self, component, sensor):
        sensor_name = yield self._client.sensor_subarray_lookup(
            component=component, sensor=sensor, return_katcp_name=False)
        sensor_sample = yield self._client.sensor_value(sensor_name,
            include_value_ts=False)
        raise Return(sensor_sample)

    @coroutine
    def get_observer_string(self, antenna):
        sensor_sample = yield self._query(antenna, "observer")
        raise Return(sensor_sample.value)

    @coroutine
    def get_antenna_feng_id_map(self, instrument_name, antennas):
        sensor_sample = yield self._query('cbf', '{}.input-labelling'.format(instrument_name))
        labels = eval(sensor_sample.value)
        mapping = {}
        for input_label, input_index, _, _ in labels:
            antenna_name = input_label.strip("vh").lower()
            if antenna_name.startswith("m") and antenna_name in antennas:
                mapping[antenna_name] = input_index//2
        raise Return(mapping)

    @coroutine
    def get_bandwidth(self, stream):
        sensor_sample = yield self._query('sub', 'streams.{}.bandwidth'.format(stream))
        raise Return(sensor_sample.value)

    @coroutine
    def get_cfreq(self, stream):
        sensor_sample = yield self._query('sub', 'streams.{}.centre-frequency'.format(stream))
        raise Return(sensor_sample.value)

    @coroutine
    def get_sideband(self, stream):
        sensor_sample = yield self._query('sub', 'streams.{}.sideband'.format(stream))
        raise Return(sensor_sample.value)

    @coroutine
    def get_sync_epoch(self):
        sensor_sample = yield self._query('sub', 'synchronisation-epoch')
        raise Return(sensor_sample.value)

    @coroutine
    def get_itrf_reference(self):
        sensor_sample = yield self._query('sub', 'array-position-itrf')
        x, y, z = [float(i) for i in sensor_sample.value.split(",")]
        raise Return((x, y, z))
Exemple #3
0
  def connect (self, logger):
    self.script.log(2, "PubSubThread::connect()")
    self.ws_client = KATPortalClient(self.metadata_server, self.on_update_callback, logger=logger)
    self.script.log(2, "PubSubThread::connect self.ws_client.connect()")
    yield self.ws_client.connect()
    self.script.log(2, "PubSubThread::connect self.ws_client.subscribe(" + self.title + ")")
    result = yield self.ws_client.subscribe(self.title)
    self.script.log(2, "PubSubThread::connect self.ws_client.set_sampling_strategies (" + self.title + ", " + str(self.subs) + ", " + self.policy + ")")
    results = yield self.ws_client.set_sampling_strategies( self.title, self.subs, self.policy) 

    for result in results:
      self.script.log(2, "PubSubThread::connect subscribed to " + str(result))
Exemple #4
0
 def __init__(self, host, component, sensor_name):
     log.debug(("Building sensor tracker activity tracker "
                "on {} for sensor={} and component={}").format(
                    host, sensor_name, component))
     self._client = KATPortalClient(host,
                                    on_update_callback=self.event_handler,
                                    logger=logging.getLogger('katcp'))
     self._namespace = 'namespace_' + str(uuid.uuid4())
     self._sensor_name = sensor_name
     self._component = component
     self._full_sensor_name = None
     self._state = None
     self._has_started = False
def main():
    # Change URL to point to a valid portal node.
    # If you are not interested in any subarray specific information
    # (e.g. schedule blocks), then the number can be omitted, as below.
    # Note: if on_update_callback is set to None, then we cannot use the
    #       KATPortalClient.connect() method (i.e. no websocket access).
    portal_client = KATPortalClient('http://{}/api/client'.format(args.host),
                                    on_update_callback=None,
                                    logger=logger)

    # Get the names of sensors matching the patterns
    sensor_names = yield portal_client.sensor_names(args.sensors)
    print "\nMatching sensor names for pattern {}: {}".format(
        args.sensors, sensor_names)

    # Get the names of sensors matching a pattern
    # pattern = 'anc_w.*_device_status'
    # sensor_names = yield portal_client.sensor_names(pattern)
    # print "\nMatching sensor names for pattern {} : {}".format(pattern, sensor_names)
    # Example output:
    # Matching sensor names for pattern ['anc_w.*_device_status']:
    # [u'anc_wind_device_status', u'anc_weather_device_status']

    # Get the names of sensors matching a pattern
    # pattern = 'anc_(mean|gust)_wind_speed'
    # sensor_names = yield portal_client.sensor_names(pattern)
    # print "\nMatching sensor names for pattern {} : {}".format(pattern, sensor_names)
    # Example output:
    # Matching sensor names for pattern ['anc_(mean|gust)_wind_speed']:
    # [u'anc_mean_wind_speed', u'anc_gust_wind_speed']

    # Get the names of sensors matching a list of patterns
    # pattern = 'm01[12]_pos_request_base_azim'
    # sensor_names = yield portal_client.sensor_names(pattern)
    # print "\nMatching sensor names for pattern {} : {}".format(pattern, sensor_names)
    # Example output (if sensors is 'm01[12]_pos_request_base_azim'):
    # Matching sensor names for pattern ['m01[12]_pos_request_base_azim']:
    # [u'm011_pos_request_base_azim', u'm011_pos_request_base_azim']

    # Fetch the details for the sensors found.
    if len(sensor_names) == 0:
        print "No matching sensors found!"
    else:
        for sensor_name in sensor_names:
            sensor_detail = yield portal_client.sensor_detail(sensor_name)
            print "\nDetail for sensor {}:".format(sensor_name)
            for key in sensor_detail:
                print "    {}: {}".format(key, sensor_detail[key])
Exemple #6
0
def main():
    # Change URL to point to a valid portal node.  Subarray can be 1 to 4.
    # Note: if on_update_callback is set to None, then we cannot use the
    #       KATPortalClient.connect() method (i.e. no websocket access).
    portal_client = KATPortalClient(
        'http://{host}/api/client/1'.format(**vars(args)),
        on_update_callback=None,
        logger=logger)

    lookup_args = vars(args)
    results = yield portal_client.sensor_subarray_lookup(
        sub_nr=lookup_args['sub_nr'],
        component=lookup_args['component'],
        sensor=lookup_args['sensor'],
        return_katcp_name=lookup_args['return_katcp_name'])
    print results
def main():
    # Change URL to point to a valid portal node.
    # If you are not interested in any subarray specific information
    # (e.g. schedule blocks), then the number can be omitted, as below.
    # Note: if on_update_callback is set to None, then we cannot use the
    #       KATPortalClient.connect() method (i.e. no websocket access).
    portal_client = KATPortalClient('http://{}/api/client'.format(args.host),
                                    on_update_callback=None,
                                    logger=logger)

    # Login so that we know which user to create userlogs for!
    yield portal_client.login(username="******", password="******")
    tags = yield portal_client.userlog_tags()
    userlogs = yield portal_client.userlogs()

    print "There are %s userlog tags." % len(tags)
    print "=============================="
    print "Here is a list of userlogs for today:"
    print userlogs

    # To create an userlog use the following code
    # To add tags, make an array of tag id's
    userlog_tags_to_add = [tags[0].get('id'), tags[1].get('id')]
    userlog_content = "This is where you would put the content of the userlog!"
    # Start time and end times needs to be in this format 'YYYY-MM-DD HH:mm:ss'
    # All times are in UTC
    start_time = time.strftime('%Y-%m-%d 00:00:00')
    end_time = time.strftime('%Y-%m-%d 23:59:59')

    userlog_created = yield portal_client.create_userlog(
        content=userlog_content,
        tag_ids=userlog_tags_to_add,
        start_time=start_time,
        end_time=end_time)

    print "=============================="
    print "Created a userlog! This is the new userlog: "
    print userlog_created
    print "=============================="

    # To edit an existing userlog, user modify_userlog with the modified userlog
    # Here we are modifying the userlog we created using create_userlog
    userlog_created['content'] = 'This content is edited by katportalclient!'
    userlog_created['end_time'] = userlog_created['start_time']
    result = yield portal_client.modify_userlog(userlog_created)
    print "=============================="
    print "Edited userlog! Result: "
    print result

    # Remember to logout when you are done!
    print "=============================="
    print "Logging out!"
    yield portal_client.logout()
def main():
    # Change URL to point to a valid portal node.  Subarray can be 1 to 4.
    # Note: if on_update_callback is set to None, then we cannot use the
    #       KATPortalClient.connect() method (i.e. no websocket access).
    portal_client = KATPortalClient('http://{}/api/client/{}'.format(
        args.host, args.sub_nr),
                                    on_update_callback=None,
                                    logger=logger)

    # Get the IDs of schedule blocks assigned to the subarray specified above.
    sb_ids = yield portal_client.schedule_blocks_assigned()
    print "\nSchedule block IDs on subarray {}\n{}".format(args.sub_nr, sb_ids)
    # Example output:
    #   Schedule block IDs on subarray 1:
    #   [u'20161010-0001', u'20161010-0002', u'20161010-0003']

    # Fetch the details for one of the schedule blocks found.
    if len(sb_ids) > 0:
        sb_detail = yield portal_client.schedule_block_detail(sb_ids[0])
        print "\nDetail for SB {}:\n{}\n".format(sb_ids[0], sb_detail)
Exemple #9
0
def simple_activity_wait(host, state, interrupt):
    sensor_name = "observation_activity"
    component = "subarray"
    log.debug("Waiting for observation_activity to go to {}".format(state))
    client = KATPortalClient(host,
                             on_update_callback=None,
                             logger=logging.getLogger('katcp'))
    #namespace = 'namespace_' + str(uuid.uuid4())
    #NOTE: Commenting this out based on comment seen in examples for katportal
    #yield client.connect()
    full_sensor_name = yield client.sensor_subarray_lookup(
        component=component, sensor=sensor_name, return_katcp_name=False)
    while True:
        sensor_sample = yield client.sensor_value(full_sensor_name,
                                                  include_value_ts=False)
        if interrupt.is_set():
            raise Interrupt
        elif sensor_sample.value == state:
            break
        else:
            yield sleep(1)
Exemple #10
0
class SensorTracker(object):
    """This class heavily based on that from E. Barr (2020)
    """
    def __init__(self, host, component, sensor_name):
        log.debug(("Building sensor tracker activity tracker "
                   "on {} for sensor={} and component={}").format(
            host, sensor_name, component))
        self._client = KATPortalClient(
            host,
            on_update_callback=self.event_handler,
            logger=logging.getLogger(LOG_FILE))
        self._namespace = 'namespace_' + str(uuid.uuid4())
        self._sensor_name = sensor_name
        self._component = component
        self._full_sensor_name = None
        self._state = None
        self._has_started = False

    @coroutine
    def start(self):
        if self._has_started:
            return
        log.debug("Starting sensor tracker")
        yield self._client.connect()
        log.debug("Connected")
        result = yield self._client.subscribe(self._namespace)
        self._full_sensor_name = yield self._client.sensor_subarray_lookup(
            component=self._component, sensor=self._sensor_name,
            return_katcp_name=False)
        log.debug("Tracking sensor: {}".format(self._full_sensor_name))
        result = yield self._client.set_sampling_strategies(
            self._namespace, self._full_sensor_name,
            'event')
        sensor_sample = yield self._client.sensor_value(
            self._full_sensor_name,
            include_value_ts=False)
        self._state = sensor_sample.value
        log.debug("Initial state: {}".format(self._state))
        self._has_started = True

    @coroutine
    def stop(self):
        log.info("Unsubscribing and disconnecting")
        yield self._client.unsubscribe(self._namespace)
        yield self._client.disconnect()

    def event_handler(self, msg_dict):
        status = msg_dict['msg_data']['status']
        if status == "nominal":
            log.debug("Sensor value update: {} -> {}".format(
                self._state, msg_dict['msg_data']['value']))
            self._state = msg_dict['msg_data']['value']
            log.info("{}:{}".format(self._full_sensor_name, self._state))
Exemple #11
0
    def _configure(self, product_id):
        """Executes when configure request is processed

        Args:
            product_id (str): the product id given in the ?configure request

        Returns:
            None
        """
        cam_url = self.redis_server.get("{}:{}".format(product_id, 'cam:url'))
        client = KATPortalClient(cam_url,
                                 on_update_callback=partial(
                                     self.on_update_callback_fn, product_id),
                                 logger=logger)
        #client = KATPortalClient(cam_url, on_update_callback=lambda x: self.on_update_callback_fn(product_id), logger=logger)
        self.subarray_katportals[product_id] = client
        logger.info(
            "Created katportalclient object for : {}".format(product_id))
        sensors_to_query = []  # TODO: add sensors to query on ?configure
        sensors_and_values = self.io_loop.run_sync(
            lambda: self._get_sensor_values(product_id, sensors_to_query))
        for sensor_name, value in sensors_and_values.items():
            key = "{}:{}".format(product_id, sensor_name)
            write_pair_redis(self.redis_server, key, repr(value))
Exemple #12
0
class KatportalClientWrapper(object):
    def __init__(self, host, callback=None):
        self._host = host
        self._client = KATPortalClient(host,
                                       on_update_callback=callback,
                                       logger=logging.getLogger('katcp'))

    @coroutine
    def _query(self, component, sensor, include_value_ts=False):
        log.debug("Querying sensor '{}' on component '{}'".format(
            sensor, component))
        sensor_name = yield self._client.sensor_subarray_lookup(
            component=component, sensor=sensor, return_katcp_name=False)
        log.debug("Found sensor name: {}".format(sensor_name))
        sensor_sample = yield self._client.sensor_value(
            sensor_name, include_value_ts=include_value_ts)
        log.debug("Sensor value: {}".format(sensor_sample))
        if sensor_sample.status != Sensor.STATUSES[Sensor.NOMINAL]:
            message = "Sensor {} not in NOMINAL state".format(sensor_name)
            log.error(message)
            raise Exception(sensor_name)
        raise Return(sensor_sample)

    @coroutine
    def get_observer_string(self, antenna):
        sensor_sample = yield self._query(antenna, "observer")
        raise Return(sensor_sample.value)

    @coroutine
    def get_observer_strings(self, antennas):
        query = "^({})_observer".format("|".join(antennas))
        log.debug("Regex query '{}'".format(query))
        sensor_samples = yield self._client.sensor_values(
            query, include_value_ts=False)
        log.debug("Sensor value: {}".format(sensor_samples))
        antennas = {}
        for key, value in sensor_samples.items():
            antennas[key.strip("_observer")] = value.value
        raise Return(antennas)

    @coroutine
    def get_antenna_feng_id_map(self, instrument_name, antennas):
        sensor_sample = yield self._query(
            'cbf', '{}.input-labelling'.format(instrument_name))
        labels = eval(sensor_sample.value)
        mapping = {}
        for input_label, input_index, _, _ in labels:
            antenna_name = input_label.strip("vh").lower()
            if antenna_name.startswith("m") and antenna_name in antennas:
                mapping[antenna_name] = input_index // 2
        raise Return(mapping)

    @coroutine
    def get_bandwidth(self, stream):
        sensor_sample = yield self._query(
            'sub', 'streams.{}.bandwidth'.format(stream))
        raise Return(sensor_sample.value)

    @coroutine
    def get_cfreq(self, stream):
        sensor_sample = yield self._query(
            'sub', 'streams.{}.centre-frequency'.format(stream))
        raise Return(sensor_sample.value)

    @coroutine
    def get_sideband(self, stream):
        sensor_sample = yield self._query('sub',
                                          'streams.{}.sideband'.format(stream))
        raise Return(sensor_sample.value)

    @coroutine
    def get_sync_epoch(self):
        sensor_sample = yield self._query('sub', 'synchronisation-epoch')
        raise Return(sensor_sample.value)

    @coroutine
    def get_itrf_reference(self):
        sensor_sample = yield self._query('sub', 'array-position-itrf')
        x, y, z = [float(i) for i in sensor_sample.value.split(",")]
        raise Return((x, y, z))

    @coroutine
    def get_proposal_id(self):
        sensor_sample = yield self._query('sub', 'script-proposal-id')
        raise Return(sensor_sample.value)

    @coroutine
    def get_sb_id(self):
        sensor_sample = yield self._query('sub', 'script-experiment-id')
        raise Return(sensor_sample.value)

    @coroutine
    def get_telstate(self, key='_wide_'):
        sensor_sample = yield self._query('sdp', 'subarray-product-ids')
        products = sensor_sample.value.split(",")
        for product in products:
            print product
            if key in product:
                product_id = product
                break
        else:
            raise SDPWideProductNotFound
        sensor_sample = yield self._query(
            'sdp', 'spmc_{}_telstate_telstate'.format(product_id))
        raise Return(sensor_sample.value)

    @coroutine
    def get_last_phaseup_timestamp(self):
        sensor_sample = yield self._query('sub',
                                          'script-last-phaseup',
                                          include_value_ts=True)
        raise Return(sensor_sample.value_time)

    @coroutine
    def get_last_delay_cal_timestamp(self):
        sensor_sample = yield self._query('sub',
                                          'script-last-delay-calibration',
                                          include_value_ts=True)
        raise Return(sensor_sample.value_time)

    @coroutine
    def get_last_calibration_timestamp(self):
        delay_cal_error = None
        phase_cal_error = None
        try:
            delay_cal = yield self.get_last_delay_cal_timestamp()
        except Exception as error:
            delay_cal_error = error
        try:
            phase_cal = yield self.get_last_phaseup_timestamp()
        except Exception as error:
            phase_cal_error = error

        if phase_cal_error and delay_cal_error:
            raise Exception(
                "No valid calibration timestamps: delay error: {}, phaseup error: {}"
                .format(delay_cal_error, phase_cal_error))
        elif phase_cal_error:
            raise Return(delay_cal)
        elif delay_cal_error:
            raise Return(phase_cal)
        else:
            raise Return(max(delay_cal, phase_cal))

    @coroutine
    def get_gains(self):
        val = yield self.get_telstate()
        telstate_address = "{}:{}".format(*eval(val))
        last_calibration = yield self.get_last_calibration_timestamp()
        telstate = katsdptelstate.TelescopeState(telstate_address)
        corrections = get_phaseup_corrections(telstate, last_calibration, 1.0,
                                              False)
        raise Return(corrections)

    @coroutine
    def get_fbfuse_address(self):
        sensor_sample = yield self._query('fbfuse', 'fbfmc-address')
        raise Return(eval(sensor_sample.value))

    @coroutine
    def get_fbfuse_target_config(self, product_id):
        sensor_list = ["phase-reference", "coherent-beam-shape"]
        fbf_config = {}
        fbfuse_proxy = yield self.get_fbfuse_proxy_id()
        prefix = "{}_fbfmc_{}_".format(fbfuse_proxy, product_id)
        query = "^{}({})$".format(
            prefix, "|".join([s.replace("-", "_") for s in sensor_list]))
        log.debug("Regex query '{}'".format(query))
        sensor_samples = yield self._client.sensor_values(
            query, include_value_ts=False)
        log.debug("Sensor value: {}".format(sensor_samples))
        for sensor_name in sensor_list:
            full_name = "{}{}".format(prefix, sensor_name.replace("-", "_"))
            sensor_sample = sensor_samples[full_name]
            log.debug(sensor_sample)
            if sensor_sample.status != Sensor.STATUSES[Sensor.NOMINAL]:
                message = "Sensor {} not in NOMINAL state".format(full_name)
                log.error(message)
                raise Exception(sensor_name)
            else:
                fbf_config[sensor_name] = sensor_sample.value
        raise Return(fbf_config)

    @coroutine
    def get_fbfuse_sb_config(self, product_id):
        sensor_list = [
            "bandwidth", "nchannels", "centre-frequency",
            "coherent-beam-count", "coherent-beam-count-per-group",
            "coherent-beam-ngroups", "coherent-beam-tscrunch",
            "coherent-beam-fscrunch", "coherent-beam-antennas",
            "coherent-beam-multicast-groups",
            "coherent-beam-multicast-group-mapping",
            "coherent-beam-multicast-groups-data-rate",
            "coherent-beam-heap-size", "coherent-beam-idx1-step",
            "coherent-beam-subband-nchans", "coherent-beam-time-resolution",
            "incoherent-beam-count", "incoherent-beam-tscrunch",
            "incoherent-beam-fscrunch", "incoherent-beam-antennas",
            "incoherent-beam-multicast-group",
            "incoherent-beam-multicast-group-data-rate",
            "incoherent-beam-heap-size", "incoherent-beam-idx1-step",
            "incoherent-beam-subband-nchans", "incoherent-beam-time-resolution"
        ]
        fbf_config = {}
        fbfuse_proxy = yield self.get_fbfuse_proxy_id()
        prefix = "{}_fbfmc_{}_".format(fbfuse_proxy, product_id)
        query = "^{}({})$".format(
            prefix, "|".join([s.replace("-", "_") for s in sensor_list]))
        log.debug("Regex query '{}'".format(query))
        sensor_samples = yield self._client.sensor_values(
            query, include_value_ts=False)
        log.debug("Sensor value: {}".format(sensor_samples))
        for sensor_name in sensor_list:
            full_name = "{}{}".format(prefix, sensor_name.replace("-", "_"))
            sensor_sample = sensor_samples[full_name]
            log.debug(sensor_sample)
            if sensor_sample.status != Sensor.STATUSES[Sensor.NOMINAL]:
                message = "Sensor {} not in NOMINAL state".format(full_name)
                log.error(message)
                raise Exception(sensor_name)
            else:
                fbf_config[sensor_name] = sensor_sample.value
        raise Return(fbf_config)

    @coroutine
    def get_fbfuse_coherent_beam_positions(self, product_id):
        component = product_id.replace("array", "fbfuse")
        prefix = "{}_fbfmc_{}_".format(component, product_id)
        query = "^{}.*cfbf.*$".format(prefix)
        sensor_samples = yield self._client.sensor_values(
            query, include_value_ts=False)
        beams = {}
        for key, reading in sensor_samples.items():
            beam_name = key.split("_")[-1]
            if reading.status == Sensor.STATUSES[Sensor.NOMINAL]:
                beams[beam_name] = reading.value
        raise Return(beams)

    @coroutine
    def get_fbfuse_proxy_id(self):
        sensor_sample = yield self._query('sub', 'allocations')
        for resource, _, _ in eval(sensor_sample.value):
            if resource.startswith("fbfuse"):
                raise Return(resource)
        else:
            raise Exception("No FBFUSE proxy found in current subarray")

    def get_sensor_tracker(self, component, sensor_name):
        return SensorTracker(self._host, component, sensor_name)
Exemple #13
0
def main():
    # Change URL to point to a valid portal node.
    # If you are not interested in any subarray specific information
    # (e.g. schedule blocks), then the number can be omitted, as below.
    # Note: if on_update_callback is set to None, then we cannot use the
    #       KATPortalClient.connect() and subscribe() methods here.
    portal_client = KATPortalClient(
        'http://{host}/api/client'.format(**vars(args)),
        on_update_callback=None,
        logger=logger)

    # Get the names of sensors matching the patterns
    sensor_names = yield portal_client.sensor_names(args.sensors)
    print "\nMatching sensor names: {}".format(sensor_names)
    # Example output (if sensors is 'm01[12]_pos_request_base'):
    #   Matching sensor names: [u'm011_pos_request_base_azim',
    #   u'm012_pos_request_base_ra', u'm012_pos_request_base_dec',
    #   u'm011_pos_request_base_ra', u'm012_pos_request_base_elev',
    #   u'm011_pos_request_base_dec', u'm012_pos_request_base_azim',
    #   u'm011_pos_request_base_elev']

    # Fetch the details for the sensors found.
    for sensor_name in sensor_names:
        sensor_detail = yield portal_client.sensor_detail(sensor_name)
        print "\nDetail for sensor {}:".format(sensor_name)
        for key in sensor_detail:
            print "    {}: {}".format(key, sensor_detail[key])
        # Example output:
        #   Detail for sensor m011_pos_request_base_azim:
        #       name: m011_pos_request_base_azim
        #       systype: mkat
        #       component: m011
        #       site: deva
        #       katcp_name: m011.pos.request-base-azim
        #       params: [-195.0, 370.0]
        #       units: deg
        #       type: float
        #       description: Requested target azimuth

    num_sensors = len(sensor_names)
    if num_sensors == 0:
        print "\nNo matching sensors found - no history to request!"
    else:
        print("\nRequesting history for {} sensors, from {} to {}".format(
            num_sensors,
            datetime.utcfromtimestamp(
                args.start).strftime('%Y-%m-%dT%H:%M:%SZ'),
            datetime.utcfromtimestamp(
                args.end).strftime('%Y-%m-%dT%H:%M:%SZ')))
        if len(sensor_names) == 1:
            # Request history for just a single sensor - result is timestamp, value, status
            #    If value timestamp is also required, then add the additional argument: include_value_ts=True
            #    result is then timestmap, value_timestmap, value, status
            history = yield portal_client.sensor_history(
                sensor_names[0],
                args.start,
                args.end,
                timeout_sec=args.timeout)
            histories = {sensor_names[0]: history}
        else:
            # Request history for all the sensors - result is timestamp, value, status
            #    If value timestamp is also required, then add the additional argument: include_value_ts=True
            #    result is then timestmap, value_timestmap, value, status
            histories = yield portal_client.sensors_histories(
                sensor_names, args.start, args.end, timeout_sec=args.timeout)

        print "Found {} sensors.".format(len(histories))
        for sensor_name, history in histories.items():
            num_samples = len(history)
            print "History for: {} ({} samples)".format(
                sensor_name, num_samples)
            if num_samples > 0:
                print "\tindex,timestamp,value,status"
                for count in range(0, num_samples, args.decimate):
                    print "\t{},{}".format(count, history[count].csv())
Exemple #14
0
def main():
    # Change URL to point to a valid portal node.  Subarray can be 1 to 4.
    # Note: if on_update_callback is set to None, then we cannot use the
    #       KATPortalClient.connect() method (i.e. no websocket access).
    portal_client = KATPortalClient(
        'http://{host}/api/client/1'.format(**vars(args)),
        on_update_callback=None,
        logger=logger)

    results = yield portal_client.future_targets(
        '{sb_id_code}'.format(**vars(args)))
    print results

    # Example output:
    # [
    #     {
    #         "track_start_offset":39.8941187859,
    #         "target":"PKS 0023-26 | J0025-2602 | OB-238, radec, 0:25:49.16, -26:02:12.6, (1410.0 8400.0 -1.694 2.107 -0.4043)",
    #         "track_duration":20.0
    #     },
    #     {
    #         "track_start_offset":72.5947952271,
    #         "target":"PKS 0043-42 | J0046-4207, radec, 0:46:17.75, -42:07:51.5, (400.0 2000.0 3.12 -0.7)",
    #         "track_duration":20.0
    #     },
    #     {
    #         "track_start_offset":114.597304821,
    #         "target":"PKS 0408-65 | J0408-6545, radec, 4:08:20.38, -65:45:09.1, (1410.0 8400.0 -3.708 3.807 -0.7202)",
    #         "track_duration":20.0
    #     }
    # ]

    # Create the antenna object that is used as the reference observer for
    # the coordinate calculations.
    # The parameters for the antenna object is as follows:
    #     name : string or :class:`Antenna` object
    #         Name of antenna, or full description string or existing antenna object
    #     latitude : string or float, optional
    #         Geodetic latitude, either in 'D:M:S' string format or float in radians
    #     longitude : string or float, optional
    #         Longitude, either in 'D:M:S' string format or a float in radians
    #     altitude : string or float, optional
    #         Altitude above WGS84 geoid, in metres
    #     diameter : string or float, optional
    #         Dish diameter, in metres
    #     delay_model : :class:`DelayModel` object or equivalent, optional
    #         Delay model for antenna, either as a direct object, a file-like object
    #         representing a parameter file, or a string or sequence of float params.
    #         The first three parameters form an East-North-Up offset from WGS84
    #         reference position, in metres.
    #     pointing_model : :class:`PointingModel` object or equivalent, optional
    #         Pointing model for antenna, either as a direct object, a file-like
    #         object representing a parameter file, or a string or sequence of
    #         float parameters from which the :class:`PointingModel` object can
    #         be instantiated
    #     beamwidth : string or float, optional
    #         Full width at half maximum (FWHM) average beamwidth, as a multiple of
    #         lambda / D (wavelength / dish diameter). This depends on the dish
    #         illumination pattern, and ranges from 1.03 for a uniformly illuminated
    #         circular dish to 1.22 for a Gaussian-tapered circular dish (the
    #         default).

    # Antenna description string has format "name, latitude, longitude, altitude"
    antenna = katpoint.Antenna("MeerKAT, -30:42:39.8, 21:26:38.0, 1035")

    # The default timestamp is "now", otherwise pass in a Unix timestamp as a float, or
    # a string of the form "YYYY-MM-DD HH:MM:SS.SSS" or "YYYY/MM/DD HH:MM:SS.SSS"
    timestamp_for_calcs = katpoint.Timestamp()

    for future_target in results:
        # Create the katpoint.Target object
        # The body argument is the full description string as specified in the
        # "target" attribute of the future_targets results.
        # The antenna parameter is a katpoint.Antenna object that defines
        # the reference observer for the coordinates calculation.
        # An alternative to specifying the antenna in the target object
        # would be to pass the antenna object as a parameter to any of
        # the calculation methods.

        # Please see the katpoint.Target class docstring for a detailed
        # explanation of the usage and input parameters.
        target = katpoint.Target(body=future_target['target'], antenna=antenna)
        print "-" * 80
        # Short human-friendly string representation of target object.
        print "Target: {}".format(target)
        # Complete string representation of target object, sufficient to
        # reconstruct it.
        print "\tdescription: {}".format(target.description)
        # Type of target body, as a string tag.
        print "\tbody_type: {}".format(target.body_type)
        # Calculate target (az, el) coordinates as seen from antenna at
        # time(s).
        print "\tazel: {}".format(target.azel(timestamp=timestamp_for_calcs))
        # Calculate target's apparent (ra, dec) coordinates as seen from antenna at time(s).
        # This calculates the *apparent topocentric position* of the target for
        # the epoch-of-date in equatorial coordinates. Take note that this is
        # *not* the "star-atlas" position of the target, but the position as is
        # actually seen from the antenna at the given times. The difference is on
        # the order of a few arcminutes. These are the coordinates that a telescope
        # with an equatorial mount would use to track the target. Some targets are
        # unable to provide this (due to a limitation of pyephem), notably
        # stationary (*azel*) targets, and provide the *astrometric geocentric
        # position* instead.
        print "\tapparent_radec: {}".format(
            target.apparent_radec(timestamp=timestamp_for_calcs))
        # Calculate target's astrometric (ra, dec) coordinates as seen from antenna at time(s).
        # This calculates the J2000 *astrometric geocentric position* of the
        # target, in equatorial coordinates. This is its star atlas position for
        # the epoch of J2000.
        print "\tastrometric_radec: {}".format(
            target.astrometric_radec(timestamp=timestamp_for_calcs))
        # Calculate target's galactic (l, b) coordinates as seen from antenna at time(s).
        # This calculates the galactic coordinates of the target, based on the
        # J2000 *astrometric* equatorial coordinates. This is its position relative
        # to the Galactic reference frame for the epoch of J2000.
        print "\tgalactic: {}".format(
            target.galactic(timestamp=timestamp_for_calcs))
        # Calculate parallactic angle on target as seen from antenna at time(s).
        # This calculates the *parallactic angle*, which is the position angle of
        # the observer's vertical on the sky, measured from north toward east.
        # This is the angle between the great-circle arc connecting the celestial
        # North pole to the target position, and the great-circle arc connecting
        # the zenith above the antenna to the target, or the angle between the
        # *hour circle* and *vertical circle* through the target, at the given
        # timestamp(s).
        print "\tparallactic_angle: {}".format(
            target.parallactic_angle(timestamp=timestamp_for_calcs))
Exemple #15
0
class PubSubThread (threading.Thread):

  def __init__ (self, script, id):
    threading.Thread.__init__(self)
    self.script = script
   
    self.script.log(2, "PubSubThread.__init__()")

    self.curr_utc = times.getUTCTime()
    self.prev_utc = self.curr_utc
   
    self.metadata_server = self.script.cfg["PUBSUB_ADDRESS"]
    self.logger = logging.getLogger('katportalclient.example') 
    self.logger.setLevel(logging.INFO)
    self.beam = -1
    self.sub_array = -1
    self.subs = []
    self.io_loop = []
    self.policy = "event-rate 1.0 300.0"
    self.title  = "ptuse_unconfigured"
    self.running = False
    self.restart_io_loop = True

  def configure (self):

    self.subs = []

    dp_prefix = self.data_product_prefix.replace("_", ".")
    sa_prefix = self.sub_array_prefix.replace("_", ".")

    self.subs.append ( dp_prefix + ".cbf.synchronisation.epoch")
    self.subs.append ( sa_prefix +".state")
    self.subs.append ( sa_prefix +".pool.resources")
    self.subs.append ( sa_prefix +".script.target")
    self.subs.append ( sa_prefix +".script.ra")
    self.subs.append ( sa_prefix +".script.dec")
    self.subs.append ( sa_prefix +".script.ants")
    self.subs.append ( sa_prefix +".script.observer")
    self.subs.append ( sa_prefix +".script.tsubint")
    self.subs.append ( sa_prefix +".script.experiment.id")
    self.subs.append ( sa_prefix +".script.active.sbs")
    self.subs.append ( sa_prefix +".script.description")

  # configure the pub/sub instance to 
  def set_sub_array (self, sub_array, beam):
    self.script.log(2, "PubSubThread::set_sub_array sub_array="+ str(sub_array) + " beam=" + str(beam))
    self.sub_array = str(sub_array)
    self.beam = str(beam )
    self.data_product_prefix = "data_" + self.sub_array
    self.sub_array_prefix = "subarray_" + self.sub_array
    self.title = "ptuse_beam_" + str(beam)
    self.configure()

  def run (self):
    self.script.log(1, "PubSubThread::run starting while")
    while self.restart_io_loop:

      # open connection to CAM
      self.io_loop = tornado.ioloop.IOLoop.current()
      self.io_loop.add_callback (self.connect, self.logger)

      self.running = True
      self.restart_io_loop = False
      self.io_loop.start()
      self.running = False
      self.io_loop = []

      # unsubscribe and disconnect from CAM
      self.ws_client.unsubscribe(self.title)
      self.ws_client.disconnect()

    self.script.log(2, "PubSubThread::run exiting")

  def join (self):
    self.script.log(2, "PubSubThread::join self.stop()")
    self.stop()

  def stop (self):
    self.script.log(2, "PubSubThread::stop()")
    if self.running:
      self.script.log(2, "PubSubThread::stop io_loop.stop()")
      self.io_loop.stop()
    return

  def restart (self):
    # get the IO loop to restart on the call to stop()
    self.restart_io_loop = True
    if self.running:
      self.script.log(2, "PubSubThread::restart self.stop()")
      self.stop()
    return

  @tornado.gen.coroutine
  def connect (self, logger):
    self.script.log(2, "PubSubThread::connect()")
    self.ws_client = KATPortalClient(self.metadata_server, self.on_update_callback, logger=logger)
    self.script.log(2, "PubSubThread::connect self.ws_client.connect()")
    yield self.ws_client.connect()
    self.script.log(2, "PubSubThread::connect self.ws_client.subscribe(" + self.title + ")")
    result = yield self.ws_client.subscribe(self.title)
    self.script.log(2, "PubSubThread::connect self.ws_client.set_sampling_strategies (" + self.title + ", " + str(self.subs) + ", " + self.policy + ")")
    results = yield self.ws_client.set_sampling_strategies( self.title, self.subs, self.policy) 

    for result in results:
      self.script.log(2, "PubSubThread::connect subscribed to " + str(result))

  def on_update_callback (self, msg):

    self.curr_utc = times.getUTCTime()
    if times.diffUTCTimes(self.prev_utc, self.curr_utc) > 60:
      self.script.log(2, "PubSubThread::on_update_callback: heartbeat msg="+str(msg))
      self.prev_utc = self.curr_utc

    self.update_config (msg)

  def update_cam_config (self, key, name, value):
    if self.script.cam_config[key] != value:
      self.script.log(1, "PubSubThread::update_cam_config " + key + "=" + value + " from " + name)
      self.script.cam_config[key] = value

  def update_config (self, msg):

    # ignore empty messages
    if msg == []: 
      return

    status = msg["msg_data"]["status"]
    value = msg["msg_data"]["value"]
    name = msg["msg_data"]["name"]

    self.script.log(2, "PubSubThread::update_config " + name + "=" + str(value))
   
    if name == self.sub_array_prefix + "_script_target":
      self.update_cam_config("SOURCE", name, str(value))

    elif name == self.sub_array_prefix + "_script_ra":
      self.update_cam_config("RA", name, str(value))

    elif name == self.sub_array_prefix + "_script_dec":
      self.update_cam_config("DEC", name, str(value))

    elif name == self.data_product_prefix + "_cbf_synchronisation_epoch":
      self.update_cam_config("ADC_SYNC_TIME", name, str(value))

    elif name == self.sub_array_prefix + "_script_observer":
      self.update_cam_config("OBSERVER", name, str(value))

    elif name == self.sub_array_prefix + "_script_tsubint":
      self.update_cam_config("TSUBINT", name, str(value))

    elif name == self.sub_array_prefix + "_script_ants":
      self.update_cam_config("ANTENNAE", name, str(value))

    elif name == self.sub_array_prefix + "_active_sbs":
      self.update_cam_config("SCHEDULE_BLOCK_ID", name, str(value))

    elif name == self.sub_array_prefix + "_script_experiment_id":
      self.update_cam_config("EXPERIMENT_ID", name, str(value))

    elif name == self.sub_array_prefix + "_pool_resources":
      self.update_cam_config("POOL_RESOURCES", name, str(value))

    elif name == self.sub_array_prefix + "_script_description":
      self.update_cam_config("DESCRIPTION", name, str(value))

    elif name == self.sub_array_prefix + "_state":
      self.update_cam_config("SUBARRAY_STATE", name, str(value))

    else:
      self.script.log(1, "PubSubThread::update_config no match on " + name)

    self.script.log(3, "PubSubThread::update_config done")
Exemple #16
0
def _helper_create_from_sensors(starttime, stoptime, variables=None):
    """
    Create a list of weather objects from sensor data using tornado server.

    Parameters
    ----------
    starttime : astropy Time object
        Time to start getting history.
    stoptime : astropy Time object
        Time to stop getting history.
    variable : str
        Variable to get history for. Must be a key in weather_sensor_dict,
        defaults to all keys in weather_sensor_dict.

    Returns
    -------
    A list of WeatherData objects (only accessible via a yield call)

    """
    from katportalclient import KATPortalClient

    if not isinstance(starttime, Time):
        raise ValueError('starttime must be an astropy Time object')

    if not isinstance(stoptime, Time):
        raise ValueError('stoptime must be an astropy Time object')

    if variables is None:
        variables = weather_sensor_dict.keys()
    elif not isinstance(variables, (list, tuple)):
        variables = [variables]

    sensor_names = []
    sensor_var_dict = {}
    for var in variables:
        if var not in weather_sensor_dict.keys():
            raise ValueError('variable must be a key in weather_sensor_dict')
        sensor_names.append(weather_sensor_dict[var]['sensor_name'])
        sensor_var_dict[weather_sensor_dict[var]['sensor_name']] = var

    portal_client = KATPortalClient(katportal_url, on_update_callback=None)

    histories = yield portal_client.sensors_histories(sensor_names,
                                                      starttime.unix,
                                                      stoptime.unix,
                                                      timeout_sec=120)

    weather_obj_list = []
    for sensor_name, history in histories.items():
        variable = sensor_var_dict[sensor_name]
        sensor_times = [0.0]
        sensor_data = []
        for item in history:
            # status is usually nominal, but can indicate sensor errors.
            # Since we can't do anything about those and the data might be bad, ignore them
            if item.status != 'nominal':
                continue
            # skip it if nan is supplied
            if isnan(float(item.value)):
                continue

            # the value_time is the sensor timestamp, while the other is
            # when the recording system got it. The value_time isn't always
            # present, so test for it
            if 'value_time' in item._fields:
                timestamp = item.value_time
            else:
                timestamp = item.sample_time
            if timestamp > sensor_times[-1]:
                sensor_times.append(timestamp)
                sensor_data.append(float(item.value))
        del (sensor_times[0])
        if len(sensor_data):
            reduction = weather_sensor_dict[variable]['reduction']
            period = weather_sensor_dict[variable]['period']

            times_use, values_use = _reduce_time_vals(np.array(sensor_times),
                                                      np.array(sensor_data),
                                                      period,
                                                      strategy=reduction)
            if times_use is not None:
                for count, timestamp in enumerate(times_use.tolist()):
                    time_obj = Time(timestamp, format='unix')
                    weather_obj_list.append(
                        WeatherData.create(time_obj, variable,
                                           values_use[count]))

    raise tornado.gen.Return(weather_obj_list)
Exemple #17
0
 def __init__(self, host, callback=None):
     self._client = KATPortalClient(host,
                                    on_update_callback=callback,
                                    logger=logging.getLogger('katcp'))
    def _configure(self, product_id):
        """Executes when configure request is processed

        Args:
            product_id (str): the product id given in the ?configure request

        Returns:
            None
        """
        # Update configuration:
        try:
            (ant_sensors, cbf_conf_sensors, stream_sensors, cbf_sensors,
             conf_sensors, subarray_sensors, stream_conf_sensors,
             cbf_on_track) = self.configure_katportal(
                 os.path.join(os.getcwd(), self.config_file))
            if (ant_sensors is not None):
                self.ant_sensors = []
                self.ant_sensors.extend(ant_sensors)
            if (cbf_conf_sensors is not None):
                self.cbf_conf_sensors = []
                self.cbf_conf_sensors.extend(cbf_conf_sensors)
            if (stream_conf_sensors is not None):
                self.stream_conf_sensors = []
                self.stream_conf_sensors.extend(stream_conf_sensors)
            if (stream_sensors is not None):
                self.stream_sensors = []
                self.stream_sensors.extend(stream_sensors)
            if (conf_sensors is not None):
                self.conf_sensors = []
                self.conf_sensors.extend(conf_sensors)
            if (subarray_sensors is not None):
                self.subarray_sensors = []
                self.subarray_sensors.extend(subarray_sensors)
            if (cbf_sensors is not None):
                self.cbf_sensors = []
                self.cbf_sensors.extend(cbf_sensors)
            if (cbf_on_track is not None):
                self.cbf_on_track = []
                self.cbf_on_track.extend(cbf_on_track)
            log.info('Configuration updated')
        except:
            log.warning(
                'Configuration not updated; old configuration might be present.'
            )
        cam_url = self.redis_server.get("{}:{}".format(product_id, 'cam:url'))
        client = KATPortalClient(cam_url,
                                 on_update_callback=partial(
                                     self.on_update_callback_fn, product_id),
                                 logger=log)
        #client = KATPortalClient(cam_url,
        #    on_update_callback=lambda x: self.on_update_callback_fn(product_id),
        #    logger=log)
        self.subarray_katportals[product_id] = client
        log.info("Created katportalclient object for : {}".format(product_id))
        subarray_nr = product_id[-1]
        ant_key = '{}:antennas'.format(product_id)
        ant_list = self.redis_server.lrange(ant_key, 0,
                                            self.redis_server.llen(ant_key))
        # Enter antenna list into the history hash
        ant_history = json.dumps(ant_list)
        self.save_history(self.redis_server, product_id, 'antennas',
                          ant_history)
        # Get sensors on configure
        if (len(self.conf_sensors) > 0):
            conf_sensor_names = [
                'subarray_{}_'.format(subarray_nr) + sensor
                for sensor in self.conf_sensors
            ]
            self.fetch_once(conf_sensor_names, product_id, 3, 30, 0.5)
        # Get CBF component name (in case it has changed to
        # CBF_DEV_[product_id] instead of CBF_[product_id])
        key = '{}:subarray_{}_{}'.format(product_id, subarray_nr,
                                         'pool_resources')
        pool_resources = self.redis_server.get(key).split(',')
        self.cbf_name = self.component_name('cbf', pool_resources, log)
        key = '{}:{}'.format(product_id, 'cbf_name')
        write_pair_redis(self.redis_server, key, self.cbf_name)
        # Get CBF sensor values required on configure.
        cbf_prefix = self.redis_server.get('{}:cbf_prefix'.format(product_id))
        if (len(self.cbf_conf_sensors) > 0):
            # Complete the CBF sensor names with the CBF component name.
            cbf_sensor_prefix = '{}_{}_'.format(self.cbf_name, cbf_prefix)
            cbf_conf_sensor_names = [
                cbf_sensor_prefix + sensor for sensor in self.cbf_conf_sensors
            ]
            # Get CBF sensors and write to redis.
            self.fetch_once(cbf_conf_sensor_names, product_id, 3, 30, 0.5)
            # Calculate antenna-to-Fengine mapping
            antennas, feng_ids = self.antenna_mapping(product_id,
                                                      cbf_sensor_prefix)
            write_pair_redis(self.redis_server,
                             '{}:antenna_names'.format(product_id), antennas)
            write_pair_redis(self.redis_server,
                             '{}:feng_ids'.format(product_id), feng_ids)
        # Get stream sensors on configure:
        if (len(self.stream_conf_sensors) > 0):
            stream_conf_sensors = [
                'subarray_{}_streams_{}_{}'.format(subarray_nr, cbf_prefix,
                                                   sensor)
                for sensor in self.stream_conf_sensors
            ]
            self.fetch_once(stream_conf_sensors, product_id, 3, 30, 0.5)
        # Initialise last-target to 0
        write_pair_redis(self.redis_server,
                         '{}:last-target'.format(product_id), 0)
        # Indicate to anyone listening that the configure process is complete.
        publish_to_redis(self.redis_server, REDIS_CHANNELS.alerts,
                         'conf_complete:{}'.format(product_id))
Exemple #19
0
class SensorTracker(object):
    def __init__(self, host, component, sensor_name):
        log.debug(("Building sensor tracker activity tracker "
                   "on {} for sensor={} and component={}").format(
                       host, sensor_name, component))
        self._client = KATPortalClient(host,
                                       on_update_callback=self.event_handler,
                                       logger=logging.getLogger('katcp'))
        self._namespace = 'namespace_' + str(uuid.uuid4())
        self._sensor_name = sensor_name
        self._component = component
        self._full_sensor_name = None
        self._state = None
        self._has_started = False

    @coroutine
    def start(self):
        if self._has_started:
            return
        log.debug("Starting sensor tracker")
        yield self._client.connect()
        result = yield self._client.subscribe(self._namespace)
        self._full_sensor_name = yield self._client.sensor_subarray_lookup(
            component=self._component,
            sensor=self._sensor_name,
            return_katcp_name=False)
        log.debug("Tracking sensor: {}".format(self._full_sensor_name))
        result = yield self._client.set_sampling_strategies(
            self._namespace, self._full_sensor_name, 'event')
        sensor_sample = yield self._client.sensor_value(self._full_sensor_name,
                                                        include_value_ts=False)
        self._state = sensor_sample.value
        log.debug("Initial state: {}".format(self._state))
        self._has_started = True

    @coroutine
    def stop(self):
        yield self._client.unsubscribe(self._namespace)
        yield self._client.disconnect()

    def event_handler(self, msg_dict):
        status = msg_dict['msg_data']['status']
        if status == "nominal":
            log.debug("Sensor value update: {} -> {}".format(
                self._state, msg_dict['msg_data']['value']))
            self._state = msg_dict['msg_data']['value']

    @coroutine
    def wait_until(self, state, interrupt):
        log.debug("Waiting for state='{}'".format(state))
        while True:
            if self._state == state:
                log.debug("Desired state reached")
                raise Return(self._state)
            else:
                try:
                    log.debug("Waiting on interrupt in wait_until loop")
                    yield interrupt.wait(timeout=datetime.timedelta(seconds=1))
                    log.debug("Moving to next loop iteration")
                except TimeoutError:
                    continue
                else:
                    log.debug("Wait was interrupted")
                    raise Interrupt("Interrupt event was set")