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))
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 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): 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")
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)