procon_bypass_man 0.1.3 → 0.1.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.
@@ -1,24 +1,22 @@
1
1
  module ProconBypassMan
2
2
  class Configuration
3
3
  module Loader
4
+ require 'digest/md5'
5
+
4
6
  def self.load(setting_path: )
5
- validation_instance = ProconBypassMan::Configuration.switch_context(:validation) do |instance|
6
- begin
7
- yaml = YAML.load_file(setting_path) or raise "読み込みに失敗しました"
8
- instance.instance_eval(yaml["setting"])
9
- rescue SyntaxError
10
- instance.errors[:base] << "Rubyのシンタックスエラーです"
11
- next(instance)
12
- rescue Psych::SyntaxError
13
- instance.errors[:base] << "yamlのシンタックスエラーです"
14
- next(instance)
7
+ ProconBypassMan::Configuration.switch_new_context(:validation) do |validation_instance|
8
+ yaml = YAML.load_file(setting_path) or raise "読み込みに失敗しました"
9
+ validation_instance.instance_eval(yaml["setting"])
10
+ validator = Validator.new(validation_instance)
11
+ if validator.valid?
12
+ next
13
+ else
14
+ raise ProconBypassMan::CouldNotLoadConfigError, validator.errors
15
15
  end
16
- instance.valid?
17
- next(instance)
18
- end
19
-
20
- if !validation_instance.errors.empty?
21
- raise ProconBypassMan::CouldNotLoadConfigError, validation_instance.errors
16
+ rescue SyntaxError
17
+ raise ProconBypassMan::CouldNotLoadConfigError, "Rubyのシンタックスエラーです"
18
+ rescue Psych::SyntaxError
19
+ raise ProconBypassMan::CouldNotLoadConfigError, "yamlのシンタックスエラーです"
22
20
  end
23
21
 
24
22
  yaml = YAML.load_file(setting_path)
@@ -33,6 +31,9 @@ module ProconBypassMan
33
31
  ProconBypassMan.logger.warn "不明なバージョンです。failoverします"
34
32
  ProconBypassMan::Configuration.instance.instance_eval(yaml["setting"])
35
33
  end
34
+
35
+ File.write(ProconBypassMan.digest_path, Digest::MD5.hexdigest(yaml["setting"]))
36
+
36
37
  ProconBypassMan::Configuration.instance
37
38
  end
38
39
 
@@ -1,13 +1,19 @@
1
1
  module ProconBypassMan
2
2
  class Configuration
3
- module Validator
3
+ class Validator
4
+ def initialize(config)
5
+ @layers = config.layers
6
+ @prefix_keys = config.prefix_keys
7
+ end
8
+
4
9
  # @return [Boolean]
5
10
  def valid?
6
11
  @errors = Hash.new {|h,k| h[k] = [] }
7
- if prefix_keys.empty?
8
- @errors[:prefix_keys] ||= []
9
- @errors[:prefix_keys] << "prefix_keys_for_changing_layerに値が入っていません。"
10
- end
12
+
13
+ validate_require_prefix_keys
14
+ validate_config_of_button_lonely
15
+ validate_verify_button_existence
16
+ validate_flip_and_remap_are_hate_each_other
11
17
 
12
18
  @errors.empty?
13
19
  end
@@ -21,6 +27,65 @@ module ProconBypassMan
21
27
  def errors
22
28
  @errors ||= Hash.new {|h,k| h[k] = [] }
23
29
  end
