qcmd 0.1.8 → 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +18 -0
- data/bin/qcmd +18 -3
- data/lib/qcmd.rb +19 -2
- data/lib/qcmd/action.rb +17 -13
- data/lib/qcmd/cli.rb +127 -107
- data/lib/qcmd/handler.rb +1 -1
- data/lib/qcmd/network.rb +6 -3
- data/lib/qcmd/plaintext.rb +12 -6
- data/lib/qcmd/qlab/cue.rb +4 -0
- data/lib/qcmd/version.rb +1 -1
- data/lib/vendor/sexpistol/sexpistol/sexpistol.rb +10 -7
- data/spec/unit/parser_spec.rb +25 -0
- data/spec/unit/qcmd_spec.rb +3 -3
- metadata +3 -2
data/CHANGELOG.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
### 0.1.8 -> 0.1.9
|
2
|
+
|
3
|
+
* add "alias" command to help
|
4
|
+
* add "new" command to create new cues in QLab
|
5
|
+
* add "select" command to allow selection of a given cue (by number)
|
6
|
+
* `select 2; go` would select and start the QLab workspace at the given cue
|
7
|
+
* allow IPv4 machine addresses
|
8
|
+
|
9
|
+
Internals:
|
10
|
+
* set default log level to `:info`
|
11
|
+
* separate machine connection and workspace loading
|
12
|
+
* simplify `send_workspace_command` method, clean up `send_command`
|
13
|
+
* add list of cue types for "new" command
|
14
|
+
* unify simple reply handling in CLI
|
15
|
+
* fix `-c "COMMAND"` command line option
|
16
|
+
* various small bug fixes
|
17
|
+
|
18
|
+
|
data/bin/qcmd
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
require 'qcmd'
|
4
4
|
require 'trollop'
|
5
5
|
|
6
|
+
# require 'profile'
|
7
|
+
|
6
8
|
VERSION_STRING = "qcmd #{ Qcmd::VERSION } (c) 2012 Figure 53, Baltimore, MD."
|
7
9
|
|
8
10
|
opts = Trollop::options do
|
@@ -15,6 +17,8 @@ opts = Trollop::options do
|
|
15
17
|
opt :command, "Execute a single command and exit", :type => :string
|
16
18
|
end
|
17
19
|
|
20
|
+
Qcmd.log_level = :info
|
21
|
+
|
18
22
|
if opts[:verbose]
|
19
23
|
Qcmd.log_level = :debug
|
20
24
|
end
|
@@ -27,21 +31,32 @@ end
|
|
27
31
|
# browse local network and check for qlab + qlab workspaces
|
28
32
|
|
29
33
|
Qcmd::History.load
|
34
|
+
Qcmd::Network.init
|
30
35
|
|
31
36
|
if !opts[:machine_given]
|
32
37
|
Qcmd.ascii_qlab
|
33
38
|
Qcmd.print
|
34
39
|
Qcmd.print Qcmd.centered_text(VERSION_STRING)
|
35
|
-
|
36
40
|
Qcmd::Network.browse_and_display opts
|
37
41
|
else
|
38
|
-
Qcmd::Network
|
42
|
+
if Qcmd::Network::IPV4_MATCHER =~ opts[:machine]
|
43
|
+
Qcmd.debug "[bin/qcmd] given machine name matches IP address pattern"
|
44
|
+
elsif opts[:machine] == 'localhost'
|
45
|
+
opts[:machine] = '127.0.0.1'
|
46
|
+
else
|
47
|
+
Qcmd.debug "[bin/qcmd] browsing network #{ opts[:machine] }"
|
48
|
+
Qcmd::Network.browse
|
49
|
+
end
|
39
50
|
end
|
40
51
|
|
41
52
|
if opts[:command_given] && !(opts[:machine_given] && opts[:workspace_given])
|
42
53
|
Qcmd.print_wrapped("if you give a command, you must also give a
|
43
54
|
machine name and a workspace name to connect to")
|
55
|
+
elsif opts[:command_given] && opts[:machine_given] && opts[:workspace_given]
|
56
|
+
opts[:command_mode] = true
|
44
57
|
end
|
45
58
|
|
46
|
-
|
59
|
+
# Profiler__::start_profile
|
47
60
|
Qcmd::CLI.launch opts
|
61
|
+
# Profiler__::stop_profile
|
62
|
+
# Profiler__::print_profile($stderr)
|
data/lib/qcmd.rb
CHANGED
@@ -33,10 +33,23 @@ module Qcmd
|
|
33
33
|
class << self
|
34
34
|
include Qcmd::Plaintext
|
35
35
|
|
36
|
-
attr_accessor :log_level
|
37
36
|
attr_accessor :debug_mode
|
38
37
|
attr_accessor :context
|
39
38
|
|
39
|
+
LEVELS = %w(debug info warning error none)
|
40
|
+
|
41
|
+
def log_level
|
42
|
+
@log_level ||= :info
|
43
|
+
end
|
44
|
+
|
45
|
+
def log_level=(value)
|
46
|
+
if LEVELS.include?(value.to_s)
|
47
|
+
@log_level = value
|
48
|
+
else
|
49
|
+
raise "Invalid log_level value: #{ value.to_s }"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
40
53
|
def verbose!
|
41
54
|
self.log_level = :debug
|
42
55
|
end
|
@@ -73,7 +86,11 @@ module Qcmd
|
|
73
86
|
|
74
87
|
Qcmd::Configuration.log.puts "[%s] %s" % [Time.now.strftime('%T'), message]
|
75
88
|
|
76
|
-
log(message)
|
89
|
+
log(:debug, message)
|
90
|
+
end
|
91
|
+
|
92
|
+
def log_level_acheived? level
|
93
|
+
LEVELS.index(level.to_s) >= LEVELS.index(log_level.to_s)
|
77
94
|
end
|
78
95
|
|
79
96
|
def connected?
|
data/lib/qcmd/action.rb
CHANGED
@@ -87,7 +87,7 @@ module Qcmd
|
|
87
87
|
end
|
88
88
|
|
89
89
|
def osc_arguments
|
90
|
-
code[1..-1]
|
90
|
+
stringify code[1..-1]
|
91
91
|
end
|
92
92
|
|
93
93
|
# the raw command
|
@@ -100,6 +100,7 @@ module Qcmd
|
|
100
100
|
def send_message
|
101
101
|
responses = []
|
102
102
|
|
103
|
+
Qcmd.debug "[Action send_message] send #{ osc_message.encode }"
|
103
104
|
Qcmd.context.qlab.send(osc_message) do |response|
|
104
105
|
# puts "response to: #{ osc_message.inspect }"
|
105
106
|
# puts response.inspect
|
@@ -120,6 +121,20 @@ module Qcmd
|
|
120
121
|
responses
|
121
122
|
end
|
122
123
|
end
|
124
|
+
|
125
|
+
def stringify args
|
126
|
+
if args.nil?
|
127
|
+
nil
|
128
|
+
else
|
129
|
+
args.map {|arg|
|
130
|
+
if arg.is_a?(Symbol)
|
131
|
+
arg.to_s
|
132
|
+
else
|
133
|
+
arg
|
134
|
+
end
|
135
|
+
}
|
136
|
+
end
|
137
|
+
end
|
123
138
|
end
|
124
139
|
|
125
140
|
class CueAction < Action
|
@@ -133,18 +148,7 @@ module Qcmd
|
|
133
148
|
end
|
134
149
|
|
135
150
|
def osc_arguments
|
136
|
-
|
137
|
-
if args.nil?
|
138
|
-
nil
|
139
|
-
else
|
140
|
-
args.map {|arg|
|
141
|
-
if arg.is_a?(Symbol)
|
142
|
-
arg.to_s
|
143
|
-
else
|
144
|
-
arg
|
145
|
-
end
|
146
|
-
}
|
147
|
-
end
|
151
|
+
stringify code[3..-1]
|
148
152
|
end
|
149
153
|
|
150
154
|
# cue specific fields
|
data/lib/qcmd/cli.rb
CHANGED
@@ -23,22 +23,25 @@ module Qcmd
|
|
23
23
|
connect_to_machine_by_name(options[:machine])
|
24
24
|
end
|
25
25
|
|
26
|
+
load_workspaces
|
27
|
+
|
26
28
|
if options[:workspace_given]
|
27
|
-
Qcmd.debug "[CLI initialize] autoconnecting to workspace #{ options[:
|
29
|
+
Qcmd.debug "[CLI initialize] autoconnecting to workspace #{ options[:workspace] }"
|
28
30
|
|
29
31
|
Qcmd.while_quiet do
|
30
32
|
connect_to_workspace_by_name(options[:workspace], options[:workspace_passcode])
|
31
33
|
end
|
32
34
|
|
33
35
|
if options[:command_given]
|
34
|
-
handle_input options[:command]
|
35
|
-
|
36
|
-
exit 0
|
36
|
+
handle_input Qcmd::Parser.parse(options[:command])
|
37
|
+
return
|
37
38
|
end
|
38
39
|
elsif Qcmd.context.machine.workspaces.size == 1 &&
|
39
40
|
!Qcmd.context.machine.workspaces.first.passcode? &&
|
40
41
|
!Qcmd.context.workspace_connected?
|
41
|
-
|
42
|
+
if !connect_default_workspace
|
43
|
+
Handler.print_workspace_list
|
44
|
+
end
|
42
45
|
end
|
43
46
|
end
|
44
47
|
|
@@ -79,7 +82,7 @@ module Qcmd
|
|
79
82
|
if arg.is_a?(Array)
|
80
83
|
replace_args(arg, original_expression)
|
81
84
|
elsif (arg.is_a?(Symbol) || arg.is_a?(String)) && alias_arg_matcher =~ arg.to_s
|
82
|
-
while alias_arg_matcher
|
85
|
+
while alias_arg_matcher =~ arg.to_s
|
83
86
|
arg_idx = $1.to_i
|
84
87
|
arg_val = original_expression[arg_idx]
|
85
88
|
|
@@ -141,7 +144,7 @@ module Qcmd
|
|
141
144
|
["#{clock} #{prefix.join(' ')}", "> "]
|
142
145
|
end
|
143
146
|
|
144
|
-
def
|
147
|
+
def connect_machine machine
|
145
148
|
if machine.nil?
|
146
149
|
print "A valid machine is needed to connect!"
|
147
150
|
return
|
@@ -157,18 +160,10 @@ module Qcmd
|
|
157
160
|
# tell QLab to always reply to messages
|
158
161
|
response = Qcmd::Action.evaluate('/alwaysReply 1')
|
159
162
|
if response.nil? || response.empty?
|
160
|
-
|
163
|
+
log(:error, %[Failed to connect to QLab machine "#{ machine.name }"])
|
161
164
|
elsif response.status == 'ok'
|
162
165
|
print %[Connected to machine "#{ machine.name }"]
|
163
166
|
end
|
164
|
-
|
165
|
-
machine.workspaces = Qcmd::Action.evaluate('workspaces').map {|ws| QLab::Workspace.new(ws)}
|
166
|
-
|
167
|
-
if Qcmd.context.machine.workspaces.size == 1 && !Qcmd.context.machine.workspaces.first.passcode?
|
168
|
-
connect_to_workspace_by_index(0, nil)
|
169
|
-
else
|
170
|
-
Handler.print_workspace_list
|
171
|
-
end
|
172
167
|
end
|
173
168
|
|
174
169
|
def disconnected_machine_warning
|
@@ -183,20 +178,29 @@ module Qcmd
|
|
183
178
|
end
|
184
179
|
|
185
180
|
def connect_to_machine_by_name machine_name
|
186
|
-
|
187
|
-
|
188
|
-
|
181
|
+
machine = nil
|
182
|
+
|
183
|
+
# machine name can be found or IPv4 address is given
|
184
|
+
if Qcmd::Network.find(machine_name)
|
185
|
+
machine = Qcmd::Network.find(machine_name)
|
186
|
+
elsif Qcmd::Network::IPV4_MATCHER =~ machine_name
|
187
|
+
machine = Qcmd::Machine.new(machine_name, machine_name, 53000)
|
188
|
+
end
|
189
|
+
|
190
|
+
if machine.nil?
|
191
|
+
log(:warning, 'Sorry, that machine could not be found')
|
189
192
|
else
|
190
|
-
print
|
193
|
+
print "Connecting to machine: #{machine_name}"
|
194
|
+
connect_machine machine
|
191
195
|
end
|
192
196
|
end
|
193
197
|
|
194
198
|
def connect_to_machine_by_index machine_idx
|
195
199
|
if machine = Qcmd::Network.find_by_index(machine_idx)
|
196
200
|
print "Connecting to machine: #{machine.name}"
|
197
|
-
|
201
|
+
connect_machine machine
|
198
202
|
else
|
199
|
-
|
203
|
+
log(:warning, 'Sorry, that machine could not be found')
|
200
204
|
end
|
201
205
|
end
|
202
206
|
|
@@ -208,7 +212,7 @@ module Qcmd
|
|
208
212
|
print "That workspace isn't on the list."
|
209
213
|
end
|
210
214
|
else
|
211
|
-
|
215
|
+
log(:warning, %[You can't connect to a workspace until you've connected to a machine. ])
|
212
216
|
disconnected_machine_warning
|
213
217
|
end
|
214
218
|
end
|
@@ -218,16 +222,15 @@ module Qcmd
|
|
218
222
|
if workspace = Qcmd.context.machine.find_workspace(workspace_name)
|
219
223
|
workspace.passcode = passcode
|
220
224
|
print "Connecting to workspace: #{workspace_name}"
|
221
|
-
|
222
225
|
use_workspace workspace
|
223
226
|
else
|
224
|
-
|
227
|
+
log(:warning, "That workspace doesn't seem to exist, try one of the following:")
|
225
228
|
Qcmd.context.machine.workspaces.each do |ws|
|
226
|
-
|
229
|
+
log(:warning, %[ "#{ ws.name }"])
|
227
230
|
end
|
228
231
|
end
|
229
232
|
else
|
230
|
-
|
233
|
+
log(:warning, %[You can't connect to a workspace until you've connected to a machine. ])
|
231
234
|
disconnected_machine_warning
|
232
235
|
end
|
233
236
|
end
|
@@ -248,7 +251,7 @@ module Qcmd
|
|
248
251
|
reply = Qcmd::Action.evaluate(ws_action_string)
|
249
252
|
|
250
253
|
if reply == 'badpass'
|
251
|
-
|
254
|
+
log(:error, 'Failed to connect to workspace, bad passcode or no passcode given.')
|
252
255
|
Qcmd.context.disconnect_workspace
|
253
256
|
elsif reply == 'ok'
|
254
257
|
print %[Connected to "#{Qcmd.context.workspace.name}"]
|
@@ -284,7 +287,7 @@ module Qcmd
|
|
284
287
|
begin
|
285
288
|
if /;/ =~ cli_input
|
286
289
|
cli_input.split(';').each do |sub_input|
|
287
|
-
handle_input Qcmd::Parser.parse(sub_input)
|
290
|
+
handle_input Qcmd::Parser.parse(sub_input.strip)
|
288
291
|
end
|
289
292
|
else
|
290
293
|
handle_input Qcmd::Parser.parse(cli_input)
|
@@ -300,6 +303,8 @@ module Qcmd
|
|
300
303
|
def handle_input args
|
301
304
|
command = args[0].to_s
|
302
305
|
|
306
|
+
Qcmd.debug "[CLI handle_input] command: #{ command }; args: #{ args.inspect }"
|
307
|
+
|
303
308
|
case command
|
304
309
|
when 'exit', 'quit', 'q'
|
305
310
|
print 'exiting...'
|
@@ -318,6 +323,12 @@ module Qcmd
|
|
318
323
|
connect_to_machine_by_name machine_ident
|
319
324
|
end
|
320
325
|
|
326
|
+
load_workspaces
|
327
|
+
|
328
|
+
if !connect_default_workspace
|
329
|
+
Handler.print_workspace_list
|
330
|
+
end
|
331
|
+
|
321
332
|
when 'disconnect'
|
322
333
|
disconnect_what = args[1]
|
323
334
|
|
@@ -382,18 +393,12 @@ module Qcmd
|
|
382
393
|
print_wrapped("no workspace command given. available workspace commands
|
383
394
|
are: #{Qcmd::InputCompleter::ReservedWorkspaceWords.join(', ')}")
|
384
395
|
else
|
385
|
-
send_workspace_command(workspace_command, *args)
|
396
|
+
reply = send_workspace_command(workspace_command, *args)
|
397
|
+
handle_simple_reply reply
|
386
398
|
end
|
387
399
|
|
388
400
|
when 'help'
|
389
|
-
|
390
|
-
|
391
|
-
if help_command.nil?
|
392
|
-
# print help according to current context
|
393
|
-
Qcmd::Commands::Help.print_all_commands
|
394
|
-
else
|
395
|
-
# print command specific help
|
396
|
-
end
|
401
|
+
Qcmd::Commands::Help.print_all_commands
|
397
402
|
|
398
403
|
when 'cues'
|
399
404
|
if !Qcmd.context.workspace_connected?
|
@@ -441,33 +446,9 @@ module Qcmd
|
|
441
446
|
cue_action = Qcmd::CueAction.new(args)
|
442
447
|
|
443
448
|
reply = cue_action.evaluate
|
449
|
+
handle_simple_reply reply
|
444
450
|
|
445
|
-
|
446
|
-
if !reply.status.nil?
|
447
|
-
print reply.status
|
448
|
-
end
|
449
|
-
else
|
450
|
-
render_data reply
|
451
|
-
end
|
452
|
-
|
453
|
-
# fixate on cue
|
454
|
-
if Qcmd.context.workspace.has_cues?
|
455
|
-
_cue = Qcmd.context.workspace.cues.find {|cue|
|
456
|
-
case cue_action.id_field
|
457
|
-
when :cue
|
458
|
-
cue.number.to_s == cue_action.identifier.to_s
|
459
|
-
when :cue_id
|
460
|
-
cue.id.to_s == cue_action.identifier.to_s
|
461
|
-
end
|
462
|
-
}
|
463
|
-
|
464
|
-
if _cue
|
465
|
-
Qcmd.context.cue = _cue
|
466
|
-
Qcmd.context.cue_connected = true
|
467
|
-
|
468
|
-
Qcmd.context.cue.sync
|
469
|
-
end
|
470
|
-
end
|
451
|
+
fixate_on_cue(cue_action)
|
471
452
|
|
472
453
|
when 'aliases'
|
473
454
|
print centered_text(" Available Custom Commands ", '-')
|
@@ -483,6 +464,38 @@ module Qcmd
|
|
483
464
|
new_alias = add_alias(args[1].to_s, args[2])
|
484
465
|
print %[Added alias for "#{ args[1] }": #{ new_alias }]
|
485
466
|
|
467
|
+
when 'new'
|
468
|
+
# create new cue
|
469
|
+
|
470
|
+
if !(args.size == 2 && QLab::Cue::TYPES.include?(args.last.to_s))
|
471
|
+
log(:warning, "That cue type can't be created, try one of the following:")
|
472
|
+
log(:warning, joined_wrapped(QLab::Cue::TYPES.join(", ")))
|
473
|
+
else
|
474
|
+
reply = send_workspace_command(command, *args)
|
475
|
+
handle_simple_reply reply
|
476
|
+
end
|
477
|
+
|
478
|
+
when 'select'
|
479
|
+
|
480
|
+
if args.size == 2
|
481
|
+
reply = send_workspace_command "#{ args[0] }/#{ args[1] }"
|
482
|
+
|
483
|
+
if reply.respond_to?(:status) && reply.status == 'ok'
|
484
|
+
# cue exists, get name and fixate
|
485
|
+
cue_action = Qcmd::CueAction.new([:cue, args[1], :name])
|
486
|
+
reply = cue_action.evaluate
|
487
|
+
if reply.is_a?(QLab::Reply)
|
488
|
+
# something went wrong
|
489
|
+
handle_simple_reply reply
|
490
|
+
else
|
491
|
+
print "Selected #{args[1]} - #{reply}"
|
492
|
+
fixate_on_cue(cue_action)
|
493
|
+
end
|
494
|
+
end
|
495
|
+
else
|
496
|
+
log(:warning, "The select command should be in the form `select CUE_NUMBER`.")
|
497
|
+
end
|
498
|
+
|
486
499
|
else
|
487
500
|
if aliases[command]
|
488
501
|
Qcmd.debug "[CLI handle_input] using alias #{ command }"
|
@@ -509,7 +522,6 @@ module Qcmd
|
|
509
522
|
handle_input(new_expression)
|
510
523
|
end
|
511
524
|
|
512
|
-
|
513
525
|
elsif Qcmd.context.cue_connected? && Qcmd::InputCompleter::ReservedCueWords.include?(command)
|
514
526
|
# prepend the given command with a cue address
|
515
527
|
if Qcmd.context.cue.number.nil? || Qcmd.context.cue.number.size == 0
|
@@ -523,31 +535,18 @@ module Qcmd
|
|
523
535
|
cue_action = Qcmd::CueAction.new(args)
|
524
536
|
|
525
537
|
reply = cue_action.evaluate
|
538
|
+
handle_simple_reply reply
|
526
539
|
|
527
|
-
if reply.is_a?(QLab::Reply)
|
528
|
-
if !reply.status.nil?
|
529
|
-
print reply.status
|
530
|
-
end
|
531
|
-
else
|
532
|
-
render_data reply
|
533
|
-
end
|
534
|
-
|
535
|
-
# send_workspace_command(command, *args)
|
536
540
|
elsif Qcmd.context.workspace_connected? && Qcmd::InputCompleter::ReservedWorkspaceWords.include?(command)
|
537
|
-
send_workspace_command(command, *args)
|
541
|
+
reply = send_workspace_command(command, *args)
|
542
|
+
handle_simple_reply reply
|
538
543
|
|
539
544
|
else
|
540
545
|
# failure modes?
|
541
546
|
if %r[/] =~ command
|
542
547
|
# might be legit OSC command, try sending
|
543
548
|
reply = Qcmd::Action.evaluate(args)
|
544
|
-
|
545
|
-
if !reply.status.nil?
|
546
|
-
print reply.status
|
547
|
-
end
|
548
|
-
else
|
549
|
-
render_data reply
|
550
|
-
end
|
549
|
+
handle_simple_reply reply
|
551
550
|
else
|
552
551
|
if Qcmd.context.cue_connected?
|
553
552
|
# cue is connected, but command isn't a valid cue command
|
@@ -557,7 +556,9 @@ module Qcmd
|
|
557
556
|
# workspace is connected, but command isn't a valid workspace command
|
558
557
|
print_wrapped("Unrecognized command: '#{ command }'. Try one of these workspace commands: #{ Qcmd::InputCompleter::ReservedWorkspaceWords.join(', ') }")
|
559
558
|
elsif Qcmd.context.machine_connected?
|
560
|
-
|
559
|
+
# send a command directly to a machine
|
560
|
+
reply = Qcmd::Action.evaluate(args)
|
561
|
+
handle_simple_reply reply
|
561
562
|
else
|
562
563
|
print 'you must connect to a machine before sending commands'
|
563
564
|
end
|
@@ -588,6 +589,16 @@ module Qcmd
|
|
588
589
|
### communication actions
|
589
590
|
private
|
590
591
|
|
592
|
+
def handle_simple_reply reply
|
593
|
+
if reply.is_a?(QLab::Reply)
|
594
|
+
if !reply.status.nil?
|
595
|
+
print reply.status
|
596
|
+
end
|
597
|
+
else
|
598
|
+
render_data reply
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
591
602
|
def render_data data
|
592
603
|
if data.is_a?(Array) || data.is_a?(Hash)
|
593
604
|
begin
|
@@ -601,42 +612,51 @@ module Qcmd
|
|
601
612
|
end
|
602
613
|
end
|
603
614
|
|
615
|
+
def fixate_on_cue cue_action
|
616
|
+
# fixate on cue
|
617
|
+
if Qcmd.context.workspace.has_cues?
|
618
|
+
_cue = Qcmd.context.workspace.cues.find {|cue|
|
619
|
+
case cue_action.id_field
|
620
|
+
when :cue
|
621
|
+
cue.number.to_s == cue_action.identifier.to_s
|
622
|
+
when :cue_id
|
623
|
+
cue.id.to_s == cue_action.identifier.to_s
|
624
|
+
end
|
625
|
+
}
|
604
626
|
|
605
|
-
|
606
|
-
|
627
|
+
if _cue
|
628
|
+
Qcmd.context.cue = _cue
|
629
|
+
Qcmd.context.cue_connected = true
|
607
630
|
|
608
|
-
|
631
|
+
Qcmd.context.cue.sync
|
632
|
+
end
|
633
|
+
end
|
634
|
+
end
|
609
635
|
|
610
|
-
|
611
|
-
if
|
612
|
-
|
636
|
+
def send_workspace_command _command, *args
|
637
|
+
if !Qcmd.context.workspace.nil?
|
638
|
+
args[0] = "workspace/#{ Qcmd.context.workspace.id }/#{ _command }"
|
639
|
+
Qcmd::Action.evaluate(args)
|
613
640
|
else
|
614
|
-
|
641
|
+
log(:warning, "A workspace needs to be connected before a workspace command can be sent.")
|
615
642
|
end
|
643
|
+
end
|
616
644
|
|
617
|
-
|
645
|
+
## QLab commands
|
618
646
|
|
619
|
-
|
647
|
+
def load_workspaces
|
648
|
+
Qcmd.context.machine.workspaces = Qcmd::Action.evaluate('workspaces').map {|ws| QLab::Workspace.new(ws)}
|
649
|
+
end
|
620
650
|
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
yield QLab::Reply.new(response)
|
626
|
-
end
|
651
|
+
def connect_default_workspace
|
652
|
+
if Qcmd.context.machine.workspaces.size == 1 && !Qcmd.context.machine.workspaces.first.passcode?
|
653
|
+
connect_to_workspace_by_index(0, nil)
|
654
|
+
true
|
627
655
|
else
|
628
|
-
|
629
|
-
Qcmd.context.qlab.send(osc_message)
|
656
|
+
false
|
630
657
|
end
|
631
658
|
end
|
632
659
|
|
633
|
-
def send_workspace_command _command, *args
|
634
|
-
command = "workspace/#{ Qcmd.context.workspace.id }/#{ _command }"
|
635
|
-
send_command(command, *args)
|
636
|
-
end
|
637
|
-
|
638
|
-
## QLab commands
|
639
|
-
|
640
660
|
def load_cues
|
641
661
|
cues = Qcmd::Action.evaluate('/cueLists')
|
642
662
|
Qcmd.context.workspace.cue_lists = cues.map {|cue_list| Qcmd::QLab::CueList.new(cue_list)}
|
data/lib/qcmd/handler.rb
CHANGED
@@ -61,7 +61,7 @@ module Qcmd
|
|
61
61
|
end
|
62
62
|
|
63
63
|
Qcmd.print
|
64
|
-
Qcmd.print_wrapped('Type `use "
|
64
|
+
Qcmd.print_wrapped('Type `use "WORKSPACE NAME" PASSCODE` to load a workspace. Passcode is required if workspace is [PROTECTED].')
|
65
65
|
Qcmd.print
|
66
66
|
end
|
67
67
|
end
|
data/lib/qcmd/network.rb
CHANGED
@@ -4,14 +4,17 @@ module Qcmd
|
|
4
4
|
# Browse the LAN and find open and running QLab instances.
|
5
5
|
class Network
|
6
6
|
BROWSE_TIMEOUT = 2
|
7
|
+
IPV4_MATCHER = /\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/
|
7
8
|
|
8
9
|
class << self
|
9
10
|
attr_accessor :machines, :browse_thread
|
10
11
|
|
12
|
+
def init
|
13
|
+
self.machines ||= []
|
14
|
+
end
|
15
|
+
|
11
16
|
# browse can be used alone to populate the machines list
|
12
17
|
def browse
|
13
|
-
self.machines = []
|
14
|
-
|
15
18
|
self.browse_thread = Thread.start do
|
16
19
|
DNSSD.browse! '_qlab._tcp.' do |b|
|
17
20
|
DNSSD.resolve b.name, b.type, b.domain do |r|
|
@@ -41,7 +44,7 @@ module Qcmd
|
|
41
44
|
end
|
42
45
|
|
43
46
|
Qcmd.print
|
44
|
-
Qcmd.print 'Type `connect MACHINE` to connect to a machine'
|
47
|
+
Qcmd.print 'Type `connect "MACHINE NAME"` or `connect IP_ADDRESS` to connect to a machine'
|
45
48
|
Qcmd.print
|
46
49
|
end
|
47
50
|
|
data/lib/qcmd/plaintext.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
1
|
module Qcmd
|
2
2
|
module Plaintext
|
3
|
-
def log message=nil
|
4
|
-
if
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
def log level, message=nil
|
4
|
+
if Qcmd.log_level_acheived?(level)
|
5
|
+
if message
|
6
|
+
puts message
|
7
|
+
else
|
8
|
+
puts
|
9
|
+
end
|
8
10
|
end
|
9
11
|
end
|
10
12
|
|
11
13
|
# display output unless absolutely silent
|
12
14
|
def print message=nil
|
13
|
-
log(message) unless Qcmd.silent?
|
15
|
+
log(:info, message) unless Qcmd.silent?
|
14
16
|
end
|
15
17
|
|
16
18
|
def set_columns value
|
@@ -86,6 +88,10 @@ module Qcmd
|
|
86
88
|
word_wrap(line, options)
|
87
89
|
end
|
88
90
|
|
91
|
+
def joined_wrapped *args
|
92
|
+
wrapped_text(*args).join("\n")
|
93
|
+
end
|
94
|
+
|
89
95
|
def print_wrapped line
|
90
96
|
print wrapped_text(line)
|
91
97
|
end
|
data/lib/qcmd/qlab/cue.rb
CHANGED
@@ -44,6 +44,10 @@ module Qcmd
|
|
44
44
|
# \"armed\":true}]
|
45
45
|
|
46
46
|
class Cue
|
47
|
+
TYPES = %w(audio mic video camera fade osc midi midi file timecode group
|
48
|
+
start stop pause load reset devamp goto target arm disarm wait
|
49
|
+
memo script cuelist)
|
50
|
+
|
47
51
|
attr_accessor :data
|
48
52
|
|
49
53
|
def initialize options={}
|
data/lib/qcmd/version.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# This class contains our logic for parsing
|
2
|
-
# S-Expressions. They are turned into a
|
2
|
+
# S-Expressions. They are turned into a
|
3
3
|
# native Ruby representation like:
|
4
4
|
# [:def, :something [:lambda, [:a], [:do_something]]]
|
5
5
|
class Sexpistol
|
@@ -26,7 +26,7 @@ class Sexpistol
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
# Convert nil, true and false into (), #t and #f for compatability
|
31
31
|
# with Scheme
|
32
32
|
def convert_scheme_literals(data)
|
@@ -39,7 +39,7 @@ class Sexpistol
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
# Convert a set of nested arrays back into an S-Expression
|
44
44
|
def to_sexp(data)
|
45
45
|
data = convert_scheme_literals(data) if(@scheme_compatability)
|
@@ -47,6 +47,9 @@ class Sexpistol
|
|
47
47
|
mapped = data.map do |item|
|
48
48
|
if( item.is_a?(Array))
|
49
49
|
to_sexp(item)
|
50
|
+
elsif item.is_a?(String)
|
51
|
+
# preserve string literal double quoting
|
52
|
+
item.inspect
|
50
53
|
else
|
51
54
|
item.to_s
|
52
55
|
end
|
@@ -56,9 +59,9 @@ class Sexpistol
|
|
56
59
|
data.to_s
|
57
60
|
end
|
58
61
|
end
|
59
|
-
|
62
|
+
|
60
63
|
private
|
61
|
-
|
64
|
+
|
62
65
|
def recursive_map(data, &block)
|
63
66
|
if(data.is_a?(Array))
|
64
67
|
return data.map do |x|
|
@@ -72,5 +75,5 @@ class Sexpistol
|
|
72
75
|
block.call(data)
|
73
76
|
end
|
74
77
|
end
|
75
|
-
|
76
|
-
end
|
78
|
+
|
79
|
+
end
|
data/spec/unit/parser_spec.rb
CHANGED
@@ -32,6 +32,11 @@ describe Qcmd::Parser do
|
|
32
32
|
tokens.should eql([:cue, 10, :name, [:cue, 3, :name]])
|
33
33
|
end
|
34
34
|
|
35
|
+
it "should parse nested commands with string literals" do
|
36
|
+
tokens = Qcmd::Parser.parse 'alias cue-rename (cue $1 name "Hello World")'
|
37
|
+
tokens.should eql([:alias, :'cue-rename', [:cue, :'$1', :name, 'Hello World']])
|
38
|
+
end
|
39
|
+
|
35
40
|
it "should parse alias commands" do
|
36
41
|
tokens = Qcmd::Parser.parse 'alias copy-name (cue 10 name (cue 3 name))'
|
37
42
|
tokens.should eql([:alias, :'copy-name', [:cue, 10, :name, [:cue, 3, :name]]])
|
@@ -56,4 +61,24 @@ describe Qcmd::Parser do
|
|
56
61
|
tokens = Qcmd::Parser.parse %[cue 1 name "this is (not good)"]
|
57
62
|
tokens.should eql([:cue, 1, :name, 'this is (not good)'])
|
58
63
|
end
|
64
|
+
|
65
|
+
## Generating
|
66
|
+
|
67
|
+
describe "generating expressions" do
|
68
|
+
it "should leave string literals intact" do
|
69
|
+
expression = Qcmd::Parser.generate([:cue, :'$1', :name, 'Hello World'])
|
70
|
+
expression.should eql('(cue $1 name "Hello World")')
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should handle nesting" do
|
74
|
+
expression = Qcmd::Parser.generate([:cue, :'$1', :name, [:cue, :'$2', :name]])
|
75
|
+
expression.should eql('(cue $1 name (cue $2 name))')
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should handle escaped double quotes" do
|
79
|
+
expression = Qcmd::Parser.generate([:go, 'word word', 10, -12.3, 'life "is good" yeah'])
|
80
|
+
expression.should eql('(go "word word" 10 -12.3 "life \"is good\" yeah")')
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
59
84
|
end
|
data/spec/unit/qcmd_spec.rb
CHANGED
@@ -3,14 +3,14 @@ require 'qcmd'
|
|
3
3
|
describe Qcmd do
|
4
4
|
# tests go here
|
5
5
|
it "should log debug messages when in verbose mode" do
|
6
|
-
Qcmd.should_receive(:log).with('hello')
|
6
|
+
Qcmd.should_receive(:log).with(:debug, 'hello')
|
7
7
|
Qcmd.verbose!
|
8
8
|
Qcmd.log_level.should eql(:debug)
|
9
9
|
Qcmd.debug 'hello'
|
10
10
|
end
|
11
11
|
|
12
12
|
it 'should not log debug messages when not in verbose mode' do
|
13
|
-
|
13
|
+
Kernel.should_not_receive(:puts)
|
14
14
|
Qcmd.quiet!
|
15
15
|
Qcmd.log_level.should eql(:warning)
|
16
16
|
Qcmd.debug 'hello'
|
@@ -21,7 +21,7 @@ describe Qcmd do
|
|
21
21
|
Qcmd.log_level.should eql(:debug)
|
22
22
|
|
23
23
|
Qcmd.while_quiet do
|
24
|
-
|
24
|
+
Kernel.should_not_receive(:puts)
|
25
25
|
Qcmd.log_level.should eql(:warning)
|
26
26
|
Qcmd.debug 'hello'
|
27
27
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: qcmd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: dnssd
|
@@ -132,6 +132,7 @@ extensions: []
|
|
132
132
|
extra_rdoc_files: []
|
133
133
|
files:
|
134
134
|
- .gitignore
|
135
|
+
- CHANGELOG.md
|
135
136
|
- Gemfile
|
136
137
|
- LICENSE.txt
|
137
138
|
- README.md
|