procon_bypass_man 0.3.6 → 0.3.7
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 +4 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +3 -1
- data/bin/console +3 -0
- data/bin/validate_external_input +19 -0
- data/docs/getting_started.md +5 -0
- data/docs/setting/integration_external_input_serial_port.md +56 -0
- data/docs/setting/integration_external_input_serial_port_format.md +36 -0
- data/lib/procon_bypass_man/background/jobs/report_procon_performance_measurements_job.rb +1 -0
- data/lib/procon_bypass_man/bypass/procon_to_switch.rb +16 -1
- data/lib/procon_bypass_man/commands/print_boot_message_command.rb +2 -0
- data/lib/procon_bypass_man/configuration.rb +6 -0
- data/lib/procon_bypass_man/external_input/channels/base.rb +12 -0
- data/lib/procon_bypass_man/external_input/channels/serial_port_channel.rb +48 -0
- data/lib/procon_bypass_man/external_input/channels/tcpip.rb +16 -0
- data/lib/procon_bypass_man/external_input/channels.rb +12 -0
- data/lib/procon_bypass_man/external_input/external_data.rb +79 -0
- data/lib/procon_bypass_man/external_input.rb +35 -0
- data/lib/procon_bypass_man/processor.rb +3 -2
- data/lib/procon_bypass_man/procon/button_collection.rb +5 -0
- data/lib/procon_bypass_man/procon/performance_measurement/measurements_summarizer.rb +4 -0
- data/lib/procon_bypass_man/procon/performance_measurement.rb +7 -1
- data/lib/procon_bypass_man/procon.rb +17 -2
- data/lib/procon_bypass_man/procon_display/status.rb +0 -2
- data/lib/procon_bypass_man/support/retryable.rb +4 -2
- data/lib/procon_bypass_man/support/web_connectivity_checker.rb +39 -0
- data/lib/procon_bypass_man/version.rb +1 -1
- data/lib/procon_bypass_man.rb +7 -1
- data/project_template/app.rb +10 -1
- data/project_template/app.rb.erb +11 -1
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86e705d2907d51077191585d2cd16fb198a8a55226613d25f44b15b5e58f8806
|
4
|
+
data.tar.gz: a13290f301295913bbc30a06a1e06689dc5aae328cadd39ed1e91982ff7586d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 30618560c54d7c7a730bf97d74b601fce287d6900ed3eaf3c54b1071349b8a7d8b9e346c9099f6dbfad25a568710c5aaa4ce09ad4b12805a2f6d2d3a8134b27f
|
7
|
+
data.tar.gz: 96c796ecbbfa544a0427537a50ea17a0b7f10eee360bc6b072872c7621627dccf5403e78bdc2b580e5957fc5758f9b58979ef1f7f341da1a58b849a693ae79f8
|
data/.circleci/config.yml
CHANGED
@@ -119,6 +119,7 @@ build_jobs: &build_jobs
|
|
119
119
|
- "3.0.1"
|
120
120
|
- "3.0.2"
|
121
121
|
- "3.1.2"
|
122
|
+
- "3.2.2"
|
122
123
|
- bundle_install:
|
123
124
|
matrix:
|
124
125
|
parameters:
|
@@ -127,6 +128,7 @@ build_jobs: &build_jobs
|
|
127
128
|
- "3.0.1"
|
128
129
|
- "3.0.2"
|
129
130
|
- "3.1.2"
|
131
|
+
- "3.2.2"
|
130
132
|
- lint:
|
131
133
|
matrix:
|
132
134
|
parameters:
|
@@ -135,6 +137,7 @@ build_jobs: &build_jobs
|
|
135
137
|
- "3.0.1"
|
136
138
|
- "3.0.2"
|
137
139
|
- "3.1.2"
|
140
|
+
- "3.2.2"
|
138
141
|
requires:
|
139
142
|
- bundle_install
|
140
143
|
- type_check:
|
@@ -154,6 +157,7 @@ build_jobs: &build_jobs
|
|
154
157
|
- "3.0.1"
|
155
158
|
- "3.0.2"
|
156
159
|
- "3.1.2"
|
160
|
+
- "3.2.2"
|
157
161
|
requires:
|
158
162
|
- bundle_install
|
159
163
|
workflows:
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
@@ -13,6 +13,7 @@ gem "rubocop", require: false
|
|
13
13
|
gem "sinatra", require: false
|
14
14
|
gem "webrick", require: false
|
15
15
|
gem "stackprof", require: false
|
16
|
+
gem "serialport" # シリアル通信をする時に必要。通常はいらない
|
16
17
|
|
17
18
|
if Gem::Version.new(RUBY_VERSION) > Gem::Version.new("2.6.0")
|
18
19
|
gem 'typeprof', require: false
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
procon_bypass_man (0.3.
|
4
|
+
procon_bypass_man (0.3.7)
|
5
5
|
action_cable_client
|
6
6
|
blue_green_process (= 0.1.4.2)
|
7
7
|
pbmenv (>= 0.1.11)
|
@@ -80,6 +80,7 @@ GEM
|
|
80
80
|
parser (>= 3.0.1.1)
|
81
81
|
ruby-progressbar (1.11.0)
|
82
82
|
ruby2_keywords (0.0.5)
|
83
|
+
serialport (1.3.2)
|
83
84
|
set (1.0.3)
|
84
85
|
sinatra (2.2.0)
|
85
86
|
mustermann (~> 1.0)
|
@@ -134,6 +135,7 @@ DEPENDENCIES
|
|
134
135
|
rbs
|
135
136
|
rspec
|
136
137
|
rubocop
|
138
|
+
serialport
|
137
139
|
sinatra
|
138
140
|
stackprof
|
139
141
|
steep
|
data/bin/console
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Usage:
|
4
|
+
# NOTE: JSON形式の文字列がどういう形で読み込まれるかを確認できます
|
5
|
+
# $ echo '{"hex":"hogehoge", "buttons": ["a","b", "zr"]}' | bin/validate_external_input
|
6
|
+
# => 読み取った値: {:hex=>"hogehoge", :buttons=>["a", "b", "zr"]}
|
7
|
+
|
8
|
+
require "bundler/setup"
|
9
|
+
require "json"
|
10
|
+
require "procon_bypass_man"
|
11
|
+
require "procon_bypass_man/external_input"
|
12
|
+
|
13
|
+
begin
|
14
|
+
json_str = ARGF.read
|
15
|
+
external_data = ProconBypassMan::ExternalInput::ExternalData.parse!(json_str)
|
16
|
+
puts("読み取った値: #{{ hex: external_data.hex, buttons: external_data.buttons }}")
|
17
|
+
rescue JSON::ParserError => e
|
18
|
+
puts "failed to parse JSON: #{e.message}"
|
19
|
+
end
|
data/docs/getting_started.md
CHANGED
@@ -18,6 +18,7 @@
|
|
18
18
|
* [設定ファイルの書き方がわからない、エラーが起きるとき](#設定ファイルの書き方がわからない、エラーが起きるとき)
|
19
19
|
* [procon_bypass_manのアップグレード方法](#procon_bypass_manのアップグレード方法)
|
20
20
|
* [procon_bypass_man_cloudについて](#procon_bypass_man_cloudについて)
|
21
|
+
* [シリアルポート・GPIOから読み取る](#シリアルポートから読み取る)
|
21
22
|
* [最適化について](#最適化について)
|
22
23
|
|
23
24
|
## はじめに
|
@@ -251,6 +252,10 @@ procon_bypass_man_cloudとの接続が完了後、Raspberry Piを起動時にpro
|
|
251
252
|
|
252
253
|
セットアップ方法は https://pbm-cloud.jiikko.com/faq に書いています。
|
253
254
|
|
255
|
+
## シリアルポートから読み取る
|
256
|
+
* [ラズベリーパイのシリアルポート(GPIO)へ書き込んでPBM経由してSwitchへ入力をする方法](/docs/setting/integration_external_input_serial_port.md)
|
257
|
+
* [ラズベリーパイのシリアルポート(GPIO)に書き込むフォーマットについて](/docs/setting/integration_external_input_serial_port_format.md)
|
258
|
+
|
254
259
|
## 最適化について
|
255
260
|
本稿では、Rubyの最適化について書きます。上級者向けです。適用しなくても普通に動きますが、逆に適用したことで何らかのケースで遅くなる場合があるかもしれません。
|
256
261
|
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# ラズベリーパイのシリアルポート(GPIO)へ書き込んでPBM経由してSwitchへ入力をする方法
|
2
|
+
* procon_bypass_man: 0.3.7以上が必要です
|
3
|
+
* GPIOからSwitchへ入力できます。本テキストでは設定方法を記載します
|
4
|
+
|
5
|
+
## 1. シリアルポートへ書き込みができるようにラズベリーパイのセットアップする
|
6
|
+
https://toki-blog.com/pi-serial/ の「汎用シリアルとして使う」 を実施してください
|
7
|
+
|
8
|
+
## 2. GPIOへケーブルを挿す
|
9
|
+
対応しているケーブルをGPIOとPCに接続してください。
|
10
|
+
|
11
|
+
## 3. PBMのapp.rbを編集し、シリアルポートから読み出せるようにPBMのapp.rbを編集する
|
12
|
+
`gemの追加`と`デバイスファイルを指す修正` の2つ必要です。
|
13
|
+
|
14
|
+
* 1) シリアルポートから読み出すために追加でgemが必要です。 `app.rb` に `gem "serialport"` を追加してください。
|
15
|
+
|
16
|
+
```diff
|
17
|
+
gemfile do
|
18
|
+
source 'https://rubygems.org'
|
19
|
+
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
20
|
+
gem 'procon_bypass_man', '0.3.7'
|
21
|
+
+ gem "serialport"
|
22
|
+
end
|
23
|
+
```
|
24
|
+
|
25
|
+
* 2) 以下を参考にして`config.external_input_channels`の行を追加してください。
|
26
|
+
|
27
|
+
```diff
|
28
|
+
ProconBypassMan.configure do |config|
|
29
|
+
config.root = File.expand_path(__dir__)
|
30
|
+
config.logger = Logger.new("#{ProconBypassMan.root}/app.log", 1, 1024 * 1024 * 1)
|
31
|
+
config.logger.level = :debug
|
32
|
+
|
33
|
+
# バイパスするログを全部app.logに流すか
|
34
|
+
config.verbose_bypass_log = false
|
35
|
+
|
36
|
+
# webからProconBypassManを操作できるwebサービスと連携します
|
37
|
+
# 連携中はエラーログ、パフォーマンスに関するメトリクスを送信します
|
38
|
+
# config.api_servers = 'https://pbm-cloud.jiikko.com'
|
39
|
+
|
40
|
+
# エラーが起きたらerror.logに書き込みます
|
41
|
+
config.enable_critical_error_logging = true
|
42
|
+
|
43
|
+
# pbm-cloudで使う場合はnever_exitにtrueをセットしてください. trueがセットされている場合、不慮の事故が発生してもプロセスが終了しなくなります
|
44
|
+
config.never_exit_accidentally = true
|
45
|
+
|
46
|
+
# 接続に成功したらコントローラーのHOME LEDを光らせるか
|
47
|
+
config.enable_home_led_on_connect = true
|
48
|
+
|
49
|
+
+ config.external_input_channels = [
|
50
|
+
+ ProconBypassMan::ExternalInput::Channels::SerialPortChannel.new(device_path: '/dev/serial0', baud_rate: 9600),
|
51
|
+
+ ]
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
以上で設定は完了です。PBMを起動し、連携ツールからGPIOへ書き込んでください。
|
56
|
+
書き込みフォーマットについては [ラズベリーパイのシリアルポート(GPIO)に書き込むフォーマットについて](/docs/setting/integration_external_input_serial_port_format.md) を参照してください。
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# ラズベリーパイのシリアルポート(GPIO)に書き込むフォーマットについて
|
2
|
+
* procon_bypass_man: 0.3.7からGPIOを経由してSwitchへ入力できるようになりました。本テキストではPBMが読み取れるフォーマットについて記載します
|
3
|
+
|
4
|
+
## フォーマット
|
5
|
+
JSON形式, plain textに対応しています
|
6
|
+
|
7
|
+
* JSON形式
|
8
|
+
* 受付可能なカラム
|
9
|
+
* hex
|
10
|
+
* 16進数でエンコードした`INPUT 0x30` を書いてください。スティック操作を除くボタン部分のみが読み込まれます
|
11
|
+
* https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/ac8093c84194b3232acb675ac1accce9bcb456a3/bluetooth_hid_notes.md#input-0x30
|
12
|
+
* ex: '30f28100800078c77448287509550274ff131029001b0022005a0271ff191028001e00210064027cff1410280020002100000000000000000000000000000000'
|
13
|
+
* buttons
|
14
|
+
* 「入力したいボタン」「入力したくないボタン」を配列で渡してください
|
15
|
+
* 「入力したいボタン」
|
16
|
+
* `"y", "x", "b", "a", "sl", "sr", "r", "zr", "minus", "plus", "thumbr", "thumbl", "home", "cap", "down", "up", "right", "left", "l", "zl"`
|
17
|
+
* 「入力したくないボタン」
|
18
|
+
* `"uny", "unx", "unb", "una", "unsl", "unsr", "unr", "unzr", "unminus", "unplus", "unthumbr", "unthumbl", "unhome", "uncap", "undown", "unup", "unright", "unleft", "unl", "unzl"`
|
19
|
+
* ex: `['a', 'b']`
|
20
|
+
* 仕様
|
21
|
+
* JSONにhex, buttonsの両方が含まれている場合、hexを優先し、buttonsは無視します
|
22
|
+
* JSONの例
|
23
|
+
* `'{"hex":"30f28100800078c77448287509550274ff131029001b0022005a0271ff191028001e00210064027cff1410280020002100000000000000000000000000000000", "buttons": ["a"] }'`
|
24
|
+
* plain text
|
25
|
+
* コロンで囲ったボタンを1つの入力として解釈します
|
26
|
+
* `:a::b::uny:` を入力した場合には、a, b, uny を読み込みます
|
27
|
+
|
28
|
+
## 正しいJSONかを検証する
|
29
|
+
本リポジトリにJSONを検証する `bin/validate_external_input` を同梱しています。実行例を以下に示します。
|
30
|
+
|
31
|
+
```shell
|
32
|
+
$ echo '{"hex":"30f28100800078c77448287509550274ff131029001b0022005a0271ff191028001e00210064027cff1410280020002100000000000000000000000000000000", "buttons": ["a"] }' | bin/validate_external_input
|
33
|
+
読み取った値: {:hex=>"30f28100800078c77448287509550274ff131029001b0022005a0271ff191028001e00210064027cff1410280020002100000000000000000000000000000000", :buttons=>[:a]}
|
34
|
+
```
|
35
|
+
|
36
|
+
以上。
|
@@ -21,6 +21,7 @@ class ProconBypassMan::ReportProconPerformanceMeasurementsJob < ProconBypassMan:
|
|
21
21
|
time_taken_p50: metric.time_taken_p50,
|
22
22
|
time_taken_p95: metric.time_taken_p95,
|
23
23
|
time_taken_p99: metric.time_taken_p99,
|
24
|
+
external_input_time_max: metric.external_input_time_max,
|
24
25
|
read_error_count: metric.read_error_count,
|
25
26
|
write_error_count: metric.write_error_count,
|
26
27
|
gc_count: metric.gc_count,
|
@@ -50,14 +50,29 @@ class ProconBypassMan::Bypass::ProconToSwitch
|
|
50
50
|
# 後続処理で入力値を取得できるように詰めておく
|
51
51
|
ProconBypassMan::ProconDisplay::Status.instance.current = bypass_value.binary.to_procon_reader.to_hash
|
52
52
|
|
53
|
+
# NOTE: 外部からの入力を受け取る
|
54
|
+
external_input_data = nil
|
55
|
+
measurement.record_external_input_time do
|
56
|
+
# TODO: シリアルぽーとから読み取ると252.chrみたいなゴミデータを受け取ってEncoding::UndefinedConversionErrorが発生する可能性がある. 発生したら上限までretryした方がいいかも
|
57
|
+
if(data = ProconBypassMan::ExternalInput.read)
|
58
|
+
begin
|
59
|
+
external_input_data = ProconBypassMan::ExternalInput::ExternalData.parse!(data)
|
60
|
+
ProconBypassMan.logger.debug { "[ExternalInput] 読み取った値: { hex: #{external_input_data.hex}, raw_data: '#{external_input_data.raw_data}', buttons: #{external_input_data.buttons} }" }
|
61
|
+
rescue ProconBypassMan::ExternalInput::ParseError => e
|
62
|
+
ProconBypassMan.logger.error "[ExternalInput][#{e}] #{data.force_encoding('UTF-8').scrub}, #{data.force_encoding('ASCII-8BIT').codepoints} をparseできませんでした"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
53
67
|
result = measurement.record_write_time do
|
54
68
|
begin
|
55
69
|
ProconBypassMan::Retryable.retryable(tries: 5, on_no_retry: [Errno::EIO, Errno::ENODEV, Errno::EPROTO, IOError, Errno::ESHUTDOWN, Errno::ETIMEDOUT]) do
|
56
70
|
begin
|
57
71
|
# 終了処理を希望されているのでブロックを無視してメソッドを抜けてOK
|
58
72
|
return(false) if will_terminate? # rubocop:disable Lint/NoReturnInBeginEndBlocks
|
73
|
+
|
59
74
|
binary = ::ProconBypassMan::Procon::Rumbler.monitor do
|
60
|
-
ProconBypassMan::Processor.new(bypass_value.binary).process
|
75
|
+
ProconBypassMan::Processor.new(bypass_value.binary).process(external_input_data: external_input_data)
|
61
76
|
end
|
62
77
|
self.gadget.write_nonblock(binary)
|
63
78
|
|
@@ -34,6 +34,7 @@ class ProconBypassMan::PrintBootMessageCommand
|
|
34
34
|
ProconBypassMan::VERSION: #{@table[:pbm_version]}
|
35
35
|
RUBY_VERSION: #{@table[:ruby_version]}
|
36
36
|
Pbmenv::VERSION: #{@table[:pbmenv_version]}
|
37
|
+
PBM-Cloud Integration: #{ProconBypassMan::WebConnectivityChecker.new(ProconBypassMan.config.api_server)}
|
37
38
|
pid: #{@table[:pid]}
|
38
39
|
root: #{@table[:root_path]}
|
39
40
|
pid_path: #{@table[:pid_path]}
|
@@ -56,6 +57,7 @@ class ProconBypassMan::PrintBootMessageCommand
|
|
56
57
|
def self.execute
|
57
58
|
message = BootMessage.new
|
58
59
|
ProconBypassMan::ReportBootJob.perform_async(message.to_hash)
|
60
|
+
ProconBypassMan.logger.info message.to_s
|
59
61
|
puts message.to_s
|
60
62
|
end
|
61
63
|
end
|
@@ -54,6 +54,7 @@ class ProconBypassMan::Configuration
|
|
54
54
|
|
55
55
|
attr_accessor :enable_critical_error_logging
|
56
56
|
attr_writer :verbose_bypass_log, :raw_setting, :never_exit_accidentally, :enable_home_led_on_connect
|
57
|
+
attr_writer :external_input_channels
|
57
58
|
# 削除予定
|
58
59
|
attr_writer :enable_reporting_pressed_buttons
|
59
60
|
|
@@ -193,4 +194,9 @@ class ProconBypassMan::Configuration
|
|
193
194
|
true
|
194
195
|
end
|
195
196
|
end
|
197
|
+
|
198
|
+
# @return [Array<ProconBypassMan::ExternalInput::Channel::TCPIP, ProconBypassMan::ExternalInput::Channel::SerialPort>]
|
199
|
+
def external_input_channels
|
200
|
+
@external_input_channels || []
|
201
|
+
end
|
196
202
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module ProconBypassMan
|
2
|
+
module ExternalInput
|
3
|
+
module Channels
|
4
|
+
class SerialPortChannel < Base
|
5
|
+
attr_reader :serial_port
|
6
|
+
|
7
|
+
# @param [String] device_path
|
8
|
+
# @param [Integer] baud_rate
|
9
|
+
def initialize(device_path: , baud_rate: 9600)
|
10
|
+
require 'serialport'
|
11
|
+
|
12
|
+
super()
|
13
|
+
# data_bitsあたりは必要があれば設定ができるようにしたいがよくわからないのでとりあえずnilを入れる
|
14
|
+
data_bits = 8
|
15
|
+
stop_bits = 1
|
16
|
+
parity = SerialPort::NONE
|
17
|
+
@serial_port= SerialPort.new(device_path, baud_rate, data_bits, stop_bits, parity)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [String, NilClass]
|
21
|
+
# NOTE: この実装では、::IO::EAGAINWaitReadableを実質的な終端として扱っているので、高速に書き込みがされると取りこぼす可能性がある.
|
22
|
+
# NOTE: read_nonblockでバッファから読み出すとき、バッファが空になるまでは::IO::EAGAINWaitReadableが起きない前提で実装している.
|
23
|
+
# NOTE: 取りこぼししないよう精度を上げるには、終端文字が来るまで1文字ずつ読む必要があるがパフォーマンスが犠牲になってしまう. この対策をするには、bypass処理の開始で非同期に1文字ずつ読み込むことをすると多少マシになるはず
|
24
|
+
def read
|
25
|
+
buffer = ''
|
26
|
+
loop do
|
27
|
+
begin
|
28
|
+
buffer += @serial_port.read_nonblock(1024) || ''
|
29
|
+
rescue ::IO::EAGAINWaitReadable
|
30
|
+
break
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
return nil if buffer.empty?
|
35
|
+
|
36
|
+
# NOTE: 高速に書き込まれた場合、複数のチャンクを含む可能性があるので、最初だけを切り取る
|
37
|
+
chunks = buffer.split("\n")
|
38
|
+
if(chunks.size > 1)
|
39
|
+
ProconBypassMan::SendErrorCommand.execute(
|
40
|
+
error: "[ExternalInput] シリアルポートから読み込んだchunkが複数あります. 高い書き込み頻度に耐えられていないので実装を見直してください。 (chunks.size: #{chunks.size})"
|
41
|
+
)
|
42
|
+
end
|
43
|
+
chunks.first
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# TODO: 実装する
|
2
|
+
# module ProconBypassMan
|
3
|
+
# module ExternalInput
|
4
|
+
# module Channels
|
5
|
+
# class TCPIP < Base
|
6
|
+
# def initialize(port: )
|
7
|
+
# super()
|
8
|
+
# # TODO: ここでTCPサーバを起動する(port)
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# def read
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
# end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ProconBypassMan
|
4
|
+
module ExternalInput
|
5
|
+
module Channels
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
require "procon_bypass_man/external_input/channels/base"
|
11
|
+
require "procon_bypass_man/external_input/channels/serial_port_channel"
|
12
|
+
require "procon_bypass_man/external_input/channels/tcpip"
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module ProconBypassMan
|
2
|
+
module ExternalInput
|
3
|
+
class ExternalData
|
4
|
+
UNPRESS_BUTTONS = Set.new(ProconBypassMan::Procon::ButtonCollection.available.map { |x| "un#{x}".to_sym })
|
5
|
+
|
6
|
+
# @return [String, NilClass] 16進数表現のデータ
|
7
|
+
attr_reader :hex
|
8
|
+
|
9
|
+
# @return [String, NilClass] ログに表示する用
|
10
|
+
attr_reader :raw_data
|
11
|
+
|
12
|
+
# @raise [ParseError]
|
13
|
+
# @return [ExternalData] JSON か カンマ区切りのbuttons
|
14
|
+
def self.parse!(raw_data)
|
15
|
+
raise ParseError unless raw_data.ascii_only?
|
16
|
+
|
17
|
+
if is_json(raw_data)
|
18
|
+
begin
|
19
|
+
json = JSON.parse(raw_data)
|
20
|
+
return new(hex: json['hex'], buttons: json['buttons'])
|
21
|
+
rescue JSON::ParserError
|
22
|
+
raise ParseError
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
return new(hex: nil, buttons: raw_data.scan(/:\w+:/).map { |x| x.gsub(':', '') }, raw_data: raw_data)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param [String] raw_data
|
30
|
+
# @return [Boolean]
|
31
|
+
def self.is_json(raw_data)
|
32
|
+
raw_data.start_with?('{')
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param [String, NilClass] hex
|
36
|
+
# @param [Array<String>, NilClass] buttons
|
37
|
+
# @param [String, NilClass] raw_data
|
38
|
+
def initialize(hex: , buttons: , raw_data: nil)
|
39
|
+
@hex = hex
|
40
|
+
@buttons = buttons || []
|
41
|
+
@raw_data = raw_data
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [String, NilClass]
|
45
|
+
def to_binary
|
46
|
+
return nil if @hex.nil?
|
47
|
+
[@hex].pack('H*')
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [Array<Symbol>]
|
51
|
+
def press_buttons
|
52
|
+
buttons.select do |button|
|
53
|
+
ProconBypassMan::Procon::ButtonCollection::BUTTONS_MAP[button]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [Array<Symbol>]
|
58
|
+
def unpress_buttons
|
59
|
+
buttons.select { |button|
|
60
|
+
UNPRESS_BUTTONS.include?(button)
|
61
|
+
}.map { |b| to_button(b.to_s).to_sym }
|
62
|
+
end
|
63
|
+
|
64
|
+
# NOTE: ログに表示する用
|
65
|
+
# @return [Array<Symbol>]
|
66
|
+
def buttons
|
67
|
+
@buttons.map(&:to_sym)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# @return [String]
|
73
|
+
# NOTE: un#{button} って名前をbuttonに変換する
|
74
|
+
def to_button(button)
|
75
|
+
button.sub(/^un/, '')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ProconBypassMan
|
4
|
+
module ExternalInput
|
5
|
+
class ParseError < StandardError; end
|
6
|
+
|
7
|
+
@@channels = nil
|
8
|
+
|
9
|
+
# @return [Array<ProconBypassMan::ExternalInput::Channels::Base>]
|
10
|
+
def self.channels
|
11
|
+
@@channels
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [void]
|
15
|
+
def self.prepare_channels
|
16
|
+
@@channels = ProconBypassMan.config.external_input_channels
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [NilClass, String]
|
20
|
+
# NOTE: 外部入力からのreadがボトルネックになるなら、Concurrent::Futureを使ってプロコンからの読み出しと並列化することを検討する
|
21
|
+
def self.read
|
22
|
+
raise '外部入力が未初期化です' if @@channels.nil?
|
23
|
+
|
24
|
+
value = nil
|
25
|
+
@@channels.each do |channel|
|
26
|
+
value = channel.read
|
27
|
+
break if value
|
28
|
+
end
|
29
|
+
value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
require "procon_bypass_man/external_input/external_data"
|
35
|
+
require "procon_bypass_man/external_input/channels.rb"
|
@@ -5,12 +5,13 @@ class ProconBypassMan::Processor
|
|
5
5
|
@binary = binary
|
6
6
|
end
|
7
7
|
|
8
|
+
# @param [ProconBypassMan::ExternalInput::ExternalData, NilClass] external_input_data
|
8
9
|
# @return [String] 加工後の入力データ
|
9
|
-
def process
|
10
|
+
def process(external_input_data: nil)
|
10
11
|
return @binary.raw unless @binary.user_operation_data?
|
11
12
|
|
12
13
|
procon = ProconBypassMan::Procon.new(@binary.raw)
|
13
14
|
procon.apply!
|
14
|
-
procon.to_binary
|
15
|
+
procon.to_binary(external_input_data: external_input_data)
|
15
16
|
end
|
16
17
|
end
|
@@ -9,6 +9,7 @@ class ProconBypassMan::Procon::PerformanceMeasurement::MeasurementsSummarizer
|
|
9
9
|
:time_taken_p95,
|
10
10
|
:time_taken_p99,
|
11
11
|
:time_taken_max,
|
12
|
+
:external_input_time_max,
|
12
13
|
:read_error_count,
|
13
14
|
:write_error_count,
|
14
15
|
:gc_count,
|
@@ -26,6 +27,7 @@ class ProconBypassMan::Procon::PerformanceMeasurement::MeasurementsSummarizer
|
|
26
27
|
write_time_max = 0
|
27
28
|
read_time_max = 0
|
28
29
|
time_taken_max = 0
|
30
|
+
external_input_time_max = 0
|
29
31
|
interval_from_previous_succeed_max = 0
|
30
32
|
@spans.each do |span|
|
31
33
|
# NOTE @spans.map(&:write_time).sort.last と同じことだけど、処理コストを軽くするためにループを共通化する
|
@@ -33,6 +35,7 @@ class ProconBypassMan::Procon::PerformanceMeasurement::MeasurementsSummarizer
|
|
33
35
|
read_time_max = span.read_time if write_time_max < span.read_time
|
34
36
|
time_taken_max = span.time_taken if span.succeed && time_taken_max < span.time_taken
|
35
37
|
interval_from_previous_succeed_max = span.interval_from_previous_succeed if span.succeed && interval_from_previous_succeed_max < span.interval_from_previous_succeed
|
38
|
+
external_input_time_max = span.external_input_time if span.succeed && external_input_time_max < span.external_input_time
|
36
39
|
end
|
37
40
|
|
38
41
|
# NOTE 今はGCを無効にしており、集計するまでもないのでコメントアウトにする. 今後GCを有効にしたバイパスをするかもしれないので残しておく
|
@@ -79,6 +82,7 @@ class ProconBypassMan::Procon::PerformanceMeasurement::MeasurementsSummarizer
|
|
79
82
|
time_taken_p95,
|
80
83
|
time_taken_p99,
|
81
84
|
time_taken_max,
|
85
|
+
external_input_time_max,
|
82
86
|
total_read_error_count,
|
83
87
|
total_write_error_count,
|
84
88
|
gc_count,
|
@@ -12,7 +12,7 @@ require 'procon_bypass_man/procon/performance_measurement/last_bypass_at'
|
|
12
12
|
module ProconBypassMan::Procon::PerformanceMeasurement
|
13
13
|
class PerformanceSpan
|
14
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
|
15
|
+
attr_reader :write_error_count, :read_error_count, :write_time, :read_time, :external_input_time
|
16
16
|
|
17
17
|
def initialize
|
18
18
|
@write_error_count = 0
|
@@ -40,9 +40,15 @@ module ProconBypassMan::Procon::PerformanceMeasurement
|
|
40
40
|
return result
|
41
41
|
end
|
42
42
|
|
43
|
+
# @return [void]
|
43
44
|
def record_read_time(&block)
|
44
45
|
@read_time = Benchmark.realtime { block.call }
|
45
46
|
end
|
47
|
+
|
48
|
+
# @return [void]
|
49
|
+
def record_external_input_time(&block)
|
50
|
+
@external_input_time = Benchmark.realtime { block.call }
|
51
|
+
end
|
46
52
|
end
|
47
53
|
|
48
54
|
# 全部送ると負荷になるので適当にまびく
|
@@ -128,7 +128,7 @@ class ProconBypassMan::Procon
|
|
128
128
|
end
|
129
129
|
|
130
130
|
# remote macro or pbm action
|
131
|
-
if
|
131
|
+
if(task = ProconBypassMan::RemoteAction::TaskQueueInProcess.non_blocking_shift)
|
132
132
|
case task.type
|
133
133
|
when ProconBypassMan::RemoteAction::Task::TYPE_MACRO
|
134
134
|
no_op_step = :wait_for_0_3 # マクロの最後に固まって最後の入力をし続けるので、無の状態を最後に注入する
|
@@ -187,12 +187,27 @@ class ProconBypassMan::Procon
|
|
187
187
|
status
|
188
188
|
end
|
189
189
|
|
190
|
+
# @param [ProconBypassMan::ExternalInput::ExternalData, NilClass] external_input_data
|
190
191
|
# @return [String]
|
191
|
-
def to_binary
|
192
|
+
def to_binary(external_input_data: nil)
|
192
193
|
if ongoing_mode.name != :manual
|
193
194
|
return user_operation.binary.raw
|
194
195
|
end
|
195
196
|
|
197
|
+
if external_input_data
|
198
|
+
if(external_input_data_raw_binary = external_input_data.to_binary)
|
199
|
+
self.user_operation.merge(external_input_data_raw_binary)
|
200
|
+
else
|
201
|
+
external_input_data.press_buttons.each do |button|
|
202
|
+
self.user_operation.press_button(button)
|
203
|
+
end
|
204
|
+
external_input_data.unpress_buttons.each do |button|
|
205
|
+
self.user_operation.unpress_button(button)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
return self.user_operation.binary.raw
|
209
|
+
end
|
210
|
+
|
196
211
|
if ongoing_macro.ongoing? && (step = ongoing_macro.next_step)
|
197
212
|
BlueGreenProcess::SharedVariable.extend_run_on_this_process = true
|
198
213
|
ongoing_macro.force_neutral_buttons&.each do |force_neutral_button|
|
@@ -1,14 +1,16 @@
|
|
1
1
|
module ProconBypassMan
|
2
2
|
class Retryable
|
3
|
-
def self.retryable(tries: , retried: 0, on_no_retry: [])
|
3
|
+
def self.retryable(tries: , retried: 0, on_no_retry: [], log_label: nil)
|
4
4
|
return yield(retried)
|
5
5
|
rescue *on_no_retry
|
6
6
|
raise
|
7
|
-
rescue
|
7
|
+
rescue => e
|
8
8
|
if tries <= retried
|
9
9
|
raise
|
10
10
|
else
|
11
11
|
retried = retried + 1
|
12
|
+
ProconBypassMan.logger.debug "[Retryable]#{log_label && "[#{log_label}]"} #{e}が起きました。retryします。#{retried} / #{tries}"
|
13
|
+
|
12
14
|
retry
|
13
15
|
end
|
14
16
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class ProconBypassMan::WebConnectivityChecker
|
2
|
+
# @param [String, NilClass] url
|
3
|
+
def initialize(url)
|
4
|
+
@url = url
|
5
|
+
end
|
6
|
+
|
7
|
+
# @return [String]
|
8
|
+
def to_s
|
9
|
+
if @url.nil?
|
10
|
+
return "DISABLE"
|
11
|
+
end
|
12
|
+
|
13
|
+
if alive?
|
14
|
+
return "ENABLE (#{@url})"
|
15
|
+
else
|
16
|
+
return "UNREACHABLE (#{@url})"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
# @return [Boolean]
|
23
|
+
def alive?
|
24
|
+
uri = URI.parse(@url)
|
25
|
+
response = nil
|
26
|
+
|
27
|
+
begin
|
28
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
29
|
+
request = Net::HTTP::Head.new(uri)
|
30
|
+
response = http.request(request)
|
31
|
+
end
|
32
|
+
rescue StandardError => e
|
33
|
+
ProconBypassMan.logger.error e
|
34
|
+
return false
|
35
|
+
end
|
36
|
+
|
37
|
+
response.is_a?(Net::HTTPSuccess) or response.is_a?(Net::HTTPMovedPermanently)
|
38
|
+
end
|
39
|
+
end
|
data/lib/procon_bypass_man.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'singleton'
|
3
4
|
require "logger"
|
4
5
|
require 'yaml'
|
5
6
|
require "json"
|
@@ -40,6 +41,7 @@ require_relative "procon_bypass_man/support/cycle_sleep"
|
|
40
41
|
require_relative "procon_bypass_man/support/can_over_process"
|
41
42
|
require_relative "procon_bypass_man/support/retryable"
|
42
43
|
require_relative "procon_bypass_man/support/renice_command"
|
44
|
+
require_relative "procon_bypass_man/support/web_connectivity_checker"
|
43
45
|
require_relative "procon_bypass_man/procon_display"
|
44
46
|
require_relative "procon_bypass_man/background"
|
45
47
|
require_relative "procon_bypass_man/commands"
|
@@ -61,7 +63,7 @@ require_relative "procon_bypass_man/worker"
|
|
61
63
|
require_relative "procon_bypass_man/websocket/client"
|
62
64
|
require_relative "procon_bypass_man/websocket/watchdog"
|
63
65
|
require_relative "procon_bypass_man/websocket/forever"
|
64
|
-
|
66
|
+
require_relative "procon_bypass_man/external_input"
|
65
67
|
require_relative "procon_bypass_man/remote_action"
|
66
68
|
|
67
69
|
STDOUT.sync = true
|
@@ -122,6 +124,8 @@ module ProconBypassMan
|
|
122
124
|
return
|
123
125
|
end
|
124
126
|
|
127
|
+
ProconBypassMan::ExternalInput.prepare_channels
|
128
|
+
|
125
129
|
ready_pbm
|
126
130
|
Runner.new(gadget: gadget, procon: procon).run # ここでblockingする
|
127
131
|
terminate_pbm
|
@@ -130,6 +134,8 @@ module ProconBypassMan
|
|
130
134
|
# 実行ファイル(app.rb)から呼び出している
|
131
135
|
# @return [void]
|
132
136
|
def self.configure(&block)
|
137
|
+
require_relative "procon_bypass_man/external_input"
|
138
|
+
|
133
139
|
@@configuration = ProconBypassMan::Configuration.new
|
134
140
|
@@configuration.instance_eval(&block)
|
135
141
|
nil
|
data/project_template/app.rb
CHANGED
@@ -12,7 +12,11 @@ begin
|
|
12
12
|
gemfile do
|
13
13
|
source 'https://rubygems.org'
|
14
14
|
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
15
|
-
gem 'procon_bypass_man', '0.3.
|
15
|
+
gem 'procon_bypass_man', '0.3.7'
|
16
|
+
# uncomment if you want to use master branch
|
17
|
+
# gem 'procon_bypass_man', github: 'splaplapla/procon_bypass_man', branch: 'master'
|
18
|
+
# uncomment if you want to use serial communication feature
|
19
|
+
# gem "serialport"
|
16
20
|
end
|
17
21
|
rescue Bundler::Source::Git::GitCommandError => e
|
18
22
|
retry_count_on_git_command_error = retry_count_on_git_command_error + 1
|
@@ -49,6 +53,11 @@ ProconBypassMan.configure do |config|
|
|
49
53
|
|
50
54
|
# 接続に成功したらコントローラーのHOME LEDを光らせるか
|
51
55
|
config.enable_home_led_on_connect = true
|
56
|
+
|
57
|
+
# シリアル通信やTCP/IP経由で入力するときに設定してください
|
58
|
+
# config.external_input_channels = [
|
59
|
+
# ProconBypassMan::ExternalInput::Channels::SerialPortChannel.new(device_path: '/dev/serial0', baud_rate: 9600),
|
60
|
+
# ]
|
52
61
|
end
|
53
62
|
|
54
63
|
ProconBypassMan.run(setting_path: "/usr/share/pbm/current/setting.yml")
|
data/project_template/app.rb.erb
CHANGED
@@ -12,7 +12,11 @@ begin
|
|
12
12
|
gemfile do
|
13
13
|
source 'https://rubygems.org'
|
14
14
|
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
15
|
-
gem 'procon_bypass_man', '0.3.
|
15
|
+
gem 'procon_bypass_man', '0.3.7'
|
16
|
+
# uncomment if you want to use master branch
|
17
|
+
# gem 'procon_bypass_man', github: 'splaplapla/procon_bypass_man', branch: 'master'
|
18
|
+
# uncomment if you want to use serial communication feature
|
19
|
+
# gem "serialport"
|
16
20
|
end
|
17
21
|
rescue Bundler::Source::Git::GitCommandError => e
|
18
22
|
retry_count_on_git_command_error = retry_count_on_git_command_error + 1
|
@@ -54,6 +58,12 @@ ProconBypassMan.configure do |config|
|
|
54
58
|
|
55
59
|
# 接続に成功したらコントローラーのHOME LEDを光らせるか
|
56
60
|
config.enable_home_led_on_connect = true
|
61
|
+
|
62
|
+
# シリアル通信やTCP/IP経由で入力するときに設定してください
|
63
|
+
# config.external_input_channels = [
|
64
|
+
# ProconBypassMan::ExternalInput::Channels::SerialPortChannel.new(device_path: '/dev/serial0', baud_rate: 9600),
|
65
|
+
<%-# ProconBypassMan::ExternalInput::Channels::TCPIP.new(port: 9000), # TODO: まだ実装していない -%>
|
66
|
+
# ]
|
57
67
|
end
|
58
68
|
|
59
69
|
ProconBypassMan.run(setting_path: "/usr/share/pbm/current/setting.yml")
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: procon_bypass_man
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- jiikko
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pbmenv
|
@@ -95,9 +95,12 @@ files:
|
|
95
95
|
- bin/generate_default_app
|
96
96
|
- bin/generate_output_report
|
97
97
|
- bin/setup
|
98
|
+
- bin/validate_external_input
|
98
99
|
- docs/getting_started.md
|
99
100
|
- docs/how_to_connect_procon.md
|
100
101
|
- docs/run_pbm_on_my_branch.md
|
102
|
+
- docs/setting/integration_external_input_serial_port.md
|
103
|
+
- docs/setting/integration_external_input_serial_port_format.md
|
101
104
|
- docs/setting/left-analogstick-cap.md
|
102
105
|
- docs/setting/splatoon2_macro_dasei_cancel.md
|
103
106
|
- docs/setting/splatoon2_macro_forward_ikarole.md
|
@@ -176,6 +179,12 @@ files:
|
|
176
179
|
- lib/procon_bypass_man/device_model.rb
|
177
180
|
- lib/procon_bypass_man/device_status.rb
|
178
181
|
- lib/procon_bypass_man/ephemeral_configuration.rb
|
182
|
+
- lib/procon_bypass_man/external_input.rb
|
183
|
+
- lib/procon_bypass_man/external_input/channels.rb
|
184
|
+
- lib/procon_bypass_man/external_input/channels/base.rb
|
185
|
+
- lib/procon_bypass_man/external_input/channels/serial_port_channel.rb
|
186
|
+
- lib/procon_bypass_man/external_input/channels/tcpip.rb
|
187
|
+
- lib/procon_bypass_man/external_input/external_data.rb
|
179
188
|
- lib/procon_bypass_man/plugin/splatoon2/macro/charge_tansan_bomb.rb
|
180
189
|
- lib/procon_bypass_man/plugin/splatoon2/macro/dasei_cancel.rb
|
181
190
|
- lib/procon_bypass_man/plugin/splatoon2/macro/fast_return.rb
|
@@ -281,6 +290,7 @@ files:
|
|
281
290
|
- lib/procon_bypass_man/support/update_remote_pbm_job_status_http_client.rb
|
282
291
|
- lib/procon_bypass_man/support/uptime.rb
|
283
292
|
- lib/procon_bypass_man/support/usb_device_controller.rb
|
293
|
+
- lib/procon_bypass_man/support/web_connectivity_checker.rb
|
284
294
|
- lib/procon_bypass_man/support/yaml_loader.rb
|
285
295
|
- lib/procon_bypass_man/support/yaml_writer.rb
|
286
296
|
- lib/procon_bypass_man/version.rb
|