procon_bypass_man 0.1.23 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/gitleacks.yml +11 -0
  3. data/CHANGELOG.md +10 -0
  4. data/Gemfile.lock +1 -1
  5. data/docs/setup_raspi.md +0 -1
  6. data/lib/procon_bypass_man/buttons_setting_configuration/loader.rb +1 -1
  7. data/lib/procon_bypass_man/{commands → bypass}/bypass_command.rb +11 -19
  8. data/lib/procon_bypass_man/bypass.rb +6 -9
  9. data/lib/procon_bypass_man/commands/print_boot_message_command.rb +1 -2
  10. data/lib/procon_bypass_man/commands/send_error_command.rb +2 -2
  11. data/lib/procon_bypass_man/commands.rb +0 -3
  12. data/lib/procon_bypass_man/configuration.rb +21 -2
  13. data/lib/procon_bypass_man/device_connection/command.rb +28 -0
  14. data/lib/procon_bypass_man/{device_connector.rb → device_connection/executor.rb} +51 -38
  15. data/lib/procon_bypass_man/device_connection/output_report_generator.rb +42 -0
  16. data/lib/procon_bypass_man/device_connection/output_report_markerable.rb +28 -0
  17. data/lib/procon_bypass_man/device_connection/output_report_sub_command_table.rb +133 -0
  18. data/lib/procon_bypass_man/device_connection/output_report_watcher.rb +41 -0
  19. data/lib/procon_bypass_man/device_connection/pre_bypass.rb +67 -0
  20. data/lib/procon_bypass_man/device_connection/procon_setting_overrider.rb +78 -0
  21. data/lib/procon_bypass_man/device_connection/spoofing_output_report_watcher.rb +39 -0
  22. data/lib/procon_bypass_man/device_connection.rb +16 -0
  23. data/lib/procon_bypass_man/device_model.rb +17 -0
  24. data/lib/procon_bypass_man/io_monitor.rb +20 -1
  25. data/lib/procon_bypass_man/procon/macro_builder.rb +5 -3
  26. data/lib/procon_bypass_man/{commands → remote_pbm_action/commands}/run_remote_pbm_action_dispatch_command.rb +0 -0
  27. data/lib/procon_bypass_man/remote_pbm_action/restore_pbm_setting.rb +7 -1
  28. data/lib/procon_bypass_man/remote_pbm_action.rb +1 -0
  29. data/lib/procon_bypass_man/runner.rb +3 -7
  30. data/lib/procon_bypass_man/support/never_exit_accidentally.rb +3 -3
  31. data/lib/procon_bypass_man/support/usb_device_controller.rb +2 -2
  32. data/lib/procon_bypass_man/support/yaml_loader.rb +12 -0
  33. data/lib/procon_bypass_man/version.rb +1 -1
  34. data/lib/procon_bypass_man/websocket/client.rb +1 -4
  35. data/lib/procon_bypass_man.rb +65 -41
  36. data/project_template/app.rb +7 -1
  37. data/sig/main.rbs +3 -3
  38. metadata +17 -6
  39. data/lib/procon_bypass_man/commands/connect_device_command.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f0250a1a8546982fa0d1924606cfeb65b0376b1f0bbd2849a8c6faba39551c1a
4
- data.tar.gz: 94301f2c9540d370e55479f9ae818de0b0e64ece99b7ad7b44698f8828e241d4
3
+ metadata.gz: 6ca0c509c42d13cd0a9177c80b09c7f31bbeb6fad810e0f85210811cd69bffa2
4
+ data.tar.gz: ae97eb1796fb709840dcac7138e8ac8a2fd8be15f134585da8538f3958fb3442
5
5
  SHA512:
