seriamp 0.1.11 → 0.1.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f9a46cb5fe16a4b24f54a6552e98db18e6e97281f7d12ce76c78f1ca5d18d8b8
4
- data.tar.gz: '082c51ad737e37e237248acca21d9ee6d0b74bca0be135cf99f52fbf26679c31'
3
+ metadata.gz: 3a8c5974a39756c641ab5fb6e600d0d67095e4e9f18c872225ffb580cea4e70c
4
+ data.tar.gz: ba47852332bdb66070cf4105c4929ed691fce8eca8dd7f47aaefbeb4effab932
5
5
  SHA512:
6
- metadata.gz: 2b450a12d445a5b9199212eee5dba2eac298a9aa19e0e826df2fd322cd1d86d78a05815d39b3f75e417afa357720edb5ead5d1c9bfff1efefc171cb0b7e73f6e
7
- data.tar.gz: 9461488acf8478e6f7d52ea4f824042eede1a13a99f357092f37f55afddee6608e70a0306f23844a12a594e4658c5f87a40b1b2e090539a910f6ef963acd52ae
6
+ metadata.gz: 185e56b171c6db9ed0822f57de19ac2fea395d075bc615cddf0795c82c2f044b5212e9eb210b90b3dbc4aa603ce6b1553a610924c9ce8b9f02ae860c1f0c873c
7
+ data.tar.gz: 277dc1f126ca440e3a8539491e1548511f1abfb66e6a21c615ab4ab8bfce67c76c2499b6c6a2ec9efe20050cd1ac732f70cd3870cff98aec927ae57f6f46e1e4
data/bin/sonamp-web CHANGED
@@ -23,9 +23,10 @@ end.parse!
23
23
 
24
24
  logger = Logger.new(STDERR)
25
25
 
26
- #Sonamp::App.set :device, options[:device]
27
- #Sonamp::App.set :logger, logger
28
- Seriamp::Sonamp::App.set :client, Seriamp::Sonamp::Client.new(device: options[:device], logger: logger)
26
+ #Seriamp::Sonamp::App.set :device, options[:device]
27
+ #Seriamp::Sonamp::App.set :logger, logger
28
+ Seriamp::Sonamp::App.set :client, Seriamp::Sonamp::Client.new(
29
+ device: options[:device], logger: logger, thread_safe: true)
29
30
 
30
31
  options = Rack::Server::Options.new.parse!(ARGV)
31
32
  Rack::Server.start(options.merge(app: Seriamp::Sonamp::App))
data/bin/yamaha-web CHANGED
@@ -23,9 +23,10 @@ end.parse!
23
23
 
24
24
  logger = Logger.new(STDERR)
25
25
 
26
- #Yamaha::App.set :device, options[:device]
27
- #Yamaha::App.set :logger, logger
28
- Seriamp::Yamaha::App.set :client, Seriamp::Yamaha::Client.new(device: options[:device], logger: logger)
26
+ #Seriamp::Yamaha::App.set :device, options[:device]
27
+ #Seriamp::Yamaha::App.set :logger, logger
28
+ Seriamp::Yamaha::App.set :client, Seriamp::Yamaha::Client.new(
29
+ device: options[:device], logger: logger, thread_safe: true)
29
30
 
30
31
  options = Rack::Server::Options.new.parse!(ARGV)
31
32
  Rack::Server.start(options.merge(app: Seriamp::Yamaha::App))
@@ -12,6 +12,7 @@ module Seriamp
12
12
  set :device, nil
13
13
  set :logger, nil
14
14
  set :client, nil
15
+ set :retries, true
15
16
 
16
17
  get '/' do
17
18
  render_json(client.status)
@@ -75,7 +76,8 @@ module Seriamp
75
76
 
76
77
  def client
77
78
  settings.client || begin
78
- @client ||= Sonamp::Client.new(settings.device, logger: settings.logger)
79
+ @client ||= Sonamp::Client.new(settings.device,
80
+ logger: settings.logger, retries: settings.retries, thread_safe: true)
79
81
  end
80
82
  end
81
83
 
@@ -10,7 +10,7 @@ module Seriamp
10
10
  RS232_TIMEOUT = 3
11
11
 
12
12
  class Client
13
- def initialize(device: nil, glob: nil, logger: nil, retries: true)
13
+ def initialize(device: nil, glob: nil, logger: nil, retries: true, thread_safe: false)
14
14
  @logger = logger
15
15
 
16
16
  @device = device
@@ -26,6 +26,11 @@ module Seriamp
26
26
  else
27
27
  raise ArgumentError, "retries must be an integer, true, false or nil: #{retries}"
28
28
  end
29
+ @thread_safe = !!thread_safe
30
+
31
+ if thread_safe?
32
+ @lock = Mutex.new
33
+ end
29
34
  end
