procon_bypass_man 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +19 -6
  3. data/.github/workflows/gitleacks.yml +1 -1
  4. data/.rubocop.yml +4 -0
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG.md +15 -0
  7. data/Gemfile +6 -3
  8. data/Gemfile.lock +5 -3
  9. data/README.md +28 -41
  10. data/Steepfile +0 -1
  11. data/bin/generate_default_app +10 -0
  12. data/docs/getting_started.md +102 -28
  13. data/docs/setup_raspi_by_mitamae.md +43 -2
  14. data/lib/procon_bypass_man/background/job_performer.rb +1 -2
  15. data/lib/procon_bypass_man/background/job_queue.rb +50 -0
  16. data/lib/procon_bypass_man/background/jobs/base_job.rb +1 -1
  17. data/lib/procon_bypass_man/background/jobs/concerns/has_external_api_setting.rb +2 -2
  18. data/lib/procon_bypass_man/background/jobs/concerns/job_performable.rb +16 -0
  19. data/lib/procon_bypass_man/background/jobs/post_completed_remote_macro_job.rb +1 -1
  20. data/lib/procon_bypass_man/background/jobs/report_boot_job.rb +1 -1
  21. data/lib/procon_bypass_man/background/jobs/report_completed_upgrade_pbm_job.rb +1 -1
  22. data/lib/procon_bypass_man/background/jobs/report_error_job.rb +1 -1
  23. data/lib/procon_bypass_man/background/jobs/report_error_reload_config_job.rb +1 -1
  24. data/lib/procon_bypass_man/background/jobs/report_load_config_job.rb +1 -1
  25. data/lib/procon_bypass_man/background/jobs/report_procon_performance_measurements_job.rb +43 -0
  26. data/lib/procon_bypass_man/background/jobs/report_reload_config_job.rb +1 -1
  27. data/lib/procon_bypass_man/background/jobs/report_start_reboot_job.rb +1 -1
  28. data/lib/procon_bypass_man/background/jobs/sync_device_stats_job.rb +1 -1
  29. data/lib/procon_bypass_man/background.rb +3 -4
  30. data/lib/procon_bypass_man/bypass/bypass_command.rb +16 -20
  31. data/lib/procon_bypass_man/bypass/bypass_value.rb +6 -0
  32. data/lib/procon_bypass_man/bypass/procon_to_switch.rb +99 -0
  33. data/lib/procon_bypass_man/bypass/switch_to_procon.rb +66 -0
  34. data/lib/procon_bypass_man/bypass.rb +5 -109
  35. data/lib/procon_bypass_man/commands/print_boot_message_command.rb +0 -2
  36. data/lib/procon_bypass_man/configuration.rb +18 -39
  37. data/lib/procon_bypass_man/device_connection/procon_setting_overrider.rb +12 -3
  38. data/lib/procon_bypass_man/procon/analog_stick_manipulator.rb +2 -0
  39. data/lib/procon_bypass_man/procon/layer_changer.rb +2 -0
  40. data/lib/procon_bypass_man/procon/macro.rb +3 -1
  41. data/lib/procon_bypass_man/procon/macro_builder.rb +2 -0
  42. data/lib/procon_bypass_man/procon/macro_registry.rb +2 -0
  43. data/lib/procon_bypass_man/procon/performance_measurement/last_bypass_at.rb +17 -0
  44. data/lib/procon_bypass_man/procon/performance_measurement/measurement_collection.rb +9 -0
  45. data/lib/procon_bypass_man/procon/performance_measurement/measurements_summarizer.rb +101 -0
  46. data/lib/procon_bypass_man/procon/performance_measurement/procon_performance_span_transfer_job.rb +8 -0
  47. data/lib/procon_bypass_man/procon/performance_measurement/queue_over_process.rb +38 -0
  48. data/lib/procon_bypass_man/procon/performance_measurement/span_queue.rb +42 -0
  49. data/lib/procon_bypass_man/procon/performance_measurement/span_transfer_buffer.rb +39 -0
  50. data/lib/procon_bypass_man/procon/performance_measurement.rb +110 -0
  51. data/lib/procon_bypass_man/procon/suppress_rumble.rb +2 -0
  52. data/lib/procon_bypass_man/procon/user_operation.rb +2 -0
  53. data/lib/procon_bypass_man/procon/value_objects/analog_stick.rb +2 -0
  54. data/lib/procon_bypass_man/procon/value_objects/binary/inbound_procon_binary.rb +2 -0
  55. data/lib/procon_bypass_man/procon/value_objects/binary/processing_procon_binary.rb +2 -0
  56. data/lib/procon_bypass_man/procon/value_objects/rumble_binary.rb +2 -0
  57. data/lib/procon_bypass_man/procon.rb +26 -7
  58. data/lib/procon_bypass_man/procon_display/bypass_hook.rb +12 -0
  59. data/lib/procon_bypass_man/procon_display.rb +1 -0
  60. data/lib/procon_bypass_man/remote_macro/queue_over_process.rb +26 -44
  61. data/lib/procon_bypass_man/remote_macro/remote_macro_object.rb +22 -24
  62. data/lib/procon_bypass_man/remote_macro/remote_macro_receiver.rb +1 -1
  63. data/lib/procon_bypass_man/remote_macro/remote_macro_sender.rb +1 -1
  64. data/lib/procon_bypass_man/remote_macro/task.rb +1 -5
  65. data/lib/procon_bypass_man/remote_macro/task_queue.rb +6 -10
  66. data/lib/procon_bypass_man/remote_macro.rb +2 -0
  67. data/lib/procon_bypass_man/remote_pbm_action/commands/update_remote_pbm_action_status_command.rb +1 -1
  68. data/lib/procon_bypass_man/remote_pbm_action.rb +2 -0
  69. data/lib/procon_bypass_man/runner.rb +4 -10
  70. data/lib/procon_bypass_man/scheduler.rb +15 -6
  71. data/lib/procon_bypass_man/support/callbacks.rb +79 -34
  72. data/lib/procon_bypass_man/support/can_over_process.rb +60 -0
  73. data/lib/procon_bypass_man/support/gc.rb +8 -0
  74. data/lib/procon_bypass_man/support/http_client.rb +12 -6
  75. data/lib/procon_bypass_man/support/load_agv.rb +20 -0
  76. data/lib/procon_bypass_man/support/procon_performance_http_client.rb +7 -0
  77. data/lib/procon_bypass_man/support/renice_command.rb +17 -0
  78. data/lib/procon_bypass_man/support/retryable.rb +16 -0
  79. data/lib/procon_bypass_man/support/signal_handler.rb +1 -1
  80. data/lib/procon_bypass_man/version.rb +1 -1
  81. data/lib/procon_bypass_man/websocket/client.rb +2 -2
  82. data/lib/procon_bypass_man/worker.rb +32 -0
  83. data/lib/procon_bypass_man.rb +53 -10
  84. data/procon_bypass_man.gemspec +4 -2
  85. data/project_template/README.md +0 -5
  86. data/project_template/app.rb +17 -11
  87. data/project_template/app.rb.erb +59 -0
  88. data/project_template/lib/app_generator.rb +31 -0
  89. data/project_template/web.rb +1 -1
  90. data/sig/main.rbs +10 -52
  91. data/tmp/.keep +0 -0
  92. metadata +43 -10
  93. data/lib/procon_bypass_man/background/job_runner.rb +0 -45
  94. data/lib/procon_bypass_man/background/jobs/concerns/has_internal_api_setting.rb +0 -5
  95. data/lib/procon_bypass_man/background/jobs/concerns/job_runnable.rb +0 -16
  96. data/lib/procon_bypass_man/background/jobs/report_pressed_buttons_job.rb +0 -15
  97. data/lib/procon_bypass_man/bypass/usb_hid_logger.rb +0 -47
  98. data/lib/procon_bypass_man/io_monitor.rb +0 -108
  99. data/lib/procon_bypass_man/support/server_pool.rb +0 -46
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ProconBypassMan::Configuration
2
4
  module ClassMethods