6
- metadata.gz: 8819eed593368519dde453cdb44168c4d05066ec6a52d22b8ca61f57a4f67d59307b41d4cb814a9b89dc2590cab17036ab2c2acf6c460213193cecf3380f8cb7
7
- data.tar.gz: 5ba74b2733493a2efb74fd9f73ffa5d6a9103acb5e1bd2d050e5c14146e739da4b48e25e84431cab1655e3d9a48a5f8249974f66da5ecc55f8efb4a66c3e86e7
6
+ metadata.gz: 33f58604e017bc01b6f7057877ab79045456f7fb10ff880fe640b47195374c8f170f5d5e7f97f643148d5b06313a3b9c6cfbd77d0e2701875e3bd1e6d4e397b1
7
+ data.tar.gz: d69d376c75ef7692ae7d2c74e8715aa00bea4dfbe2ff6d7359b69bbdff5ef65cd39132f82dcaa297c60fd3459ac9aff66f0751d2cc2e818e613bbd53cef2f829
@@ -0,0 +1,11 @@
1
+ name: gitleaks
2
+
3
+ on: [push]
4
+
5
+ jobs:
6
+ gitleaks:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v1
10
+ - name: gitleaks-action
11
+ uses: zricethezav/gitleaks-action@master
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## [0.2.0] - 2022-05-20
2
+ - 3つ以上のボタンを操作するマクロに対応する
3
+ - IOに関するログをデフォルトで無効にしました
4
+ - 有効にする場合はapp.rbに `config.io_monitor_logging = true` を記述してください
5
+ - 接続が安定化するまでの時間が短くなりました
6
+ - 接続中にプロコンのケーブルを抜くと、エラーを吐いて終了できなくなる不具合を修正しました
7
+ - プロセスをCtrl+Cで停止するときに終了するまでの時間が短くなりました
8
+ - 接続に成功するとhomeボタンのledが光るようになりました
9
+ - 無効にする場合はapp.rbに `config.enable_home_led_on_connect = false` を記述してください
10
+
1
11
  ## [0.1.23] - 2022-05-04
2
12
  - プロセスを停止するとプロコンがBluetooth経由でSwitchと接続するようになりました
3
13
  - 起動時に表示するdevice_idを一部のみ表示するようになりました
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- procon_bypass_man (0.1.23)
4
+ procon_bypass_man (0.2.0)
5
5
  action_cable_client
6
6
  pbmenv (>= 0.1.9)
7
7
  sorted_set
data/docs/setup_raspi.md CHANGED
@@ -33,7 +33,6 @@ https://github.com/splaplapla/procon_bypass_man/blob/master/docs/setup_raspi_by_
33
33
  * chmod 755 ~/add_procon_gadget.sh
34
34
  * sudo reboot
35
35
  * sudo sh ~/add_procon_gadget.sh の実行に成功させる
36
- * /etc/rc.local に sh /home/pi/add_procon_gadget.sh って書く
37
36
  * cd ~ && mkdir -p src && cd ~/src && git clone https://github.com/jiikko/procon_bypass_man_sample && cd procon_bypass_man_sample
38
37
 
39
38
  おわり. 起動する時は都度 sudo ruby app.rb を実行する
@@ -32,7 +32,7 @@ module ProconBypassMan
32
32
  ProconBypassMan::ButtonsSettingConfiguration.instance.reset!
33
33
  ProconBypassMan.reset!
34
34
 
35
- yaml = YAML.load_file(setting_path)
35
+ yaml = ProconBypassMan::YamlLoader.load(path: setting_path)
36
36
  ProconBypassMan.config.raw_setting = yaml.dup
37
37
  case yaml["version"]
38
38
  when 1.0, nil
@@ -10,7 +10,7 @@ class ProconBypassMan::BypassCommand
10
10
  @gadget = gadget
11
11
  @procon = procon
12
12
 
13
- ProconBypassMan::IOMonitor.start!
13
+ ProconBypassMan::IOMonitor.start! if ProconBypassMan.io_monitor_logging
14
14
  ProconBypassMan::Background::JobRunner.queue.clear # forkしたときに残留物も移ってしまうため
15
15
  ProconBypassMan::Background::JobRunner.start!
16
16
  end
@@ -33,32 +33,22 @@ class ProconBypassMan::BypassCommand
33
33
  monitor2 = ProconBypassMan::IOMonitor.new(label: "procon -> switch")
34
34
  ProconBypassMan.logger.info "Thread1を起動します"
35
35
 
36
- cycle_sleep = ProconBypassMan::CycleSleep.new(cycle_interval: 0.005, execution_cycle: 0)
36
+ cycle_sleep = ProconBypassMan::CycleSleep.new(cycle_interval: 1, execution_cycle: ProconBypassMan.config.bypass_mode.gadget_to_procon_interval)
37
37
 
38
38
  t1 = Thread.new do
39
- timer = ProconBypassMan::SafeTimeout.new(timeout: Time.now + 10)
40
- @did_first_step = false
39
+ if ProconBypassMan.config.bypass_mode.mode == ProconBypassMan::BypassMode::TYPE_AGGRESSIVE
40
+ ProconBypassMan.logger.info "TYPE_AGGRESSIVEなのでThread1を終了します"
41
+ monitor1.shutdown
42
+ next
43
+ end
44
+
41
45
  loop do
42
46
  break if $will_terminate_token
43
47
 
44
48
  cycle_sleep.sleep_or_execute do
45
49
  bypass = ProconBypassMan::Bypass.new(gadget: @gadget, procon: @procon, monitor: monitor1)