30
35
 
31
36
  attr_reader :device
@@ -33,6 +38,10 @@ module Seriamp
33
38
  attr_reader :logger
34
39
  attr_reader :retries
35
40
 
41
+ def thread_safe?
42
+ @thread_safe
43
+ end
44
+
36
45
  def detect_device?
37
46
  @detect_device
38
47
  end
@@ -182,10 +191,22 @@ module Seriamp
182
191
  private
183
192
 
184
193
  def with_device(&block)
185
- if @io
186
- yield @io
194
+ with_lock do
195
+ if @io
196
+ yield @io
197
+ else
198
+ open_device(&block)
199
+ end
200
+ end
201
+ end
202
+
203
+ def with_lock
204
+ if thread_safe?
205
+ @lock.synchronize do
206
+ yield
207
+ end
187
208
  else
188
- open_device(&block)
209
+ yield
189
210
  end
190
211
  end
191
212
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Seriamp
4
- VERSION = '0.1.11'
4
+ VERSION = '0.1.12'
5
5
  end
@@ -13,6 +13,7 @@ module Seriamp
13
13
  set :device, nil
14
14
  set :logger, nil
15
15
  set :client, nil
16
+ set :retries, true
16
17
 
17
18
  get '/' do
18
19
  clear_cache
@@ -153,7 +154,8 @@ module Seriamp
153
154
 
154
155
  def client
155
156
  settings.client || begin
156
- @client ||= Yamaha::Client.new(settings.device, logger: settings.logger)
157
+ @client ||= Yamaha::Client.new(settings.device,
158
+ logger: settings.logger, retries: settings.retries, thread_safe: true)
157
159
  end
158
160
  end
159
161
 
@@ -14,7 +14,7 @@ module Seriamp
14
14
  class Client
15
15
  include Protocol::Methods
16
16
 
17
- def initialize(device: nil, glob: nil, logger: nil, retries: true)
17
+ def initialize(device: nil, glob: nil, logger: nil, retries: true, thread_safe: false)
18
18
  @logger = logger
19
19
 
20
20
  @device = device
@@ -30,6 +30,11 @@ module Seriamp
30
30
  else
31
31
  raise ArgumentError, "retries must be an integer, true, false or nil: #{retries}"
32
32
  end
33
+ @thread_safe = !!thread_safe
34
+
35
+ if thread_safe?
36
+ @lock = Mutex.new
37
+ end
33
38
 
34
39
  if block_given?
35
40
  begin
@@ -45,6 +50,10 @@ module Seriamp
45
50
  attr_reader :logger
46
51
  attr_reader :retries
47
52
 
53
+ def thread_safe?
54
+ @thread_safe
55
+ end
56
+
48
57
  def detect_device?
49
58
  @detect_device
50
59
  end
@@ -56,18 +65,29 @@ module Seriamp
56
65
 
57
66
  def last_status
58
67
  unless @status
59
- with_device do
60
- unless @status
61
- do_status
68
+ with_lock do
69
+ with_retry do
70
+ with_device do
71
+ unless @status
72
+ do_status
73
+ end
74
+ end
62
75
  end
63
76
  end
64
77
  end
78
+ if @status.nil?
79
+ raise "This should not happen"
80
+ end
65
81
  @status.dup
66
82
  end
67
83
 
68
84
  def last_status_string
69
85
  unless @status_string
70
- with_device do
86
+ with_lock do
87
+ with_retry do
88
+ with_device do
89
+ end
90
+ end
71
91
  end
72
92
  end
73
93
  @status_string.dup
@@ -121,6 +141,16 @@ module Seriamp
121
141
  end
122
142
  end
123
143
 
144
+ def with_lock
145
+ if thread_safe?
146
+ @lock.synchronize do
147
+ yield
148
+ end
149
+ else
150
+ yield
151
+ end
152
+ end
153
+
124
154
  # Shows a message via the on-screen display. The message must be 16
125
155
  # characters or fewer. The message is NOT displayed on the front panel,
126
156
  # it is shown only on the connected TV's OSD.
@@ -134,13 +164,15 @@ module Seriamp
134
164
  raise ArgumentError, "Message must be no more than 16 characters, #{msg.length} given"
135
165
  end
136
166
 
