seriamp 0.1.11 → 0.1.12

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 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