qcmd 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +7 -2
- data/TODO.md +31 -0
- data/bin/qcmd +2 -1
- data/lib/qcmd.rb +15 -3
- data/lib/qcmd/action.rb +160 -0
- data/lib/qcmd/aliases.rb +25 -0
- data/lib/qcmd/cli.rb +503 -108
- data/lib/qcmd/commands.rb +198 -142
- data/lib/qcmd/configuration.rb +83 -0
- data/lib/qcmd/context.rb +19 -13
- data/lib/qcmd/core_ext/osc/tcp_client.rb +155 -0
- data/lib/qcmd/handler.rb +49 -67
- data/lib/qcmd/history.rb +26 -0
- data/lib/qcmd/input_completer.rb +12 -2
- data/lib/qcmd/network.rb +9 -3
- data/lib/qcmd/parser.rb +14 -83
- data/lib/qcmd/plaintext.rb +0 -4
- data/lib/qcmd/qlab.rb +1 -0
- data/lib/qcmd/qlab/cue.rb +20 -0
- data/lib/qcmd/qlab/cue_list.rb +83 -0
- data/lib/qcmd/qlab/reply.rb +18 -4
- data/lib/qcmd/qlab/workspace.rb +23 -1
- data/lib/qcmd/version.rb +1 -1
- data/lib/vendor/sexpistol/LICENSE +20 -0
- data/lib/vendor/sexpistol/sexpistol.rb +2 -0
- data/lib/vendor/sexpistol/sexpistol/sexpistol.rb +76 -0
- data/lib/vendor/sexpistol/sexpistol/sexpistol_parser.rb +94 -0
- data/sample/dnssd.rb +20 -3
- data/sample/simple_console.rb +186 -43
- data/sample/tcp_qlab_connection.rb +67 -0
- data/spec/unit/action_spec.rb +84 -0
- data/spec/unit/commands_spec.rb +135 -14
- data/spec/unit/parser_spec.rb +36 -5
- metadata +124 -122
- data/lib/qcmd/core_ext/osc/stopping_server.rb +0 -84
- data/lib/qcmd/server.rb +0 -175
- data/spec/unit/osc_server_spec.rb +0 -78
@@ -1,84 +0,0 @@
|
|
1
|
-
module OSC
|
2
|
-
class StoppingServer < Server
|
3
|
-
def initialize *args
|
4
|
-
@state = :initialized
|
5
|
-
@port = args.first
|
6
|
-
super(*args)
|
7
|
-
end
|
8
|
-
|
9
|
-
def run
|
10
|
-
@state = :starting
|
11
|
-
super
|
12
|
-
end
|
13
|
-
|
14
|
-
def stop
|
15
|
-
@state = :stopping
|
16
|
-
stop_detector
|
17
|
-
stop_dispatcher
|
18
|
-
end
|
19
|
-
|
20
|
-
def state
|
21
|
-
@state
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def stop_detector
|
27
|
-
# send listening port a "CLOSE" signal on the open UDP port
|
28
|
-
_closer = UDPSocket.new
|
29
|
-
_closer.connect('', @port)
|
30
|
-
_closer.puts "CLOSE-#{@port}"
|
31
|
-
_closer.close unless _closer.closed? || !_closer.respond_to?(:close)
|
32
|
-
end
|
33
|
-
|
34
|
-
def stop_dispatcher
|
35
|
-
@queue << :stop
|
36
|
-
end
|
37
|
-
|
38
|
-
def dispatcher
|
39
|
-
loop do
|
40
|
-
mesg = @queue.pop
|
41
|
-
dispatch_message( mesg )
|
42
|
-
end
|
43
|
-
rescue StopException
|
44
|
-
@state = :stopped
|
45
|
-
end
|
46
|
-
|
47
|
-
def dispatch_message message
|
48
|
-
if message.is_a?(Symbol) && message.to_s == 'stop'
|
49
|
-
raise StopException.new
|
50
|
-
end
|
51
|
-
|
52
|
-
super(message)
|
53
|
-
end
|
54
|
-
|
55
|
-
def detector
|
56
|
-
@state = :listening
|
57
|
-
|
58
|
-
loop do
|
59
|
-
osc_data, network = @socket.recvfrom( 16384 )
|
60
|
-
|
61
|
-
# quit if socket receives the close signal
|
62
|
-
if osc_data == "CLOSE-#{@port}"
|
63
|
-
@socket.close if !@socket.closed? && @socket.respond_to?(:close)
|
64
|
-
break
|
65
|
-
end
|
66
|
-
|
67
|
-
unpack_socket_receipt osc_data, network
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def unpack_socket_receipt osc_data, network
|
72
|
-
ip_info = Array.new
|
73
|
-
ip_info << network[1]
|
74
|
-
ip_info.concat(network[2].split('.'))
|
75
|
-
OSC::OSCPacket.messages_from_network( osc_data, ip_info ).each do |message|
|
76
|
-
@queue.push(message)
|
77
|
-
end
|
78
|
-
rescue EOFError
|
79
|
-
# pass
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
class StopException < Exception; end
|
84
|
-
end
|
data/lib/qcmd/server.rb
DELETED
@@ -1,175 +0,0 @@
|
|
1
|
-
require 'osc-ruby'
|
2
|
-
|
3
|
-
require 'json'
|
4
|
-
|
5
|
-
module Qcmd
|
6
|
-
class TimeoutError < Exception; end
|
7
|
-
|
8
|
-
class Server
|
9
|
-
attr_accessor :receive_channel, :receive_thread, :receive_port, :send_channel, :machine
|
10
|
-
|
11
|
-
def initialize *args
|
12
|
-
options = args.extract_options!
|
13
|
-
|
14
|
-
self.receive_port = options[:receive]
|
15
|
-
connect_to_client
|
16
|
-
|
17
|
-
@handler = Qcmd::Handler.new
|
18
|
-
@sent_messages = []
|
19
|
-
@sent_messages_expecting_reply = []
|
20
|
-
@received_messages = []
|
21
|
-
end
|
22
|
-
|
23
|
-
def connect_to_client
|
24
|
-
self.machine = Qcmd.context.machine
|
25
|
-
self.send_channel = OSC::Client.new machine.address, machine.port
|
26
|
-
|
27
|
-
Qcmd.debug '(setting up listening connection)'
|
28
|
-
listen
|
29
|
-
end
|
30
|
-
|
31
|
-
def generic_responding_proc
|
32
|
-
proc do |osc_message|
|
33
|
-
@received_messages << osc_message
|
34
|
-
|
35
|
-
begin
|
36
|
-
Qcmd.debug "(received message: #{ osc_message.address })"
|
37
|
-
reply_received QLab::Reply.new(osc_message)
|
38
|
-
rescue => ex
|
39
|
-
Qcmd.debug "(ERROR #{ ex.message })"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# initialize
|
45
|
-
def listen
|
46
|
-
if receive_channel
|
47
|
-
stop
|
48
|
-
end
|
49
|
-
|
50
|
-
self.receive_channel = OSC::StoppingServer.new(self.receive_port)
|
51
|
-
|
52
|
-
Qcmd.debug "(opening receiving channel: #{ self.receive_channel.inspect })"
|
53
|
-
|
54
|
-
receive_channel.add_method %r{/reply/?(.*)}, &generic_responding_proc
|
55
|
-
end
|
56
|
-
|
57
|
-
def replies_expected?
|
58
|
-
@sent_messages_expecting_reply.size > 0
|
59
|
-
end
|
60
|
-
|
61
|
-
def reply_received reply
|
62
|
-
Qcmd.debug "(receiving #{ reply })"
|
63
|
-
|
64
|
-
# update world state
|
65
|
-
begin
|
66
|
-
@handler.handle reply
|
67
|
-
rescue => ex
|
68
|
-
print "(ERROR: #{ ex.message })"
|
69
|
-
end
|
70
|
-
|
71
|
-
# FIFO
|
72
|
-
@sent_messages_expecting_reply.shift
|
73
|
-
|
74
|
-
Qcmd.debug "(#{ @sent_messages_expecting_reply.size } messages awaiting reply)"
|
75
|
-
end
|
76
|
-
|
77
|
-
def wait_for_replies
|
78
|
-
begin
|
79
|
-
yield
|
80
|
-
|
81
|
-
naps = 0
|
82
|
-
while replies_expected? do
|
83
|
-
if naps > 20
|
84
|
-
# FAILED TO GET RESPONSE
|
85
|
-
raise TimeoutError.new
|
86
|
-
end
|
87
|
-
|
88
|
-
naps += 1
|
89
|
-
sleep 0.1
|
90
|
-
end
|
91
|
-
rescue TimeoutError => ex
|
92
|
-
Qcmd.log "[error: reply timeout]"
|
93
|
-
# clear expecting reply item, assume it will never arrive
|
94
|
-
@sent_messages_expecting_reply.shift
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def send_command command, *args
|
99
|
-
options = args.extract_options!
|
100
|
-
|
101
|
-
Qcmd.debug "(building command from command, args, options: #{ command.inspect }, #{ args.inspect }, #{ options.inspect })"
|
102
|
-
|
103
|
-
# make sure command is valid OSC Address
|
104
|
-
if %r[^/] =~ command
|
105
|
-
address = command
|
106
|
-
else
|
107
|
-
address = "/#{ command }"
|
108
|
-
end
|
109
|
-
|
110
|
-
osc_message = OSC::Message.new address, *args
|
111
|
-
|
112
|
-
send_message osc_message
|
113
|
-
end
|
114
|
-
|
115
|
-
def send_message osc_message
|
116
|
-
Qcmd.debug "(sending osc message #{ osc_message.address } #{osc_message.has_arguments? ? 'with' : 'without'} args)"
|
117
|
-
|
118
|
-
@sent_messages << osc_message
|
119
|
-
if Qcmd::Commands.expects_reply?(osc_message)
|
120
|
-
Qcmd.debug "(this command expects a reply)"
|
121
|
-
@sent_messages_expecting_reply << osc_message
|
122
|
-
end
|
123
|
-
|
124
|
-
wait_for_replies do
|
125
|
-
send_channel.send osc_message
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
def stop
|
130
|
-
receive_channel.stop if receive_channel && receive_channel.state == :listening
|
131
|
-
end
|
132
|
-
|
133
|
-
def run
|
134
|
-
Qcmd.debug '(starting server)'
|
135
|
-
self.receive_thread = Thread.new do
|
136
|
-
Qcmd.debug '(server is up)'
|
137
|
-
receive_channel.run
|
138
|
-
end
|
139
|
-
end
|
140
|
-
alias :start :run
|
141
|
-
|
142
|
-
def send_workspace_command _command, *args
|
143
|
-
command = "workspace/#{ Qcmd.context.workspace.id }/#{ _command }"
|
144
|
-
send_command(command, *args)
|
145
|
-
end
|
146
|
-
|
147
|
-
def send_cue_command number, action, *args
|
148
|
-
command = "cue/#{ number }/#{ action }"
|
149
|
-
send_workspace_command(command, *args)
|
150
|
-
end
|
151
|
-
|
152
|
-
## QLab commands
|
153
|
-
|
154
|
-
def load_workspaces
|
155
|
-
send_command 'workspaces'
|
156
|
-
end
|
157
|
-
|
158
|
-
def load_cues
|
159
|
-
send_workspace_command 'cueLists'
|
160
|
-
end
|
161
|
-
|
162
|
-
def connect_to_workspace workspace
|
163
|
-
if workspace.passcode?
|
164
|
-
send_command "workspace/#{workspace.id}/connect", "%04i" % workspace.passcode
|
165
|
-
else
|
166
|
-
send_command "workspace/#{workspace.id}/connect"
|
167
|
-
end
|
168
|
-
|
169
|
-
# if it worked, load cues automatically
|
170
|
-
if Qcmd.context.workspace
|
171
|
-
load_cues
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
@@ -1,78 +0,0 @@
|
|
1
|
-
require File.join( File.dirname(__FILE__) , '..', 'spec_helper' )
|
2
|
-
require 'qcmd'
|
3
|
-
require 'socket'
|
4
|
-
|
5
|
-
class PortFactory
|
6
|
-
@@counter = 12345
|
7
|
-
|
8
|
-
def self.new_port
|
9
|
-
@@counter += 1
|
10
|
-
@@counter
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
describe OSC::StoppingServer do
|
15
|
-
before :each do
|
16
|
-
@port = PortFactory.new_port
|
17
|
-
end
|
18
|
-
|
19
|
-
it "should bind to a socket when initialized" do
|
20
|
-
UDPSocket.any_instance.should_receive(:bind).with('', @port)
|
21
|
-
server = OSC::StoppingServer.new @port
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'should start a listening thread when started' do
|
25
|
-
server = OSC::StoppingServer.new @port
|
26
|
-
|
27
|
-
test_thread = Thread.new do
|
28
|
-
Thread.should_receive :fork
|
29
|
-
server.run
|
30
|
-
end
|
31
|
-
|
32
|
-
server.stop
|
33
|
-
end
|
34
|
-
|
35
|
-
it 'should kill the listening thread and close socket when stopped' do
|
36
|
-
server = OSC::StoppingServer.new @port
|
37
|
-
|
38
|
-
test_thread = Thread.new do
|
39
|
-
server.run
|
40
|
-
end
|
41
|
-
|
42
|
-
sleep 0.1
|
43
|
-
server.stop
|
44
|
-
sleep 0.1
|
45
|
-
|
46
|
-
# server has stopped blocking
|
47
|
-
test_thread.alive?.should == false
|
48
|
-
|
49
|
-
# server claims it is closed
|
50
|
-
server.state.should == :stopped
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'should create messages for legitimate OSC commands' do
|
54
|
-
server = OSC::StoppingServer.new @port
|
55
|
-
|
56
|
-
received = nil
|
57
|
-
|
58
|
-
server.add_method '/test' do |message|
|
59
|
-
received = message
|
60
|
-
end
|
61
|
-
|
62
|
-
test_thread = Thread.new do
|
63
|
-
server.run
|
64
|
-
end
|
65
|
-
|
66
|
-
received.should == nil
|
67
|
-
|
68
|
-
client = OSC::Client.new 'localhost', @port
|
69
|
-
client.send OSC::Message.new('/test', 'ansible')
|
70
|
-
|
71
|
-
sleep 0.1
|
72
|
-
server.stop
|
73
|
-
|
74
|
-
received.is_a?(OSC::Message).should == true
|
75
|
-
received.to_a.first.should == 'ansible'
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|