46
- !@did_first_step && timer.throw_if_timeout!
47
50
  bypass.send_gadget_to_procon!
48
51
  end
49
- rescue ProconBypassMan::SafeTimeout::Timeout
50
- case ProconBypassMan.config.bypass_mode.mode
51
- when ProconBypassMan::BypassMode::TYPE_AGGRESSIVE
52
- ProconBypassMan.logger.info "10秒経過したのでThread1を終了します"
53
- monitor1.shutdown
54
- break
55
- when ProconBypassMan::BypassMode::TYPE_NORMAL
56
- ProconBypassMan.logger.info "10秒経過したのでsend_intervalを長くします"
57
- cycle_sleep = ProconBypassMan::CycleSleep.new(cycle_interval: 1, execution_cycle: ProconBypassMan.config.bypass_mode.gadget_to_procon_interval)
58
- else
59
- raise "unknown type"
60
- end
61
- @did_first_step = true
62
52
  rescue Errno::EIO, Errno::ENODEV, Errno::EPROTO, IOError, Errno::ESHUTDOWN => e
63
53
  ProconBypassMan::SendErrorCommand.execute(error: "Switchとの切断されました.終了処理を開始します. #{e.full_message}")
64
54
  Process.kill "TERM", Process.ppid
@@ -67,6 +57,7 @@ class ProconBypassMan::BypassCommand
67
57
  ProconBypassMan::SendErrorCommand.execute(error: "Switchと意図せず切断されました.終了処理を開始します. #{e.full_message}")
68
58
  Process.kill "TERM", Process.ppid
69
59
  end
60
+
70
61
  ProconBypassMan.logger.info "Thread1を終了します"
71
62
  end
72
63
 
@@ -79,7 +70,6 @@ class ProconBypassMan::BypassCommand
79
70
  if $will_terminate_token
80
71
  if $will_terminate_token == WILL_TERMINATE_TOKEN::TERMINATE
81
72
  bypass.direct_connect_switch_via_bluetooth
82
- bypass.be_empty_procon
83
73
  end
84
74
  break
85
75
  end
@@ -88,9 +78,11 @@ class ProconBypassMan::BypassCommand
88
78
  rescue EOFError => e
89
79
  ProconBypassMan::SendErrorCommand.execute(error: "Proconが切断されました。終了処理を開始します. #{e.full_message}")
90
80
  Process.kill "TERM", Process.ppid
81
+ break
91
82
  rescue Errno::EIO, Errno::ENODEV, Errno::EPROTO, IOError, Errno::ESHUTDOWN => e
92
83
  ProconBypassMan::SendErrorCommand.execute(error: "Proconが切断されました。終了処理を開始します2. #{e.full_message}")
93
84
  Process.kill "TERM", Process.ppid
85
+ break
94
86
  end
95
87
  ProconBypassMan.logger.info "Thread2を終了します"
96
88
  end
@@ -1,4 +1,5 @@
1
1
  require "procon_bypass_man/bypass/usb_hid_logger"
2
+ require "procon_bypass_man/bypass/bypass_command"
2
3
 
3
4
  class ProconBypassMan::Bypass
4
5
  include ProconBypassMan::Bypass::UsbHidLogger
@@ -28,7 +29,6 @@ class ProconBypassMan::Bypass
28
29
 
29
30
  raw_input = nil
30
31
  begin
31
- # TODO blocking readにしたいが、接続時のフェーズによって長さが違うので厳しい
32
32
  raw_input = self.gadget.read_nonblock(64)
33
33
  self.bypass_value.binary = ProconBypassMan::Domains::InboundProconBinary.new(binary: raw_input)
34
34
  rescue IO::EAGAINWaitReadable
@@ -97,6 +97,10 @@ class ProconBypassMan::Bypass
97
97
  # @return [void]
98
98
  def direct_connect_switch_via_bluetooth
99
99
  ProconBypassMan.logger.debug { "direct_connect_switch_via_bluetooth!" }
100
+ self.procon.write_nonblock(["010500000000000000003800"].pack("H*")) # home led off
101
+ self.procon.write_nonblock(["010600000000000000003800"].pack("H*")) # home led off
102
+ self.procon.write_nonblock(["010700000000000000003800"].pack("H*")) # home led off
103
+ self.procon.write_nonblock(["010800000000000000003800"].pack("H*")) # home led off
100
104
  self.procon.write_nonblock(["8005"].pack("H*"))
101
105
  self.procon.write_nonblock(["8005"].pack("H*"))
102
106
  self.procon.write_nonblock(["8005"].pack("H*"))
@@ -104,13 +108,6 @@ class ProconBypassMan::Bypass
104
108
 
