qcmd 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
-
|