stf-client 0.2.6 → 0.3.0.pre.rc.12

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 7db448c2c0164d028021aa694aa0a726cee55941
4
- data.tar.gz: 5dca57695b59bfa254ced82f32ba0c8cc10489b4
2
+ SHA256:
3
+ metadata.gz: 52a4f2a68edf69cda4d74621215c184f14ea2fbf97d2c27948581f7dee93e838
4
+ data.tar.gz: 622083a2cd27df2e8b3ba9dd78ff79cf9054bdfe27049544816dc04fed77b3b6
5
5
  SHA512:
6
- metadata.gz: 597d3bd7df408f76a32f0a6b6f79534a450e0a8233df2e70893fdf7981cebee79c810bb9dadef5fa275d622d6062a4a6ad06abf50a21f5a5d388581ea1b055a6
7
- data.tar.gz: 762ca50470092120eeda8b84348f650a95da64406f8da2c75b1d1a1f68bef1a8be99d14768fb506d185c12d7e7591730d67730eb349902265a424d00ebbdf6b9
6
+ metadata.gz: 42f63749eb0a0f748b3a66051f3a409d92765d7b519627608b52e34a7241ae8b58b91a35dcc1c28ef61d18c482d18b0aebba88af50e52227cfd78fbdc4f6671d
7
+ data.tar.gz: 3a27762217fad2da4895997c33def45bd35044b3b4a0a3e17f48fa0434236b899ae058802c0fa1c6e9a110aae3ccfc4fd2f72f068360adbd8c47f04ab0085554
data/README.md CHANGED
@@ -48,7 +48,7 @@ COMMANDS
48
48
  connect - Search for a device available in STF and attach it to local adb server
49
49
  disconnect - Disconnect device(s) from local adb server and remove device(s) from user devices in STF
50
50
  help - Shows a list of commands or help for one command
51
- keys - Show avaliable keys for filtering
51
+ keys - Show available keys for filtering
52
52
  values - Show known values for the filtering key
53
53
 
54
54
  ENVIRONMENT VARIABLES
data/lib/di.rb CHANGED
@@ -9,6 +9,7 @@ require 'stf/interactor/stop_all_debug_sessions_interactor'
9
9
  require 'stf/interactor/remove_all_user_devices_interactor'
10
10
  require 'stf/interactor/get_keys_interactor'
11
11
  require 'stf/interactor/get_values_interactor'
12
+ require 'stf/validate/uri_validator'
12
13
 
13
14
  class DI
14
15
  class << self
@@ -60,6 +61,9 @@ class DI
60
61
  -> {Stf::RemoveAllUserDevicesInteractor.new},
61
62
  memoize: true)
62
63
 
64
+ c.register(:uri_validator,
65
+ -> {Stf::URIValidator.new},
66
+ memoize: true)
63
67
  end
64
68
 
65
69
  def [](what)
data/lib/stf/client.rb CHANGED
@@ -4,7 +4,6 @@ require 'ostruct'
4
4
 
5
5
  require 'stf/version'
6
6
  require 'stf/log/log'
7
- require 'stf/errors'
8
7
 
9
8
  module Stf
10
9
  class Client
@@ -3,8 +3,6 @@ require 'di'
3
3
 
4
4
  require 'stf/client'
5
5
  require 'stf/log/log'
6
- require 'stf/errors'
7
- require 'stf/model/session'
8
6
  require 'stf/model/device'
9
7
 
10
8
  module Stf
@@ -3,8 +3,6 @@ require 'ADB'
3
3
 
4
4
  require 'stf/client'
5
5
  require 'stf/log/log'
6
- require 'stf/errors'
7
- require 'stf/model/session'
8
6
  require 'stf/model/device'
9
7
 
10
8
  module Stf
@@ -2,8 +2,6 @@ require 'ADB'
2
2
 
3
3
  require 'stf/client'
4
4
  require 'stf/log/log'
5
- require 'stf/errors'
6
- require 'stf/model/session'
7
5
  require 'stf/model/device_list'
8
6
 
9
7
  module Stf
@@ -14,30 +12,34 @@ module Stf
14
12
 
15
13
  def execute(opts = {})