3
5
  def root
@@ -47,14 +49,15 @@ class ProconBypassMan::Configuration
47
49
  def fallback_setting_path
48
50
  "/tmp/procon_bypass_man_fallback_setting.yaml"
49
51
  end
50
-
51
- def io_monitor_logging
52
- config.io_monitor_logging
53
- end
54
52
  end
55
53
 
56
54
  attr_accessor :enable_critical_error_logging
57
- attr_writer :verbose_bypass_log, :raw_setting, :enable_reporting_pressed_buttons, :never_exit_accidentally, :io_monitor_logging, :enable_home_led_on_connect
55
+ attr_writer :verbose_bypass_log, :raw_setting, :never_exit_accidentally, :enable_home_led_on_connect
56
+ # 削除予定
57
+ attr_writer :enable_reporting_pressed_buttons
58
+
59
+ # NOTE 非推奨. 削除したいが設定ファイルに残っているときにエラーにしたくないので互換性維持のため残す
60
+ attr_writer :io_monitor_logging
58
61
 
59
62
  def root=(path)
60
63
  @root = path
@@ -69,6 +72,7 @@ class ProconBypassMan::Configuration
69
72
  end
70
73
  end
71
74
 
75
+ # NOTE 不具合の原因は修正済みなので可変である必要は無くなった。削除したいが各端末内の設定ファイルに存在している場合があるのでしばらく残す
72
76
  def bypass_mode=(value)
