#!/usr/bin/env ruby help_message = <<-EOS After starting qcmd-proxy, you should be able to to connect to "OSC QLab Proxy Server" from QLab Remote for iPad and see all the messages flying back and forth in your terminal. Due to the way QLab responds via blind UDP, you may have to manually connect to the proxy server. qcmd-proxy can also be used to debug your own OSC apps. Simply start QLab, then start qcmd-proxy from the command line and then connect your app to port 52000 of your machine instead of 53000. Running your OSC app/script/tool/toy on localhost will cause an infinite looping scenario where qcmd-proxy receives a response from QLab and forwards it to itself. To avoid this, qcmd-proxy will not forward UDP messages to apps running on 127.0.0.1, 0.0.0.0, or "localhost". NOTE: if you're running a very active OSC app, you could be generating more data than qcmd can spit to the console easily, you should redirect output to a logfile if you're going to be sending more than, say, 8 commands a second. Usage: qcmd-proxy [options] where [options] are: EOS require 'qcmd' require 'dnssd' require 'trollop' VERSION_STRING = "qcmd-proxy #{ Qcmd::VERSION } (c) 2013 Figure 53, Baltimore, MD." opts = Trollop::options do version VERSION_STRING banner help_message opt :receiving_port, 'The port qcmd-proxy will receive requests from client apps on.', :default => 52000 opt :qlab_listening_port, 'The port QLab is listening on.', :default => 53000 opt :qlab_response_port, 'The port QLab will respond on.', :default => 53001 opt :qlab_host_name, 'The hostname of the running QLab instance.', :default => 'localhost' end receiving_port = opts[:receiving_port] # 52001 for testing on localhost, 53001 for real-world use. udp_response_port = 53001 # These are QLab's built in ports and cannot be changed. qlab_listening_port = opts[:qlab_listening_port] qlab_response_port = opts[:qlab_response_port] qlab_host_name = opts[:qlab_host_name] # qcmd-proxy registers itself on the local network as an application supporting # the QLab OSC protocol. dnssd_thread = Thread.new do # name, type, domain, port service = DNSSD.register 'OSC QLab Proxy Server', '_qlab._udp', 'local.', receiving_port service = DNSSD.register 'OSC QLab Proxy Server', '_qlab._tcp', 'local.', receiving_port end # TCP & UDP servers that talk directly to QLab begin outbound_qlab_tcp_connection = OSC::TCP::Client.new qlab_host_name, qlab_listening_port rescue Errno::ECONNREFUSED Qcmd.print_wrapped "Could not connect to QLab. Make sure you have QLab running on this machine before starting qcmd-proxy." exit 1 end outbound_qlab_udp_connection = OSC::Client.new qlab_host_name, qlab_listening_port # Forwarding UDP Server # # This server gets requests from a remote control app and forwards them to QLab begin forwarding_udp_server = OSC::Server.new(receiving_port) rescue Errno::EADDRINUSE Qcmd.print_wrapped "Port #{ receiving_port } already seems to be in use, please choose a different recieving port." end # we won't know which IP Adress is making the UDP request until a request has been received. requesting_ip_address = nil requesting_clients = {} forwarding_udp_server.add_method(/.*/) do |message| Qcmd.print "[udp from client] #{ message.debug }" # get client app's ip address, we'll use it to forward traffic from QLab back # to the remote control app. requesting_ip_address = message.ip_address.sub(/\.\.\./, '') # immediately send message to QLab begin outbound_qlab_udp_connection.send message rescue Errno::ECONNREFUSED Qcmd.print "Connection to QLab lost!" end end # try to avoid forwarding messages to qcmd-proxy def is_loopback_address?(ip_address) /^(127\.0\.0\.1|0\.0\.0\.0|localhost)$/ =~ ip_address end # Receive real responses from QLab and forward them back to original requestor # on port 53001, just like the real QLab. Slight problem, we don't know who # sent the request that inspired this response, so we guess it's the requestor # who made a request most recently. inbound_qlab_udp_connection = OSC::Server.new qlab_response_port inbound_qlab_udp_connection.add_method(/.*/) do |qlab_response_message| if requesting_ip_address Qcmd.print "[udp from QLab for #{ requesting_ip_address }:#{ udp_response_port }] #{ qlab_response_message.debug }" if !is_loopback_address?(requesting_ip_address.to_s) requesting_clients[requesting_ip_address] = OSC::Client.new(requesting_ip_address, udp_response_port) requesting_clients[requesting_ip_address].send(qlab_response_message) end else Qcmd.print "Cannot forward UDP from QLab! No requesting_ip_address available" end end ## OSC::TCPServer gets requests and forwards them to QLab forwarding_tcp_server = OSC::TCP::Server.new(receiving_port) forwarding_tcp_server.add_method(/.*/) do |message| Qcmd.print "[tcp from client] #{ message.debug }" outbound_qlab_udp_connection.send(message) do |qlab_response_message| if qlab_response_message Qcmd.print "[tcp from QLab] #{ qlab_response_message.debug }" message.responder.send(qlab_response_message) else Qcmd.print "[tcp from QLab] nil" end end end Qcmd.print "starting forwarding UDP server on port #{ receiving_port }" udp_forwarding_thread = Thread.new do forwarding_udp_server.run end Qcmd.print "starting inbound UDP server on port #{ qlab_response_port }" udp_inbound_thread = Thread.new do inbound_qlab_udp_connection.run end Qcmd.print "starting forwarding TCP server on port #{ receiving_port }" forwarding_tcp_server.run