Twisted KatCP is an alternative implementation of KatCP protocol using Twisted framework. Providing alternative implementation using a well known networking library has multiple advantages. Among the most important is an alternative to threads concurrency model, which work better for some cases and robustness that come from years of testing.
Note
general information how to write clients using Twisted.
The main client interface is a ClientKatCP, similar in purpose to the CallbackClient.
Interface is built around two ways of communicating. Asynchronous informs, which are not associated with any requests will be handled by overloaded inform_xyz methods, where xyz is a name of a method. Requests are sent via send_request method on the client class, which will return a deferred. Deferred will be called with a tuple ((informs, reply)) where informs is a list of katcp Message and reply is one message object.
Note
an example client is in scripts/demotxclient.py.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | """ This is a demo client, which cooperate with demotxserver
"""
import sys
from katcp.tx import run_client, ClientKatCPProtocol
from twisted.internet import reactor
class DemoClient(ClientKatCPProtocol):
def inform_out_of_band(self, msg):
print "out of band", msg
def got_sensor_value((informs, reply), protocol):
print informs
print reply
reactor.stop()
def got_foo((informs, reply), protocol):
print informs
print reply
protocol.send_request('sensor-value',
'int_sensor').addCallback(got_sensor_value, protocol)
def connected(protocol):
print "Connected"
protocol.send_request('foo').addCallback(got_foo, protocol)
if __name__ == '__main__':
# override with options if necessary
if len(sys.argv) > 1:
port = int(sys.argv[1])
else:
port = 1235
run_client(('localhost', port), DemoClient, connected)
reactor.run()
|
Note
general information how to write servers using Twisted.
The DeviceServer is very similar to DeviceServer. You overload request_xxx methods on a protocol (like DeviceProtocol) where xxx is a name of katcp request. A request method either returns a Message reply instance, possibly sending informs along the way (using self.send_message interface). Device server must overload setup_sensors method, which should register sensors by calling self.add_sensor and must provide a protocol member pointing to a correct DeviceProtocol subclass.
Note
an example server is in scripts/demotxserver.py.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | """ This is a demo device server for showing capabilities of twisted based
katcp implementation of a device server
"""
from twisted.internet import reactor
from katcp.tx import DeviceServer, DeviceProtocol
from katcp import Sensor, Message
from twisted.internet.protocol import Factory
from twisted.python import log
from katcp import Message
from katcp.tx.test.testserver import IntSensor, FloatSensor
PORT = 1235 # or 0
import sys
class DemoProtocol(DeviceProtocol):
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 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 DemoServerFactory(DeviceServer):
production = True # says whether halt request should stop the reactor
# (and hence the whole process)
protocol = DemoProtocol # meaning we're using custom protocol
def setup_sensors(self):
""" This is a method that overloaded provides a way to add sensors
to the device
"""
self.add_sensor(FloatSensor(Sensor.FLOAT, "float_sensor", "descr",
"milithaum", params=[-1.0, 1.0]))
self.add_sensor(IntSensor(Sensor.INTEGER, "int_sensor", "descr2",
"cows", params=[-100, 100]))
def main():
factory = DemoServerFactory(PORT, '')
print factory.start().getHost()
reactor.run() # run the main twisted reactor
if __name__ == '__main__':
main()
|