105
109
  # @return [void] 入力してから取り出さないと接続しっぱなしになるっぽいのでこれが必要っぽい
106
110
  def be_empty_procon
107
- timer = ProconBypassMan::SafeTimeout.new(timeout: Time.now + 5)
108
- loop do
109
- break if timer.timeout?
110
- output = self.procon.read_nonblock(64)
111
- ProconBypassMan.logger.debug { "[ProconBypassMan::Bypass#be_empty_procon] #{output.unpack("H*").first}" }
112
- rescue IO::EAGAINWaitReadable
113
- # no-op
114
- end
111
+ # タイムアウトまでブロッキングされるので、プロセスに逃す
115
112
  end
116
113
  end
@@ -32,8 +32,8 @@ class ProconBypassMan::PrintBootMessageCommand
32
32
  def to_s
33
33
  booted_message = <<~EOF
34
34
  ----
35
- RUBY_VERSION: #{@table[:ruby_version]}
36
35
  ProconBypassMan::VERSION: #{@table[:pbm_version]}
36
+ RUBY_VERSION: #{@table[:ruby_version]}
37
37
  Pbmenv::VERSION: #{@table[:pbmenv_version]}
38
38
  pid: #{@table[:pid]}
39
39
  root: #{@table[:root_path]}
@@ -58,7 +58,6 @@ class ProconBypassMan::PrintBootMessageCommand
58
58
  def self.execute
59
59
  message = BootMessage.new
60
60
  ProconBypassMan::ReportBootJob.perform_async(message.to_hash)
61
- ProconBypassMan::ReportLoadConfigJob.perform_async(ProconBypassMan.config.raw_setting)
62
61
  puts message.to_s
63
62
  end
64
63
  end
@@ -1,7 +1,7 @@
1
1
  class ProconBypassMan::SendErrorCommand
2
2
  # @param [String, Hash, Exception] error
3
3
  # @return [void]
4
- def self.execute(error: )
4
+ def self.execute(error: , stdout: true)
5
5
  body =
6
6
  case error
7
7
  when String, Hash
@@ -12,7 +12,7 @@ class ProconBypassMan::SendErrorCommand
12
12
 
13
13
  ProconBypassMan.logger.error body
14
14
  ProconBypassMan.error_logger.error body
15
- puts body
15
+ puts body if stdout
16
16
 
17
17
  ProconBypassMan::ReportErrorJob.perform(error)
18
18
  end
@@ -4,6 +4,3 @@ require "procon_bypass_man/commands/write_session_id_command"
4
4
  require "procon_bypass_man/commands/write_device_id_command"
5
5
  require "procon_bypass_man/commands/send_reload_config_event_command"
6
6
  require "procon_bypass_man/commands/send_error_command"
7
- require "procon_bypass_man/commands/connect_device_command"
8
- require "procon_bypass_man/commands/bypass_command"
9
- require "procon_bypass_man/commands/run_remote_pbm_action_dispatch_command"
@@ -47,10 +47,14 @@ class ProconBypassMan::Configuration
47
47
  def fallback_setting_path
48
48
  "/tmp/procon_bypass_man_fallback_setting.yaml"
49
49
  end
50
+
51
+ def io_monitor_logging
52
+ config.io_monitor_logging
53
+ end
50
54
  end
51
55
 
52
56
  attr_accessor :enable_critical_error_logging
53
- attr_writer :verbose_bypass_log, :raw_setting, :enable_reporting_pressed_buttons, :never_exit_accidentally
57
+ attr_writer :verbose_bypass_log, :raw_setting, :enable_reporting_pressed_buttons, :never_exit_accidentally, :io_monitor_logging, :enable_home_led_on_connect
54
58
 
55
59
  def root=(path)
56
60
  @root = path
@@ -186,12 +190,27 @@ class ProconBypassMan::Configuration
186
190
  @raw_setting ||= {}
187
191
  end
188
192
 
193
+ # @return [Boolean] default false
189
194
  def enable_reporting_pressed_buttons
190
195
  @enable_reporting_pressed_buttons ||= false
191
196
  end
192
197
 
193
- # @return [Boolean]
198
+ # @return [Boolean] default false
194
199
  def never_exit_accidentally
195
200
  @never_exit_accidentally || false
196
201
  end
202
+
203
+ # @return [Boolean] default false
204
+ def io_monitor_logging
205
+ @io_monitor_logging ||= false
206
+ end
207
+
208
+ # @return [Boolean] default true
209
+ def enable_home_led_on_connect
210
+ if defined?(@enable_home_led_on_connect)
211
+ return @enable_home_led_on_connect
212
+ else
213
+ true
214
+ end
215
+ end
197
216
  end