30
+
31
+ private
32
+
33
+ def validate_config_of_button_lonely
34
+ @layers.each do |layer_key, value|
35
+ if ProconBypassMan::Procon::ModeRegistry::PRESETS.key?(value.mode)
36
+ next
37
+ else
38
+ if !value.flips.empty? || !value.macros.empty?
39
+ @errors[:layers] << "#{layer_key}でmodeを設定しているのでボタンの設定はできません。"
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ def validate_require_prefix_keys
46
+ if @prefix_keys.empty?
47
+ @errors[:prefix_keys] ||= []
48
+ @errors[:prefix_keys] << "prefix_keys_for_changing_layerに値が入っていません。"
49
+ end
50
+ end
51
+
52
+ def validate_verify_button_existence
53
+ @layers.each do |layer_key, value|
54
+ unverified_buttons = []
55
+ # teplevel target button
56
+ value.flips.keys.map(&:to_sym).each { |b| unverified_buttons << b }
57
+ value.remaps.keys.map(&:to_sym).each { |b| unverified_buttons << b }
58
+ # internal target button
59
+ value.flips.flat_map { |_flip_button, flip_option|
60
+ flip_option.flat_map { |flip_option_key, flip_option_target_button|
61
+ next if flip_option_key == :flip_interval
62
+ next if flip_option_target_button.is_a?(FalseClass) || flip_option_target_button.is_a?(TrueClass)
63
+ flip_option_target_button
64
+ }
65
+ }.compact.each { |b| unverified_buttons << b }
66
+ value.remaps.flat_map { |_button, option|
67
+ option.flat_map { |_flip_option_key, flip_option_target_button|
68
+ next if flip_option_target_button.is_a?(FalseClass) || flip_option_target_button.is_a?(TrueClass)
69
+ flip_option_target_button
70
+ }
71
+ }.compact.each { |b| unverified_buttons << b }
72
+ unless(nunsupport_buttons = (unverified_buttons - ProconBypassMan::Procon::ButtonCollection::BUTTONS)).length.zero?
73
+ @errors[:layers] << "#{layer_key}で存在しないボタン#{nunsupport_buttons.join(", ")}があります"
74
+ end
75
+ end
76
+ end
77
+
78
+ def validate_flip_and_remap_are_hate_each_other
79
+ @layers.each do |layer_key, value|
80
+ flip_buttons = []
81
+ remap_buttons = []
82
+ value.flips.keys.map(&:to_sym).each { |b| flip_buttons << b }
83
+ value.remaps.keys.map(&:to_sym).each { |b| remap_buttons << b }
84
+ if(duplicated_buttons = flip_buttons & remap_buttons).length > 0
85
+ @errors[:layers] << "レイヤー#{layer_key}で、連打とリマップの定義が重複しているボタン#{duplicated_buttons.join(", ")}があります"
86
+ end
87
+ end
88
+ end
24
89
  end
25
90
  end
26
91
  end
@@ -4,7 +4,6 @@ require "procon_bypass_man/configuration/layer"
4
4
 
5
5
  module ProconBypassMan
6
6
  class Configuration
7
- include Validator
8
7
 
9
8
  attr_accessor :layers,
10
9
  :setting_path,
@@ -19,13 +18,12 @@ module ProconBypassMan
19
18
  @@context[@@current_context_key] ||= new
20
19
  end
21
20
 
22
- def self.switch_context(key)
23
- @@context[key] ||= new
21
+ def self.switch_new_context(key)
22
+ @@context[key] = new
24
23
  previous_key = @@current_context_key
25
24
  if block_given?
26
25
  @@current_context_key = key
27
26
  value = yield(@@context[key])
28
- @@context[key].reset!
29
27
  @@current_context_key = previous_key
30
28
  return value
31
29
  else
@@ -39,11 +37,16 @@ module ProconBypassMan
39
37
 
40
38
  MODES = [:manual]
41
39
  def layer(direction, mode: :manual, &block)
42
- unless (MODES + ProconBypassMan::Procon::ModeRegistry.plugins.keys).include?(mode)
43
- raise("#{mode} mode is unknown")
40
+ if mode.respond_to?(:name)
41
+ mode_name = mode.name.to_sym
42
+ else
43
+ mode_name = mode
44
+ end
45
+ unless (MODES + ProconBypassMan::Procon::ModeRegistry.plugins.keys).include?(mode_name)
46
+ raise("#{mode_name} mode is unknown")
44
47
  end
45
48
 
46
- layer = Layer.new(mode: mode)
49
+ layer = Layer.new(mode: mode_name)
47
50
  layer.instance_eval(&block) if block_given?
48
51
  self.layers[direction] = layer
49
52
  self
