qcmd 0.1.8 → 0.1.9
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/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
|