137
- with_retry do
138
- with_device do
139
- @io.syswrite("#{STX}21000#{ETX}".encode('ascii'))
140
- @io.syswrite("#{STX}3#{msg[0..3]}#{ETX}".encode('ascii'))
141
- @io.syswrite("#{STX}3#{msg[4..7]}#{ETX}".encode('ascii'))
142
- @io.syswrite("#{STX}3#{msg[8..11]}#{ETX}".encode('ascii'))
143
- @io.syswrite("#{STX}3#{msg[12..15]}#{ETX}".encode('ascii'))
167
+ with_lock do
168
+ with_retry do
169
+ with_device do
170
+ @io.syswrite("#{STX}21000#{ETX}".encode('ascii'))
171
+ @io.syswrite("#{STX}3#{msg[0..3]}#{ETX}".encode('ascii'))
172
+ @io.syswrite("#{STX}3#{msg[4..7]}#{ETX}".encode('ascii'))
173
+ @io.syswrite("#{STX}3#{msg[8..11]}#{ETX}".encode('ascii'))
174
+ @io.syswrite("#{STX}3#{msg[12..15]}#{ETX}".encode('ascii'))
175
+ end
144
176
  end
145
177
  end
146
178
 
@@ -265,9 +297,12 @@ module Seriamp
265
297
  end
266
298
  break unless again
267
299
  end
300
+ if resp.length < 10
301
+ raise HandshakeFailure, "Broken status response: expected at least 10 bytes, got #{resp.length} bytes; concurrent operation on device?"
302
+ end
268
303
  payload = resp[1...-1]
269
- @model_code = payload[0..4]
270
- @version = payload[5]
304
+ model_code = payload[0..4]
305
+ version = payload[5]
271
306
  length = payload[6..7].to_i(16)
272
307
  data = payload[8...-2]
273
308
  if data.length != length
@@ -276,11 +311,11 @@ module Seriamp
276
311
  unless data.start_with?('@E01900')
277
312
  raise HandshakeFailure, "Broken status response: expected to start with @E01900, actual #{data[0..6]}"
278
313
  end
279
- @status_string = data
280
- @status = {
281
- model_code: @model_code,
282
- model_name: MODEL_NAMES[@model_code],
283
- firmware_version: @version,
314
+ status_string = data
315
+ status = {
316
+ model_code: model_code,
317
+ model_name: MODEL_NAMES[model_code],
318
+ firmware_version: version,
284
319
  system_status: data[7].ord - ZERO_ORD,
285
320
  power: power = data[8].ord - ZERO_ORD,
286
321
  main_power: [1, 4, 5, 2].include?(power),
@@ -288,7 +323,7 @@ module Seriamp
288
323
  zone3_power: [1, 5, 3, 7].include?(power),
289
324
  }
290
325
  if data.length > 9
291
- @status.update(
326
+ status.update(
292
327
  input: input = data[9],
293
328
  input_name: MAIN_INPUTS_GET.fetch(input),
294
329
  multi_ch_input: data[10] == '1',
@@ -319,34 +354,41 @@ module Seriamp
319
354
  sleep: SLEEP_GET.fetch(data[24]),
320
355
  night: night = data[27],
321
356
  night_name: NIGHT_GET.fetch(night),
322
- pure_direct: data[PURE_DIRECT_FIELD.fetch(@model_code)] == '1',
357
+ pure_direct: data[PURE_DIRECT_FIELD.fetch(model_code)] == '1',
323
358
  speaker_a: data[29] == '1',
324
359
  speaker_b: data[30] == '1',
325
360
  # 2 positions on RX-Vx700
326
361
  #format: data[31..32],
327
362
  #sampling: data[33..34],
328
363
  )
329
- if @model_code == 'R0178'
330
- @status.update(
364
+ if model_code == 'R0178'
365
+ status.update(
331
366
  input_mode: INPUT_MODE_R0178.fetch(data[11]),
332
367
  sampling: data[32],
333
368
  sample_rate: SAMPLE_RATE_R0178.fetch(data[32]),
334
369
  )
335
370
  end
336
371
  end
337
- @status
372
+
373
+ @model_code, @version, @status_string =
374
+ model_code, version, status_string
375
+ @status = status
338
376
  end
339
377
  end
340
378
 
341
379
  def remote_command(cmd)
342
- with_retry do
343
- dispatch("#{STX}0#{cmd}#{ETX}")
380
+ with_lock do
381
+ with_retry do
382
+ dispatch("#{STX}0#{cmd}#{ETX}")
383
+ end
344
384
  end
345
385
  end
346
386
 
347
387
  def system_command(cmd)
348
- with_retry do
349
- dispatch("#{STX}2#{cmd}#{ETX}")
388
+ with_lock do
389
+ with_retry do
390
+ dispatch("#{STX}2#{cmd}#{ETX}")
391
+ end
350
392
  end
351
393
  end
352
394
 
data/seriamp.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "seriamp"
5
- spec.version = '0.1.11'
5
+ spec.version = '0.1.12'
6
6
  spec.authors = ['Oleg Pudeyev']
7
7
  spec.email = ['code@olegp.name']
8
8
  spec.summary = %q{Serial control for amplifiers & A/V receivers}
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: seriamp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.11
4
+ version: 0.1.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oleg Pudeyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-23 00:00:00.000000000 Z
11
+ date: 2023-01-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: serialport