Class: Oschii::Cloud

Inherits:
Object
  • Object
show all
Defined in:
lib/oschii/cloud.rb

Constant Summary collapse

PING_PORT =
3333
PING_ADDR =
'/hello_oschii'
RESPONSE_PORT =
3333
RESPONSE_ADDR =
'/i_am_oschii'
HTTP_PORT =
8000
BASE_IP_FILE =
'.base_ip'.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(silent: false) ⇒ Cloud

Returns a new instance of Cloud.



16
17
18
19
20
21
22
23
24
# File 'lib/oschii/cloud.rb', line 16

def initialize(silent: false)
  @osc_server = OSC::EMServer.new(RESPONSE_PORT)
  @http_server = WEBrick::HTTPServer.new(Port: HTTP_PORT)

  @devices = {}
  @silent = silent

  start_listening
end

Instance Attribute Details

#devicesObject (readonly)

Returns the value of attribute devices.



26
27
28
# File 'lib/oschii/cloud.rb', line 26

def devices
  @devices
end

#http_serverObject (readonly)

Returns the value of attribute http_server.



26
27
28
# File 'lib/oschii/cloud.rb', line 26

def http_server
  @http_server
end

#monitorObject (readonly)

Returns the value of attribute monitor.



122
123
124
# File 'lib/oschii/cloud.rb', line 122

def monitor
  @monitor
end

#osc_serverObject (readonly)

Returns the value of attribute osc_server.



26
27
28
# File 'lib/oschii/cloud.rb', line 26

def osc_server
  @osc_server
end

#silentObject (readonly)

Returns the value of attribute silent.



26
27
28
# File 'lib/oschii/cloud.rb', line 26

def silent
  @silent
end

Instance Method Details

#base_ipObject



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/oschii/cloud.rb', line 156

def base_ip
  return @base_ip if @base_ip

  ip = Socket.ip_address_list.detect{|intf| intf.ipv4_private?}

  addr = ip.ip_address.to_s

  puts "Local IP: #{addr}"

  @base_ip = addr.split('.')[0..-2].join('.')

  # if File.exists? BASE_IP_FILE
  #   @base_ip = File.read(BASE_IP_FILE).strip
  # else
  #   @base_ip = '192.168.1'
  #   File.write BASE_IP_FILE, @base_ip
  # end
  # @base_ip = @base_ip[0..-2] if @base_ip[-1] == '.'
  # @base_ip
end

#base_ip=(value) ⇒ Object



151
152
153
154
# File 'lib/oschii/cloud.rb', line 151

def base_ip=(value)
  File.write BASE_IP_FILE, value unless value.nil?
  @base_ip = value
end

#capture_osc(address, simple: true, &block) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/oschii/cloud.rb', line 136

def capture_osc(address, simple: true, &block)
  address = "/#{address}" if address[0] != '/'

  osc_server.add_method address do |message|
    value = simple ? message.to_a.first&.to_i : message.to_a
    if block_given?
      yield value
    else
      puts "--> OSC #{address} [ #{value} ]"
    end
  end
end

#find_devices(echo: false) ⇒ Object



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/oschii/cloud.rb', line 177

def find_devices(echo: false)
  @devices = {}
  saved = saved_devices
  saved.each do |ip|
    begin
      print "\r#{ip}" if echo

      client = OSC::Client.new(ip, PING_PORT)
      client.send(OSC::Message.new(PING_ADDR, 1))
    rescue => e
      # puts e.message
    end
  end

  Thread.new do
    (1..254).each do |i|
      begin
        ip = "#{base_ip}.#{i}"

        next if saved.include? ip

        print "\r#{ip}" if echo

        client = OSC::Client.new(ip, PING_PORT)
        client.send(OSC::Message.new(PING_ADDR, 1))

        sleep 0.08
      rescue => e
        # puts e.message
      end
    end
  end

  puts if echo
end

#find_serial(port = nil) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/oschii/cloud.rb', line 28

def find_serial(port = nil)
  return ::Oschii::SerialDevice.new(port) if port

  puts "\n~~> Scanning Serial Ports...."

  9.times do |i|
    port = "/dev/ttyUSB#{i}"
    print "\n~~> #{port}: "
    begin
      node = ::Oschii::SerialDevice.new(port)
      name = "serial_#{node.name.downcase}".to_sym
      if (existing_node = devices[name.downcase.to_sym])
        existing_node.refresh
      else
        devices[name] = node
        node.refresh
      end
      puts "'#{node.name}'"
    rescue ::Oschii::DeviceUnavailable => e
      puts e.message
    end
  end

  print_devices

  self
end

#get(name) ⇒ Object



265
266
267
# File 'lib/oschii/cloud.rb', line 265

def get(name)
  devices[name.to_s.downcase.to_sym]
end

#inspectObject



269
270
271
# File 'lib/oschii/cloud.rb', line 269