@@ -0,0 +1,28 @@
1
+ class ProconBypassMan::DeviceConnection::Command
2
+ MAX_RETRY_COUNT = 3
3
+
4
+ # @return [void]
5
+ def self.execute!(retry_count: 0)
6
+ begin
7
+ gadget, procon = ProconBypassMan::DeviceConnection::Executer.execute!
8
+ rescue ProconBypassMan::DeviceConnection::TimeoutErrorInConditionalRoute
9
+ if retry_count >= MAX_RETRY_COUNT
10
+ ProconBypassMan::SendErrorCommand.execute(error: "リトライしましたが、接続できませんでした")
11
+ raise ProconBypassMan::DeviceConnection::TimeoutError
12
+ else
13
+ ProconBypassMan::SendErrorCommand.execute(error: "接続に失敗したのでリトライします")
14
+ end
15
+
16
+ retry_count = retry_count + 1
17
+ retry
18
+ rescue ProconBypassMan::DeviceConnection::NotFoundProconError => e
19
+ raise
20
+ rescue ProconBypassMan::SafeTimeout::Timeout
21
+ raise ProconBypassMan::DeviceConnection::TimeoutError
22
+ end
23
+
24
+ ProconBypassMan::DeviceConnection::PreBypass.new(gadget: gadget, procon: procon).execute!
25
+ ProconBypassMan::DeviceConnection::ProconSettingOverrider.new(procon: procon).execute!
26
+ return [gadget, procon]
27
+ end
28
+ end
@@ -1,9 +1,6 @@
1
1
  require "timeout"
2
2
 
3
- class ProconBypassMan::DeviceConnector
4
- class BytesMismatchError < StandardError; end
5
- class NotFoundProconError < StandardError; end
6
-
3
+ class ProconBypassMan::DeviceConnection::Executer
7
4
  class Value
8
5
  attr_accessor :read_from, :values, :call_block_if_receive, :block
9
6
 
@@ -15,33 +12,41 @@ class ProconBypassMan::DeviceConnector
15
12
  end
16
13
  end
17
14
 