16
14
  all_flag = opts[:all]
17
- forever_flag = opts[:forever]
18
15
  nodaemon_flag = opts[:nodaemon]
19
16
  filter = opts[:filter]
20
17
  max_n = opts[:n].to_i > 0 ? opts[:n].to_i : 1
21
18
  start_timeout = opts[:starttime].to_i > 0 ? opts[:starttime].to_i : 120
22
19
  session = opts[:worktime].to_i > 0 ? opts[:session].to_i : 10800
23
- min_n = opts[:min].to_s.empty? ? (max_n + 1) / 2 : opts[:min].to_i
20
+ min_n = opts[:min].to_s.empty? ? (max_n + 1) / 2 : [opts[:min].to_i, max_n].min
21
+ healthcheck = opts[:health]
22
+ force_filter = opts[:forcefilter]
24
23
 
25
24
  DI[:demonizer].kill unless opts[:nokill]
26
25
 
27
- if filter
28
- DI[:stop_all_debug_sessions_interactor].execute(exceptFilter: filter)
29
- end
30
-
31
26
  wanted = nodaemon_flag ? max_n : min_n
32
27
 
33
28
  begin
34
- connect_loop(all_flag, wanted, filter, false, 5, start_timeout)
29
+ connect_loop(all_flag: all_flag,
30
+ wanted: wanted,
31
+ filter: filter,
32
+ force_filter: force_filter,
33
+ healthcheck: healthcheck,
34
+ delay: 5,
35
+ timeout: start_timeout)
36
+
35
37
  rescue SignalException => e
36
- logger.info "Caught signal #{e}"
38
+ logger.info "Caught signal \"#{e.message}\""
37
39
  DI[:stop_all_debug_sessions_interactor].execute
38
40
  return false
39
- rescue
40
- logger.info "Exception #{e} during initial connect loop"
41
+ rescue Exception => e
42
+ logger.info "Exception \"#{e.message}\" during initial connect loop"
41
43
  DI[:stop_all_debug_sessions_interactor].execute
42
44
  return false
43
45
  end
@@ -49,13 +51,14 @@ module Stf
49
51
 
50
52
  # will be daemon here
51
53
  DI[:demonizer].run do
52
- connect_loop(all_flag,
53
- max_n,
54
- filter,
55
- true,
56
- 30,
57
- session,
58
- forever_flag)
54
+ connect_loop(all_flag: all_flag,
55
+ wanted: max_n,
56
+ filter: filter,
57
+ force_filter: force_filter,
58
+ healthcheck: healthcheck,
59
+ daemon_mode: true,
60
+ delay: 30,
61
+ timeout: session)
59
62
 
60
63
  DI[:stop_all_debug_sessions_interactor].execute(byFilter: filter, nokill: true)
61
64
  end
@@ -63,74 +66,91 @@ module Stf
63
66
  return true
64
67
  end
65
68
 
66
- def connect_loop(all_flag, wanted, filter, infinite_flag, delay, timeout, forever_flag = false)
69
+ def connect_loop(all_flag: false,
70
+ wanted: 1,
71
+
72
+ filter: nil,
73
+ force_filter: false,
74
+ healthcheck: nil,
75
+
76
+ daemon_mode: false,
77
+ delay: 5,
78
+ timeout: 120)
67
79
  finish_time = Time.now + timeout
80
+ one_time_mode = !daemon_mode
68
81
 
69
82
  while true do
70
- cleanup_disconnected_devices(filter)
83
+ cleanup_disconnected_devices(filter, force_filter, healthcheck)
84
+
85
+ if one_time_mode && Time.now > finish_time
86
+ raise "Connect loop timeout reached"
87
+ end
71
88
 
72
- stf_devices = DeviceList.new(DI[:stf].get_devices)
73
- stf_devices = stf_devices.byFilter(filter) if filter
89
+ all_devices = DeviceList.new(DI[:stf].get_devices)
90
+ stf_devices = all_devices.select_ready_to_connect
91
+ stf_devices = stf_devices.by_filter(filter) if filter
92
+ stf_devices = stf_devices.select_healthy_for_connect(healthcheck) if healthcheck
74
93
 
