procon_bypass_man 0.1.23 → 0.2.2
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 +4 -4
- data/.circleci/config.yml +13 -0
- data/.github/workflows/gitleacks.yml +12 -0
- data/.github/workflows/release.yml +1 -0
- data/.github/workflows/ruby.yml +1 -1
- data/CHANGELOG.md +20 -0
- data/Gemfile +6 -3
- data/Gemfile.lock +3 -3
- data/README.md +2 -2
- data/bin/generate_default_app +10 -0
- data/docs/getting_started.md +52 -7
- data/docs/setup_raspi.md +0 -1
- data/lib/procon_bypass_man/background/jobs/base_job.rb +1 -1
- data/lib/procon_bypass_man/background/jobs/concerns/{job_runnable.rb → job_performable.rb} +1 -1
- data/lib/procon_bypass_man/background.rb +1 -1
- data/lib/procon_bypass_man/buttons_setting_configuration/loader.rb +1 -1
- data/lib/procon_bypass_man/{commands → bypass}/bypass_command.rb +11 -19
- data/lib/procon_bypass_man/bypass/usb_hid_logger.rb +0 -1
- data/lib/procon_bypass_man/bypass.rb +9 -14
- data/lib/procon_bypass_man/commands/print_boot_message_command.rb +1 -2
- data/lib/procon_bypass_man/commands/send_error_command.rb +2 -2
- data/lib/procon_bypass_man/commands.rb +0 -3
- data/lib/procon_bypass_man/configuration.rb +22 -3
- data/lib/procon_bypass_man/device_connection/command.rb +28 -0
- data/lib/procon_bypass_man/{device_connector.rb → device_connection/executor.rb} +51 -38
- data/lib/procon_bypass_man/device_connection/output_report_generator.rb +42 -0
- data/lib/procon_bypass_man/device_connection/output_report_markerable.rb +28 -0
- data/lib/procon_bypass_man/device_connection/output_report_sub_command_table.rb +133 -0
- data/lib/procon_bypass_man/device_connection/output_report_watcher.rb +41 -0
- data/lib/procon_bypass_man/device_connection/pre_bypass.rb +67 -0
- data/lib/procon_bypass_man/device_connection/procon_setting_overrider.rb +78 -0
- data/lib/procon_bypass_man/device_connection/spoofing_output_report_watcher.rb +39 -0
- data/lib/procon_bypass_man/device_connection.rb +16 -0
- data/lib/procon_bypass_man/device_model.rb +17 -0
- data/lib/procon_bypass_man/io_monitor.rb +20 -1
- data/lib/procon_bypass_man/procon/macro_builder.rb +5 -3
- data/lib/procon_bypass_man/procon_display/bypass_hook.rb +11 -0
- data/lib/procon_bypass_man/procon_display/http_request.rb +31 -0
- data/lib/procon_bypass_man/procon_display/http_response.rb +23 -0
- data/lib/procon_bypass_man/procon_display/server.rb +33 -0
- data/lib/procon_bypass_man/procon_display/server_app.rb +17 -0
- data/lib/procon_bypass_man/procon_display/status.rb +20 -0
- data/lib/procon_bypass_man/procon_display.rb +11 -0
- data/lib/procon_bypass_man/{commands → remote_pbm_action/commands}/run_remote_pbm_action_dispatch_command.rb +0 -0
- data/lib/procon_bypass_man/remote_pbm_action/restore_pbm_setting.rb +7 -1
- data/lib/procon_bypass_man/remote_pbm_action.rb +1 -0
- data/lib/procon_bypass_man/runner.rb +4 -7
- data/lib/procon_bypass_man/support/callbacks.rb +68 -34
- data/lib/procon_bypass_man/support/never_exit_accidentally.rb +3 -3
- data/lib/procon_bypass_man/support/usb_device_controller.rb +2 -2
- data/lib/procon_bypass_man/support/yaml_loader.rb +12 -0
- data/lib/procon_bypass_man/version.rb +1 -1
- data/lib/procon_bypass_man/websocket/client.rb +1 -4
- data/lib/procon_bypass_man.rb +66 -41
- data/procon_bypass_man.gemspec +1 -1
- data/project_template/README.md +0 -5
- data/project_template/app.rb +24 -6
- data/project_template/app.rb.erb +64 -0
- data/project_template/lib/app_generator.rb +31 -0
- data/project_template/web.rb +1 -1
- data/sig/main.rbs +3 -3
- data/tmp/.keep +0 -0
- metadata +30 -8
- data/lib/procon_bypass_man/commands/connect_device_command.rb +0 -18
@@ -1,9 +1,6 @@
|
|
1
1
|
require "timeout"
|
2
2
|
|
3
|
-
class ProconBypassMan::
|
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.
|
19
|
-
|
20
|
-
|
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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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(
|
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
|
80
|
-
|
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::
|
163
|
+
raise(ProconBypassMan::DeviceConnection::NotFoundProconError)
|
152
164
|
end
|
153
|
-
@gadget = File.open('/dev/hidg0', "w+b")
|
154
165
|
|
155
|
-
|
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
|
+
"10-28" => true, # 返ってこないことがあった
|
28
|
+
}
|
29
|
+
# レスポンスに引数が含まれない
|
30
|
+
SPECIAL_SUB_COMMANDS = [
|
31
|
+
"01",
|
32
|
+
"02",
|
33
|
+
"03",
|
34
|
+
"30",
|
35
|
+
"38", # home led
|
36
|
+
"40",
|
37
|
+
"48",
|
38
|
+
]
|
39
|
+
|
40
|
+
EXPECTED_SUB_COMMANDS = [
|
41
|
+
"01-04",
|
42
|
+
"02-",
|
43
|
+
"04-00",
|
44
|
+
"08-00",
|
45
|
+
"10-00",
|
46
|
+
"10-50",
|
47
|
+
"10-80",
|
48
|
+
"10-98",
|
49
|
+
"10-10",
|
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
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class ProconBypassMan::DeviceConnection::OutputReportWatcher
|
2
|
+
include ProconBypassMan::DeviceConnection::OutputReportMarkerable
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@hid_sub_command_request_table = ProconBypassMan::DeviceConnection::OutputReportSubCommandTable.new
|
6
|
+
@timer = ProconBypassMan::SafeTimeout.new
|
7
|
+
end
|
8
|
+
|
9
|
+
# @param [String] sub_command
|
10
|
+
# @param [String] sub_command_arg
|
11
|
+
# @return [Boolean]
|
12
|
+
def sent?(sub_command: , sub_command_arg: )
|
13
|
+
@hid_sub_command_request_table.has_key?(sub_command: sub_command, sub_command_arg: sub_command_arg)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param [String] sub_command
|
17
|
+
# @param [String] sub_command_arg
|
18
|
+
# @return [Boolean]
|
19
|
+
def received?(sub_command: , sub_command_arg: )
|
20
|
+
@hid_sub_command_request_table.has_value?(sub_command: sub_command, sub_command_arg: sub_command_arg)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Boolean]
|
24
|
+
def completed?
|
25
|
+
@hid_sub_command_request_table.completed?
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Boolean]
|
29
|
+
# @raise [Timeout::Error]
|
30
|
+
def timeout_or_completed?
|
31
|
+
if @timer.timeout?
|
32
|
+
ProconBypassMan::SendErrorCommand.execute(error: "[pre_bypass] pre_bypassフェーズがタイムアウトしました")
|
33
|
+
return true
|
34
|
+
end
|
35
|
+
|
36
|
+
if completed?
|
37
|
+
ProconBypassMan.logger.info "[pre_bypass] pre_bypassフェーズが想定通り終了しました"
|
38
|
+
return true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class ProconBypassMan::DeviceConnection::PreBypass
|
2
|
+
attr_accessor :gadget, :procon, :output_report_watcher
|
3
|
+
|
4
|
+
def initialize(gadget: , procon: )
|
5
|
+
self.gadget = ProconBypassMan::DeviceModel.new(gadget)
|
6
|
+
self.procon = ProconBypassMan::DeviceModel.new(procon)
|
7
|
+
self.output_report_watcher = ProconBypassMan::DeviceConnection::OutputReportWatcher.new
|
8
|
+
end
|
9
|
+
|
10
|
+
# @return [void]
|
11
|
+
def execute!
|
12
|
+
loop do
|
13
|
+
run_once
|
14
|
+
|
15
|
+
if output_report_watcher.timeout_or_completed?
|
16
|
+
break
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [void]
|
22
|
+
def run_once
|
23
|
+
begin
|
24
|
+
raw_data = non_blocking_read_switch
|
25
|
+
output_report_watcher.mark_as_send(raw_data)
|
26
|
+
ProconBypassMan.logger.info "[pre_bypass] >>> #{raw_data.unpack("H*").first}"
|
27
|
+
send_procon(raw_data)
|
28
|
+
rescue IO::EAGAINWaitReadable
|
29
|
+
# no-op
|
30
|
+
end
|
31
|
+
|
32
|
+
3.times do
|
33
|
+
begin
|
34
|
+
raw_data = non_blocking_read_procon
|
35
|
+
output_report_watcher.mark_as_receive(raw_data)
|
36
|
+
ProconBypassMan.logger.info "[pre_bypass] <<< #{raw_data.unpack("H*").first}"
|
37
|
+
send_switch(raw_data)
|
38
|
+
rescue IO::EAGAINWaitReadable
|
39
|
+
# no-op
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# @raise [IO::EAGAINWaitReadable]
|
47
|
+
# @return [String]
|
48
|
+
def non_blocking_read_switch
|
49
|
+
gadget.non_blocking_read
|
50
|
+
end
|
51
|
+
|
52
|
+
# @raise [IO::EAGAINWaitReadable]
|
53
|
+
# @return [String]
|
54
|
+
def non_blocking_read_procon
|
55
|
+
procon.non_blocking_read
|
56
|
+
end
|
57
|
+
|
58
|
+
# @return [void]
|
59
|
+
def send_procon(raw_data)
|
60
|
+
procon.send(raw_data)
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [void]
|
64
|
+
def send_switch(raw_data)
|
65
|
+
gadget.send(raw_data)
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class ProconBypassMan::DeviceConnection::ProconSettingOverrider
|
2
|
+
attr_accessor :procon, :output_report_watcher, :output_report_generator
|
3
|
+
|
4
|
+
SUB_COMMAND_HOME_LED_ON = "38"
|
5
|
+
SUB_COMMAND_ARG_HOME_LED_ON = "1FF0FF"
|
6
|
+
SPECIAL_SUB_COMMAND_ARGS = {
|
7
|
+
SUB_COMMAND_HOME_LED_ON => SUB_COMMAND_ARG_HOME_LED_ON,
|
8
|
+
}
|
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
|
+
|
12
|
+
def initialize(procon: )
|
13
|
+
use_steps = {}
|
14
|
+
if ProconBypassMan.config.enable_home_led_on_connect
|
15
|
+
use_steps.merge!(SETTING_HOME_LED_ON)
|
16
|
+
end
|
17
|
+
|
18
|
+
@setting_steps = use_steps.keys
|
19
|
+
self.procon = ProconBypassMan::DeviceModel.new(procon)
|
20
|
+
self.output_report_watcher = ProconBypassMan::DeviceConnection::SpoofingOutputReportWatcher.new(expected_sub_commands: use_steps.values)
|
21
|
+
self.output_report_generator = ProconBypassMan::DeviceConnection::OutputReportGenerator.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def execute!
|
25
|
+
loop do
|
26
|
+
run_once
|
27
|
+
|
28
|
+
if output_report_watcher.timeout_or_completed?
|
29
|
+
break
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def run_once
|
35
|
+
begin
|
36
|
+
raw_data = non_blocking_read_procon
|
37
|
+
rescue IO::EAGAINWaitReadable
|
38
|
+
return
|
39
|
+
end
|
40
|
+
|
41
|
+
ProconBypassMan.logger.info "[procon_setting_overrider] <<< #{raw_data.unpack("H*").first}"
|
42
|
+
output_report_watcher.mark_as_receive(raw_data)
|
43
|
+
if output_report_watcher.has_unreceived_command?
|
44
|
+
re_override_setting_by_cmd(output_report_watcher.unreceived_sub_command_with_arg)
|
45
|
+
else
|
46
|
+
if(setting_step = @setting_steps.shift)
|
47
|
+
override_setting_by_step(setting_step)
|
48
|
+
else
|
49
|
+
return
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# @return [void]
|
57
|
+
def override_setting_by_step(setting_step)
|
58
|
+
raw_data = output_report_generator.generate_by_step(setting_step)
|
59
|
+
ProconBypassMan.logger.info "[procon_setting_overrider] >>> #{raw_data.unpack("H*").first}"
|
60
|
+
output_report_watcher.mark_as_send(raw_data)
|
61
|
+
send_procon(raw_data)
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [void]
|
65
|
+
def re_override_setting_by_cmd(sub_command_with_arg)
|
66
|
+
raw_data = output_report_generator.generate_by_sub_command_with_arg(sub_command_with_arg)
|
67
|
+
ProconBypassMan.logger.info "[procon_setting_overrider] >>> #{raw_data.unpack("H*").first}"
|
68
|
+
send_procon(raw_data)
|
69
|
+
end
|
70
|
+
|
71
|
+
def non_blocking_read_procon
|
72
|
+
procon.non_blocking_read
|
73
|
+
end
|
74
|
+
|
75
|
+
def send_procon(raw_data)
|
76
|
+
procon.send(raw_data)
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class ProconBypassMan::DeviceConnection::SpoofingOutputReportWatcher
|
2
|
+
include ProconBypassMan::DeviceConnection::OutputReportMarkerable
|
3
|
+
|
4
|
+
def initialize(expected_sub_commands: )
|
5
|
+
@timer = ProconBypassMan::SafeTimeout.new
|
6
|
+
@hid_sub_command_request_table = ProconBypassMan::DeviceConnection::OutputReportSubCommandTable.new
|
7
|
+
@expected_sub_commands = expected_sub_commands
|
8
|
+
end
|
9
|
+
|
10
|
+
# @return [Boolean]
|
11
|
+
def has_unreceived_command?
|
12
|
+
@hid_sub_command_request_table.has_unreceived_command?
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [String, NilClass]
|
16
|
+
def unreceived_sub_command_with_arg
|
17
|
+
@hid_sub_command_request_table.unreceived_sub_command_with_arg
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [Boolean]
|
21
|
+
def completed?
|
22
|
+
@expected_sub_commands.all? do |sub_command, sub_command_arg|
|
23
|
+
@hid_sub_command_request_table.has_value?(sub_command: sub_command, sub_command_arg: sub_command_arg)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [Boolean]
|
28
|
+
def timeout_or_completed?
|
29
|
+
if @timer.timeout?
|
30
|
+
ProconBypassMan.logger.info "[procon setting override] プロコンの設定上書き処理がタイムアウトしました"
|
31
|
+
return true
|
32
|
+
end
|
33
|
+
|
34
|
+
if completed?
|
35
|
+
ProconBypassMan.logger.info "[procon setting override] プロコンの設定上書き処理が想定通り終了しました"
|
36
|
+
return true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ProconBypassMan::DeviceConnection
|
2
|
+
class BytesMismatchError < StandardError; end
|
3
|
+
class NotFoundProconError < StandardError; end
|
4
|
+
class TimeoutErrorInConditionalRoute < StandardError; end
|
5
|
+
class TimeoutError < StandardError; end
|
6
|
+
end
|
7
|
+
|
8
|
+
require_relative "device_connection/executor"
|
9
|
+
require_relative "device_connection/pre_bypass"
|
10
|
+
require_relative "device_connection/command"
|
11
|
+
require_relative "device_connection/output_report_markerable"
|
12
|
+
require_relative "device_connection/procon_setting_overrider"
|
13
|
+
require_relative "device_connection/output_report_generator"
|
14
|
+
require_relative "device_connection/output_report_sub_command_table"
|
15
|
+
require_relative "device_connection/spoofing_output_report_watcher"
|
16
|
+
require_relative "device_connection/output_report_watcher"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class ProconBypassMan::DeviceModel
|
2
|
+
# @param [File] device
|
3
|
+
def initialize(device)
|
4
|
+
@device = device
|
5
|
+
end
|
6
|
+
|
7
|
+
# @param [String] raw_data
|
8
|
+
def send(raw_data)
|
9
|
+
@device.write_nonblock(raw_data)
|
10
|
+
end
|
11
|
+
|
12
|
+
# @raise [IO::EAGAINWaitReadable]
|
13
|
+
# @return [String]
|
14
|
+
def non_blocking_read
|
15
|
+
@device.read_nonblock(64)
|
16
|
+
end
|
17
|
+
end
|
@@ -1,4 +1,15 @@
|
|
1
1
|
module ProconBypassMan
|
2
|
+
class NullCounter
|
3
|
+
def initialize(label: )
|
4
|
+
end
|
5
|
+
|
6
|
+
def record(_)
|
7
|
+
end
|
8
|
+
|
9
|
+
def shutdown
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
2
13
|
class Counter
|
3
14
|
attr_accessor :label, :table, :previous_table, :active
|
4
15
|
|
@@ -40,7 +51,11 @@ module ProconBypassMan
|
|
40
51
|
end
|
41
52
|
|
42
53
|
module IOMonitor
|
54
|
+
@@thread = nil
|
55
|
+
|
43
56
|
def self.new(label: )
|
57
|
+
return NullCounter.new(label: label) if not started?
|
58
|
+
|
44
59
|
counter = Counter.new(label: label)
|
45
60
|
@@list << counter
|
46
61
|
counter
|
@@ -51,9 +66,13 @@ module ProconBypassMan
|
|
51
66
|
@@list
|
52
67
|
end
|
53
68
|
|
69
|
+
def self.started?
|
70
|
+
!!@@thread
|
71
|
+
end
|
72
|
+
|
54
73
|
# ここで集計する
|
55
74
|
def self.start!
|
56
|
-
Thread.start do
|
75
|
+
@@thread = Thread.start do
|
57
76
|
max_output_length = 0
|
58
77
|
loop do
|
59
78
|
list = @@list.select(&:active).dup
|
@@ -5,9 +5,11 @@ class ProconBypassMan::Procon::MacroBuilder
|
|
5
5
|
return subjects.first.to_steps
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
subjects.inject([[], []]) do |acc, item|
|
9
|
+
acc[0] << item.to_steps[0]
|
10
|
+
acc[1] << item.to_steps[1]
|
11
|
+
acc
|
12
|
+
end
|
11
13
|
end
|
12
14
|
end
|
13
15
|
|