18
- def self.connect
19
- s = new(throw_error_if_timeout: true)
20
- s.add([
15
+ def self.new_with_default_args
16
+ new(throw_error_if_timeout: true)
17
+ end
18
+
19
+ def self.execute!
20
+ s = new_with_default_args
21
+ s.add(expected_to_receive: [
21
22
  ["0000"],
22
23
  ["0000"],
23
24
  ["8005"],
24
25
  ["0000"],
25
26
  ], read_from: :switch)
26
27
  # 1. Sends current connection status, and if the Joy-Con are connected,
27
- s.add([["8001"]], read_from: :switch)
28
- s.add([/^8101/], read_from: :procon) # <<< 81010003176d96e7a5480000000, macaddressとコントローラー番号を返す
28
+ s.add(expected_to_receive: [["8001"]], read_from: :switch)
29
+ s.add(expected_to_receive: [/^8101/], read_from: :procon) # <<< 81010003176d96e7a5480000000, macaddressとコントローラー番号を返す
29
30
  # 2. Sends handshaking packets over UART to the Joy-Con or Pro Controller Broadcom chip. This command can only be called once per session.
30
- s.add([["8002"]], read_from: :switch)
31
- s.add([/^8102/], read_from: :procon)
31
+ s.add(expected_to_receive: [["8002"]], read_from: :switch)
32
+ s.add(expected_to_receive: [/^8102/], read_from: :procon)
32
33
  # 3
33
- s.add([/^0100/], read_from: :switch)
34
- s.add([/^21/], read_from: :procon, call_block_if_receive: /^8101/) do |this|
35
- ProconBypassMan.logger.info "(start special route)"
36
- this.blocking_read_with_timeout_from_procon # <<< 810100032dbd42e9b698000
37
- this.write_to_procon("8002")
38
- this.blocking_read_with_timeout_from_procon # <<< 8102
39
- this.write_to_procon("01000000000000000000033000000000000000000000000000000000000000000000000000000000000000000000000000")
40
- this.blocking_read_with_timeout_from_procon # <<< 21
34
+ s.add(expected_to_receive: [/^0100/], read_from: :switch)
35
+ s.add(expected_to_receive: [/^21/], read_from: :procon, call_block_if_receive: /^8101/) do |this|
36
+ begin
37
+ ProconBypassMan.logger.info "(start special route)"
38
+ this.blocking_read_with_timeout_from_procon # <<< 810100032dbd42e9b698000
39
+ this.write_to_procon("8002")
40
+ this.blocking_read_with_timeout_from_procon # <<< 8102
41
+ this.write_to_procon("01000000000000000000033000000000000000000000000000000000000000000000000000000000000000000000000000")
42
+ this.blocking_read_with_timeout_from_procon # <<< 21
43
+ rescue ProconBypassMan::SafeTimeout::Timeout, Timeout::Error
44
+ raise ProconBypassMan::DeviceConnection::TimeoutErrorInConditionalRoute
45
+ end
41
46
  end
42
47
 
43
48
  # 4. Forces the Joy-Con or Pro Controller to only talk over USB HID without any timeouts. This is required for the Pro Controller to not time out and revert to Bluetooth.
44
- s.add([["8004"]], read_from: :switch)
49
+ s.add(expected_to_receive: [["8004"]], read_from: :switch)
45
50
  s.drain_all
46
51
  return [s.switch, s.procon]
47
52
  end
@@ -53,7 +58,8 @@ class ProconBypassMan::DeviceConnector
53
58
  @throw_error_if_mismatch = throw_error_if_mismatch
54
59
  end
55
60
 
56
- def add(values, read_from: , call_block_if_receive: false, &block)
61
+ def add(expected_to_receive: , read_from: , call_block_if_receive: nil, &block)
62
+ values = expected_to_receive
57
63
  @queue << Value.new(values: values, read_from: read_from, call_block_if_receive: call_block_if_receive, &block)
58
64
  end
59
65
 
@@ -67,6 +73,7 @@ class ProconBypassMan::DeviceConnector
67
73
  item.values.each do |value|
68
74
  raw_data = nil
69
75
  timer = ProconBypassMan::SafeTimeout.new
76
+
70
77
  begin
71
78
  timer.throw_if_timeout!
72
79
  raw_data = from_device(item).read_nonblock(64)
@@ -76,8 +83,11 @@ class ProconBypassMan::DeviceConnector
76
83
  retry
77
84
  end
78
85
 
79
- if item.call_block_if_receive =~ raw_data.unpack("H*").first
80
- raw_data = item.block.call(self)
86
+ if item.call_block_if_receive
87
+ ProconBypassMan.logger.info "call block if receive: #{item.call_block_if_receive}, actual: #{raw_data.unpack("H*")} from: #{item.read_from}"
88
+ if item.call_block_if_receive =~ raw_data.unpack("H*").first
89
+ raw_data = item.block.call(self)
90
+ end
81
91
  end
82
92
 
83
93
  result =
@@ -89,13 +99,14 @@ class ProconBypassMan::DeviceConnector
89
99
  else
90
100
  raise "#{value}は知りません"
91
101
  end
102
+
92
103
  if result
93
- ProconBypassMan.logger.info "OK(expected: #{value}, got: #{raw_data.unpack("H*")})"
94
- debug_log_buffer << "OK(expected: #{value}, got: #{raw_data.unpack("H*")})"
104
+ ProconBypassMan.logger.info "OK(expected: #{value}, got: #{raw_data.unpack("H*")}) from: #{item.read_from}"
105
+ debug_log_buffer << "OK(expected: #{value}, got: #{raw_data.unpack("H*")}) from: #{item.read_from}"
95
106
  else
96
- ProconBypassMan.logger.info "NG(expected: #{value}, got: #{raw_data.unpack("H*")})"
97
- debug_log_buffer << "NG(expected: #{value}, got: #{raw_data.unpack("H*")})"
98
- raise BytesMismatchError.new(debug_log_buffer) if @throw_error_if_mismatch
107
+ ProconBypassMan.logger.info "NG(expected: #{value}, got: #{raw_data.unpack("H*")}) from: #{item.read_from}"
108
+ debug_log_buffer << "NG(expected: #{value}, got: #{raw_data.unpack("H*")}) from: #{item.read_from}"
109
+ raise ProconBypassMan::DeviceConnection::BytesMismatchError.new(debug_log_buffer) if @throw_error_if_mismatch
99
110
  end
100
111
  to_device(item).write_nonblock(raw_data)
101
112
  end
@@ -103,8 +114,8 @@ class ProconBypassMan::DeviceConnector
103
114
  rescue ProconBypassMan::SafeTimeout::Timeout, Timeout::Error => e
104
115
  ProconBypassMan.logger.error "timeoutになりました(#{e.message})"
105
116
  compressed_buffer_text = ProconBypassMan::CompressArray.new(debug_log_buffer).compress.join("\n")
106
- ProconBypassMan::SendErrorCommand.execute(error: compressed_buffer_text)
107
- raise if @throw_error_if_timeout
117
+ ProconBypassMan::SendErrorCommand.execute(error: compressed_buffer_text, stdout: false)
118
+ raise ProconBypassMan::SafeTimeout::Timeout if @throw_error_if_timeout
108
119
  end
109
120
 
110
121
  def from_device(item)
@@ -143,23 +154,25 @@ class ProconBypassMan::DeviceConnector
143
154
  return
144
155
  end
145
156
  ProconBypassMan::UsbDeviceController.init
157
+ ProconBypassMan::UsbDeviceController.reset
146
158
 
147
159
  if path = ProconBypassMan::DeviceProconFinder.find
148
160
  @procon = File.open(path, "w+b")
149
161
  ProconBypassMan.logger.info "proconのデバイスファイルは#{path}を使います"
150
162
  else
151
- raise(ProconBypassMan::DeviceConnector::NotFoundProconError)
163
+ raise(ProconBypassMan::DeviceConnection::NotFoundProconError)
152
164
  end
153
- @gadget = File.open('/dev/hidg0', "w+b")
154
165
 
155
- ProconBypassMan::UsbDeviceController.reset
166
+ begin
167
+ @gadget = File.open('/dev/hidg0', "w+b")
168
+ rescue Errno::ENXIO => e
169
+ # /dev/hidg0 をopenできないときがある
170
+ ProconBypassMan::SendErrorCommand.execute(error: "Errno::ENXIOが起きたのでresetします.\n #{e.full_message}", stdout: false)
171
+ ProconBypassMan::UsbDeviceController.reset
172
+ retry
173
+ end
156
174
 
157
175
  @initialized_devices = true
158
- rescue Errno::ENXIO => e
159
- # /dev/hidg0 をopenできないときがある
160
- ProconBypassMan::SendErrorCommand.execute(error: "Errno::ENXIO (No such device or address @ rb_sysopen - /dev/hidg0)が起きました。resetします.\n #{e.full_message}")
161
- ProconBypassMan::UsbDeviceController.reset
162
- retry
163
176
  end
164
177
 
165
178
  def blocking_read_with_timeout_from_procon
@@ -0,0 +1,42 @@
1
+ class ProconBypassMan::DeviceConnection::OutputReportGenerator
2
+ def initialize
3
+ @counter = 0
4
+ end
5
+
6
+ # @return [String]
7
+ def generate_by_step(step)
8
+ sub_command_with_arg = ProconBypassMan::DeviceConnection::ProconSettingOverrider::ALL_SETTINGS[step]&.join or raise("Unsupport step")
9
+ raw_data = generate(sub_command_with_arg)
10
+ count_up
11
+ raw_data
12
+ end
13
+
14
+ # @return [String]
15
+ def generate_by_sub_command_with_arg(sub_command_with_arg)
16
+ raw_data = generate(sub_command_with_arg)
17
+ count_up
18
+ raw_data
19
+ end
20
+
21
+ private
22
+
23
+ # @return [String]
24
+ def generate(sub_command_with_arg)
25
+ [
26
+ ["01", counter, "00" * 8, sub_command_with_arg].join
27
+ ].pack("H*")
28
+ end
29
+
30
+ def count_up
31
+ @counter = @counter + 1
32
+ if @counter >= 256
33
+ @counter = 0
34
+ else
35
+ @counter
36
+ end
37
+ end
38
+
39
+ def counter
40
+ @counter.to_s(16).rjust(2, "0")
41
+ end
42
+ end
@@ -0,0 +1,28 @@
1
+ module ProconBypassMan::DeviceConnection::OutputReportMarkerable
2
+ OUTPUT_REPORT_FORMAT = /^01/
3
+ INPUT_REPORT_FORMAT = /^21/
4
+
5
+ # @param [String] raw_data
6
+ # @return [void]
7
+ def mark_as_send(raw_data)
8
+ data = raw_data.unpack("H*").first
9
+ case data
10
+ when OUTPUT_REPORT_FORMAT
11
+ sub_command = data[20..21]
12
+ sub_command_arg = data[22..23]
13
+ @hid_sub_command_request_table.mask_as_send(sub_command: sub_command, sub_command_arg: sub_command_arg)
14
+ end
15
+ end
16
+
17
+ # @param [String] raw_data
18
+ # @return [void]
19
+ def mark_as_receive(raw_data)
20
+ data = raw_data.unpack("H*").first
21
+ case data
22
+ when INPUT_REPORT_FORMAT
23
+ sub_command = data[28..29]
24
+ sub_command_arg = data[30..31]
25
+ @hid_sub_command_request_table.mark_as_receive(sub_command: sub_command, sub_command_arg: sub_command_arg)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,133 @@
1
+ class ProconBypassMan::DeviceConnection::OutputReportSubCommandTable
2
+ class HIDSubCommandResponse
3
+ attr_accessor :sub_command, :sub_command_arg
4
+
5
+ def initialize(sub_command: , sub_command_arg: )
6
+ @sub_command = sub_command
7
+ @sub_command_arg = sub_command_arg
8
+ end
9
+
10
+ def sub_command_with_arg
11
+ case @sub_command
12
+ when *SPECIAL_SUB_COMMANDS
13
+ @sub_command
14
+ else
15
+ if @sub_command_arg
16
+ "#{@sub_command}-#{@sub_command_arg}"
17
+ else
18
+ @sub_command
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ IGNORE_SUB_COMMANDS = {
25
+ "48-01" => true,
26
+ "04-00" => true,
27
+ }
28
+ # レスポンスに引数が含まれない
29
+ SPECIAL_SUB_COMMANDS = [
30
+ "01",
31
+ "02",
32
+ "03",
33
+ "30",
34
+ "38", # home led
35
+ "40",
36
+ "48",
37
+ ]
38
+
39
+ EXPECTED_SUB_COMMANDS = [
40
+ "01-04",
41
+ "02-",
42
+ "04-00",
43
+ "08-00",
44
+ "10-00",
45
+ "10-50",
46
+ "10-80",
47
+ "10-98",
48
+ "10-10",
49
+ "10-28",
50
+ "30-",
51
+ "40-",
52
+ "48-", # Enable vibration
53
+ ]
54
+
55
+ def initialize
56
+ @table = {}
57
+ end
58
+
59
+ # @param [String] sub_command
60
+ # @param [String] sub_command_arg
61
+ # @return [void]
62
+ def mask_as_send(sub_command: , sub_command_arg: )
63
+ case sub_command
64
+ when *SPECIAL_SUB_COMMANDS
65
+ @table[sub_command] = false
66
+ else
67
+ response = HIDSubCommandResponse.new(sub_command: sub_command, sub_command_arg: sub_command_arg)
68
+ @table[response.sub_command_with_arg] = false
69
+ end
70
+ end
71
+
72
+ # @param [String] sub_command
73
+ # @param [String] sub_command_arg
74
+ # @return [void]
75
+ def mark_as_receive(sub_command: , sub_command_arg: )
76
+ response = HIDSubCommandResponse.new(sub_command: sub_command, sub_command_arg: sub_command_arg)
77
+ if @table.key?(response.sub_command_with_arg)
78
+ @table[response.sub_command_with_arg] = true
79
+ end
80
+ end
81
+
82
+ # @param [String] sub_command
83
+ # @param [String] sub_command_arg
84
+ # @return [Boolean]
85
+ def has_key?(sub_command: , sub_command_arg: )
86
+ if IGNORE_SUB_COMMANDS["#{sub_command}-#{sub_command_arg}"]
87
+ return true
88
+ end
89
+
90
+ case sub_command
91
+ when *SPECIAL_SUB_COMMANDS
92
+ @table.key?(sub_command)
93
+ else
94
+ response = HIDSubCommandResponse.new(sub_command: sub_command, sub_command_arg: sub_command_arg)
95
+ @table.key?(response.sub_command_with_arg)
96
+ end
97
+ end
98
+
99
+ # @param [String] sub_command
100
+ # @param [String] sub_command_arg
101
+ # @return [Boolean]
102
+ def has_value?(sub_command: , sub_command_arg: )
103
+ if IGNORE_SUB_COMMANDS["#{sub_command}-#{sub_command_arg}"]
104
+ return true
105
+ end
106
+
107
+ response = HIDSubCommandResponse.new(sub_command: sub_command, sub_command_arg: sub_command_arg)
108
+ !!@table[response.sub_command_with_arg]
109
+ end
110
+
111
+ # @return [Boolean]
112
+ def has_unreceived_command?
113
+ !@table.values.all?(&:itself)
114
+ end
115
+
116
+ # @return [String, NilClass]
117
+ def unreceived_sub_command_with_arg
118
+ sub_command = @table.detect { |_key, value| !value }&.first
119
+ if(arg = ProconBypassMan::DeviceConnection::ProconSettingOverrider::SPECIAL_SUB_COMMAND_ARGS[sub_command])
120
+ "#{sub_command}#{arg}"
121
+ else
122
+ sub_command
123
+ end
124
+ end
125
+
126
+ # @return [Boolean]
127
+ def completed?
128
+ EXPECTED_SUB_COMMANDS.all? do |key|
129
+ sub_command, sub_command_arg = key.split("-")
130
+ has_value?(sub_command: sub_command, sub_command_arg: sub_command_arg)
131
+ end
132
+ end
133
+ end