75
94
  if all_flag
76
- batch = stf_devices.filterReadyToConnect.size
95
+ to_connect = stf_devices.size
77
96
  else
78
- connected = devices & stf_devices.asConnectUrlList
79
- batch = wanted - connected.size
97
+ connected = devices & all_devices.as_connect_url_list
98
+ to_connect = wanted - connected.size
80
99
  end
81
100
 
82
- if batch > 0
83
- n = connect(filter, all_flag, batch)
84
- break if n == batch && !infinite_flag
85
- elsif !infinite_flag
86
- break
101
+ return if one_time_mode && to_connect <= 0
102
+
103
+ if to_connect > 0
104
+ if stf_devices.empty?
105
+ logger.error 'There is no available devices with criteria ' + filter
106
+ else
107
+ random_device = stf_devices.asArray.sample
108
+ DI[:start_one_debug_session_interactor].execute(random_device)
109
+ next
110
+ end
87
111
  end
88
112
 
89
113
  sleep delay
90
-
91
- if !forever_flag || Time.now > finish_time
92
- raise "Connect loop timeout reached"
93
- end
94
114
  end
95
-
96
115
  end
97
116
 
98
117
  def count_connected_devices(filter)
99
118
  stf_devices = DeviceList.new(DI[:stf].get_user_devices)
100
- stf_devices = stf_devices.byFilter(filter) if filter
101
- connected = devices & stf_devices.asConnectUrlList
119
+ stf_devices = stf_devices.by_filter(filter) if filter
120
+ connected = devices & stf_devices.as_connect_url_list
102
121
  connected.size
103
122
  end
104
123
 
105
- def cleanup_disconnected_devices(filter)
124
+ def cleanup_disconnected_devices(filter, force_filter, healthcheck)
125
+ to_disconnect = []
106
126
  stf_devices = DeviceList.new(DI[:stf].get_user_devices)
107
- stf_devices = stf_devices.byFilter(filter) if filter
108
- connected = stf_devices.asConnectUrlList - devices
109
127
 
110
- connected.reject {|url| url.to_s.empty?}.each do |url|
111
- logger.info 'Cleanup the device ' + url.to_s
112
- DI[:stop_debug_session_interactor].execute(url)
128
+ if filter && force_filter
129
+ disconnect_because_filter = stf_devices.except_filter(filter).as_connect_url_list
130
+ unless disconnect_because_filter.empty?
131
+ logger.info 'will be disconnected by filter: ' + disconnect_because_filter.join(',')
132
+ to_disconnect += disconnect_because_filter
133
+ end
113
134
  end
114
- end
115
-
116
- def connect(filter, all_flag, wanted)
117
- devices = DeviceList.new(DI[:stf].get_devices)
118
- devices = devices.filterReadyToConnect
119
- devices = devices.byFilter(filter) if filter
120
135
 
121
- if devices.empty?
122
- logger.error 'There is no available devices with criteria ' + filter
123
- return 0
136
+ if healthcheck
137
+ disconnect_by_health = stf_devices.select_not_healthy(healthcheck).as_connect_url_list
138
+ unless disconnect_by_health.empty?
139
+ logger.info 'will be disconnected by health check: ' + disconnect_by_health.join(',')
140
+ to_disconnect += disconnect_by_health
141
+ end
124
142
  end
125
143
 
126
- n = 0
127
- devices.asArray.shuffle.each do |d|
128
- n += 1 if DI[:start_one_debug_session_interactor].execute(d)
129
- break if !all_flag && n >= wanted
144
+ dead_persons = stf_devices.as_connect_url_list - devices
145
+ unless dead_persons.empty?
146
+ logger.info 'will be disconnected because not present locally: ' + dead_persons.join(',')
147
+ to_disconnect += dead_persons
130
148
  end
131
149
 
132
- n
150
+ to_disconnect.reject {|url| url.to_s.empty?}.uniq.each do |url|
151
+ logger.info 'Cleanup the device ' + url.to_s
152
+ DI[:stop_debug_session_interactor].execute(url)
153
+ end
133
154
  end
134
-
135
155
  end
136
156
  end
