This URL has Read-Only access.

Statistics
| Branch: | Tag: | Revision:

root / txosc / async.py @ 9167485b

History | View | Annotate | Download (5.2 kB)

1
#!/usr/bin/env python
2
# -*- test-case-name: txosc.test.test_async -*-
3
# Copyright (c) 2009 Alexandre Quessy, Arjan Scherpenisse
4
# See LICENSE for details.
5

    
6
"""
7
Asynchronous implementation of OSC for Twisted
8
"""
9
import struct
10

    
11
from twisted.internet import defer, protocol
12
from twisted.application.internet import MulticastServer
13
from txosc.osc import *
14
from txosc.osc import _elementFromBinary
15

    
16
#
17
# Stream based client/server protocols
18
#
19

    
20
class StreamBasedProtocol(protocol.Protocol):
21
    """
22
    OSC over TCP sending and receiving protocol.
23
    """
24

    
25
    def connectionMade(self):
26
        self.factory.connectedProtocol = self
27
        if hasattr(self.factory, 'deferred'):
28
            self.factory.deferred.callback(True)
29
        self._buffer = ""
30
        self._pkgLen = None
31

    
32

    
33
    def dataReceived(self, data):
34
        """
35
        Called whenever data is received.
36

37
        In a stream-based protocol such as TCP, the stream should
38
        begin with an int32 giving the size of the first packet,
39
        followed by the contents of the first packet, followed by the
40
        size of the second packet, etc.
41

42
        @type data: L{str}
43
        """
44
        self._buffer += data
45
        if len(self._buffer) < 4:
46
            return
47
        if self._pkgLen is None:
48
            self._pkgLen = struct.unpack(">i", self._buffer[:4])[0]
49
        if len(self._buffer) < self._pkgLen + 4:
50
            print "waiting for %d more bytes" % (self._pkgLen + 4 - len(self._buffer))
51
            return
52
        payload = self._buffer[4:4 + self._pkgLen]
53
        self._buffer = self._buffer[4 + self._pkgLen:]
54
        self._pkgLen = None
55

    
56
        if payload:
57
            element = _elementFromBinary(payload)
58
            self.factory.gotElement(element)
59

    
60
        if len(self._buffer):
61
            self.dataReceived("")
62

    
63

    
64
    def send(self, element):
65
        """
66
        Send an OSC element over the TCP wire.
67
        @param element: L{txosc.osc.Message} or L{txosc.osc.Bundle}
68
        """
69
        binary = element.toBinary()
70
        self.transport.write(struct.pack(">i", len(binary)) + binary)
71
        #TODO: return a Deferred
72

    
73

    
74

    
75
class StreamBasedFactory(object):
76
    """
77
    Factory object for the sending and receiving of elements in a
78
    stream-based protocol (e.g. TCP, serial).
79

80
    @ivar receiver:  A L{Receiver} object which is used to dispatch
81
        incoming messages to.
82
    @ivar connectedProtocol: An instance of L{StreamBasedProtocol}
83
        representing the current connection.
84
    """
85
    receiver = None
86
    connectedProtocol = None
87

    
88
    def __init__(self, receiver=None):
89
        if receiver:
90
            self.receiver = receiver
91

    
92

    
93
    def send(self, element):
94
        self.connectedProtocol.send(element)
95

    
96

    
97
    def gotElement(self, element):
98
        if self.receiver:
99
            self.receiver.dispatch(element, self)
100
        else:
101
            raise OscError("Element received, but no Receiver in place: " + str(element))
102

    
103
    def __str__(self):
104
        return str(self.connectedProtocol.transport.client)
105

    
106

    
107
class ClientFactory(protocol.ClientFactory, StreamBasedFactory):
108
    """
109
    TCP client factory
110
    """
111
    protocol = StreamBasedProtocol
112

    
113
    def __init__(self, receiver=None):
114
        StreamBasedFactory.__init__(self, receiver)
115
        self.deferred = defer.Deferred()
116

    
117

    
118
class ServerFactory(protocol.ServerFactory, StreamBasedFactory):
119
    """
120
    TCP server factory
121
    """
122
    protocol = StreamBasedProtocol
123

    
124

    
125
#
126
# Datagram client/server protocols
127
#
128

    
129
class DatagramServerProtocol(protocol.DatagramProtocol):
130
    """
131
    The UDP OSC server protocol.
132

133
    @ivar receiver: The L{Receiver} instance to dispatch received
134
        elements to.
135
    """
136

    
137
    def __init__(self, receiver):
138
        """
139
        @param receiver: L{Receiver} instance.
140
        """
141
        self.receiver = receiver
142

    
143
    def datagramReceived(self, data, (host, port)):
144
        element = _elementFromBinary(data)
145
        self.receiver.dispatch(element, (host, port))
146

    
147
class MulticastDatagramServerProtocol(DatagramServerProtocol):
148
    """
149
    UDP OSC server protocol that can listen to multicast.
150
    
151
    Here is an example on how to use it:
152
    
153
      reactor.listenMulticast(8005, MulticastServerUDP(receiver, "224.0.0.1"), listenMultiple=True)
154
    
155
    This way, many listeners can listen on the same port, same host, to the same multicast group. (in this case, the 224.0.0.1 multicast group)
156
    """
157
    def __init__(self, receiver, multicast_addr="224.0.0.1"):
158
        """
159
        @param multicast_addr: IP address of the multicast group.
160
        @param receiver: L{txosc.dispatch.Receiver} instance.
161
        @type multicast_addr: str
162
        @type receiver: L{txosc.dispatch.Receiver}
163
        """
164
        self.multicast_addr = multicast_addr
165
        DatagramServerProtocol.__init__(self, receiver)
166
        
167
    def startProtocol(self):
168
        """
169
        Join a specific multicast group, which is the IP we will respond to
170
        """
171
        self.transport.joinGroup(self.multicast_addr)
172

    
173
class DatagramClientProtocol(protocol.DatagramProtocol):
174
    """
175
    The UDP OSC client protocol.
176
    """
177

    
178
    def send(self, element, (host, port)):
179
        """
180
        Send a L{txosc.osc.Message} or L{txosc.osc.Bundle} to the address specified.
181
        @type element: L{txosc.osc.Message}
182
        """
183
        data = element.toBinary()
184
        self.transport.write(data, (host, port))
185

    
186