procon_bypass_man 0.3.6 → 0.3.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +4 -0
  3. data/.github/workflows/gitleacks.yml +1 -1
  4. data/.github/workflows/release.yml +1 -1
  5. data/.github/workflows/ruby.yml +2 -2
  6. data/CHANGELOG.md +10 -0
  7. data/Gemfile +1 -0
  8. data/Gemfile.lock +4 -2
  9. data/README.md +2 -2
  10. data/bin/validate_external_input +19 -0
  11. data/docs/getting_started.md +28 -0
  12. data/docs/setting/integration_external_input_serial_port.md +56 -0
  13. data/docs/setting/integration_external_input_serial_port_format.md +36 -0
  14. data/lib/procon_bypass_man/background/jobs/report_procon_performance_measurements_job.rb +1 -0
  15. data/lib/procon_bypass_man/bypass/procon_to_switch.rb +16 -1
  16. data/lib/procon_bypass_man/commands/print_boot_message_command.rb +4 -1
  17. data/lib/procon_bypass_man/configuration.rb +31 -14
  18. data/lib/procon_bypass_man/external_input/boot_message.rb +21 -0
  19. data/lib/procon_bypass_man/external_input/channels/base.rb +22 -0
  20. data/lib/procon_bypass_man/external_input/channels/serial_port_channel.rb +56 -0
  21. data/lib/procon_bypass_man/external_input/channels/tcpip_channel.rb +131 -0
  22. data/lib/procon_bypass_man/external_input/channels.rb +12 -0
  23. data/lib/procon_bypass_man/external_input/external_data.rb +79 -0
  24. data/lib/procon_bypass_man/external_input.rb +31 -0
  25. data/lib/procon_bypass_man/processor.rb +3 -2
  26. data/lib/procon_bypass_man/procon/button_collection.rb +5 -0
  27. data/lib/procon_bypass_man/procon/performance_measurement/measurements_summarizer.rb +4 -0
  28. data/lib/procon_bypass_man/procon/performance_measurement.rb +7 -1
  29. data/lib/procon_bypass_man/procon.rb +17 -2
  30. data/lib/procon_bypass_man/procon_display/status.rb +0 -2
  31. data/lib/procon_bypass_man/support/forever.rb +51 -0
  32. data/lib/procon_bypass_man/support/proccess_cheacker.rb +14 -0
  33. data/lib/procon_bypass_man/support/retryable.rb +5 -2
  34. data/lib/procon_bypass_man/support/simple_tcp_server.rb +63 -0
  35. data/lib/procon_bypass_man/support/watchdog.rb +23 -0
  36. data/lib/procon_bypass_man/support/web_connectivity_checker.rb +41 -0
  37. data/lib/procon_bypass_man/version.rb +1 -1
  38. data/lib/procon_bypass_man/websocket/client.rb +11 -9
  39. data/lib/procon_bypass_man.rb +16 -3
  40. data/project_template/app.rb +11 -1
  41. data/project_template/app.rb.erb +11 -1
  42. data/sig/main.rbs +2 -2
  43. metadata +17 -4
  44. data/lib/procon_bypass_man/websocket/forever.rb +0 -47
  45. data/lib/procon_bypass_man/websocket/watchdog.rb +0 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f19608c134a980243f4e45da1a1685367dd4bffc114482682b5d8d6bef03a90
4
- data.tar.gz: c0ce801d735e63e5d6a385854ad83abb08fe044342bda63b5d70d2d50b3589f4
3
+ metadata.gz: 1933db5fe8c39267f980712a5477a457277cdd3ce9148a81966c1e1d818805e8
4
+ data.tar.gz: 404ce5c7ada74a92afb5abf2b2f583c3fe1c87bbc4d3ae2372e0ff3f961d9245
5
5
  SHA512:
6
- metadata.gz: 11e84f9b7fa368adc1cbbb8593aca61d2a1a362453a13c5b226912d490c82933d476072f11ecb36226657107aa68b08be734ac19ec8901b56a00deb980d218b9
7
- data.tar.gz: 642a6813619dccbb9d28f634bb7bb866ec92a2ed34fac254336562ed462512b1e9eb6ec2672dae17727e9c6790651f4e481e24a9a919dbd87381e4a457c62796
6
+ metadata.gz: eeeb32ff42fc02d2b40b19be2a1eba55a9fc5ffc6a3f08af47b4ed86bb67edba60dc7ef409f892ba9ef21953a349abda01a28d04f4a4f99f0032382a68c2f904
7
+ data.tar.gz: 811f537dd6ea2659678911678022c02734a2a21ef099d7ed140067a56e88edf0fabc4aec8264c052e8e8e9759c54085a17ab7a72c668a338560250b880395e2f
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:
@@ -7,6 +7,6 @@ jobs:
7
7
  runs-on: ubuntu-latest
