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 request_drop_connection(self, msg): """ drops connection to specified device, for demo purposes only """ if not msg.arguments: return Message.reply('drop-connection', 'fail', 'Argument required') try: dev_name = msg.arguments[0] self.factory.devices[dev_name].transport.loseConnection() print dev_name, "disconnected" return Message.reply('drop-connection', 'ok') except KeyError: return Message.reply('drop-connection', 'fail', 'Unknown device %s' % dev_name)
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 test_request_one(self): """Test request with no defaults.""" req = mock.Mock() req.msg.name = 'one' self.assertEqual(str(self.device.request_one(req, Message.request( "one", "2", "on", "0"))), "!one ok 2 on 0") self.assertRaises(FailReply, self.device.request_one, req, Message.request("one", "14", "on", "0")) self.assertRaises(FailReply, self.device.request_one, req, Message.request("one", "2", "dsfg", "0")) self.assertRaises(FailReply, self.device.request_one, req, Message.request("one", "2", "on", "3")) self.assertRaises(FailReply, self.device.request_one, req, Message.request("one", "2", "on", "0", "3")) self.assertRaises(FailReply, self.device.request_one, req, Message.request("one", "2", "on")) self.assertEqual(str(self.device.request_one(req, Message.request( "one", "3", "on", "0"))), "!one fail I\\_failed!") self.assertRaises(ValueError, self.device.request_one, req, Message.request("one", "5", "on", "0")) self.assertRaises(ValueError, self.device.request_one, req, Message.request("one", "6", "on", "0")) req.reset_mock() self.assertRaises(AsyncReply, self.device.request_one, req, Message.request("one", "9", "on", "0")) self.assertEqual(req.reply_with_message.call_count, 1) req.reply_with_message.assert_called_once_with(Message.reply( 'one', 'ok', '9', 'on', '0'))
def callback(msg): if device.state is device.UNSYNCED: return Message.reply(dev_name + "-" + req_name, "fail", "Device not synced") d = device.send_request(req_name, *msg.arguments) d.addCallbacks(request_returned, request_failed) raise AsyncReply()
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_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 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_add_sensor(self, msg): self.factory.add_sensor( Sensor(int, 'int_sensor%d' % len(self.factory.sensors), 'descr', 'unit', params=[-10, 10])) return Message.reply('add-sensor', 'ok')
def handle_request(self, msg): name = msg.name name = name.replace('-', '_') try: rep_msg = getattr(self, 'request_' + name, self._request_unknown)(msg) if not isinstance(rep_msg, Message): raise ShouldReturnMessage('request_' + name + ' should return a' 'message or raise AsyncReply, instead' 'it returned %r' % rep_msg) self.send_message(rep_msg) except FailReply, fr: self.send_message(Message.reply(name, "fail", str(fr)))
def __getattr__(self, attr): # TODO: It would be cleaner if a new request method / callback # wasn't created for every proxied request. # TODO: These proxied methods should appear in the ?help for the proxy # but currently don't. def request_returned((informs, reply), reqmsg): assert informs == [] # for now # we *could* in theory just change message name, but let's copy # just in case self.send_reply(Message.reply(dev_name + "-" + req_name, *reply.arguments), reqmsg)
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 request_watchdog(self, msg): """Check that the server is still alive. Returns ------- success : {'ok'} Examples -------- :: ?watchdog !watchdog ok """ return Message.reply("watchdog", "ok")
def request_halt(self, msg): """ drops connection to specified device """ def got_halt((informs, reply)): self.send_reply(Message.reply('halt', dev_name, 'ok'), msg) if not msg.arguments: return DeviceProtocol.halt(self, msg) try: dev_name = msg.arguments[0] device = self.factory.devices[dev_name] except KeyError: return Message.reply('halt', 'fail', 'Unknown device %s' % dev_name) else: self.factory.unregister_device(dev_name) device.send_request('halt').addCallback(got_halt) raise AsyncReply()
def request_halt(self, msg): """ drops connection to specified device """ def got_halt((informs, reply)): self.send_message(Message.reply('halt', dev_name, 'ok')) if not msg.arguments: return DeviceProtocol.halt(self, msg) try: dev_name = msg.arguments[0] device = self.factory.devices[dev_name] except KeyError: return Message.reply('halt', 'fail', 'Unknown device %s' % dev_name) else: self.factory.unregister_device(dev_name) device.send_request('halt').addCallback(got_halt) raise AsyncReply()
def request_halt(self, msg): """Halt the device server. Returns ------- success : {'ok', 'fail'} Whether scheduling the halt succeeded. Examples -------- :: ?halt !halt ok """ self.send_message(Message.reply("halt", "ok")) self.factory.stop() raise AsyncReply()
def callback((informs, reply)): assert len(informs) == 2 self.assertEquals(reply, Message.reply("device-list", "ok", "2"))
def callback((informs, reply)): assert len(informs) == 4 assert reply == Message.reply('sensor-list', 'ok', '4')
def callback((informs, reply)): self.assertEquals(reply, Message.reply('device2-req', 'fail', 'Device not synced'))
def request_sensor_sampling(self, msg): """Configure or query the way a sensor is sampled. Sampled values are reported asynchronously using the #sensor-status message. Parameters ---------- name : str Name of the sensor whose sampling strategy to query or configure. strategy : {'none', 'auto', 'event', 'differential', 'period'}, optional Type of strategy to use to report the sensor value. The differential strategy type may only be used with integer or float sensors. params : list of str, optional Additional strategy parameters (dependent on the strategy type). For the differential strategy, the parameter is an integer or float giving the amount by which the sensor value may change before an updated value is sent. For the period strategy, the parameter is the period to sample at in milliseconds. For the event strategy, an optional minimum time between updates in milliseconds may be given. Returns ------- success : {'ok', 'fail'} Whether the sensor-sampling request succeeded. name : str Name of the sensor queried or configured. strategy : {'none', 'auto', 'event', 'differential', 'period'} Name of the new or current sampling strategy for the sensor. params : list of str Additional strategy parameters (see description under Parameters). Examples -------- :: ?sensor-sampling cpu.power.on !sensor-sampling ok cpu.power.on none ?sensor-sampling cpu.power.on period 500 !sensor-sampling ok cpu.power.on period 500 """ if not msg.arguments: return Message.reply(msg.name, "fail", "No sensor name given.") sensor = self.factory.sensors.get(msg.arguments[0], None) if sensor is None: return Message.reply(msg.name, "fail", "Unknown sensor name.") if len(msg.arguments) == 1: StrategyClass = NoStrategy else: StrategyClass = self.SAMPLING_STRATEGIES.get(msg.arguments[1], None) if StrategyClass is None: return Message.reply(msg.name, "fail", "Unknown strategy name.") # stop the previous strategy try: self.strategies[sensor.name].cancel() except KeyError: pass strategy = StrategyClass(self, sensor) strategy.run(*msg.arguments[2:]) self.strategies[sensor.name] = strategy if len(msg.arguments) == 1: msg.arguments.append('none') return Message.reply(msg.name, "ok", *msg.arguments)
def callback((informs, reply)): self.assertEquals(reply, Message.reply('halt', 'device', 'ok')) assert self.proxy.devices.keys() == ['device2']
def request_sparkling_new(self, req, msg): """A new command.""" return Message.reply(msg.name, "ok", "bling1", "bling2")
def callback((informs, reply)): self.assertEquals( reply, Message.reply('device2-req', 'fail', 'Device not synced'))
def request_returned((informs, reply)): assert informs == [] # for now # we *could* in theory just change message name, but let's copy # just in case self.send_message( Message.reply(dev_name + "-" + req_name, *reply.arguments))
def _request_unknown(self, msg): return Message.reply(msg.name, "invalid", "Unknown request.")
def callback((informs, reply)): assert len(informs) == 2 self.assertEquals(reply, Message.reply('sensor-list', 'ok', '2'))
def issue_request(self, *args, **kwargs): reply_msg = Message.reply('fake', 'ok') reply = KATCPReply(reply_msg, []) fut = Future() fut.set_result(reply) return fut
def request_add_sensor(self, sock, msg): """ add a sensor """ self.add_sensor(Sensor(int, 'int_sensor%d' % len(self._sensors), 'descr', 'unit', params=[-10, 10])) return Message.reply('add-sensor', 'ok')
def works((informs, reply)): self.assertEquals(reply, Message.reply('device-watchdog', 'ok')) self.port.stopListening() self.proxy.stop() self.client.transport.loseConnection() self.finish.callback(None)
def callback((informs, reply)): self.assertEquals(reply, Message.reply("device-req", "ok", "3"))
def handler(self, req, msg): """A new command.""" return Message.reply(msg.name, "ok", "bling1", "bling2")
def request_failed(failure): self.send_message( Message.reply(dev_name + '-' + req_name, "fail", failure.getErrorMessage()))
def wrapped_request(request_name, *args, **kwargs): f = tornado.concurrent.Future() retval = resource.KATCPReply(Message.reply(request_name, 'ok'), []) f.set_result(retval) return f
def all_finished(lst): # XXX write a test where lst is not-empty so we can fail self.send_message(Message.reply(msg.name, "ok", len(sensors)))
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))
raise ShouldReturnMessage('request_' + name + ' should return a' 'message or raise AsyncReply, instead' 'it returned %r' % rep_msg) self.send_message(rep_msg) except FailReply, fr: self.send_message(Message.reply(name, "fail", str(fr))) except AsyncReply: return except Exception: e_type, e_value, trace = sys.exc_info() log.err() reason = "\n".join(traceback.format_exception( e_type, e_value, trace, TB_LIMIT )) self.send_message(Message.reply(msg.name, "fail", reason)) def handle_reply(self, msg): if not self.queries: raise NoQuerriesProcessed() name, d, queue = self.queries[0] if name != msg.name: d.errback("Wrong request order") self.queries.pop(0) # hopefully it's not large d.callback((queue, msg)) def send_message(self, msg): # just serialize a message self.transport.write(str(msg) + self.delimiter) def connectionLost(self, failure):
def request_sensor_value(self, msg): """Request the value of a sensor or sensors. A list of sensor values as a sequence of #sensor-value informs. Parameters ---------- name : str, optional Name of the sensor to poll (the default is to send values for 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 ------- timestamp : float Timestamp of the sensor reading in milliseconds since the Unix epoch. count : {1} Number of sensors described in this #sensor-value inform. Will always be one. It exists to keep this inform compatible with #sensor-status. name : str Name of the sensor whose value is being reported. value : object Value of the named sensor. Type depends on the type of the sensor. Returns ------- success : {'ok', 'fail'} Whether sending the list of values succeeded. informs : int Number of #sensor-value inform messages sent. Examples -------- :: ?sensor-value #sensor-value 1244631611415.231 1 psu.voltage 4.5 #sensor-value 1244631611415.200 1 cpu.status off ... !sensor-value ok 5 ?sensor-value cpu.power.on #sensor-value 1244631611415.231 1 cpu.power.on 0 !sensor-value 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.") 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 all_finished(lst): # XXX write a test where lst is not-empty so we can fail self.send_message(Message.reply(msg.name, "ok", len(sensors))) lst = [] for name, sensor in sensors: self.read_formatted_from_sensor(sensor, one_ok, one_fail, lst) DeferredList(lst).addCallback(all_finished) raise AsyncReply()