73
77
  @bypass_mode = ProconBypassMan::BypassMode.new(
74
78
  mode: value[:mode],
@@ -114,35 +118,14 @@ class ProconBypassMan::Configuration
114
118
  "#{root}/.setting_yaml_digest"
115
119
  end
116
120
 
117
- # @return [String] pbm-webの接続先
118
- def internal_api_servers
119
- if !!ENV["INTERNAL_API_SERVER"]
120
- [ENV["INTERNAL_API_SERVER"]]
121
- else
122
- [ 'http://localhost:9090',
123
- 'http://localhost:8080',
124
- ].compact
125
- end
126
- end
127
-
128
- # @return [Array<ProconBypassMan::ServerPool>]
129
- def internal_server_pool
130
- @internal_server_pool ||= ProconBypassMan::ServerPool.new(servers: internal_api_servers)
131
- end
132
-
133
- # @return [Array<ProconBypassMan::ServerPool>]
134
- def server_pool
135
- @server_pool ||= ProconBypassMan::ServerPool.new(servers: api_servers)
136
- end
137
-
138
121
  # @return [String, NilClass]
139
- def current_server
140
- server_pool.server
122
+ def api_server
123
+ api_servers&.first
141
124
  end
142
125
 
143
126
  # @return [String, NilClass]
144
127
  def current_ws_server
145
- if (uri = URI.parse(server_pool.server))
128
+ if (uri = URI.parse(api_server))
146
129
  if uri.port == 443
147
130
  return "ws://#{uri.host}"
148
131
  else
@@ -161,7 +144,7 @@ class ProconBypassMan::Configuration
161
144
 
162
145
  # @return [Boolean]
163
146
  def enable_ws?
164
- !!current_server
147
+ !!api_server
165
148
  end
166
149
 
167
150
  # @return [Boolean]
@@ -178,8 +161,9 @@ class ProconBypassMan::Configuration
178
161
  end
179
162
  end
180
163
 
164
+ # @return [Boolean]
181
165
  def has_api_server?
182
- not api_servers.length.zero?
166
+ !!api_server
183
167
  end
184
168
 
185
169
  def verbose_bypass_log
@@ -190,19 +174,14 @@ class ProconBypassMan::Configuration
190
174
  @raw_setting ||= {}
191
175
  end
192
176
 
193
- # @return [Boolean] default false
194
- def enable_reporting_pressed_buttons
195
- @enable_reporting_pressed_buttons ||= false
196
- end
197
-
198
177
  # @return [Boolean] default false
199
178
  def never_exit_accidentally
200
179
  @never_exit_accidentally || false
201
180
  end
202
181
 
203
- # @return [Boolean] default false
204
- def io_monitor_logging
205
- @io_monitor_logging ||= false
182
+ # @return [Boolean] プロコンから「入力にかかっている時間」と「1秒間あたり何回入力できているか」をサーバに送信する
183
+ def enable_procon_performance_measurement?
184
+ has_api_server?
206
185
  end
207
186
 
208
187
  # @return [Boolean] default true
@@ -2,17 +2,26 @@ class ProconBypassMan::DeviceConnection::ProconSettingOverrider
2
2
  attr_accessor :procon, :output_report_watcher, :output_report_generator
3
3
 
4
4
  SUB_COMMAND_HOME_LED_ON = "38"
5
+ SUB_COMMAND_VIBRATION = "48"
6
+
5
7
  SUB_COMMAND_ARG_HOME_LED_ON = "1FF0FF"
8
+ SUB_COMMAND_ARG_VIBRATION_OFF = "00"
9
+
10
+ ALL_SETTINGS = {
11
+ home_led_on: [SUB_COMMAND_HOME_LED_ON, SUB_COMMAND_ARG_HOME_LED_ON],
12
+ vibration_off: [SUB_COMMAND_VIBRATION, SUB_COMMAND_ARG_VIBRATION_OFF],
13
+ }
14
+
15
+ # TODO 自動生成する
6
16
  SPECIAL_SUB_COMMAND_ARGS = {
7
17
  SUB_COMMAND_HOME_LED_ON => SUB_COMMAND_ARG_HOME_LED_ON,
18
+ SUB_COMMAND_VIBRATION => SUB_COMMAND_ARG_VIBRATION_OFF,
8
19
  }
9
- SETTING_HOME_LED_ON = { home_led_on: [SUB_COMMAND_HOME_LED_ON, SUB_COMMAND_ARG_HOME_LED_ON] }
10
- ALL_SETTINGS = SETTING_HOME_LED_ON
11
20
 
12
21
  def initialize(procon: )
13
22
  use_steps = {}
14
23
  if ProconBypassMan.config.enable_home_led_on_connect
15
- use_steps.merge!(SETTING_HOME_LED_ON)
24
+ use_steps.merge!(ALL_SETTINGS)
16
25
  end
17
26
 
18
27
  @setting_steps = use_steps.keys
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ProconBypassMan::Procon::AnalogStickManipulator
2
4
  attr_accessor :manipulated_abs_x, :manipulated_abs_y
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ProconBypassMan::Procon::LayerChanger
2
4
  # @param [ProconBypassMan::Domains::ProcessingProconBinary] binary
3
5
  def initialize(binary: )
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ProconBypassMan::Procon::Macro
2
4
  class BaseNestedStep
3
5
  def initialize(value)
@@ -99,7 +101,7 @@ class ProconBypassMan::Procon::Macro
99
101
 
100
102
  if nested_step.over?
101
103
  steps.shift # NestedStepを破棄する
102
- self.after_callback_block.call if self.after_callback_block
104
+ self.after_callback_block.call if self.after_callback_block && steps.empty?
103
105
  return next_step
104
106
  else
105
107
  return nested_step.next_step
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ProconBypassMan::Procon::MacroBuilder
2
4
  class SubjectMerger
3
5
  def self.merge(subjects)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ProconBypassMan::Procon::MacroRegistry
2
4
  PRESETS = {
3
5
  null: [],
@@ -0,0 +1,17 @@
1
+ class ProconBypassMan::Procon::PerformanceMeasurement::LastBypassAt
2
+ include Singleton
3
+
4
+ attr_accessor :mutex, :last_bypass_at
5
+
6
+ def initialize
7
+ self.mutex = Mutex.new
8
+ self.last_bypass_at = Time.now
9
+ end
10
+
11
+ def self.touch(&block)
12
+ instance.mutex.synchronize do
13
+ block.call(Time.now - instance.last_bypass_at)
14
+ instance.last_bypass_at = Time.now
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ # TODO SpanCollection にする
2
+ class ProconBypassMan::Procon::PerformanceMeasurement::MeasurementCollection
3
+ attr_accessor :timestamp_key, :spans
4
+
5
+ def initialize(timestamp_key: , spans: )
6
+ self.timestamp_key = timestamp_key
7
+ self.spans = spans
8
+ end
9
+ end
@@ -0,0 +1,101 @@
1
+ class ProconBypassMan::Procon::PerformanceMeasurement::MeasurementsSummarizer
2
+ class PerformanceMetric < Struct.new(:interval_from_previous_succeed_max,
3
+ :interval_from_previous_succeed_p50,
4
+ :write_time_max,
5
+ :write_time_p50,
6
+ :read_time_max,
7
+ :read_time_p50,
8
+ :time_taken_p50,
9
+ :time_taken_p95,
10
+ :time_taken_p99,
11
+ :time_taken_max,
12
+ :read_error_count,
13
+ :write_error_count,
14
+ :gc_count,
15
+ :gc_time,
16
+ :succeed_rate); end
17
+
18
+ def initialize(spans: )
19
+ @spans = spans
20
+ end
21
+
22
+ # @return [PerformanceMetric]
23
+ # NOTE 中央値の表示価値が低いのでコメントアウト
24
+ def summarize
25
+
26
+ write_time_max = 0
27
+ read_time_max = 0
28
+ time_taken_max = 0
29
+ interval_from_previous_succeed_max = 0
30
+ @spans.each do |span|
31
+ # NOTE @spans.map(&:write_time).sort.last と同じことだけど、処理コストを軽くするためにループを共通化する
32
+ write_time_max = span.write_time if write_time_max < span.write_time
33
+ read_time_max = span.read_time if write_time_max < span.read_time
34
+ time_taken_max = span.time_taken if span.succeed && time_taken_max < span.time_taken
35
+ interval_from_previous_succeed_max = span.interval_from_previous_succeed if span.succeed && interval_from_previous_succeed_max < span.interval_from_previous_succeed
36
+ end
37
+
38
+ # NOTE 今はGCを無効にしており、集計するまでもないのでコメントアウトにする. 今後GCを有効にしたバイパスをするかもしれないので残しておく
39
+ gc_count = 0 # @spans.map(&:gc_count).sum
40
+ gc_time = 0 # @spans.map(&:gc_time).sum
41
+
42
+ # sorted_interval_from_previous_succeed = @spans.select(&:succeed).map(&:interval_from_previous_succeed).sort
43
+ # interval_from_previous_succeed_max = sorted_interval_from_previous_succeed.last || 0
44
+ interval_from_previous_succeed_p50 = 0 # percentile(sorted_list: sorted_interval_from_previous_succeed , percentile: 0.50)
45
+
46
+ # sorted_read_time = @spans.map(&:read_time).sort
47
+ read_time_p50 = 0 # percentile(sorted_list: sorted_read_time , percentile: 0.50)
48
+ # read_time_max = sorted_read_time.last || 0
49
+
50
+ # sorted_write_time = @spans.map(&:write_time).sort
51
+ write_time_p50 = 0 # percentile(sorted_list: sorted_write_time , percentile: 0.50)
52
+ # write_time_max = sorted_write_time.last || 0
53
+
54
+ # sorted_time_taken = @spans.select(&:succeed).map(&:time_taken).sort
55
+ time_taken_p50 = 0 # percentile(sorted_list: sorted_time_taken, percentile: 0.50)
56
+ time_taken_p95 = 0 # percentile(sorted_list: sorted_time_taken, percentile: 0.95)
57
+ time_taken_p99 = 0 # percentile(sorted_list: sorted_time_taken, percentile: 0.99)
58
+ # time_taken_max = sorted_time_taken.last || 0
59
+
60
+ # NOTE webに表示していないのでコメントアウト. デバッグ時に見ることがあるので残しておく
61
+ total_read_error_count = 0 # @spans.map(&:read_error_count).sum
62
+ total_write_error_count = 0 # @spans.map(&:write_error_count).sum
63
+
64
+ # succeed_rate =
65
+ # if @spans.length.zero?
66
+ # 0
67
+ # else
68
+ # succeed_rate = (sorted_time_taken.length / @spans.length.to_f).floor(3)
69
+ # end
70
+ succeed_rate = 1 # Switchへの書き込みに失敗した時にretryしているので100%になるようになってる. succeedの個数をカウントコストを減らすためにハードコード
71
+
72
+ PerformanceMetric.new(interval_from_previous_succeed_max,
73
+ interval_from_previous_succeed_p50,
74
+ write_time_max,
75
+ write_time_p50,
76
+ read_time_max,
77
+ read_time_p50,
78
+ time_taken_p50,
79
+ time_taken_p95,
80
+ time_taken_p99,
81
+ time_taken_max,
82
+ total_read_error_count,
83
+ total_write_error_count,
84
+ gc_count,
85
+ gc_time,
86
+ succeed_rate)
87
+ end
88
+
89
+ private
90
+
91
+ # @param [Array<Numeric>] sorted_list
92
+ # @param [Float] percentile
93
+ # @return [Float]
94
+ def percentile(sorted_list: , percentile: )
95
+ return 0.0 if sorted_list.empty?
96
+ values_sorted = sorted_list
97
+ k = ((percentile*(values_sorted.length-1))+1).floor - 1
98
+ f = ((percentile*(values_sorted.length-1))+1).modulo(1)
99
+ return(values_sorted[k] + (f * (values_sorted[k+1] - values_sorted[k]))).floor(3)
100
+ end
101
+ end
@@ -0,0 +1,8 @@
1
+ # Bypassプロセスが収集したパフォーマンスメトリクスを、集計するためにmasterプロセスに転送するためジョブ
2
+ class ProconBypassMan::ProconPerformanceSpanTransferJob
3
+ extend ProconBypassMan::Background::JobPerformable
4
+
5
+ def self.perform(spans)
6
+ ProconBypassMan::Procon::PerformanceMeasurement::QueueOverProcess.push(spans)
7
+ end
8
+ end
@@ -0,0 +1,38 @@
1
+ class ProconBypassMan::Procon::PerformanceMeasurement::QueueOverProcess
2
+ extend ProconBypassMan::CanOverProcess
3
+
4
+ include Singleton
5
+
6
+ attr_reader :distributed_queue
7
+
8
+ # @override
9
+ def self.enable?
10
+ ProconBypassMan.config.enable_procon_performance_measurement?
11
+ end
12
+
13
+ # @override
14
+ def self.distributed_class
15
+ ProconBypassMan::Procon::PerformanceMeasurement::SpanQueue
16
+ end
17
+
18
+ # @override
19
+ def self.socket_file_path
20
+ "/tmp/procon_bypass_man_procon_performance_queue".freeze
21
+ end
22
+
23
+ def self.push(value)
24
+ return unless enable?
25
+
26
+ instance.distributed_queue.push(value)
27
+ end
28
+
29
+ def self.pop
30
+ return unless enable?
31
+
32
+ instance.distributed_queue.pop
33
+ end
34
+
35
+ def initialize
36
+ @distributed_queue = DRbObject.new_with_uri(self.class.socket_path)
37
+ end
38
+ end
@@ -0,0 +1,42 @@
1
+ class ProconBypassMan::Procon::PerformanceMeasurement::SpanQueue
2
+ def initialize
3
+ @current_table = {} # 1つのスレッドからしか触らないのでlockはいらない
4
+ @measurement_collection_list = [] # main threadとjob worker threadから触るのでlockが必要
5
+ end
6
+
7
+ # @param [Array<PerformanceSpan>] spans
8
+ # bypassプロセスから呼ばれる. 実際に実行を行なっているのはmasterプロセス
9
+ def push(new_spans)
10
+ current_key = generate_bucket_key
11
+
12
+ if @current_table[current_key].nil?
13
+ if not @current_table.empty?
14
+ timestamp_key = @current_table.keys.first
15
+ spans = @current_table.values.first
16
+ # 本当ならmutexでlockする必要があるけど、正確性はいらないのでパフォーマンスを上げるためにlockしない
17
+ @measurement_collection_list.push(
18
+ ProconBypassMan::Procon::PerformanceMeasurement::MeasurementCollection.new(timestamp_key: timestamp_key, spans: spans)
19
+ )
20
+ end
21
+
22
+ @current_table = {}
23
+ @current_table[current_key] = []
24
+ @current_table[current_key].concat(new_spans)
25
+ else
26
+ @current_table[current_key].concat(new_spans)
27
+ end
28
+ end
29
+
30
+ # job workerから呼ばれる
31
+ # @return [ProconBypassMan::Procon::PerformanceMeasurement::MeasurementCollection]
32
+ def pop
33
+ @measurement_collection_list.pop
34
+ end
35
+
36
+ private
37
+
38
+ # 1分単位で次の値になる
39
+ def generate_bucket_key
40
+ Time.new.strftime("%Y-%m-%d %H:%M:00%:z")
41
+ end
42
+ end
@@ -0,0 +1,39 @@
1
+ class ProconBypassMan::Procon::PerformanceMeasurement::SpanTransferBuffer
2
+ include Singleton
3
+
4
+ def initialize
5
+ @buff = []
6
+ end
7
+
8
+ # @param [Span]
9
+ # @return [void]
10
+ def push_and_run_block_if_buffer_over(value, &block)
11
+ push(value)
12
+ return unless buffer_over?
13
+
14
+ block.call(spans)
15
+ clear
16
+ end
17
+
18
+ private
19
+
20
+ def spans
21
+ @buff
22
+ end
23
+
24
+ def clear
25
+ @buff.clear
26
+ end
27
+
28
+ def push(value)
29
+ @buff << value
30
+ end
31
+
32
+ def buffer_over?
33
+ @buff.length > max_buffer
34
+ end
35
+
36
+ def max_buffer
37
+ 50
38
+ end
39
+ end
@@ -0,0 +1,110 @@
1
+ module ProconBypassMan::Procon::PerformanceMeasurement; end
2
+
3
+ require 'benchmark'
4
+ require 'procon_bypass_man/procon/performance_measurement/measurements_summarizer'
5
+ require 'procon_bypass_man/procon/performance_measurement/span_queue'
6
+ require 'procon_bypass_man/procon/performance_measurement/procon_performance_span_transfer_job'
7
+ require 'procon_bypass_man/procon/performance_measurement/span_transfer_buffer'
8
+ require 'procon_bypass_man/procon/performance_measurement/measurement_collection'
9
+ require 'procon_bypass_man/procon/performance_measurement/queue_over_process'
10
+ require 'procon_bypass_man/procon/performance_measurement/last_bypass_at'
11
+
12
+ module ProconBypassMan::Procon::PerformanceMeasurement
13
+ class PerformanceSpan
14
+ attr_accessor :time_taken, :succeed, :interval_from_previous_succeed, :gc_count, :gc_time
15
+ attr_reader :write_error_count, :read_error_count, :write_time, :read_time
16
+
17
+ def initialize
18
+ @write_error_count = 0
19
+ @read_error_count = 0
20
+ @time_taken = 0.0
21
+ @succeed = nil
22
+ @interval_from_previous_succeed = nil
23
+ @custom_metric = {}
24
+ @write_time = 0.0
25
+ @read_time = 0.0
26
+ @gc_time = 0.0
27
+ end
28
+
29
+ def record_read_error
30
+ @read_error_count += 1
31
+ end
32
+
33
+ def record_write_error
34
+ @write_error_count += 1
35
+ end
36
+
37
+ def record_write_time(&block)
38
+ result = nil
39
+ @write_time = Benchmark.realtime { result = block.call }
40
+ return result
41
+ end
42
+
43
+ def record_read_time(&block)
44
+ @read_time = Benchmark.realtime { block.call }
45
+ end
46
+ end
47
+
48
+ # 全部送ると負荷になるので適当にまびく
49
+ def self.is_not_measure_with_random_or_if_fast(span: )
50
+ return false if span.time_taken > 0.1
51
+ return true if rand(20) != 0 # 19/20は捨てる
52
+ return false
53
+ end
54
+
55
+ # measureをして、measureの結果をためる
56
+ # @return [Boolean] 成功したか. テスト時に戻り値を使いたい
57
+ def self.measure(&bypass_process_block)
58
+ unless ProconBypassMan.config.enable_procon_performance_measurement?
59
+ bypass_process_block.call(PerformanceSpan.new)
60
+ return
61
+ end
62
+
63
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.1.0")
64
+ snapshot_gc_time = GC.stat(:time) / 1000.0
65
+ end
66
+ snapshot_gc_count = GC.count
67
+ span = PerformanceSpan.new
68
+
69
+ span.time_taken = Benchmark.realtime {
70
+ span.succeed = bypass_process_block.call(span)
71
+ }.floor(3)
72
+
73
+ return if is_not_measure_with_random_or_if_fast(span: span)
74
+
75
+ if span.succeed
76
+ ProconBypassMan::Procon::PerformanceMeasurement::LastBypassAt.touch do |interval_from_previous_succeed|
77
+ span.interval_from_previous_succeed = interval_from_previous_succeed.floor(3)
78
+ end
79
+ end
80
+
81
+ (GC.count - snapshot_gc_count).tap do |increased_gc_count|
82
+ span.gc_count = increased_gc_count
83
+ end
84
+
85
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.1.0")
86
+ ((GC.stat(:time) / 1000.0) - snapshot_gc_time).tap do |increased_time|
87
+ span.gc_time = increased_time
88
+ end
89
+ end
90
+
91
+ # measureするたびにperform_asyncしているとjob queueが詰まるのでbufferingしている
92
+ ProconBypassMan::Procon::PerformanceMeasurement::SpanTransferBuffer.instance.push_and_run_block_if_buffer_over(span) do |spans|
93
+ ProconBypassMan::ProconPerformanceSpanTransferJob.perform_async(spans)
94
+ end
95
+ return span.succeed
96
+ end
97
+
98
+ # @return [MeasurementCollection, NilClass]
99
+ # bypassしているプロセスから呼ばれる
100
+ def self.pop_measurement_collection
101
+ ProconBypassMan::Procon::PerformanceMeasurement::QueueOverProcess.pop
102
+ end
103
+
104
+ # @param [MeasurementCollection] spans
105
+ # @return [ProconBypassMan::Procon::PerformanceMeasurement::MeasurementsSummarizer::PerformanceMetric]
106
+ # jobから呼ばれる予定
107
+ def self.summarize(spans: )
108
+ ProconBypassMan::Procon::PerformanceMeasurement::MeasurementsSummarizer.new(spans: spans).summarize
109
+ end
110
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ProconBypassMan::SuppressRumble
2
4
  # @param [String] binary
3
5
  def initialize(binary: )
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # ボタンを押しているか判断するクラス。バイナリの書き換えはしない
2
4
  class ProconBypassMan::Procon::UserOperation
3
5
  attr_reader :binary
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ProconBypassMan::Procon::AnalogStick
2
4
  attr_accessor :neutral_position
3
5
  attr_writer :bin_x, :bin_y
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # proconから取得したばかりのバイナリ
2
4
  class ProconBypassMan::Domains::InboundProconBinary < ProconBypassMan::Domains::Binary::Base
3
5
  include ProconBypassMan::Domains::HasImmutableBinary
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # バイナリの書き換えのみをする
2
4
  class ProconBypassMan::Domains::ProcessingProconBinary < ProconBypassMan::Domains::Binary::Base
3
5
  include ProconBypassMan::Domains::HasMutableBinary
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ProconBypassMan::RumbleBinary
2
4
  # @param [String] binary
3
5
  def initialize(binary: )