8
8
  timeout-minutes: 5
9
9
  steps:
10
- - uses: actions/checkout@v1
10
+ - uses: actions/checkout@v3
11
11
  - name: gitleaks-action
12
12
  uses: zricethezav/gitleaks-action@v1.6.0
@@ -11,7 +11,7 @@ jobs:
11
11
  contents: write
12
12
 
13
13
  steps:
14
- - uses: actions/checkout@v2
14
+ - uses: actions/checkout@v3
15
15
  with:
16
16
  token: ${{ secrets.GITHUB_TOKEN }}
17
17
  - name: Set up Ruby 3.0.1
@@ -16,10 +16,10 @@ jobs:
16
16
  timeout-minutes: 5
17
17
  strategy:
18
18
  matrix:
19
- ruby-version: ['3.0.1', '3.1.1']
19
+ ruby-version: ['3.0', '3.1', '3.2']
20
20
 
21
21
  steps:
22
- - uses: actions/checkout@v2
22
+ - uses: actions/checkout@v3
23
23
  - name: Set up Ruby
24
24
  # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
25
25
  # change this to (see https://github.com/ruby/setup-ruby#versioning):
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## [0.3.8] 2023-05-03
2
+ * TCP/IP経由でPBMに入力ができるようになりました
3
+ * 多重起動ができないようにしました
4
+ * pbm-cloudとの連携時に、pbmenvを使っているかの判定を修正しました
5
+ * actioncable(websocket)serverのURLをpbm-cloudから取得するようにしました
6
+
7
+ ## [0.3.7] 2023-04-06
8
+ * PBM-Cloudと連携しているかをbootメッセージに表示ようになりました
9
+ * シリアルポート経由でPBMに入力ができるようになりました
10
+
1
11
  ## [0.3.6] 2023-03-12
2
12
  - プロコンの入力状態をpbm-cloudサーバに送信できるようになりました
3
13
 
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.6)
4
+ procon_bypass_man (0.3.8)
5
5
  action_cable_client
6
6
  blue_green_process (= 0.1.4.2)
7
7
  pbmenv (>= 0.1.11)
@@ -38,7 +38,7 @@ GEM
38
38
  parallel (1.21.0)
39
39
  parser (3.0.3.2)
40
40
  ast (~> 2.4.1)
41
- pbmenv (0.1.11)
41
+ pbmenv (0.1.12)
42
42
  pry (0.14.1)
43
43
  coderay (~> 1.1)
44
44
  method_source (~> 1.0)
@@ -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/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
 
12
12
  https://user-images.githubusercontent.com/1664497/171327108-f12f56a5-fc36-48da-95a5-65e976553a20.mov
13
13
 
14
- ## 使うハードウェア
14
+ ## 必要なハードウェア
15
15
  * Nintendo Switch Proコントローラー
16
16
  * Switch本体とドック
17
17
  * Raspberry Pi4 (Raspberry Pi OS)
@@ -38,7 +38,7 @@ https://user-images.githubusercontent.com/1664497/171327108-f12f56a5-fc36-48da-9
38
38
  * レイヤーを切り替える方法は?
39
39
  * 設定ファイルに記述している `prefix_keys_for_changing_layer`の後ろにあるキーを同時押しながら、十字キーのどれかを押すことで任意のレイヤーに切り替わります
40
40
  * このツールでできることは?
41
- * キーリマップ, 連射, マクロ
41
+ * キーリマップ, 連射, マクロ, 外部ツールからの入力
42
42
  * リマップは1つのキーを別のキーに割り当てます
43
43
  * 連射中には特定のキーの入力を無視したり、複数のキーをトリガーに連射することができます
44
44
  * どうしてsudoが必要なの?
