18
18
require 'fileutils'
19
19
require 'time'
20
20
21
+ require_relative "../lib/sonicpi/osc/osc"
22
+ require_relative "../lib/sonicpi/promise"
23
+
21
24
# Make sure vendored tomlrb lib is on the Ruby path so it can be required
22
25
Dir [ "#{ File . expand_path ( "../../vendor" , __FILE__ ) } /*/lib/" ] . each do |vendor_lib |
23
26
$:. unshift vendor_lib
@@ -148,7 +151,15 @@ def initialize
148
151
@scsynth_booter = ScsynthBooter . new ( ports )
149
152
150
153
Util . log "Booting Tau"
151
- @tau_booter = TauBooter . new ( ports )
154
+ begin
155
+ @tau_booter = TauBooter . new ( ports )
156
+ rescue StandardError => e
157
+ Util . log "Oh no, something went wrong booting Tau"
158
+ Util . log "Error Class: #{ e . class } "
159
+ Util . log "Error Message: #{ e . message } "
160
+ Util . log "Error Backtrace: #{ e . backtrace . inspect } "
161
+ end
162
+
152
163
153
164
Util . log "Booting Spider Server"
154
165
@spider_booter = SpiderBooter . new ( ports )
@@ -193,7 +204,7 @@ def spawn_zombie_kill_switch(port_num, &blk)
193
204
Util . log "Waiting for GUI on port #{ port_num } ...."
194
205
195
206
unless IO . select ( [ keep_alive_server ] , nil , nil , connect_timeout )
196
- Util . log "Error. Unable to connect to GUI process on TCP port #{ port_num } "
207
+ Util . log "Critical Error. Unable to connect to GUI process on TCP port #{ port_num } "
197
208
@safe_exit . exit
198
209
end
199
210
@@ -215,9 +226,10 @@ def spawn_zombie_kill_switch(port_num, &blk)
215
226
Util . log "Error Backtrace: #{ e . backtrace . inspect } "
216
227
end
217
228
218
- Util . log "Shutting down.. ."
229
+ Util . log "Critical Error. Lost comms with GUI ."
219
230
client . close if client
220
231
keep_alive_server . close if keep_alive_server
232
+
221
233
@safe_exit . exit
222
234
end
223
235
end
@@ -371,16 +383,23 @@ def idempotent_exit_cleanup
371
383
end
372
384
end
373
385
374
-
375
-
376
386
class ProcessBooter
377
387
attr_reader :pid , :args , :cmd
378
388
def initialize ( cmd , args , log_path )
389
+ @pid = nil
379
390
@args = args . map { |el | el . to_s }
380
391
@cmd = cmd
381
392
@log_file = File . open ( log_path , 'a' )
382
393
raise "Unable to create log file at path: #{ log_path } " unless @log_file
383
- boot
394
+ begin
395
+ boot
396
+ rescue StandardError => e
397
+ Util . log "Error: something went wrong booting process: #{ cmd } , #{ args } , #{ log_path } "
398
+ Util . log "Error Class: #{ e . class } "
399
+ Util . log "Error Message: #{ e . message } "
400
+ Util . log "Error Backtrace: #{ e . backtrace . inspect } "
401
+ @log_file . close if @log_file
402
+ end
384
403
end
385
404
386
405
def inspect
@@ -499,21 +518,72 @@ def initialize(ports)
499
518
500
519
class TauBooter < ProcessBooter
501
520
def initialize ( ports )
521
+ @tau_pid = Promise . new
502
522
enabled = true
503
523
internal = false
504
524
midi_enabled = true
505
525
link_enabled = true
506
526
in_port = ports [ "osc-cues" ]
507
527
api_port = ports [ "tau" ]
508
528
spider_port = ports [ "listen-to-tau" ]
529
+ daemon_port = ports [ "daemon-listen-to-tau" ]
530
+
531
+ tau_comms = TCPServer . new "127.0.0.1" , daemon_port
532
+ osc_decoder = SonicPi ::OSC ::OscDecode . new
533
+
534
+ Thread . new do
535
+ client = nil
536
+
537
+ @tau_comms_thread = Thread . new do
538
+ Util . log "-----> Accepting incoming connection from Tau"
539
+ client = tau_comms . accept # Wait for a client to connect
540
+ Util . log "-----> Connection accepted"
541
+
542
+ recv_osc = lambda do
543
+ size_str = client . recvfrom ( 4 , Socket ::MSG_WAITALL ) [ 0 ] . chomp
544
+ size = size_str . unpack ( 'N' ) [ 0 ]
545
+ raise "Critical Error: Bad TCP OSC size received from Tau #{ size_str } " unless size
546
+ data_raw = client . recvfrom ( size , Socket ::MSG_WAITALL ) [ 0 ] . chomp
547
+ osc_decoder . decode_single_message ( data_raw )
548
+ end
549
+
550
+
551
+ begin
552
+ data = recv_osc . call
553
+ tau_pid = Integer ( data [ 1 ] [ 0 ] )
554
+ @tau_pid . deliver! ( tau_pid )
555
+ Util . log "-----> Tau Pid: #{ tau_pid } "
556
+
557
+ loop do
558
+ data = recv_osc . call
559
+ Util . log "Received OSC from Tau: #{ data } "
560
+ # In the future this is where we can handle any callbacks from Tau
561
+ end
562
+ rescue Errno ::ECONNRESET
563
+ Util . log "Tau closed TCP connection"
564
+ rescue StandardError => e
565
+ Util . log "Critical Error, whilst communicating with Tau:"
566
+ Util . log "Error Class: #{ e . class } "
567
+ Util . log "Error Message: #{ e . message } "
568
+ Util . log "Error Backtrace: #{ e . backtrace . inspect } "
569
+ end
570
+ end
571
+
572
+ @tau_comms_thread . join
573
+ client . close if client
574
+ tau_comms . close if tau_comms
575
+ end
509
576
510
- args = [ enabled ,
577
+ args = [
578
+ enabled ,
511
579
internal ,
512
580
midi_enabled ,
513
581
link_enabled ,
514
582
in_port ,
515
583
api_port ,
516
- spider_port ]
584
+ spider_port ,
585
+ daemon_port
586
+ ]
517
587
518
588
if Util . os == :windows
519
589
cmd = Paths . mix_release_boot_path
@@ -524,6 +594,20 @@ def initialize(ports)
524
594
525
595
super ( cmd , args , Paths . tau_log_path )
526
596
end
597
+
598
+ def process_running?
599
+ @tau_comms_thread . alive?
600
+ end
601
+
602
+ def kill
603
+ @tau_comms_thread . kill
604
+ @pid = @tau_pid . get
605
+ super
606
+ end
607
+
608
+ def wait
609
+ @tau_comms_thread . join
610
+ end
527
611
end
528
612
529
613
@@ -749,6 +833,8 @@ class PortDiscovery
749
833
# Port which the Ruby server listens to messages back from the Tau server
750
834
"listen-to-tau" => :dynamic ,
751
835
836
+ "daemon-listen-to-tau" => :dynamic ,
837
+
752
838
# Port which the server uses to communicate via websockets
753
839
# (This is currently unused.)
754
840
"websocket" => :dynamic
@@ -777,6 +863,7 @@ def initialize(safe_exit)
777
863
"osc-cues" ,
778
864
"tau" ,
779
865
"listen-to-tau" ,
866
+ "daemon-listen-to-tau" ,
780
867
"websocket" ] . inject ( { } ) do |res , port_name |
781
868
782
869
default = nil
@@ -788,15 +875,19 @@ def initialize(safe_exit)
788
875
elsif default == :paired
789
876
port = res [ port_name [ 1 ] ]
790
877
else
791
- port = default
878
+ if check_port ( default )
879
+ port = default
880
+ else
881
+ port = find_free_port
882
+ end
792
883
end
793
884
res [ port_name [ 0 ] ] = port . to_i
794
885
else
795
886
default = PORT_CONFIG [ port_name ]
796
887
if default == :dynamic
797
888
port = find_free_port
798
889
elsif default == :paired
799
- raise " Invalid port default for port: #{ port_name } . This port can not be paired."
890
+ Util . log "Critical error: Invalid port default for port: #{ port_name } . This port can not be paired."
800
891
@safe_exit . exit
801
892
else
802
893
port = default
@@ -816,6 +907,7 @@ def check_port(port)
816
907
begin
817
908
socket = UDPSocket . new
818
909
socket . bind ( '127.0.0.1' , port )
910
+ Util . log "checked port #{ port } , #{ socket } "
819
911
socket . close
820
912
available = true
821
913
rescue StandardError
@@ -827,6 +919,7 @@ def check_port(port)
827
919
def find_free_port
828
920
while !check_port ( @last_free_port += 1 )
829
921
if @last_free_port > 65535
922
+ Util . log "Critical error: Unable to find a free port."
830
923
@safe_exit . exit
831
924
end
832
925
end
0 commit comments