def test_request_add_remove(self): """Test a request being added and then remove it.""" yield self.client.until_synced() def request_sparkling_new(self, req, msg): """A new command.""" return Message.reply(msg.name, "ok", "bling1", "bling2") # Check that the request did not exist before self.assertNotIn('sparkling-new', self.client.requests) # Add a request. self.server.request_sparkling_new = request_sparkling_new self.server._request_handlers['sparkling-new'] = request_sparkling_new self.server.mass_inform(Message.inform('interface-changed')) # Do a blocking request to ensure #interface-changed has been received yield self.client.simple_request('watchdog') yield self.client.until_synced() self.assertIn('sparkling-new', self.client.requests) req = yield self.client.future_get_request('sparkling-new') self.assertEqual(req.name, 'sparkling-new') # Remove a request. self.server.request_sparkling_new = None del(self.server._request_handlers['sparkling-new']) self.server.mass_inform(Message.inform('interface-changed')) # Do a blocking request to ensure #interface-changed has been received self.client.simple_request('watchdog') yield self.client.until_synced() self.assertNotIn('sparkling_new', self.client.requests)
def test_sensor_add_remove(self): """Test a sensor being added and then remove it.""" yield self.client.until_synced() sensor = DeviceTestSensor(Sensor.INTEGER, "another.int", "An Integer.", "count", [-5, 5], timestamp=time.time(), status=Sensor.NOMINAL, value=3) # Check that the sensor does not exist currently self.assertNotIn(sensor.name, self.client.sensors) # Add a sensor. self.server.add_sensor(sensor) self.server.mass_inform(Message.inform('interface-changed')) # Do a blocking request to ensure #interface-changed has been received yield self.client.simple_request('watchdog') yield self.client.until_synced() self.assertIn('another.int', self.client.sensors) # Remove a sensor. self.server.remove_sensor(sensor) self.server.mass_inform(Message.inform('interface-changed')) # Do a blocking request to ensure #interface-changed has been received yield self.client.simple_request('watchdog') yield self.client.until_synced() self.assertNotIn('another.int', self.client.sensors)
def callback((informs, reply)): self.assertEquals(informs[2:], [Message.inform('sensor-value', '1000', '1', 'device.sensor1', 'unknown', '0'), Message.inform('sensor-value', '0', '1', 'device.sensor2', 'unknown', '0')]) self.assertEquals(reply, Message.reply('sensor-value', 'ok', '4'))
def request_help(self, msg): """Return help on the available requests. Return a description of the available requests using a seqeunce of #help informs. Parameters ---------- request : str, optional The name of the request to return help for (the default is to return help for all requests). Informs ------- request : str The name of a request. description : str Documentation for the named request. Returns ------- success : {'ok', 'fail'} Whether sending the help succeeded. informs : int Number of #help inform messages sent. Examples -------- :: ?help #help halt ...description... #help help ...description... ... !help ok 5 ?help halt #help halt ...description... !help ok 1 """ if msg.arguments: name = msg.arguments[0] meth = getattr(self, 'request_' + name.replace('-', '_'), None) if meth is None: return Message.reply('help', 'fail', 'Unknown request method.') doc = meth.__doc__ if doc is not None: doc = doc.strip() self.send_message(Message.inform('help', name, doc)) return Message.reply('help', 'ok', '1') count = 0 for name in dir(self.__class__): item = getattr(self, name) if name.startswith('request_') and callable(item): sname = name[len('request_'):] doc = item.__doc__ if doc is not None: doc = doc.strip() self.send_message(Message.inform('help', sname, doc)) count += 1 return Message.reply(msg.name, "ok", str(count))
def callback((informs, reply)): self.assertEquals(informs[2:], [ Message.inform('sensor-value', '1000', '1', 'device.sensor1', 'unknown', '0'), Message.inform('sensor-value', '0', '1', 'device.sensor2', 'unknown', '0') ]) self.assertEquals(reply, Message.reply('sensor-value', 'ok', '4'))
def request_version_list(self, msg): """Request the list of versions of roles and subcomponents. Informs ------- name : str Name of the role or component. version : str A string identifying the version of the component. Individual components may define the structure of this argument as they choose. In the absence of other information clients should treat it as an opaque string. build_state_or_serial_number : str A unique identifier for a particular instance of a component. This should change whenever the component is replaced or updated. Returns ------- success : {'ok', 'fail'} Whether sending the version list succeeded. informs : int Number of #version-list inform messages sent. Examples -------- :: ?version-list #version-list katcp-protocol 5.0-MI #version-list katcp-library katcp-python-tx-0.4 katcp-python-0.4.1 #version-list katcp-device foodevice-1.0 foodevice-1.0.0rc1 !version-list ok 3 """ versions = [ ("katcp-protocol", (self.PROTOCOL_INFO, None)), ("katcp-library", ("katcp-python-tx-%d.%d" % VERSION[:2], VERSION_STR)), ("katcp-device", (self.version(), self.build_state())), ] extra_versions = sorted(self.extra_versions.items()) for name, (version, build_state) in versions + extra_versions: if build_state is None: inform = Message.inform(msg.name, name, version) else: inform = Message.inform(msg.name, name, version, build_state) self.send_reply_inform(inform, msg) num_versions = len(versions) + len(extra_versions) return Message.reply(msg.name, "ok", str(num_versions))
def setup_sensors(self): """ @brief Setup the default KATCP sensors. @note As this call is made only upon an PAF configure call a mass inform is required to let connected clients know that the proxy interface has changed. """ self._state_sensor = LoggingSensor.discrete( "state", description="Denotes the state of this PAF instance", params=self.STATES, default=self.IDLE, initial_status=Sensor.NOMINAL) self._state_sensor.set_logger(self.log) self.add_sensor(self._state_sensor) self._servers_sensor = Sensor.string( "servers", description= "The worker server instances currently allocated to this product", default=",".join([ "{s.hostname}:{s.port}".format(s=server) for server in self._servers ]), initial_status=Sensor.UNKNOWN) self.add_sensor(self._servers_sensor) self._parent.mass_inform(Message.inform('interface-changed')) self._state_sensor.set_value(self.IDLE)
def request_foo(self, msg): """ This is called when ?foo is called from the other side. """ # send one inform self.send_message(Message.inform('foo', 'fine')) # return reply return Message.reply('foo', 'ok', '1')
def request_device_list(self, reqmsg): """Return a list of devices aggregated by the proxy. Returns the list of devices a sequence of #device-list informs. Inform Arguments ---------------- device : str Name of a device. Returns ------- success : {'ok', 'fail'} Whether sending the list of devices succeeded. informs : int Number of #device-list informs sent. Examples -------- ?device-list #device-list antenna #device-list enviro !device-list ok 2 """ for name in sorted(self.factory.devices): self.send_reply_inform(Message.inform("device-list", name, self.factory.devices[name].TYPE), reqmsg) return "ok", len(self.factory.devices)
def request_deconfigure(self, req): """ @brief Deconfigure the apsuse worker @param req A katcp request object @return katcp reply object [[[ !deconfigure ok | (fail [error description]) ]]] """ log.info("Received deconfigure request") self._state_sensor.set_value(self.STOPPING) futures = [] for capture_instance in self._capture_instances: capture_instance.target_stop() for sensor in capture_instance._sensors: try: self.remove_sensor(sensor) except Exception as error: log.exception( "Failed to remove sensor with error: {}".format( str(error))) log.warning("Current sensor list: {}".format(self._sensors)) futures.append(capture_instance.capture_stop()) self.mass_inform(Message.inform('interface-changed')) for future in futures: try: yield future except Exception as error: log.exception("Error during capture_stop") raise Return(("fail", str(error))) self._state_sensor.set_value(self.IDLE) log.info("Deconfigured worker server") raise Return(("ok",))
def request_client_list(self, msg): """Request the list of connected clients. The list of clients is sent as a sequence of #client-list informs. Informs ------- addr : str The address of the client as host:port with host in dotted quad notation. If the address of the client could not be determined (because, for example, the client disconnected suddenly) then a unique string representing the client is sent instead. Returns ------- success : {'ok', 'fail'} Whether sending the client list succeeded. informs : int Number of #client-list inform messages sent. Examples -------- :: ?client-list #client-list 127.0.0.1:53600 !client-list ok 1 """ for ip, port in self.factory.clients: self.send_message(Message.inform(msg.name, "%s:%s" % (ip, port))) return Message.reply(msg.name, "ok", len(self.factory.clients))
def request_target_configuration_start(self, req, product_id, target_string): """ @brief Set up a beam configuration sensor for the FBFUSE instance @param product_id The product identifier @param target_string A KATPOINT target string (boresight pointing position) """ log.info( "Received target configuration request for '{}' with target: {}". format(product_id, target_string)) if not product_id in self._configuration_sensors: log.debug( "Creating configuration sensor for '{}'".format(product_id)) self._configuration_sensors[product_id] = Sensor.string( "{}-beam-position-configuration".format(product_id), description="Configuration description for FBF beam placement", default="", initial_status=Sensor.UNKNOWN) self.add_sensor(self._configuration_sensors[product_id]) self.mass_inform(Message.inform('interface-changed')) initial_config = yield self.get_target_config(product_id, target_string) self.update_target_config(product_id, initial_config) raise Return(("ok", ))
def request_device_list(self, reqmsg): """Return a list of devices aggregated by the proxy. Returns the list of devices a sequence of #device-list informs. Inform Arguments ---------------- device : str Name of a device. Returns ------- success : {'ok', 'fail'} Whether sending the list of devices succeeded. informs : int Number of #device-list informs sent. Examples -------- ?device-list #device-list antenna #device-list enviro !device-list ok 2 """ for name in sorted(self.factory.devices): self.send_message( Message.inform("device-list", name, self.factory.devices[name].TYPE)) return "ok", len(self.factory.devices)
def worked((informs, reply)): self.flushLoggedErrors() # clean up error about conn lost self.proxy.on_device_ready = Deferred().addCallback(back) self.assertEquals(informs, [ Message.inform("sensor-value", "device.rogue", "Sensor reading failed.") ])
def connectionMade(self): """ Called when connection is made. Send default informs - version and build data """ self.transport.registerProducer(self, True) katcp_version = self.PROTOCOL_INFO.major if katcp_version >= VERSION_CONNECT_KATCP_MAJOR: self.send_message(Message.inform( "version-connect", "katcp-protocol", self.PROTOCOL_INFO)) self.send_message(Message.inform("version-connect", "katcp-library", "katcp-python-tx-%s" % VERSION_STR)) self.send_message(Message.inform("version-connect", "katcp-device", self.version(), self.build_state())) else: self.send_message(Message.inform("version", self.version())) self.send_message(Message.inform("build-state", self.build_state()))
def remove_pipeline_sensors(self): """ @brief Remove pipeline sensors from the managed sensors list """ for sensor in self._managed_sensors: self.remove_sensor(sensor) self._managed_sensors = [] self.mass_inform(Message.inform('interface-changed'))
def add_pipeline_sensors(self): """ @brief Add pipeline sensors to the managed sensors list """ for sensor in self._pipeline_instance.sensors: self.add_sensor(sensor) self._managed_sensors.append(sensor) self.mass_inform(Message.inform('interface-changed'))
def test_interface_change(self): DUT = yield self._get_DUT_and_sync(self.default_resource_spec) sensors_before = set(DUT.sensor) reqs_before = set(DUT.req) # Add a new sensor to the server sensor = DeviceTestSensor(DeviceTestSensor.INTEGER, "another.int", "An Integer.", "count", [-5, 5], timestamp=self.io_loop.time(), status=DeviceTestSensor.NOMINAL, value=3) self.server.add_sensor(sensor) # Check that the sensor does not exist currently self.assertNotIn(resource.escape_name(sensor.name), sensors_before) # Add a new request to the server def request_sparkling_new(self, req, msg): """A new command.""" return Message.reply(msg.name, "ok", "bling1", "bling2") self.server._request_handlers['sparkling-new'] = request_sparkling_new # Check that the request did not exist before self.assertNotIn('sparkling-new', reqs_before) # Issue #interface-changed self.server.mass_inform(Message.inform('interface-changed')) yield DUT.until_state('syncing') yield DUT.until_state('synced') # Check if sensor/request was added self.assertEqual(set(DUT.sensor) - sensors_before, set(['another_int'])) self.assertEqual(set(DUT.req) - reqs_before, set(['sparkling_new'])) # And now remove them again self.server._request_handlers.pop('sparkling-new') self.server.remove_sensor('another.int') # Issue #interface-changed self.server.mass_inform(Message.inform('interface-changed')) yield DUT.until_state('syncing') yield DUT.until_state('synced') # Check if sensor/request was removed self.assertEqual(set(DUT.sensor), sensors_before) self.assertEqual(set(DUT.req), reqs_before)
def teardown_sensors(self): """ @brief Remove all sensors created by this product from the parent server. @note This method is required for cleanup to stop the APS sensor pool becoming swamped with unused sensors. """ for sensor in self._managed_sensors: self._parent.remove_sensor(sensor) self._managed_sensors = [] self._parent.mass_inform(Message.inform('interface-changed'))
def connectionMade(self): """ Called when connection is made. Send default informs - version and build data """ self.transport.registerProducer(self, True) katcp_version = self.PROTOCOL_INFO.major if katcp_version >= VERSION_CONNECT_KATCP_MAJOR: self.send_message( Message.inform("version-connect", "katcp-protocol", self.PROTOCOL_INFO)) self.send_message( Message.inform("version-connect", "katcp-library", "katcp-python-tx-%s" % VERSION_STR)) self.send_message( Message.inform("version-connect", "katcp-device", self.version(), self.build_state())) else: self.send_message(Message.inform("version", self.version())) self.send_message(Message.inform("build-state", self.build_state()))
def _log_msg(self, level_name, msg, name, timestamp=None): """Create a katcp logging inform message. Usually this will be called from inside a DeviceLogger object, but it is also used by the methods in this class when errors need to be reported to the client. """ if timestamp is None: timestamp = time.time() return Message.inform("log", level_name, str(int(timestamp * 1000.0)), # time since epoch in ms name, msg, )
def _kcs_sensor_set(self, sensor): """ Generate #sensor-status informs to update sensors" #sensor-status timestamp 1 sensor-name status value :param sensor: A katcp.Sensor object :return: """ assert self.kcs_sensors supdate_inform = Message.inform('sensor-status', time.time(), 1, sensor.name, sensor.STATUSES[sensor.status()], sensor.value()) self.katcp_server.mass_inform(supdate_inform)
def _kcs_sensor_create(self, sensor): """ Create a sensor the katcp server with a sensor-list inform: #sensor-list sensor-name description units type :param sensor: A katcp.Sensor object :return: """ assert self.kcs_sensors descr = sensor.description.replace(' ', '\_') screate_inform = Message.inform('sensor-list', sensor.name, descr, sensor.units, sensor.type) self.katcp_server.mass_inform(screate_inform) self._kcs_sensor_set(sensor)
def add_input_stream_sensor(self, streamid): """ Add sensors for i/o buffers for an input stream with given streamid. """ self._polarization_sensors[streamid] = {} self._polarization_sensors[streamid]["mkrecv_sensors"] = MkrecvSensors( streamid) for s in self._polarization_sensors[streamid][ "mkrecv_sensors"].sensors.values(): self.add_sensor(s) self._polarization_sensors[streamid][ "input-buffer-fill-level"] = Sensor.float( "input-buffer-fill-level-{}".format(streamid), description="Fill level of the input buffer for polarization{}" .format(streamid), initial_status=Sensor.UNKNOWN, params=[0, 1]) self.add_sensor( self._polarization_sensors[streamid]["input-buffer-fill-level"]) self._polarization_sensors[streamid][ "input-buffer-total-write"] = Sensor.float( "input-buffer-total-write-{}".format(streamid), description="Total write into input buffer for polarization {}" .format(streamid), initial_status=Sensor.UNKNOWN, params=[0, 1]) self.add_sensor( self._polarization_sensors[streamid]["input-buffer-total-write"]) self._polarization_sensors[streamid][ "output-buffer-fill-level"] = Sensor.float( "output-buffer-fill-level-{}".format(streamid), description="Fill level of the output buffer for polarization {}" .format(streamid), initial_status=Sensor.UNKNOWN) self._polarization_sensors[streamid][ "output-buffer-total-read"] = Sensor.float( "output-buffer-total-read-{}".format(streamid), description="Total read from output buffer for polarization {}" .format(streamid), initial_status=Sensor.UNKNOWN) self.add_sensor( self._polarization_sensors[streamid]["output-buffer-total-read"]) self.add_sensor( self._polarization_sensors[streamid]["output-buffer-fill-level"]) self.mass_inform(Message.inform('interface-changed'))
def request_get_schedule_block_configuration(self, req, product_id, sb_id): """ @brief Get an FBFUSE configuration for the current instance @param product_id The product identifier @param sb_id The schedule block identifier @note The product_id argument may be superfluous, although it allows the CA server to look up parameters on the unconfigured product from the FBFUSE sensor set through katportalclient """ if product_id in self._configuration_sensors: self.remove_sensor(self._configuration_sensors[product_id]) del self._configuration_sensors[product_id] self.mass_inform(Message.inform('interface-changed')) config = yield self.get_sb_config(product_id, sb_id) raise Return(("ok", json.dumps(config)))
def configure(self, config_json): """ @brief Configure the pipeline with the given config_json """ log.info("Configuring EDD backend for processing") log.info("Configuration string: '{}'".format(config_json)) yield self.set(config_json) if isinstance(self._config['input_data_streams'], dict): log.warning( "CHANGING INPUT DATA STREAM TYPE FROM DICT TO LIST - THIS IS A HACKY HACK AND BE DONE PROPERLY!" ) l = [i for i in self._config['input_data_streams'].values()] self._config['input_data_streams'] = l log.debug(self._config) cfs = json.dumps(self._config, indent=4) log.info("Final configuration:\n" + cfs) self.__coreManager = CoreManager(self.numa_number) self.__coreManager.add_task("mkrecv", self._config["nstreams"] + 1, prefere_isolated=True) self.__coreManager.add_task("dspsr", self._config["dspsr_threads"]) self.__coreManager.add_task("merge", self._config["merge_threads"]) # The master contoller provides the data store IP as default gloal # config to all pipelines self.__eddDataStore = EDDDataStore(self._config["data_store"]["ip"], self._config["data_store"]["port"]) self.sync_epoch = self._config['input_data_streams'][0]['sync_time'] log.debug("sync_epoch = {}".format(self.sync_epoch)) for key in self._dada_buffers: self._create_ring_buffer(key, self.numa_number) yield self.tzpar_dir = os.path.join("/mnt/", self._config["tzpar"]) if not os.path.isdir(self.tzpar_dir): log.error("Not a directory {} !".format(self.tzpar_dir)) raise RuntimeError("tzpar directory is no directory: {}".format( self.tzpar_dir)) if os.path.isfile("/tmp/t2pred.dat"): os.remove("/tmp/t2pred.dat") self.add_input_stream_sensor("") self.mass_inform(Message.inform('interface-changed'))
def setup_sensors(self): """ @brief Setup the default KATCP sensors. @note As this call is made only upon an EDD product configure call a mass inform is required to let connected clients know that the proxy interface has changed. """ super(EddRoach2ProductController, self).setup_sensors() self._firmware_server_sensor = Sensor.string( "firmware-server", description= "The address of the firmware server started by this product", default="", initial_status=Sensor.UNKNOWN) self.add_sensor(self._firmware_server_sensor) self._parent.mass_inform(Message.inform('interface-changed'))
def _kcs_sensor_set(self, sensor): """ Generate #sensor-status informs to update sensors" #sensor-status timestamp 1 sensor-name status value :param sensor: A katcp.Sensor object :return: """ if self._debug_mode: self.logger.info('SENSOR_DEBUG: sensor-status ' + str(time.time()) + ' ' + sensor.name + ' ' + str(sensor.STATUSES[sensor.status()]) + ' ' + str(sensor.value())) return assert self.kcs_sensors supdate_inform = Message.inform( 'sensor-status', time.time(), 1, sensor.name, sensor.STATUSES[sensor.status()], sensor.value()) self.katcp_server.mass_inform(supdate_inform)
def _kcs_sensor_create(self, sensor): """ Create a sensor on the katcp server with a sensor-list inform. #sensor-list sensor-name description units type :param sensor: A katcp.Sensor object :return: """ # descr = sensor.description.replace(' ', '\_') descr = sensor.description if self._debug_mode: self.logger.info('SENSOR_DEBUG: sensor-list ' + sensor.name + ' ' + descr + ' ' + sensor.units + ' ' + str(sensor.type)) return assert self.kcs_sensors screate_inform = Message.inform( 'sensor-list', sensor.name, descr, sensor.units, sensor.type) self.katcp_server.mass_inform(screate_inform) self._kcs_sensor_set(sensor)
def test_inform_one(self): """Test inform with no defaults.""" req = "" self.assertEqual(self.device.inform_one(Message.inform( "one", "2", "on", "0")), None)
def request_sensor_list(self, msg): """Request the list of sensors. The list of sensors is sent as a sequence of #sensor-list informs. Parameters ---------- name : str or pattern, optional If the name is not a pattern, list just the sensor with the given name. A pattern starts and ends with a slash ('/') and uses the Python re module's regular expression syntax. All sensors whose names contain the pattern are listed. The default is to list all sensors. Inform Arguments ---------------- name : str The name of the sensor being described. description : str Description of the named sensor. units : str Units for the value of the named sensor. type : str Type of the named sensor. params : list of str, optional Additional sensor parameters (type dependent). For integer and float sensors the additional parameters are the minimum and maximum sensor value. For discrete sensors the additional parameters are the allowed values. For all other types no additional parameters are sent. Returns ------- success : {'ok', 'fail'} Whether sending the sensor list succeeded. informs : int Number of #sensor-list inform messages sent. Examples -------- ?sensor-list #sensor-list psu.voltage PSU\_voltage. V float 0.0 5.0 #sensor-list cpu.status CPU\_status. \@ discrete on off error ... !sensor-list ok 5 ?sensor-list /voltage/ #sensor-list psu.voltage PSU\_voltage. V float 0.0 5.0 #sensor-list cpu.voltage CPU\_voltage. V float 0.0 3.0 !sensor-list ok 2 ?sensor-list cpu.power.on #sensor-list cpu.power.on Whether\_CPU\_hase\_power. \@ boolean !sensor-list ok 1 """ # handle non-regex cases if not msg.arguments or not (msg.arguments[0].startswith("/") and msg.arguments[0].endswith("/")): return DeviceProtocol.request_sensor_list(self, msg) # handle regex name_re = re.compile(msg.arguments[0][1:-1]) sensors = dict([(name, sensor) for name, sensor in self.factory.sensors.iteritems() if name_re.search(name)]) for name, sensor in sorted(sensors.items(), key=lambda x: x[0]): self.send_reply_inform(Message.inform("sensor-list", name, sensor.description, sensor.units, sensor.stype, *sensor.formatted_params), msg) return Message.reply(msg.name, "ok", len(sensors))
def connectionMade(self): """ Called when a connection is made, send out-of-band inform """ self.send_message(Message.inform('out-of-band')) DeviceProtocol.connectionMade(self)
class ProxyProtocol(DeviceProtocol): @request(include_msg=True) @return_reply(Int(min=0)) def request_device_list(self, reqmsg): """Return a list of devices aggregated by the proxy. Returns the list of devices a sequence of #device-list informs. Inform Arguments ---------------- device : str Name of a device. Returns ------- success : {'ok', 'fail'} Whether sending the list of devices succeeded. informs : int Number of #device-list informs sent. Examples -------- ?device-list #device-list antenna #device-list enviro !device-list ok 2 """ for name in sorted(self.factory.devices): self.send_message( Message.inform("device-list", name, self.factory.devices[name].TYPE)) return "ok", len(self.factory.devices) def request_sensor_list(self, msg): """Request the list of sensors. The list of sensors is sent as a sequence of #sensor-list informs. Parameters ---------- name : str or pattern, optional If the name is not a pattern, list just the sensor with the given name. A pattern starts and ends with a slash ('/') and uses the Python re module's regular expression syntax. All sensors whose names contain the pattern are listed. The default is to list all sensors. Inform Arguments ---------------- name : str The name of the sensor being described. description : str Description of the named sensor. units : str Units for the value of the named sensor. type : str Type of the named sensor. params : list of str, optional Additional sensor parameters (type dependent). For integer and float sensors the additional parameters are the minimum and maximum sensor value. For discrete sensors the additional parameters are the allowed values. For all other types no additional parameters are sent. Returns ------- success : {'ok', 'fail'} Whether sending the sensor list succeeded. informs : int Number of #sensor-list inform messages sent. Examples -------- ?sensor-list #sensor-list psu.voltage PSU\_voltage. V float 0.0 5.0 #sensor-list cpu.status CPU\_status. \@ discrete on off error ... !sensor-list ok 5 ?sensor-list /voltage/ #sensor-list psu.voltage PSU\_voltage. V float 0.0 5.0 #sensor-list cpu.voltage CPU\_voltage. V float 0.0 3.0 !sensor-list ok 2 ?sensor-list cpu.power.on #sensor-list cpu.power.on Whether\_CPU\_hase\_power. \@ boolean !sensor-list ok 1 """ # handle non-regex cases if not msg.arguments or not (msg.arguments[0].startswith("/") and msg.arguments[0].endswith("/")): return DeviceProtocol.request_sensor_list(self, msg) # handle regex name_re = re.compile(msg.arguments[0][1:-1]) sensors = dict([(name, sensor) for name, sensor in self.factory.sensors.iteritems() if name_re.search(name)]) for name, sensor in sorted(sensors.items(), key=lambda x: x[0]): self.send_message( Message.inform("sensor-list", name, sensor.description, sensor.units, sensor.stype, *sensor.formatted_params)) return Message.reply(msg.name, "ok", len(sensors)) def _send_all_sensors(self, filter=None): """ Sends all sensor values with given filter (None = all) """ counter = [0] # this has to be a list or an object, thanks to # python lexical scoping rules (we could not write count += 1 # in a function) def device_ok((informs, reply), device): for inform in informs: inform.arguments[2] = device.name + '.' + \ inform.arguments[2] if filter is None or re.match(filter, inform.arguments[2]): self.send_message(inform) counter[0] += 1 def all_ok(_): self.send_message( Message.reply('sensor-value', 'ok', str(counter[0]))) wait_for = [] for device in self.factory.devices.itervalues(): if device.state == device.SYNCED: d = device.send_request('sensor-value') d.addCallback(device_ok, device) wait_for.append(d) # otherwise we don't have the list of sensors, so we don't # send the message DeferredList(wait_for).addCallback(all_ok) for name, sensor in self.factory.sensors.iteritems(): if not isinstance(sensor, ProxiedSensor): if filter is None or re.match(filter, name): timestamp_ms, status, value = sensor.read_formatted() counter[0] += 1 self.send_message( Message.inform('sensor-value', timestamp_ms, "1", name, status, value))
def request_sensor_list(self, msg): """Request the list of sensors. The list of sensors is sent as a sequence of #sensor-list informs. Parameters ---------- name : str or pattern, optional If the name is not a pattern, list just the sensor with the given name. A pattern starts and ends with a slash ('/') and uses the Python re module's regular expression syntax. All sensors whose names contain the pattern are listed. The default is to list all sensors. Inform Arguments ---------------- name : str The name of the sensor being described. description : str Description of the named sensor. units : str Units for the value of the named sensor. type : str Type of the named sensor. params : list of str, optional Additional sensor parameters (type dependent). For integer and float sensors the additional parameters are the minimum and maximum sensor value. For discrete sensors the additional parameters are the allowed values. For all other types no additional parameters are sent. Returns ------- success : {'ok', 'fail'} Whether sending the sensor list succeeded. informs : int Number of #sensor-list inform messages sent. Examples -------- ?sensor-list #sensor-list psu.voltage PSU\_voltage. V float 0.0 5.0 #sensor-list cpu.status CPU\_status. \@ discrete on off error ... !sensor-list ok 5 ?sensor-list /voltage/ #sensor-list psu.voltage PSU\_voltage. V float 0.0 5.0 #sensor-list cpu.voltage CPU\_voltage. V float 0.0 3.0 !sensor-list ok 2 ?sensor-list cpu.power.on #sensor-list cpu.power.on Whether\_CPU\_hase\_power. \@ boolean !sensor-list ok 1 """ # handle non-regex cases if not msg.arguments or not (msg.arguments[0].startswith("/") and msg.arguments[0].endswith("/")): return DeviceProtocol.request_sensor_list(self, msg) # handle regex name_re = re.compile(msg.arguments[0][1:-1]) sensors = dict([(name, sensor) for name, sensor in self.factory.sensors.iteritems() if name_re.search(name)]) for name, sensor in sorted(sensors.items(), key=lambda x: x[0]): self.send_message( Message.inform("sensor-list", name, sensor.description, sensor.units, sensor.stype, *sensor.formatted_params)) return Message.reply(msg.name, "ok", len(sensors))
def test_inform_one(self): """Test inform with no defaults.""" sock = "" self.assertEqual( self.device.inform_one(sock, Message.inform("one", "2", "on", "0")), None)
def worked((informs, reply)): self.flushLoggedErrors() # clean up error about conn lost self.proxy.on_device_ready = Deferred().addCallback(back) self.assertEquals(informs, [Message.inform("sensor-value", "device.rogue", "Sensor reading failed.")])
def connectionMade(self): """ Called when connection is made. Send default informs - version and build data """ self.send_message(Message.inform("version", *self.VERSION)) self.send_message(Message.inform("build-state", *self.BUILD_STATE))
def callback(sensor, timestamp_ms, status, value): self.send_message(Message.inform('sensor-status', timestamp_ms, "1", sensor.name, status, value))
def fail(_, sensor): self.send_message(Message.inform('log', 'Connection lost.'))
def prepare(self, sb_id): """ @brief Prepare the beamformer for streaming @detail This method evaluates the current configuration creates a new DelayEngine and passes a prepare call to all allocated servers. """ if not self.idle: raise FbfProductStateError([self.IDLE], self.state) self.log.info("Preparing FBFUSE product") self._state_sensor.set_value(self.PREPARING) self.log.debug("Product moved to 'preparing' state") # Here we need to parse the streams and assign beams to streams: #mcast_addrs, mcast_port = parse_stream(self._streams['cbf.antenna_channelised_voltage']['i0.antenna-channelised-voltage']) if not self._ca_client: self.log.warning("No configuration authority found, using default configuration parameters") cm = self.set_sb_configuration(self._default_sb_config) else: #TODO: get the schedule block ID into this call from somewhere (configure?) try: config = yield self.get_ca_sb_configuration(sb_id) cm = self.set_sb_configuration(config) except Exception as error: self.log.error("Configuring from CA failed with error: {}".format(str(error))) self.log.warning("Reverting to default configuration") cm = self.set_sb_configuration(self._default_sb_config) cbc_antennas_names = parse_csv_antennas(self._cbc_antennas_sensor.value()) cbc_antennas = [self._antenna_map[name] for name in cbc_antennas_names] self._beam_manager = BeamManager(self._cbc_nbeams_sensor.value(), cbc_antennas) self._delay_config_server = DelayConfigurationServer("127.0.0.1", 0, self._beam_manager) self._delay_config_server.start() self.log.info("Started delay engine at: {}".format(self._delay_config_server.bind_address)) de_ip, de_port = self._delay_config_server.bind_address self._delay_config_server_sensor.set_value((de_ip, de_port)) # Need to tear down the beam sensors here # Here calculate the beam to multicast map self._beam_sensors = [] mcast_to_beam_map = {} groups = [ip for ip in self._cbc_mcast_groups] idxs = [beam.idx for beam in self._beam_manager.get_beams()] for group in groups: self.log.debug("Allocating beams to {}".format(str(group))) key = str(group) for _ in range(self._cbc_nbeams_per_group.value()): if not key in mcast_to_beam_map: mcast_to_beam_map[str(group)] = [] value = idxs.pop(0) self.log.debug("--> Allocated {} to {}".format(value, str(group))) mcast_to_beam_map[str(group)].append(value) self._cbc_mcast_groups_mapping_sensor.set_value(json.dumps(mcast_to_beam_map)) for beam in self._beam_manager.get_beams(): sensor = Sensor.string( "coherent-beam-{}".format(beam.idx), description="R.A. (deg), declination (deg) and source name for coherent beam with ID {}".format(beam.idx), default=self._beam_to_sensor_string(beam), initial_status=Sensor.UNKNOWN) beam.register_observer(lambda beam, sensor=sensor: sensor.set_value(self._beam_to_sensor_string(beam))) self._beam_sensors.append(sensor) self.add_sensor(sensor) self._parent.mass_inform(Message.inform('interface-changed')) #Here we actually start to prepare the remote workers ip_splits = self._streams.split(N_FENG_STREAMS_PER_WORKER) # This is assuming lower sideband and bandwidth is always +ve fbottom = self._feng_config['centre-frequency'] - self._feng_config['bandwidth']/2. coherent_beam_config = { 'tscrunch':self._cbc_tscrunch_sensor.value(), 'fscrunch':self._cbc_fscrunch_sensor.value(), 'antennas':self._cbc_antennas_sensor.value() } incoherent_beam_config = { 'tscrunch':self._ibc_tscrunch_sensor.value(), 'fscrunch':self._ibc_fscrunch_sensor.value(), 'antennas':self._ibc_antennas_sensor.value() } prepare_futures = [] for ii, (server, ip_range) in enumerate(zip(self._servers, ip_splits)): chan0_idx = cm.nchans_per_worker * ii chan0_freq = fbottom + chan0_idx * cm.channel_bandwidth future = server.prepare(ip_range.format_katcp(), cm.nchans_per_group, chan0_idx, chan0_freq, cm.channel_bandwidth, mcast_to_beam_map, self._feng_config['feng-antenna-map'], coherent_beam_config, incoherent_beam_config, de_ip, de_port) prepare_futures.append(future) failure_count = 0 for future in prepare_futures: try: yield future except Exception as error: log.error("Failed to configure server with error: {}".format(str(error))) failure_count += 1 if failure_count > 0: self._state_sensor.set_value(self.ERROR) self.log.info("Failed to prepare FBFUSE product") else: self._state_sensor.set_value(self.READY) self.log.info("Successfully prepared FBFUSE product")
def one_ok(sensor, timestamp_ms, status, value): self.send_message(Message.inform(msg.name, timestamp_ms, "1", sensor.name, status, value))
def one_fail(failure, sensor): self.send_message(Message.inform(msg.name, sensor.name, "Sensor reading failed."))
def request_sensor_list(self, msg): """Request the list of sensors. The list of sensors is sent as a sequence of #sensor-list informs. Parameters ---------- name : str, optional Name of the sensor to list (the default is to list all sensors). If name starts and ends with '/' it is treated as a regular expression and all sensors whose names contain the regular expression are returned. Informs ------- name : str The name of the sensor being described. description : str Description of the named sensor. units : str Units for the value of the named sensor. type : str Type of the named sensor. params : list of str, optional Additional sensor parameters (type dependent). For integer and float sensors the additional parameters are the minimum and maximum sensor value. For discrete sensors the additional parameters are the allowed values. For all other types no additional parameters are sent. Returns ------- success : {'ok', 'fail'} Whether sending the sensor list succeeded. informs : int Number of #sensor-list inform messages sent. Examples -------- :: ?sensor-list #sensor-list psu.voltage PSU\_voltage. V float 0.0 5.0 #sensor-list cpu.status CPU\_status. \@ discrete on off error ... !sensor-list ok 5 ?sensor-list cpu.power.on #sensor-list cpu.power.on Whether\_CPU\_hase\_power. \@ boolean !sensor-list ok 1 """ exact, name_filter = construct_name_filter(msg.arguments[0] if msg.arguments else None) sensors = [(name, sensor) for name, sensor in sorted(self.factory.sensors.iteritems()) if name_filter(name)] if exact and not sensors: return Message.reply(msg.name, "fail", "Unknown sensor name.") for name, sensor in sensors: self.send_message(Message.inform(msg.name, name, sensor.description, sensor.units, sensor.stype, *sensor.formatted_params)) return Message.reply(msg.name, "ok", len(sensors))