Advertisement
Guest User

pythonosc server with dispatched address

a guest
Jul 6th, 2016
177
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.04 KB | None | 0 0
  1. """OSC Servers that receive UDP packets and invoke handlers accordingly.
  2.  
  3. Use like this:
  4.  
  5. dispatcher = dispatcher.Dispatcher()
  6. # This will print all parameters to stdout.
  7. dispatcher.map("/bpm", print)
  8. server = ForkingOSCUDPServer((ip, port), dispatcher)
  9. server.serve_forever()
  10.  
  11. or run the server on its own thread:
  12. server = ForkingOSCUDPServer((ip, port), dispatcher)
  13. server_thread = threading.Thread(target=server.serve_forever)
  14. server_thread.start()
  15. ...
  16. server.shutdown()
  17.  
  18.  
  19. Those servers are using the standard socketserver from the standard library:
  20. http://docs.python.org/library/socketserver.html
  21.  
  22.  
  23. Alternatively, the AsyncIOOSCUDPServer server can be integrated with an
  24. asyncio event loop:
  25.  
  26. loop = asyncio.get_event_loop()
  27. server = AsyncIOOSCUDPServer(server_address, dispatcher, loop)
  28. server.serve()
  29. loop.run_forever()
  30.  
  31. """
  32.  
  33. import asyncio
  34. import socketserver
  35. import time
  36.  
  37. from pythonosc import osc_bundle
  38. from pythonosc import osc_message
  39. from pythonosc import osc_packet
  40.  
  41.  
  42. def _call_handlers_for_packet(data, dispatcher, client_address):
  43.   """
  44.  This function calls the handlers registered to the dispatcher for
  45.  every message it found in the packet.
  46.  The process/thread granularity is thus the OSC packet, not the handler.
  47.  
  48.  If parameters were registered with the dispatcher, then the handlers are
  49.  called this way:
  50.    handler('/address that triggered the message', client_address,
  51.            registered_param_list, osc_msg_arg1, osc_msg_arg2, ...)
  52.  if no parameters were registered, then it is just called like this:
  53.    handler('/address that triggered the message', client_address
  54.            osc_msg_arg1, osc_msg_arg2, osc_msg_param3, ...)
  55.  """
  56.  
  57.   # Get OSC messages from all bundles or standalone message.
  58.   try:
  59.     packet = osc_packet.OscPacket(data)
  60.     for timed_msg in packet.messages:
  61.       now = time.time()
  62.       handlers = dispatcher.handlers_for_address(
  63.           timed_msg.message.address)
  64.       if not handlers:
  65.         continue
  66.       # If the message is to be handled later, then so be it.
  67.       if timed_msg.time > now:
  68.         time.sleep(timed_msg.time - now)
  69.       for handler in handlers:
  70.         if handler.args:
  71.           handler.callback(
  72.               timed_msg.message.address, client_address, handler.args,
  73.               *timed_msg.message)
  74.         else:
  75.           handler.callback(timed_msg.message.address, client_address,
  76.                            *timed_msg.message)
  77.   except osc_packet.ParseError:
  78.     pass
  79.  
  80.  
  81. class _UDPHandler(socketserver.BaseRequestHandler):
  82.   """Handles correct UDP messages for all types of server.
  83.  
  84.  Whether this will be run on its own thread, the server's or a whole new
  85.  process depends on the server you instanciated, look at their documentation.
  86.  
  87.  This method is called after a basic sanity check was done on the datagram,
  88.  basically whether this datagram looks like an osc message or bundle,
  89.  if not the server won't even bother to call it and so no new
  90.  threads/processes will be spawned.
  91.  """
  92.   def handle(self):
  93.     _call_handlers_for_packet(self.request[0], self.server.dispatcher,
  94.                               self.client_address)
  95.  
  96.  
  97. def _is_valid_request(request):
  98.   """Returns true if the request's data looks like an osc bundle or message."""
  99.   data = request[0]
  100.   return (
  101.       osc_bundle.OscBundle.dgram_is_bundle(data)
  102.       or osc_message.OscMessage.dgram_is_message(data))
  103.  
  104.  
  105. class OSCUDPServer(socketserver.UDPServer):
  106.   """Superclass for different flavors of OSCUDPServer"""
  107.  
  108.   def __init__(self, server_address, dispatcher):
  109.     super().__init__(server_address, _UDPHandler)
  110.     self._dispatcher = dispatcher
  111.  
  112.   def verify_request(self, request, client_address):
  113.     """Returns true if the data looks like a valid OSC UDP datagram."""
  114.     return _is_valid_request(request)
  115.  
  116.   @property
  117.   def dispatcher(self):
  118.     """Dispatcher accessor for handlers to dispatch osc messages."""
  119.     return self._dispatcher
  120.  
  121.  
  122. class BlockingOSCUDPServer(OSCUDPServer):
  123.   """Blocking version of the UDP server.
  124.  
  125.  Each message will be handled sequentially on the same thread.
  126.  Use this is you don't care about latency in your message handling or don't
  127.  have a multiprocess/multithread environment (really?).
  128.  """
  129.  
  130.  
  131. class ThreadingOSCUDPServer(socketserver.ThreadingMixIn, OSCUDPServer):
  132.   """Threading version of the OSC UDP server.
  133.  
  134.  Each message will be handled in its own new thread.
  135.  Use this when lightweight operations are done by each message handlers.
  136.  """
  137.  
  138.  
  139. class ForkingOSCUDPServer(socketserver.ForkingMixIn, OSCUDPServer):
  140.   """Forking version of the OSC UDP server.
  141.  
  142.  Each message will be handled in its own new process.
  143.  Use this when heavyweight operations are done by each message handlers
  144.  and forking a whole new process for each of them is worth it.
  145.  """
  146.  
  147.  
  148. class AsyncIOOSCUDPServer():
  149.   """Asyncio version of the OSC UDP Server.
  150.  Each UDP message is handled by _call_handlers_for_packet, the same method as in the
  151.  OSCUDPServer family of blocking, threading, and forking servers
  152.  """
  153.  
  154.   def __init__(self, server_address, dispatcher, loop):
  155.     """
  156.    :param server_address: tuple of (IP address to bind to, port)
  157.    :param dispatcher: a pythonosc.dispatcher.Dispatcher
  158.    :param loop: an asyncio event loop
  159.    """
  160.  
  161.     self._server_address = server_address
  162.     self._dispatcher = dispatcher
  163.     self._loop = loop
  164.  
  165.   class _OSCProtocolFactory(asyncio.DatagramProtocol):
  166.     """OSC protocol factory which passes datagrams to _call_handlers_for_packet"""
  167.  
  168.     def __init__(self, dispatcher):
  169.       self.dispatcher = dispatcher
  170.  
  171.     def datagram_received(self, data, unused_addr):
  172.       _call_handlers_for_packet(data, self.dispatcher)
  173.  
  174.   def serve(self):
  175.     """creates a datagram endpoint and registers it with our event loop"""
  176.     listen = self._loop.create_datagram_endpoint(
  177.       lambda: self._OSCProtocolFactory(self.dispatcher),
  178.       local_addr=self._server_address)
  179.     self._loop.run_until_complete(listen)
  180.  
  181.   @property
  182.   def dispatcher(self):
  183.     return self._dispatcher
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement