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)
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))
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 __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])
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)
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)
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))
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))
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)
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())
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))
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")
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)
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))
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")