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