@@ -3,8 +3,6 @@ require 'ADB'
3
3
 
4
4
  require 'stf/client'
5
5
  require 'stf/log/log'
6
- require 'stf/errors'
7
- require 'stf/model/session'
8
6
 
9
7
  module Stf
10
8
  class StartOneDebugSessionInteractor
@@ -40,23 +38,26 @@ module Stf
40
38
 
41
39
  return true
42
40
 
43
- rescue SignalException => e
44
- raise e
45
- rescue => e
41
+ rescue StandardError, SignalException => e
46
42
  begin
47
43
  # we will try clean anyway
48
44
  DI[:stf].remove_device serial
49
45
  if test ?d, '/custom-metrics'
50
- File.open('/custom-metrics/openstf_connect_fail', 'a') do
51
- |f| f.write("openstf_connect_fail #{serial} #{Time.now.to_i}")
46
+ File.open('/custom-metrics/openstf_connect_fail', 'a') do |f|
47
+ message = (!e.nil? || !e.message.nil?) ? e.message : ""
48
+ f.write("openstf_connect_fail,reason=\"#{escape(message)}\",serial=\"#{escape(serial)}\" count=1i #{Time.now.to_i}\n")
52
49
  end
53
50
  end
54
51
  rescue
55
52
  end
56
53
 
57
- logger.error "Failed to connect to #{serial}: " + e.message
54
+ logger.error "Failed to connect to #{serial}: " + e&.message
58
55
  return false
59
56
  end
60
57
  end
