procon_bypass_man 0.3.7 → 0.3.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/gitleacks.yml +1 -1
- data/.github/workflows/release.yml +1 -1
- data/.github/workflows/ruby.yml +2 -2
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +2 -2
- data/README.md +2 -2
- data/bin/console +0 -3
- data/docs/getting_started.md +25 -2
- data/lib/procon_bypass_man/commands/print_boot_message_command.rb +3 -2
- data/lib/procon_bypass_man/configuration.rb +25 -14
- data/lib/procon_bypass_man/external_input/boot_message.rb +21 -0
- data/lib/procon_bypass_man/external_input/channels/base.rb +10 -0
- data/lib/procon_bypass_man/external_input/channels/serial_port_channel.rb +9 -1
- data/lib/procon_bypass_man/external_input/channels/tcpip_channel.rb +131 -0
- data/lib/procon_bypass_man/external_input/channels.rb +1 -1
- data/lib/procon_bypass_man/external_input.rb +5 -9
- data/lib/procon_bypass_man/support/forever.rb +51 -0
- data/lib/procon_bypass_man/support/proccess_cheacker.rb +14 -0
- data/lib/procon_bypass_man/support/retryable.rb +2 -1
- data/lib/procon_bypass_man/support/simple_tcp_server.rb +63 -0
- data/lib/procon_bypass_man/support/watchdog.rb +23 -0
- data/lib/procon_bypass_man/support/web_connectivity_checker.rb +4 -2
- data/lib/procon_bypass_man/version.rb +1 -1
- data/lib/procon_bypass_man/websocket/client.rb +11 -9
- data/lib/procon_bypass_man.rb +11 -4
- data/project_template/app.rb +2 -1
- data/project_template/app.rb.erb +2 -2
- data/sig/main.rbs +2 -2
- metadata +8 -5
- data/lib/procon_bypass_man/external_input/channels/tcpip.rb +0 -16
- data/lib/procon_bypass_man/websocket/forever.rb +0 -47
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1933db5fe8c39267f980712a5477a457277cdd3ce9148a81966c1e1d818805e8
|
4
|
+
data.tar.gz: 404ce5c7ada74a92afb5abf2b2f583c3fe1c87bbc4d3ae2372e0ff3f961d9245
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eeeb32ff42fc02d2b40b19be2a1eba55a9fc5ffc6a3f08af47b4ed86bb67edba60dc7ef409f892ba9ef21953a349abda01a28d04f4a4f99f0032382a68c2f904
|
7
|
+
data.tar.gz: 811f537dd6ea2659678911678022c02734a2a21ef099d7ed140067a56e88edf0fabc4aec8264c052e8e8e9759c54085a17ab7a72c668a338560250b880395e2f
|
data/.github/workflows/ruby.yml
CHANGED
@@ -16,10 +16,10 @@ jobs:
|
|
16
16
|
timeout-minutes: 5
|
17
17
|
strategy:
|
18
18
|
matrix:
|
19
|
-
ruby-version: ['3.0.1', '3.
|
19
|
+
ruby-version: ['3.0', '3.1', '3.2']
|
20
20
|
|
21
21
|
steps:
|
22
|
-
- uses: actions/checkout@
|
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,9 @@
|
|
1
|
+
## [0.3.8] 2023-05-03
|
2
|
+
* TCP/IP経由でPBMに入力ができるようになりました
|
3
|
+
* 多重起動ができないようにしました
|
4
|
+
* pbm-cloudとの連携時に、pbmenvを使っているかの判定を修正しました
|
5
|
+
* actioncable(websocket)serverのURLをpbm-cloudから取得するようにしました
|
6
|
+
|
1
7
|
## [0.3.7] 2023-04-06
|
2
8
|
* PBM-Cloudと連携しているかをbootメッセージに表示ようになりました
|
3
9
|
* シリアルポート経由でPBMに入力ができるようになりました
|
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.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.
|
41
|
+
pbmenv (0.1.12)
|
42
42
|
pry (0.14.1)
|
43
43
|
coderay (~> 1.1)
|
44
44
|
method_source (~> 1.0)
|
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が必要なの?
|
data/bin/console
CHANGED
data/docs/getting_started.md
CHANGED
@@ -18,7 +18,8 @@
|
|
18
18
|
* [設定ファイルの書き方がわからない、エラーが起きるとき](#設定ファイルの書き方がわからない、エラーが起きるとき)
|
19
19
|
* [procon_bypass_manのアップグレード方法](#procon_bypass_manのアップグレード方法)
|
20
20
|
* [procon_bypass_man_cloudについて](#procon_bypass_man_cloudについて)
|
21
|
-
* [
|
21
|
+
* [シリアルポート連携](#シリアルポート連携)
|
22
|
+
* [TCPIP連携](#TCPIP連携)
|
22
23
|
* [最適化について](#最適化について)
|
23
24
|
|
24
25
|
## はじめに
|
@@ -252,10 +253,32 @@ procon_bypass_man_cloudとの接続が完了後、Raspberry Piを起動時にpro
|
|
252
253
|
|
253
254
|
セットアップ方法は https://pbm-cloud.jiikko.com/faq に書いています。
|
254
255
|
|
255
|
-
##
|
256
|
+
## シリアルポート連携
|
256
257
|
* [ラズベリーパイのシリアルポート(GPIO)へ書き込んでPBM経由してSwitchへ入力をする方法](/docs/setting/integration_external_input_serial_port.md)
|
257
258
|
* [ラズベリーパイのシリアルポート(GPIO)に書き込むフォーマットについて](/docs/setting/integration_external_input_serial_port_format.md)
|
258
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
|
+
|
259
282
|
## 最適化について
|
260
283
|
本稿では、Rubyの最適化について書きます。上級者向けです。適用しなくても普通に動きますが、逆に適用したことで何らかのケースで遅くなる場合があるかもしれません。
|
261
284
|
|
@@ -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] =
|
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,7 +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)}
|
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)}
|
38
39
|
pid: #{@table[:pid]}
|
39
40
|
root: #{@table[:root_path]}
|
40
41
|
pid_path: #{@table[:pid_path]}
|
@@ -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
|
@@ -70,6 +72,7 @@ class ProconBypassMan::Configuration
|
|
70
72
|
if defined?(@root)
|
71
73
|
@root
|
72
74
|
else
|
75
|
+
ProconBypassMan.logger.warn 'root pathが未設定です'
|
73
76
|
File.expand_path('..', __dir__ || ".").freeze
|
74
77
|
end
|
75
78
|
end
|
@@ -126,22 +129,30 @@ class ProconBypassMan::Configuration
|
|
126
129
|
end
|
127
130
|
|
128
131
|
# @return [String, NilClass]
|
129
|
-
def
|
130
|
-
|
131
|
-
|
132
|
-
|
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
|
133
148
|
else
|
134
|
-
|
149
|
+
ProconBypassMan.logger.warn { "#{ws_server_url} is invalid." }
|
150
|
+
return nil
|
135
151
|
end
|
152
|
+
rescue URI::InvalidURIError => e
|
153
|
+
ProconBypassMan.logger.warn { "#{ws_server_url} is invalid. #{e}" }
|
154
|
+
nil
|
136
155
|
end
|
137
|
-
rescue URI::InvalidURIError
|
138
|
-
nil
|
139
|
-
end
|
140
|
-
|
141
|
-
# @return [String, NilClass]
|
142
|
-
def current_ws_server_url
|
143
|
-
return unless current_ws_server
|
144
|
-
"#{current_ws_server}/websocket/"
|
145
156
|
end
|
146
157
|
|
147
158
|
# @return [Boolean]
|
@@ -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
|
@@ -6,6 +6,16 @@ module ProconBypassMan
|
|
6
6
|
def read
|
7
7
|
raise NotImplementedError
|
8
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
|
9
19
|
end
|
10
20
|
end
|
11
21
|
end
|
@@ -14,7 +14,7 @@ module ProconBypassMan
|
|
14
14
|
data_bits = 8
|
15
15
|
stop_bits = 1
|
16
16
|
parity = SerialPort::NONE
|
17
|
-
@serial_port= SerialPort.new(device_path, baud_rate, data_bits, stop_bits, parity)
|
17
|
+
@serial_port = SerialPort.new(device_path, baud_rate, data_bits, stop_bits, parity)
|
18
18
|
end
|
19
19
|
|
20
20
|
# @return [String, NilClass]
|
@@ -42,6 +42,14 @@ module ProconBypassMan
|
|
42
42
|
end
|
43
43
|
chunks.first
|
44
44
|
end
|
45
|
+
|
46
|
+
def shutdown
|
47
|
+
# no-op
|
48
|
+
end
|
49
|
+
|
50
|
+
def display_name_for_boot_message
|
51
|
+
'SerialPort'
|
52
|
+
end
|
45
53
|
end
|
46
54
|
end
|
47
55
|
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module ProconBypassMan
|
2
|
+
module ExternalInput
|
3
|
+
module Channels
|
4
|
+
class TCPIPChannel < Base
|
5
|
+
class ShutdownSignal < StandardError; end
|
6
|
+
|
7
|
+
class AppServer < SimpleTCPServer
|
8
|
+
@command_queue = Queue.new
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :command_queue
|
12
|
+
end
|
13
|
+
|
14
|
+
def post_init
|
15
|
+
ProconBypassMan.logger.info { "[ExternalInput][TCPIPChannel] A client has connected" }
|
16
|
+
end
|
17
|
+
|
18
|
+
def unbind
|
19
|
+
ProconBypassMan.logger.info { "[ExternalInput][TCPIPChannel] A client has disconnected" }
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [String]
|
23
|
+
def receive_data(client, data)
|
24
|
+
case data
|
25
|
+
when /^{/
|
26
|
+
self.class.command_queue.push(data)
|
27
|
+
client.write("OK\n")
|
28
|
+
return
|
29
|
+
when /^\n/
|
30
|
+
if self.class.command_queue.empty?
|
31
|
+
client.write("EMPTY\n")
|
32
|
+
return
|
33
|
+
end
|
34
|
+
|
35
|
+
data = self.class.command_queue.pop
|
36
|
+
client.write("#{data}\n")
|
37
|
+
return
|
38
|
+
else
|
39
|
+
client.write("Unknown command\n")
|
40
|
+
return
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(port: )
|
46
|
+
@port = port
|
47
|
+
super()
|
48
|
+
|
49
|
+
begin
|
50
|
+
@server = AppServer.new('0.0.0.0', @port)
|
51
|
+
rescue Errno::EADDRINUSE # NOTE: Address already in use - bind(2) for "0.0.0.0" port XXXX
|
52
|
+
ProconBypassMan::SendErrorCommand.execute(error: "[ExternalInput][TCPIPChannel] 起動に失敗しました。#{e.message}")
|
53
|
+
@server_thread = Thread.start {}
|
54
|
+
return
|
55
|
+
end
|
56
|
+
|
57
|
+
# NOTE: masterプロセスで起動する
|
58
|
+
@server_thread = Thread.start do
|
59
|
+
begin
|
60
|
+
loop do
|
61
|
+
@server.start_server
|
62
|
+
@server.run
|
63
|
+
end
|
64
|
+
rescue Errno::EPIPE, EOFError, Errno::ECONNRESET => e
|
65
|
+
ProconBypassMan::SendErrorCommand.execute(error: "[ExternalInput][TCPIPChannel] #{e.message}(#{e})")
|
66
|
+
sleep(5)
|
67
|
+
|
68
|
+
@server.shutdown
|
69
|
+
retry
|
70
|
+
rescue ShutdownSignal => e
|
71
|
+
ProconBypassMan::SendErrorCommand.execute(error: "[ExternalInput][TCPIPChannel] ShutdownSignalを受け取りました。終了します。")
|
72
|
+
@server.shutdown
|
73
|
+
rescue => e
|
74
|
+
ProconBypassMan::SendErrorCommand.execute(error: "[ExternalInput][TCPIPChannel] #{e.message}(#{e})")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# NOTE: bypassプロセスから呼ばれ、masterプロセスへ繋ぐ
|
80
|
+
# @return [String, NilClass]
|
81
|
+
def read
|
82
|
+
@socket ||= TCPSocket.new('0.0.0.0', @port)
|
83
|
+
read_command = "\n"
|
84
|
+
@socket.write(read_command)
|
85
|
+
response = @socket.gets&.strip
|
86
|
+
# ProconBypassMan.logger.debug { "Received: #{response}" }
|
87
|
+
|
88
|
+
case response
|
89
|
+
when /^{/
|
90
|
+
return response
|
91
|
+
when /^EMPTY/, ''
|
92
|
+
return nil
|
93
|
+
else
|
94
|
+
ProconBypassMan.logger.warn { "[ExternalInput][TCPIPChannel] Unknown response(#{response}, codepoints: #{response.codepoints})" }
|
95
|
+
return nil
|
96
|
+
end
|
97
|
+
rescue Errno::EPIPE, EOFError => e
|
98
|
+
@socket = nil
|
99
|
+
sleep(10)
|
100
|
+
ProconBypassMan.logger.error { "[ExternalInput][TCPIPChannel] #{e.message}!!!!!!!(#{e})" }
|
101
|
+
retry
|
102
|
+
rescue => e
|
103
|
+
@socket = nil
|
104
|
+
ProconBypassMan.logger.error { "[ExternalInput][TCPIPChannel] #{e.message} が起きました(#{e})" }
|
105
|
+
return nil
|
106
|
+
end
|
107
|
+
|
108
|
+
def shutdown
|
109
|
+
ProconBypassMan.logger.info { "[ExternalInput][TCPIPChannel] shutdown" }
|
110
|
+
@server_thread.raise(ShutdownSignal)
|
111
|
+
end
|
112
|
+
|
113
|
+
def alive_server?
|
114
|
+
return false if not @server_thread.alive?
|
115
|
+
|
116
|
+
begin
|
117
|
+
TCPSocket.new('0.0.0.0', @port).close
|
118
|
+
rescue Errno::ECONNREFUSED, Errno::ECONNRESET
|
119
|
+
return false
|
120
|
+
end
|
121
|
+
|
122
|
+
true
|
123
|
+
end
|
124
|
+
|
125
|
+
def display_name_for_boot_message
|
126
|
+
"TCPIP(port: #{@port})"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -4,25 +4,20 @@ module ProconBypassMan
|
|
4
4
|
module ExternalInput
|
5
5
|
class ParseError < StandardError; end
|
6
6
|
|
7
|
-
@@channels = nil
|
8
|
-
|
9
7
|
# @return [Array<ProconBypassMan::ExternalInput::Channels::Base>]
|
10
8
|
def self.channels
|
11
|
-
@@channels
|
9
|
+
@@channels ||= ProconBypassMan.config.external_input_channels
|
12
10
|
end
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
@@channels = ProconBypassMan.config.external_input_channels
|
12
|
+
def self.shutdown
|
13
|
+
channels.each(&:shutdown)
|
17
14
|
end
|
18
15
|
|
19
16
|
# @return [NilClass, String]
|
20
17
|
# NOTE: 外部入力からのreadがボトルネックになるなら、Concurrent::Futureを使ってプロコンからの読み出しと並列化することを検討する
|
21
18
|
def self.read
|
22
|
-
raise '外部入力が未初期化です' if @@channels.nil?
|
23
|
-
|
24
19
|
value = nil
|
25
|
-
|
20
|
+
channels.each do |channel|
|
26
21
|
value = channel.read
|
27
22
|
break if value
|
28
23
|
end
|
@@ -33,3 +28,4 @@ end
|
|
33
28
|
|
34
29
|
require "procon_bypass_man/external_input/external_data"
|
35
30
|
require "procon_bypass_man/external_input/channels.rb"
|
31
|
+
require "procon_bypass_man/external_input/boot_message"
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module ProconBypassMan
|
2
|
+
class Forever
|
3
|
+
# 動作確認方法
|
4
|
+
# - 10秒ごとにrefreshするのでタイムアウトは起きない
|
5
|
+
# - ProconBypassMan::Forever.run { |watchdog| loop { puts(:hi); sleep(10); watchdog.active! } }
|
6
|
+
# - タイムアウトが起きること
|
7
|
+
# - ProconBypassMan::Forever.run { |watchdog| loop { puts(:hi); sleep(10); } }
|
8
|
+
def self.run(&block)
|
9
|
+
loop do
|
10
|
+
new.run(&block)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [void]
|
15
|
+
def run(&block)
|
16
|
+
raise(ArgumentError, "need a block") unless block_given?
|
17
|
+
|
18
|
+
thread, watchdog = work_one(callable: block)
|
19
|
+
wait_and_kill_if_outdated(thread, watchdog)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param [Proc] callable
|
23
|
+
# @return [Array<Thread, ProconBypassMan::Watchdog>]
|
24
|
+
def work_one(callable: )
|
25
|
+
watchdog = ProconBypassMan::Watchdog.new
|
26
|
+
thread = Thread.start do
|
27
|
+
callable.call(watchdog)
|
28
|
+
rescue => e
|
29
|
+
ProconBypassMan.logger.error("[Forever] #{e.full_message}")
|
30
|
+
end
|
31
|
+
|
32
|
+
return [thread, watchdog]
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param [ProconBypassMan::Watchdog] watchdog
|
36
|
+
# @param [Thread] thread
|
37
|
+
# @return [void]
|
38
|
+
def wait_and_kill_if_outdated(thread, watchdog)
|
39
|
+
loop do
|
40
|
+
if watchdog.outdated?
|
41
|
+
watchdog.active!
|
42
|
+
ProconBypassMan.logger.error("watchdog timeout!!")
|
43
|
+
thread.kill
|
44
|
+
return
|
45
|
+
end
|
46
|
+
|
47
|
+
sleep(10)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module ProconBypassMan
|
2
2
|
class Retryable
|
3
|
-
def self.retryable(tries: , retried: 0, on_no_retry: [], log_label: nil)
|
3
|
+
def self.retryable(tries: , retried: 0, on_no_retry: [], log_label: nil, interval_on_retry: 0)
|
4
4
|
return yield(retried)
|
5
5
|
rescue *on_no_retry
|
6
6
|
raise
|
@@ -11,6 +11,7 @@ module ProconBypassMan
|
|
11
11
|
retried = retried + 1
|
12
12
|
ProconBypassMan.logger.debug "[Retryable]#{log_label && "[#{log_label}]"} #{e}が起きました。retryします。#{retried} / #{tries}"
|
13
13
|
|
14
|
+
sleep(interval_on_retry)
|
14
15
|
retry
|
15
16
|
end
|
16
17
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
class SimpleTCPServer
|
4
|
+
def initialize(host, port)
|
5
|
+
@host = host
|
6
|
+
@port = port
|
7
|
+
end
|
8
|
+
|
9
|
+
def start_server
|
10
|
+
@connections = []
|
11
|
+
@server = TCPServer.new(@host, @port)
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
loop do
|
16
|
+
timeout = 5 # 5秒のタイムアウト
|
17
|
+
readable, _ = IO.select(@connections + [@server], nil, nil, timeout)
|
18
|
+
next if readable.nil? # timeoutを迎えるとnilになる
|
19
|
+
|
20
|
+
readable.each do |socket|
|
21
|
+
if socket == @server
|
22
|
+
client = @server.accept
|
23
|
+
post_init
|
24
|
+
@connections << client
|
25
|
+
else
|
26
|
+
data = socket.gets
|
27
|
+
if data.nil?
|
28
|
+
@connections.delete(socket)
|
29
|
+
unbind
|
30
|
+
socket.close
|
31
|
+
else
|
32
|
+
receive_data(socket, data)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
rescue Errno::EBADF, IOError => e
|
37
|
+
unbind
|
38
|
+
@connections = []
|
39
|
+
@server.close
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def shutdown
|
44
|
+
@server.close
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [Integer]
|
48
|
+
def connections_size
|
49
|
+
@connections.size
|
50
|
+
end
|
51
|
+
|
52
|
+
def post_init
|
53
|
+
# Override this method
|
54
|
+
end
|
55
|
+
|
56
|
+
def receive_data(socket, data)
|
57
|
+
# Override this method
|
58
|
+
end
|
59
|
+
|
60
|
+
def unbind
|
61
|
+
# Override this method
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ProconBypassMan
|
2
|
+
class Watchdog
|
3
|
+
def initialize(timeout: 100)
|
4
|
+
@timeout = timeout
|
5
|
+
active!
|
6
|
+
end
|
7
|
+
|
8
|
+
# @return [Boolean]
|
9
|
+
def outdated?
|
10
|
+
@time < Time.now
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Time]
|
14
|
+
def time
|
15
|
+
@time
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [void]
|
19
|
+
def active!
|
20
|
+
@time = Time.now + @timeout
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
class ProconBypassMan::WebConnectivityChecker
|
2
2
|
# @param [String, NilClass] url
|
3
|
-
|
3
|
+
# @param [String, NilClass] ws_url
|
4
|
+
def initialize(url, ws_url)
|
4
5
|
@url = url
|
6
|
+
@ws_url = ws_url
|
5
7
|
end
|
6
8
|
|
7
9
|
# @return [String]
|
@@ -11,7 +13,7 @@ class ProconBypassMan::WebConnectivityChecker
|
|
11
13
|
end
|
12
14
|
|
13
15
|
if alive?
|
14
|
-
return "ENABLE (#{@url})"
|
16
|
+
return "ENABLE (#{@url}, #{@ws_url})"
|
15
17
|
else
|
16
18
|
return "UNREACHABLE (#{@url})"
|
17
19
|
end
|
@@ -7,11 +7,13 @@ module ProconBypassMan
|
|
7
7
|
return unless ProconBypassMan.config.enable_ws?
|
8
8
|
|
9
9
|
Thread.start do
|
10
|
-
Forever.run
|
10
|
+
ProconBypassMan::Forever.run do |watchdog|
|
11
|
+
run(watchdog: watchdog)
|
12
|
+
end
|
11
13
|
end
|
12
14
|
end
|
13
15
|
|
14
|
-
def self.run
|
16
|
+
def self.run(watchdog: )
|
15
17
|
EventMachine.run do
|
16
18
|
client = ActionCableClient.new(
|
17
19
|
ProconBypassMan.config.current_ws_server_url, {
|
@@ -20,15 +22,15 @@ module ProconBypassMan
|
|
20
22
|
)
|
21
23
|
|
22
24
|
client.connected {
|
23
|
-
ProconBypassMan.logger.info('
|
25
|
+
ProconBypassMan.logger.info('[WebsocketClient] successfully connected in ProconBypassMan::Websocket::Client')
|
24
26
|
}
|
25
27
|
client.subscribed { |msg|
|
26
|
-
ProconBypassMan.logger.info("
|
28
|
+
ProconBypassMan.logger.info("[WebsocketClient] subscribed(#{msg})")
|
27
29
|
ProconBypassMan::SyncDeviceStatsJob.perform(ProconBypassMan::DeviceStatus.current)
|
28
30
|
}
|
29
31
|
|
30
32
|
client.received do |data|
|
31
|
-
ProconBypassMan.logger.info('
|
33
|
+
ProconBypassMan.logger.info('[WebsocketClient] received!!')
|
32
34
|
ProconBypassMan.logger.info(data)
|
33
35
|
|
34
36
|
dispatch(data: data, client: client)
|
@@ -37,20 +39,20 @@ module ProconBypassMan
|
|
37
39
|
end
|
38
40
|
|
39
41
|
client.disconnected {
|
40
|
-
ProconBypassMan.logger.info('
|
42
|
+
ProconBypassMan.logger.info('[WebsocketClient] disconnected!!')
|
41
43
|
client.reconnect!
|
42
44
|
sleep 2
|
43
45
|
}
|
44
46
|
client.errored { |msg|
|
45
|
-
ProconBypassMan.logger.error("
|
47
|
+
ProconBypassMan.logger.error("[WebsocketClient] errored!!, #{msg}")
|
46
48
|
client.reconnect!
|
47
49
|
sleep 2
|
48
50
|
}
|
49
51
|
client.pinged { |msg|
|
50
|
-
|
52
|
+
watchdog.active!
|
51
53
|
|
52
54
|
ProconBypassMan.cache.fetch key: 'ws_pinged', expires_in: 10 do
|
53
|
-
ProconBypassMan.logger.info('
|
55
|
+
ProconBypassMan.logger.info('[WebsocketClient] pinged!!')
|
54
56
|
ProconBypassMan.logger.info(msg)
|
55
57
|
end
|
56
58
|
}
|
data/lib/procon_bypass_man.rb
CHANGED
@@ -42,6 +42,10 @@ require_relative "procon_bypass_man/support/can_over_process"
|
|
42
42
|
require_relative "procon_bypass_man/support/retryable"
|
43
43
|
require_relative "procon_bypass_man/support/renice_command"
|
44
44
|
require_relative "procon_bypass_man/support/web_connectivity_checker"
|
45
|
+
require_relative "procon_bypass_man/support/watchdog"
|
46
|
+
require_relative "procon_bypass_man/support/forever"
|
47
|
+
require_relative "procon_bypass_man/support/simple_tcp_server"
|
48
|
+
require_relative "procon_bypass_man/support/proccess_cheacker"
|
45
49
|
require_relative "procon_bypass_man/procon_display"
|
46
50
|
require_relative "procon_bypass_man/background"
|
47
51
|
require_relative "procon_bypass_man/commands"
|
@@ -61,8 +65,6 @@ require_relative "procon_bypass_man/scheduler"
|
|
61
65
|
require_relative "procon_bypass_man/plugins"
|
62
66
|
require_relative "procon_bypass_man/worker"
|
63
67
|
require_relative "procon_bypass_man/websocket/client"
|
64
|
-
require_relative "procon_bypass_man/websocket/watchdog"
|
65
|
-
require_relative "procon_bypass_man/websocket/forever"
|
66
68
|
require_relative "procon_bypass_man/external_input"
|
67
69
|
require_relative "procon_bypass_man/remote_action"
|
68
70
|
|
@@ -124,8 +126,6 @@ module ProconBypassMan
|
|
124
126
|
return
|
125
127
|
end
|
126
128
|
|
127
|
-
ProconBypassMan::ExternalInput.prepare_channels
|
128
|
-
|
129
129
|
ready_pbm
|
130
130
|
Runner.new(gadget: gadget, procon: procon).run # ここでblockingする
|
131
131
|
terminate_pbm
|
@@ -161,6 +161,12 @@ module ProconBypassMan
|
|
161
161
|
|
162
162
|
# @return [void]
|
163
163
|
def self.initialize_pbm
|
164
|
+
if ProconBypassMan.pid && ProconBypassMan::ProcessChecker.running?(ProconBypassMan.pid)
|
165
|
+
ProconBypassMan::SendErrorCommand.execute(error: "別のプロセスでPBMがすでに起動中なので処理を停止します。")
|
166
|
+
raise 'テスト実行中でここに入ると調査が面倒なのでエラーにします' if ENV['PBM_ENV'] == 'test'
|
167
|
+
exit 1
|
168
|
+
end
|
169
|
+
|
164
170
|
ProconBypassMan::ReniceCommand.change_priority(to: :low, pid: $$)
|
165
171
|
ProconBypassMan::Background::JobQueue.start!
|
166
172
|
ProconBypassMan::Websocket::Client.start!
|
@@ -211,6 +217,7 @@ module ProconBypassMan
|
|
211
217
|
ProconBypassMan::RemoteAction::QueueOverProcess.shutdown
|
212
218
|
ProconBypassMan::Procon::PerformanceMeasurement::QueueOverProcess.shutdown
|
213
219
|
self.worker&.shutdown
|
220
|
+
ProconBypassMan::ExternalInput.shutdown
|
214
221
|
end
|
215
222
|
|
216
223
|
# @return [void]
|
data/project_template/app.rb
CHANGED
@@ -12,7 +12,7 @@ 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.8'
|
16
16
|
# uncomment if you want to use master branch
|
17
17
|
# gem 'procon_bypass_man', github: 'splaplapla/procon_bypass_man', branch: 'master'
|
18
18
|
# uncomment if you want to use serial communication feature
|
@@ -57,6 +57,7 @@ ProconBypassMan.configure do |config|
|
|
57
57
|
# シリアル通信やTCP/IP経由で入力するときに設定してください
|
58
58
|
# config.external_input_channels = [
|
59
59
|
# ProconBypassMan::ExternalInput::Channels::SerialPortChannel.new(device_path: '/dev/serial0', baud_rate: 9600),
|
60
|
+
# ProconBypassMan::ExternalInput::Channels::TCPIPChannel.new(port: 9000),
|
60
61
|
# ]
|
61
62
|
end
|
62
63
|
|
data/project_template/app.rb.erb
CHANGED
@@ -12,7 +12,7 @@ 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.8'
|
16
16
|
# uncomment if you want to use master branch
|
17
17
|
# gem 'procon_bypass_man', github: 'splaplapla/procon_bypass_man', branch: 'master'
|
18
18
|
# uncomment if you want to use serial communication feature
|
@@ -62,7 +62,7 @@ ProconBypassMan.configure do |config|
|
|
62
62
|
# シリアル通信やTCP/IP経由で入力するときに設定してください
|
63
63
|
# config.external_input_channels = [
|
64
64
|
# ProconBypassMan::ExternalInput::Channels::SerialPortChannel.new(device_path: '/dev/serial0', baud_rate: 9600),
|
65
|
-
|
65
|
+
# ProconBypassMan::ExternalInput::Channels::TCPIPChannel.new(port: 9000),
|
66
66
|
# ]
|
67
67
|
end
|
68
68
|
|
data/sig/main.rbs
CHANGED
@@ -210,7 +210,7 @@ class ProconBypassMan::Configuration
|
|
210
210
|
|
211
211
|
def current_ws_server: () -> (String | nil)
|
212
212
|
|
213
|
-
def current_ws_server_url: () ->
|
213
|
+
def current_ws_server_url: () -> (String | nil)
|
214
214
|
|
215
215
|
def enable_ws?: () -> bool
|
216
216
|
|
@@ -577,7 +577,7 @@ module ProconBypassMan
|
|
577
577
|
@retry_on_connection_error: false
|
578
578
|
|
579
579
|
def initialize: (path: String, server: untyped, ?retry_on_connection_error: false) -> void
|
580
|
-
def get: ->
|
580
|
+
def get: -> Hash[untyped, untyped]?
|
581
581
|
def post: (request_body: untyped) -> nil
|
582
582
|
def put: (request_body: untyped) -> nil
|
583
583
|
|
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.8
|
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-05-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pbmenv
|
@@ -180,10 +180,11 @@ files:
|
|
180
180
|
- lib/procon_bypass_man/device_status.rb
|
181
181
|
- lib/procon_bypass_man/ephemeral_configuration.rb
|
182
182
|
- lib/procon_bypass_man/external_input.rb
|
183
|
+
- lib/procon_bypass_man/external_input/boot_message.rb
|
183
184
|
- lib/procon_bypass_man/external_input/channels.rb
|
184
185
|
- lib/procon_bypass_man/external_input/channels/base.rb
|
185
186
|
- lib/procon_bypass_man/external_input/channels/serial_port_channel.rb
|
186
|
-
- lib/procon_bypass_man/external_input/channels/
|
187
|
+
- lib/procon_bypass_man/external_input/channels/tcpip_channel.rb
|
187
188
|
- lib/procon_bypass_man/external_input/external_data.rb
|
188
189
|
- lib/procon_bypass_man/plugin/splatoon2/macro/charge_tansan_bomb.rb
|
189
190
|
- lib/procon_bypass_man/plugin/splatoon2/macro/dasei_cancel.rb
|
@@ -276,10 +277,12 @@ files:
|
|
276
277
|
- lib/procon_bypass_man/support/cycle_sleep.rb
|
277
278
|
- lib/procon_bypass_man/support/device_mouse_finder.rb
|
278
279
|
- lib/procon_bypass_man/support/device_procon_finder.rb
|
280
|
+
- lib/procon_bypass_man/support/forever.rb
|
279
281
|
- lib/procon_bypass_man/support/http_client.rb
|
280
282
|
- lib/procon_bypass_man/support/load_agv.rb
|
281
283
|
- lib/procon_bypass_man/support/never_exit_accidentally.rb
|
282
284
|
- lib/procon_bypass_man/support/on_memory_cache.rb
|
285
|
+
- lib/procon_bypass_man/support/proccess_cheacker.rb
|
283
286
|
- lib/procon_bypass_man/support/procon_performance_http_client.rb
|
284
287
|
- lib/procon_bypass_man/support/remote_macro_http_client.rb
|
285
288
|
- lib/procon_bypass_man/support/renice_command.rb
|
@@ -287,16 +290,16 @@ files:
|
|
287
290
|
- lib/procon_bypass_man/support/retryable.rb
|
288
291
|
- lib/procon_bypass_man/support/safe_timeout.rb
|
289
292
|
- lib/procon_bypass_man/support/send_device_stats_http_client.rb
|
293
|
+
- lib/procon_bypass_man/support/simple_tcp_server.rb
|
290
294
|
- lib/procon_bypass_man/support/update_remote_pbm_job_status_http_client.rb
|
291
295
|
- lib/procon_bypass_man/support/uptime.rb
|
292
296
|
- lib/procon_bypass_man/support/usb_device_controller.rb
|
297
|
+
- lib/procon_bypass_man/support/watchdog.rb
|
293
298
|
- lib/procon_bypass_man/support/web_connectivity_checker.rb
|
294
299
|
- lib/procon_bypass_man/support/yaml_loader.rb
|
295
300
|
- lib/procon_bypass_man/support/yaml_writer.rb
|
296
301
|
- lib/procon_bypass_man/version.rb
|
297
302
|
- lib/procon_bypass_man/websocket/client.rb
|
298
|
-
- lib/procon_bypass_man/websocket/forever.rb
|
299
|
-
- lib/procon_bypass_man/websocket/watchdog.rb
|
300
303
|
- lib/procon_bypass_man/worker.rb
|
301
304
|
- procon_bypass_man.gemspec
|
302
305
|
- project_template/README.md
|
@@ -1,16 +0,0 @@
|
|
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
|
@@ -1,47 +0,0 @@
|
|
1
|
-
module ProconBypassMan
|
2
|
-
module Websocket
|
3
|
-
class Forever
|
4
|
-
# 動作確認方法
|
5
|
-
# - 10秒ごとにrefreshするのでタイムアウトは起きない
|
6
|
-
# - ProconBypassMan::Websocket::Forever.run { loop { puts(:hi); sleep(10); ProconBypassMan::Websocket::Watchdog.active! } }
|
7
|
-
# - タイムアウトが起きること
|
8
|
-
# - ProconBypassMan::Websocket::Forever.run { puts(:hi); sleep(3000); }
|
9
|
-
# - ブロックを1回評価するとThreadが死ぬので100秒後にタイムアウトが起きること
|
10
|
-
# - ProconBypassMan::Websocket::Forever.run { puts(:hi); sleep(10); ProconBypassMan::Websocket::Watchdog.active! }
|
11
|
-
def self.run(&block)
|
12
|
-
loop do
|
13
|
-
new.run(&block)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def run(&block)
|
18
|
-
raise("need a block") unless block_given?
|
19
|
-
|
20
|
-
ws_thread = work_one(callable: block)
|
21
|
-
wait_and_kill_if_outdated(ws_thread)
|
22
|
-
end
|
23
|
-
|
24
|
-
# @return [Thread]
|
25
|
-
def work_one(callable: )
|
26
|
-
Thread.start do
|
27
|
-
callable.call
|
28
|
-
rescue => e
|
29
|
-
ProconBypassMan.logger.error("websocket client with forever: #{e.full_message}")
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def wait_and_kill_if_outdated(thread)
|
34
|
-
loop do
|
35
|
-
if Watchdog.outdated?
|
36
|
-
Watchdog.active!
|
37
|
-
ProconBypassMan.logger.error("watchdog timeout!!")
|
38
|
-
thread.kill
|
39
|
-
return
|
40
|
-
end
|
41
|
-
|
42
|
-
sleep(10)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|