def inspect
  "<#{self.class.name} #{local_ip_address} devices: #{devices.size}>"
end

#local_ip_addressObject



273
274
275
276
# File 'lib/oschii/cloud.rb', line 273

def local_ip_address
  ip = Socket.ip_address_list.detect { |intf| intf.ipv4_private? }
  ip.ip_address
end

#monitor_osc(*address_list, max: 100) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
# File 'lib/oschii/cloud.rb', line 124

def monitor_osc(*address_list, max: 100)
  @monitor ||= OscMonitor.new self

  address_list = [address_list] unless address_list.is_a?(Array)

  address_list.each do |address|
    monitor.add address, max: max
  end

  monitor.run
end

#populate(echo: false) ⇒ Object



213
214
215
216
217
218
219
220
221
222
223
# File 'lib/oschii/cloud.rb', line 213

def populate(echo: false)
  find_devices(echo: echo)

  start_waiting = Time.now
  while Time.now - start_waiting < 3
    sleep 0.2
  end

  print_devices unless silent
  self
end


225
226
227
228
229
230
# File 'lib/oschii/cloud.rb', line 225

def print_devices
  puts
  puts self.to_s
  puts
  puts
end

#restartObject



116
117
118
119
120
# File 'lib/oschii/cloud.rb', line 116

def restart
  devices.each do |_name, oschii|
    Thread.new { oschii.restart }
  end
end

#save_device(ip) ⇒ Object



286
287
288
289
290
291
292
# File 'lib/oschii/cloud.rb', line 286

def save_device(ip)
  return if saved_devices.include? ip

  @saved_devices << ip

  File.write('.saved_devices', @saved_devices.join("\n"))
end

#saved_devicesObject



278
279
280
281
282
283
284
# File 'lib/oschii/cloud.rb', line 278

def saved_devices
  @saved_devices ||= if File.exists? '.saved_devices'
    File.read('.saved_devices').split("\n")
  else
    []
  end
end

#start_listeningObject



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/oschii/cloud.rb', line 56

def start_listening
  osc_server.add_method RESPONSE_ADDR do |message|
    payload = message.to_a.first
    begin
      json = JSON.parse(payload)
      name = json['name']
      ip = json['ip']
    rescue JSON::ParserError
      name = payload.split(':').last.strip
      ip = message.ip_address
    end

    if (device = devices[name.downcase.to_sym])
      puts "\n==> '#{name}' is back\n" unless silent
      device.refresh
    else
      begin
        device = ::Oschii::HttpDevice.new(ip)
        # add_http_handlers device
        puts "\n==> '#{name}' connected @ #{device.ip_address}\n" unless silent
        devices[name.downcase.to_sym] = device
      rescue Errno::ECONNREFUSED => e
        puts "\n!!! Failed connecting to #{name} on #{ip}"
      end
    end
    save_device device.ip
  end

  Thread.new do
    puts "~~> Starting OSC Server on port #{RESPONSE_PORT}" unless silent
    osc_server.run
  end

  trap 'INT' do http_server.shutdown end

  Thread.new do
    puts "~~> Starting HTTP Server on port #{HTTP_PORT}" unless silent
    http_server.start
  end

  http_server.mount '/api/devices', GetDevices, self
  http_server.mount '/api/refresh', RefreshCloud, self
end

#to_sObject



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/oschii/cloud.rb', line 232

def to_s
  return '' if devices.empty?

  result = ''

  conn_width = 0
  name_width = 0
  devices.each do |name, device|
    if device.is_a? ::Oschii::SerialDevice
      conn_width = device.serial_port.size if device.serial_port.size > conn_width
    elsif device.is_a? ::Oschii::HttpDevice
      conn_width = device.ip.size if device.ip.size > conn_width
    end
    name_width = name.size if name.size > name_width
  end

  sorted = devices.to_a.sort_by { |name, _node| name }.to_h

  result += "\e[1m#{'Name'.ljust(name_width + 3)}#{'Connection'.ljust(conn_width + 3)}#{'Version'}\e[22m"
  sorted.each do |_name, device|
    connection = if device.is_a? ::Oschii::SerialDevice
                   device.serial.ljust(conn_width)
                 elsif device.is_a? ::Oschii::HttpDevice
                   device.ip.ljust(conn_width)
                 else
                   ""
                 end
    result += "\n#{device.name.ljust(name_width)}   #{connection}   #{device.version}"
  end
  result += "\n"
  result
end

#wait_for(oschii_name, timeout: 300) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/oschii/cloud.rb', line 100

def wait_for(oschii_name, timeout: 300)
  print "==> Waiting for '#{oschii_name}'.... "
  find_devices
  oschii = nil
  started = Time.now
  while oschii.nil? && Time.now - started <= timeout
    oschii = get oschii_name
  end
  if oschii
    puts 'FOUND :D'
  else
    puts 'NOT FOUND :('
  end
  oschii
end