@@ -0,0 +1,336 @@
1
+ class ProconBypassMan::DeviceConnector
2
+ class BytesMismatchError < StandardError; end
3
+
4
+ class Value
5
+ attr_accessor :read_from, :values
6
+ def initialize(values: , read_from: )
7
+ @values = values
8
+ @read_from = read_from
9
+ end
10
+ end
11
+
12
+ PROCON_PATH = "/dev/hidraw0"
13
+ PROCON2_PATH = "/dev/hidraw1"
14
+
15
+ # 画面で再接続ができたが状況は変わらない
16
+ def self.reset_connection!
17
+ s = new
18
+ s.add([
19
+ ["0000"],
20
+ ["0000"],
21
+ ["8005"],
22
+ ["0000"],
23
+ ["8001"],
24
+ ], read_from: :switch)
25
+ s.drain_all
26
+ s.read_procon
27
+ s.write_switch("213c910080005db7723d48720a800300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
28
+ sleep(10) # いらないかも
29
+ s
30
+ end
31
+
32
+ def self.connect
33
+ s = new(throw_error_if_timeout: true, enable_at_exit: false)
34
+ s.add([
35
+ ["0000"],
36
+ ["0000"],
37
+ ["8005"],
38
+ ["0010"],
39
+ ], read_from: :switch)
40
+ # 1
41
+ s.add([["8001"]], read_from: :switch)
42
+ s.add([/^8101/], read_from: :procon) # <<< 81010003176d96e7a5480000000, macaddressとコントローラー番号を返す
43
+ # 2
44
+ s.add([["8002"]], read_from: :switch)
45
+ s.add([/^8102/], read_from: :procon)
46
+ # 3
47
+ s.add([/^0100/], read_from: :switch)
48
+ s.add([/^21/], read_from: :procon)
49
+ # 4
50
+ s.add([["8004"]], read_from: :switch)
51
+ s.drain_all
52
+ return [s.switch, s.procon]
53
+ end
54
+
55
+ def initialize(throw_error_if_timeout: false, throw_error_if_mismatch: false , enable_at_exit: true)
56
+ @stack = []
57
+ @initialized_devices = false
58
+ @throw_error_if_timeout = throw_error_if_timeout
59
+ @throw_error_if_mismatch = throw_error_if_mismatch
60
+ @enable_at_exit = enable_at_exit
61
+ end
62
+
63
+ def add(values, read_from: )
64
+ @stack << Value.new(values: values, read_from: read_from)
65
+ end
66
+
67
+ def drain_all
68
+ unless @initialized_devices
69
+ init_devices
70
+ end
71
+
72
+ while(item = @stack.shift)
73
+ item.values.each do |value|
74
+ data = nil
75
+ timer = ProconBypassMan::Timer.new
76
+ begin
77
+ timer.throw_if_timeout!
78
+ data = from_device(item).read_nonblock(128)
79
+ rescue IO::EAGAINWaitReadable
80
+ retry
81
+ end
82
+
83
+ result =
84
+ case value
85
+ when String, Array
86
+ value == data.unpack("H*")
87
+ when Regexp
88
+ value =~ data.unpack("H*").first
89
+ else
90
+ raise "#{value}は知りません"
91
+ end
92
+ if result
93
+ ProconBypassMan.logger.info "OK(expected: #{value}, got: #{data.unpack("H*")})"
94
+ else
95
+ ProconBypassMan.logger.info "NG(expected: #{value}, got: #{data.unpack("H*")})"
96
+ raise BytesMismatchError if @throw_error_if_mismatch
97
+ end
98
+ to_device(item).write_nonblock(data)
99
+ end
100
+ end
101
+ rescue ProconBypassMan::Timer::Timeout
102
+ ProconBypassMan.logger.error "timeoutになりました"
103
+ raise if @throw_error_if_timeout
104
+ end
105
+
106
+ # switchに任意の命令を入力して、switchから読み取る
107
+ def write_switch(data, only_write: false)
108
+ if data.encoding.name == "UTF-8"
109
+ data = [data].pack("H*")
110
+ end
111
+ unless @initialized_devices
112
+ init_devices
113
+ end
114
+
115
+ timer = ProconBypassMan::Timer.new
116
+ data = nil
117
+ begin
118
+ timer.throw_if_timeout!
119
+ switch.write_nonblock(data)
120
+ rescue IO::EAGAINWaitReadable
121
+ retry
122
+ rescue ProconBypassMan::Timer::Timeout
123
+ ProconBypassMan.logger.error "writeでtimeoutになりました"
124
+ raise
125
+ end
126
+ return(data.unpack("H*")) if only_write
127
+
128
+ timer = ProconBypassMan::Timer.new
129
+ begin
130
+ timer.throw_if_timeout!
131
+ data = switch.read_nonblock(128)
132
+ ProconBypassMan.logger.debug { " >>> #{data.unpack("H*")})" }
133
+ rescue IO::EAGAINWaitReadable
134
+ retry
135
+ rescue ProconBypassMan::Timer::Timeout
136
+ ProconBypassMan.logger.error "readでtimeoutになりました"
137
+ raise
138
+ end
139
+ rescue ProconBypassMan::Timer::Timeout
140
+ raise if @throw_error_if_timeout
141
+ end
142
+
143
+ def write_procon(data, only_write: false)
144
+ if data.encoding.name == "UTF-8"
145
+ data = [data].pack("H*")
146
+ end
147
+ unless @initialized_devices
148
+ init_devices
149
+ end
150
+
151
+ timer = ProconBypassMan::Timer.new
152
+ begin
153
+ timer.throw_if_timeout!
154
+ procon.write_nonblock(data)
155
+ rescue IO::EAGAINWaitReadable
156
+ retry
157
+ rescue ProconBypassMan::Timer::Timeout
158
+ ProconBypassMan.logger.error "writeでtimeoutになりました"
159
+ raise
160
+ end
161
+ return(data.unpack("H*")) if only_write
162
+
163
+ timer = ProconBypassMan::Timer.new
164
+ begin
165
+ timer.throw_if_timeout!
166
+ data = procon.read_nonblock(128)
167
+ ProconBypassMan.logger.error " <<< #{data.unpack("H*")})"
168
+ rescue IO::EAGAINWaitReadable
169
+ retry
170
+ rescue ProconBypassMan::Timer::Timeout
171
+ ProconBypassMan.logger.error "readでtimeoutになりました"
172
+ raise
173
+ end
174
+ rescue ProconBypassMan::Timer::Timeout
175
+ raise if @throw_error_if_timeout
176
+ end
177
+
178
+ def read_procon(only_read: false)
179
+ unless @initialized_devices
180
+ init_devices
181
+ end
182
+
183
+ data = nil
184
+ timer = ProconBypassMan::Timer.new
185
+ begin
186
+ timer.throw_if_timeout!
187
+ data = procon.read_nonblock(128)
188
+ ProconBypassMan.logger.debug { " <<< #{data.unpack("H*")})" }
189
+ rescue IO::EAGAINWaitReadable
190
+ retry
191
+ rescue ProconBypassMan::Timer::Timeout
192
+ ProconBypassMan.logger.error "readでtimeoutになりました"
193
+ raise
194
+ end
195
+ return(data.unpack("H*")) if only_read
196
+
197
+ timer = ProconBypassMan::Timer.new
198
+ begin
199
+ timer.throw_if_timeout!
200
+ switch.write_nonblock(data)
201
+ rescue IO::EAGAINWaitReadable
202
+ retry
203
+ rescue ProconBypassMan::Timer::Timeout
204
+ ProconBypassMan.logger.error "writeでtimeoutになりました"
205
+ raise
206
+ end
207
+ rescue ProconBypassMan::Timer::Timeout
208
+ raise if @throw_error_if_timeout
209
+ end
210
+
211
+ def read_switch(only_read: false)
212
+ unless @initialized_devices
213
+ init_devices
214
+ end
215
+
216
+ data = nil
217
+ timer = ProconBypassMan::Timer.new
218
+ begin
219
+ timer.throw_if_timeout!
220
+ data = switch.read_nonblock(128)
221
+ ProconBypassMan.logger.debug { " >>> #{data.unpack("H*")})" }
222
+ rescue IO::EAGAINWaitReadable
223
+ retry
224
+ rescue ProconBypassMan::Timer::Timeout
225
+ ProconBypassMan.logger.error "readでtimeoutになりました"
226
+ raise
227
+ end
228
+ return(data.unpack("H*")) if only_read
229
+
230
+ timer = ProconBypassMan::Timer.new
231
+ begin
232
+ timer.throw_if_timeout!
233
+ procon.write_nonblock(data)
234
+ rescue IO::EAGAINWaitReadable
235
+ retry
236
+ rescue ProconBypassMan::Timer::Timeout
237
+ ProconBypassMan.logger.error "writeでtimeoutになりました"
238
+ raise
239
+ end
240
+ rescue ProconBypassMan::Timer::Timeout
241
+ raise if @throw_error_if_timeout
242
+ end
243
+
244
+ def from_device(item)
245
+ case item.read_from
246
+ when :switch
247
+ switch
248
+ when :procon
249
+ procon
250
+ else
251
+ raise
252
+ end
253
+ end
254
+
255
+ # fromの対になる
256
+ def to_device(item)
257
+ case item.read_from
258
+ when :switch
259
+ procon
260
+ when :procon
261
+ switch
262
+ else
263
+ raise
264
+ end
265
+ end
266
+
267
+ def switch
268
+ @gadget
269
+ end
270
+
271
+ def procon
272
+ @procon
273
+ end
274
+
275
+ def is_available_device?(path)
276
+ return false if !File.exist?(path)
277
+
278
+ system('echo > /sys/kernel/config/usb_gadget/procon/UDC')
279
+ system('ls /sys/class/udc > /sys/kernel/config/usb_gadget/procon/UDC')
280
+ sleep 0.5
281
+
282
+ file = File.open(path, "w+")
283
+ begin
284
+ file.read_nonblock(128)
285
+ rescue EOFError
286
+ file.close
287
+ return false
288
+ rescue IO::EAGAINWaitReadable
289
+ file.close
290
+ return true
291
+ end
292
+ end
293
+
294
+ def to_bin(string)
295
+ string.unpack "H*"
296
+ end
297
+
298
+ def init_devices
299
+ if @initialized_devices
300
+ return
301
+ end
302
+
303
+ case
304
+ when is_available_device?(PROCON_PATH)
305
+ ProconBypassMan.logger.info "proconのデバイスファイルは#{PROCON_PATH}を使います"
306
+ @procon = File.open(PROCON_PATH, "w+")
307
+ @gadget = File.open('/dev/hidg0', "w+")
308
+ when is_available_device?(PROCON2_PATH)
309
+ ProconBypassMan.logger.info "proconのデバイスファイルは#{PROCON2_PATH}を使います"
310
+ @procon = File.open(PROCON2_PATH, "w+")
311
+ @gadget = File.open('/dev/hidg0', "w+")
312
+ else
313
+ raise "/dev/hidraw0, /dev/hidraw1の両方見つかりませんでした"
314
+ end
315
+ system('echo > /sys/kernel/config/usb_gadget/procon/UDC')
316
+ system('ls /sys/class/udc > /sys/kernel/config/usb_gadget/procon/UDC')
317
+ sleep 0.5
318
+
319
+ @initialized_devices = true
320
+
321
+ if @enable_at_exit
322
+ at_exit do
323
+ @procon&.close
324
+ @gadget&.close
325
+ end
326
+ end
327
+ rescue Errno::ENXIO => e
328
+ # /dev/hidg0 をopenできないときがある
329
+ ProconBypassMan.logger.error "Errno::ENXIO (No such device or address @ rb_sysopen - /dev/hidg0)が起きました。resetします"
330
+ ProconBypassMan.logger.error e
331
+ system('echo > /sys/kernel/config/usb_gadget/procon/UDC')
332
+ system('ls /sys/class/udc > /sys/kernel/config/usb_gadget/procon/UDC')
333
+ sleep 2
334
+ retry
335
+ end
336
+ end