@@ -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
@@ -18,6 +18,8 @@
18
18
  * [設定ファイルの書き方がわからない、エラーが起きるとき](#設定ファイルの書き方がわからない、エラーが起きるとき)
19
19
  * [procon_bypass_manのアップグレード方法](#procon_bypass_manのアップグレード方法)
20
20
  * [procon_bypass_man_cloudについて](#procon_bypass_man_cloudについて)
21
+ * [シリアルポート連携](#シリアルポート連携)
22
+ * [TCPIP連携](#TCPIP連携)
21
23
  * [最適化について](#最適化について)
22
24
 
23
25
  ## はじめに
@@ -251,6 +253,32 @@ procon_bypass_man_cloudとの接続が完了後、Raspberry Piを起動時にpro
251
253
 
252
254
  セットアップ方法は https://pbm-cloud.jiikko.com/faq に書いています。
253
255
 
256
+ ## シリアルポート連携
257
+ * [ラズベリーパイのシリアルポート(GPIO)へ書き込んでPBM経由してSwitchへ入力をする方法](/docs/setting/integration_external_input_serial_port.md)
258
+ * [ラズベリーパイのシリアルポート(GPIO)に書き込むフォーマットについて](/docs/setting/integration_external_input_serial_port_format.md)
259
+
260
+ ## TCPIP連携
261
+ procon_bypass_man: 0.3.8 からTCP/IP経由で入力ができるようになりました。
262
+ 書き込みフォーマットはJSONのみに対応しています。JSONの詳細な仕様については [ラズベリーパイのシリアルポート(GPIO)に書き込むフォーマットについて](/docs/setting/integration_external_input_serial_port_format.md) を参照してください。
263
+
264
+ 次は、PBM側の設定方法についてです。app.rbに以下の行を追加すると、連携するためのTCPサーバを起動するようになります。
265
+
266
+ ```diff
267
+ + config.external_input_channels = [
268
+ + ProconBypassMan::ExternalInput::Channels::TCPIPChannel.new(port: 9000),
269
+ + ]
270
+ ```
271
+
272
+ クライアント側のサンプル実装は次の通りです。これを実行するとAボタンを入力します。
273
+
274
+ ```ruby
275
+ socket = TCPSocket.new('ras1.local', 9000)
276
+ json = { buttons: [:a] }.to_json
277
+ message = "#{json}\r\n"
278
+ socket.write(message)
279
+ puts socket.gets
280
+ ```
281
+
254
282
  ## 最適化について
255
283
  本稿では、Rubyの最適化について書きます。上級者向けです。適用しなくても普通に動きますが、逆に適用したことで何らかのケースで遅くなる場合があるかもしれません。
256
284
 
@@ -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
 
@@ -10,7 +10,7 @@ class ProconBypassMan::PrintBootMessageCommand
10
10
  @table[:pid_path] = ProconBypassMan.pid_path
11
11
  @table[:setting_path] = ProconBypassMan::ButtonsSettingConfiguration.instance.setting_path
12
12
  @table[:uptime_from_boot] = ProconBypassMan::Uptime.from_boot
13
- @table[:use_pbmenv] = !(!!`which pbmenv`.empty?)
13
+ @table[:use_pbmenv] = ProconBypassMan.root.start_with?(Pbmenv::PBM_DIR)
14
14
  @table[:session_id] = ProconBypassMan.session_id
15
15
  @table[:device_id] = ProconBypassMan.device_id
16
16
  @table[:never_exit_accidentally] = ProconBypassMan.config.never_exit_accidentally
@@ -34,6 +34,8 @@ 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, ProconBypassMan.config.current_ws_server_url)}
38
+ ExternalInput Integration: #{ProconBypassMan::ExternalInput::BootMessage.new(channels: ProconBypassMan::ExternalInput.channels)}
37
39
  pid: #{@table[:pid]}
38
40
  root: #{@table[:root_path]}
39
41
  pid_path: #{@table[:pid_path]}
@@ -56,6 +58,7 @@ class ProconBypassMan::PrintBootMessageCommand
56
58
  def self.execute
57
59
  message = BootMessage.new
58
60
  ProconBypassMan::ReportBootJob.perform_async(message.to_hash)
61
+ ProconBypassMan.logger.info message.to_s
59
62
  puts message.to_s
60
63
  end
61
64
  end
@@ -19,9 +19,11 @@ class ProconBypassMan::Configuration
19
19
  @@pid_path ||= File.expand_path("#{root}/pbm_pid", __dir__).freeze
20
20
  end
21
21
 
22
- # @return [Integer]
22
+ # @return [Integer, nil]
23
23
  def pid
24
24
  File.read(pid_path).to_i
25
+ rescue Errno::ENOENT
26
+ nil
25
27
  end
26
28
 
27
29
  def digest_path
@@ -54,6 +56,7 @@ class ProconBypassMan::Configuration
54
56
 
55
57
  attr_accessor :enable_critical_error_logging
56
58
  attr_writer :verbose_bypass_log, :raw_setting, :never_exit_accidentally, :enable_home_led_on_connect
59
+ attr_writer :external_input_channels
57
60
  # 削除予定
58
61
  attr_writer :enable_reporting_pressed_buttons
59
62
 
@@ -69,6 +72,7 @@ class ProconBypassMan::Configuration
69
72
  if defined?(@root)
70
73
  @root
71
74
  else
75
+ ProconBypassMan.logger.warn 'root pathが未設定です'
72
76
  File.expand_path('..', __dir__ || ".").freeze
73
77
  end
74
78
  end
@@ -125,22 +129,30 @@ class ProconBypassMan::Configuration
125
129
  end
126
130
 
127
131
  # @return [String, NilClass]
128
- def current_ws_server
129
- if (uri = URI.parse(api_server))
130
- if uri.port == 443
131
- return "ws://#{uri.host}"
132
+ def current_ws_server_url
133
+ return @current_ws_server_url if defined?(@current_ws_server_url)
134
+ return unless api_server
135
+
136
+ response_json = ProconBypassMan::HttpClient.new(server: api_server, path: '/api/v1/configuration').get
137
+ ws_server_url = response_json&.fetch("ws_server_url", nil)
138
+
139
+ begin
140
+ uri = URI.parse(ws_server_url)
141
+ if uri.scheme == 'ws'
142
+ @current_ws_server_url = uri.to_s
143
+ return @current_ws_server_url
144
+ elsif uri.scheme == 'wss' # NOTE: SSL_CTX_use_certificate: ee key too small (OpenSSL::SSL::SSLError) が起きるので
145
+ uri.scheme = 'ws'
146
+ @current_ws_server_url = uri.to_s
147
+ return @current_ws_server_url
132
148
  else
133
- return "ws://#{uri.host}:#{uri.port}"
149
+ ProconBypassMan.logger.warn { "#{ws_server_url} is invalid." }
150
+ return nil
134
151
  end
152
+ rescue URI::InvalidURIError => e
153
+ ProconBypassMan.logger.warn { "#{ws_server_url} is invalid. #{e}" }
154
+ nil
135
155
  end
136
- rescue URI::InvalidURIError
137
- nil
138
- end
139
-
140
- # @return [String, NilClass]
141
- def current_ws_server_url
142
- return unless current_ws_server
143
- "#{current_ws_server}/websocket/"
144
156
  end
145
157
 
146
158
  # @return [Boolean]
@@ -193,4 +205,9 @@ class ProconBypassMan::Configuration
193
205
  true
194
206
  end
195
207
  end
208
+
209
+ # @return [Array<ProconBypassMan::ExternalInput::Channel::TCPIP, ProconBypassMan::ExternalInput::Channel::SerialPort>]
210
+ def external_input_channels
211
+ @external_input_channels || []
212
+ end
196
213
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ProconBypassMan
4
+ module ExternalInput
5
+ class BootMessage
6
+ # @return [ProconBypassMan::ExternalInput::Channels::Base]
7
+ def initialize(channels: )
8
+ @channels = channels
9
+ end
10
+
11
+ # @return [String]
12
+ def to_s
13
+ if @channels.nil? or @channels.empty?
14
+ return 'DISABLE'
15
+ end
16
+
17
+ @channels.map(&:display_name_for_boot_message).join(', ')
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ module ProconBypassMan
2
+ module ExternalInput
3
+ module Channels
4
+ class Base
5
+ # @return [String, NilClass]
6
+ def read
7
+ raise NotImplementedError
8
+ end
9
+
10
+ # @return [void]
11
+ def shutdown
12
+ raise NotImplementedError
13
+ end
14
+
15
+ # @return [String]
16
+ def display_name_for_boot_message
17
+ raise NotImplementedError
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,56 @@
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
+
46
+ def shutdown
47
+ # no-op
48
+ end
49
+
50
+ def display_name_for_boot_message
51
+ 'SerialPort'
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end