58
+
59
+ def escape(s)
60
+ s.gsub(/["]/, '\"').gsub(/[ ]/, '\ ').gsub(/[=]/, '\=').gsub(/[,]/, '\,')
61
+ end
61
62
  end
62
63
  end
@@ -3,7 +3,6 @@ require 'ADB'
3
3
 
4
4
  require 'stf/client'
5
5
  require 'stf/log/log'
6
- require 'stf/errors'
7
6
  require 'stf/interactor/stop_debug_session_interactor'
8
7
  require 'stf/model/device_list'
9
8
 
@@ -13,19 +12,14 @@ module Stf
13
12
  include ADB
14
13
 
15
14
  # byFilter:
16
- # exceptFilter:
17
15
  def execute(options = {})
18
16
  DI[:demonizer].kill unless options[:nokill]
19
17
 
20
18
  stf_devices = DeviceList.new(DI[:stf].get_user_devices)
21
19
 
22
- stf_devices = stf_devices.byFilter options[:byFilter] if options[:byFilter]
23
- stf_devices = stf_devices.exceptFilter options[:exceptFilter] if options[:exceptFilter]
20
+ stf_devices = stf_devices.by_filter options[:byFilter] if options[:byFilter]
24
21
 
25
- connected_devices = devices()
26
- remote_devices = stf_devices.asConnectUrlList
27
-
28
- pending_disconnect = connected_devices & remote_devices
22
+ pending_disconnect = stf_devices.as_connect_url_list
29
23
 
30
24
  pending_disconnect.each {|d| DI[:stop_debug_session_interactor].execute d}
31
25
  end
@@ -3,7 +3,6 @@ require 'ADB'
3
3
 
4
4
  require 'stf/client'
5
5
  require 'stf/log/log'
6
- require 'stf/errors'
7
6
 
8
7
  module Stf
9
8
  class StopDebugSessionInteractor
@@ -8,7 +8,33 @@ module Stf
8
8
  getKeysNextLevel('', self)
9
9
  end
10
10
 
11
- def checkFilter(filter)
11
+ # more pessimistic decision
12
+ def healthy_for_connect?(pattern)
13
+ return true if pattern.nil?
14
+ health = healthy?(pattern)
15
+ ppp = pattern.split(',')
16
+ ppp.each do |p|
17
+ health &&= getValue('battery.temp').to_i < 30 if ['t', 'temp', 'temperature'].include? p
18
+ health &&= getValue('battery.level').to_f > 30.0 if ['b', 'batt', 'battery'].include? p
19
+ end
20
+ health
21
+ end
22
+
23
+ def healthy?(pattern)
24
+ return true if pattern.nil?
25
+ ppp = pattern.split(',')
26
+ health = true
27
+ ppp.each do |p|
28
+ health &&= getValue('battery.temp').to_i < 32 if ['t', 'temp', 'temperature'].include? p
29
+ health &&= getValue('battery.level').to_f > 20.0 if ['b', 'batt', 'battery'].include? p
30
+ health &&= getValue('network.connected') if ['n', 'net', 'network'].include? p
31
+ health &&= getValue('network.type') == 'VPN' if ['vpn'].include? p
32
+ health &&= getValue('network.type') == 'WIFI' if ['wifi'].include? p
33
+ end
34
+ health
35
+ end
36
+
37
+ def checkFilter?(filter)
12
38
  return true if filter.nil?
13
39
  key, value = filter.split(':', 2)
14
40
  getValue(key) == value
@@ -11,20 +11,49 @@ module Stf
11
11
  end
12
12
  end
13
13
 
14
- def byFilter(filter)
15
- filter ? select {|d| d.checkFilter(filter)} : Array.new
14
+ def by_filter(filter)
15
+ filter ? select {|d| d.checkFilter?(filter)} : []
16
16
  end
17
17
 
18
- def exceptFilter(filter)
19
- filter ? reject {|d| d.checkFilter(filter)} : this
18
+ def except_filter(filter)
19
+ filter ? reject {|d| d.checkFilter?(filter)} : this
20
20
  end
21
21
 
22
- def filterReadyToConnect
23
- select {|d| d.ready == true && d.present == true && d.usage.nil? }
22
+ def select_healthy(pattern)
23
+ pattern ? select { |d| d.healthy?(pattern) } : this
24
24
  end
25
25
 
26
- def asConnectUrlList
27
- @devices.map {|d| d.remoteConnectUrl}
26
+ # more pessimistic than healthy()
27
+ def select_healthy_for_connect(pattern)
28
+ pattern ? select { |d| d.healthy_for_connect?(pattern) } : this
29
+ end
30
+
31
+ def select_not_healthy(pattern)
32
+ pattern ? reject { |d| d.healthy?(pattern) } : []
33
+ end
34
+
35
+ def select_ready_to_connect
36
+ # https://github.com/openstf/stf/blob/93d9d7fe859bb7ca71669f375d841d94fa47d751/lib/wire/wire.proto#L170
37
+ # enum DeviceStatus {
38
+ # OFFLINE = 1;
39
+ # UNAUTHORIZED = 2;
40
+ # ONLINE = 3;
41
+ # CONNECTING = 4;
42
+ # AUTHORIZING = 5;
43
+ # }
44
+ #
45
+ # https://github.com/openstf/stf/blob/93d9d7fe859bb7ca71669f375d841d94fa47d751/res/app/components/stf/device/enhance-device/enhance-device-service.js
46
+ select {|d|
47
+ d.present == true &&
48
+ d.status == 3 &&
49
+ d.ready == true &&
50
+ d.using == false &&
51
+ d.owner.nil?
52
+ }
53
+ end
54
+
55
+ def as_connect_url_list
56
+ @devices.map {|d| d.remoteConnectUrl}.reject { |c| c.nil? || c.empty? }
28
57
  end
29
58
 
30
59
  def select
@@ -32,7 +61,6 @@ module Stf
32
61
  end
33
62
 
34
63
  def reject
35
- # DeviceList.new(@devices.reject {|d| yield(d)})
36
64
  DeviceList.new(@devices.select {|d| !yield(d)})
37
65
  end
38
66
 
@@ -48,4 +76,4 @@ module Stf
48
76
  @devices
49
77
  end
50
78
  end
51
- end
79
+ end
@@ -0,0 +1,7 @@
1
+ module Stf
2
+ class URIValidator
3
+ def validate(uri)
4
+ return (uri =~ /\A#{URI::regexp(%w(http https))}\z/) != nil
5
+ end
6
+ end
7
+ end
data/lib/stf/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Stf
2
- VERSION = '0.2.6'
2
+ VERSION = '0.3.0-rc.12'
3
3
  end
data/lib/stf/view/cli.rb CHANGED
@@ -7,7 +7,7 @@ module Stf
7
7
 
8
8
  extend self
9
9
 
10
- program_desc 'Smartphone Test Lab client'
10
+ program_desc "Smartphone Test Lab client (version #{Stf::VERSION})"
11
11
 
12
12
  desc 'Be verbose'
13
13
  switch [:v, :verbose]
@@ -35,6 +35,9 @@ module Stf
35
35
  Log::verbose(global_options[:verbose])
36
36
 
37
37
  DI.init(global_options)
38
+
39
+ help_now!('Valid STF url is required, e.g. http(s)://openstf.local') if !DI[:uri_validator].validate(global_options[:url])
40
+ true
38
41
  end
39
42
 
40
43
  desc 'Search for a device available in STF and attach it to local adb server'
@@ -47,12 +50,14 @@ module Stf
47
50
  c.flag [:min]
48
51
  c.desc 'Filter key:value for devices'
49
52
  c.flag [:f, :filter]
53
+ c.desc 'Force filter check for connected devices'
54
+ c.switch [:forcefilter, :ff]
55
+ c.desc 'Check selected health parameters, could be any of the: battery,temperature,network,vpn,wifi'
56
+ c.flag [:health]
50
57
  c.desc 'Maximum session duration in seconds, 10800 (3h) by default'
51
58
  c.flag [:session]
52
59
  c.desc 'Maximum time to connect minimal quantity of devices in seconds, 120 (2m) by default'
53
60
  c.flag [:starttime]
54
- c.desc 'Maintain connactions before explicitly kill'
55
- c.switch [:forever]
56
61
  c.desc 'Do not start daemon'
57
62
  c.switch [:nodaemon]
58
63
 
@@ -63,7 +68,7 @@ module Stf
63
68
  end
64
69
  end
65
70
 
66
- desc 'Show avaliable keys for filtering'
71
+ desc 'Show available keys for filtering'
67
72
  command :keys do |c|
68
73
  c.action {puts DI[:get_keys_interactor].execute}
69
74
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stf-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.3.0.pre.rc.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Malinskiy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-28 00:00:00.000000000 Z
11
+ date: 2018-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: gli
@@ -199,7 +199,6 @@ files:
199
199
  - lib/di.rb
200
200
  - lib/stf.rb
201
201
  - lib/stf/client.rb
202
- - lib/stf/errors.rb
203
202
  - lib/stf/interactor/get_keys_interactor.rb
204
203
  - lib/stf/interactor/get_values_interactor.rb
205
204
  - lib/stf/interactor/remove_all_user_devices_interactor.rb
@@ -210,8 +209,8 @@ files:
210
209
  - lib/stf/log/log.rb
211
210
  - lib/stf/model/device.rb
212
211
  - lib/stf/model/device_list.rb
213
- - lib/stf/model/session.rb
214
212
  - lib/stf/system/demonizer.rb
213
+ - lib/stf/validate/uri_validator.rb
215
214
  - lib/stf/version.rb
216
215
  - lib/stf/view/cli.rb
217
216
  - stf-client.gemspec
@@ -230,12 +229,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
230
229
  version: '0'
231
230
  required_rubygems_version: !ruby/object:Gem::Requirement
232
231
  requirements:
233
- - - ">="
232
+ - - ">"
234
233
  - !ruby/object:Gem::Version
235
- version: '0'
234
+ version: 1.3.1
236
235
  requirements: []
237
236
  rubyforge_project:
238
- rubygems_version: 2.6.13
237
+ rubygems_version: 2.7.7
239
238
  signing_key:
240
239
  specification_version: 4
241
240
  summary: Request devices from Smartphone Test Farm for adb connection
data/lib/stf/errors.rb DELETED
@@ -1,7 +0,0 @@
1
- module Stf
2
- class DeviceNotAvailableError < StandardError
3
- def message
4
- 'Device not available'
5
- end
6
- end
7
- end
@@ -1,12 +0,0 @@
1
- module Stf
2
- class Session
3
-
4
- attr_accessor :serial, :url
5
-
6
- def initialize(serial, url)
7
- @serial = serial
8
- @url = url
9
- end
10
-
11
- end
12
- end