seriamp 0.1.10 → 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 +4 -4
- data/bin/sonamp-web +4 -3
- data/bin/yamaha-web +4 -3
- data/lib/seriamp/sonamp/app.rb +3 -1
- data/lib/seriamp/sonamp/client.rb +100 -31
- data/lib/seriamp/version.rb +1 -1
- data/lib/seriamp/yamaha/app.rb +3 -1
- data/lib/seriamp/yamaha/client.rb +70 -28
- data/seriamp.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a8c5974a39756c641ab5fb6e600d0d67095e4e9f18c872225ffb580cea4e70c
|
4
|
+
data.tar.gz: ba47852332bdb66070cf4105c4929ed691fce8eca8dd7f47aaefbeb4effab932
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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(
|
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(
|
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))
|
data/lib/seriamp/sonamp/app.rb
CHANGED
@@ -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,
|
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,17 +10,37 @@ module Seriamp
|
|
10
10
|
RS232_TIMEOUT = 3
|
11
11
|
|
12
12
|
class Client
|
13
|
-
def initialize(device: nil, glob: nil, logger: nil)
|
13
|
+
def initialize(device: nil, glob: nil, logger: nil, retries: true, thread_safe: false)
|
14
14
|
@logger = logger
|
15
15
|
|
16
16
|
@device = device
|
17
17
|
@detect_device = device.nil?
|
18
18
|
@glob = glob
|
19
|
+
@retries = case retries
|
20
|
+
when nil, false
|
21
|
+
0
|
22
|
+
when true
|
23
|
+
1
|
24
|
+
when Integer
|
25
|
+
retries
|
26
|
+
else
|
27
|
+
raise ArgumentError, "retries must be an integer, true, false or nil: #{retries}"
|
28
|
+
end
|
29
|
+
@thread_safe = !!thread_safe
|
30
|
+
|
31
|
+
if thread_safe?
|
32
|
+
@lock = Mutex.new
|
33
|
+
end
|
19
34
|
end
|
20
35
|
|
21
36
|
attr_reader :device
|
22
37
|
attr_reader :glob
|
23
38
|
attr_reader :logger
|
39
|
+
attr_reader :retries
|
40
|
+
|
41
|
+
def thread_safe?
|
42
|
+
@thread_safe
|
43
|
+
end
|
24
44
|
|
25
45
|
def detect_device?
|
26
46
|
@detect_device
|
@@ -134,7 +154,7 @@ module Seriamp
|
|
134
154
|
end
|
135
155
|
|
136
156
|
def get_voltage_trigger_input(zone = nil)
|
137
|
-
get_zone_state('VTI', zone)
|
157
|
+
get_zone_state('VTI', zone, include_all: true)
|
138
158
|
end
|
139
159
|
|
140
160
|
def get_firmware_version
|
@@ -148,7 +168,7 @@ module Seriamp
|
|
148
168
|
def status
|
149
169
|
# Reusing the opened device file makes :VTIG? fail even with a delay
|
150
170
|
# in front.
|
151
|
-
|
171
|
+
with_device do
|
152
172
|
{
|
153
173
|
firmware_version: get_firmware_version,
|
154
174
|
temperature: get_temperature,
|
@@ -165,13 +185,34 @@ module Seriamp
|
|
165
185
|
voltage_trigger_input: get_voltage_trigger_input,
|
166
186
|
channel_front_panel_level: get_channel_front_panel_level,
|
167
187
|
}
|
168
|
-
|
188
|
+
end
|
169
189
|
end
|
170
190
|
|
171
191
|
private
|
172
192
|
|
193
|
+
def with_device(&block)
|
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
|
208
|
+
else
|
209
|
+
yield
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
173
213
|
def open_device
|
174
214
|
if detect_device? && device.nil?
|
215
|
+
logger&.debug("Detecting device")
|
175
216
|
@device = Seriamp.detect_device(Sonamp, *glob, logger: logger)
|
176
217
|
if @device
|
177
218
|
logger&.info("Using #{device} as TTY device")
|
@@ -180,31 +221,58 @@ module Seriamp
|
|
180
221
|
end
|
181
222
|
end
|
182
223
|
|
183
|
-
|
224
|
+
logger&.debug("Opening #{device}")
|
225
|
+
@io = Backend::SerialPortBackend::Device.new(device, logger: logger)
|
226
|
+
|
227
|
+
begin
|
228
|
+
yield @io
|
229
|
+
ensure
|
230
|
+
@io.close rescue nil
|
231
|
+
@io = nil
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def with_retry
|
236
|
+
try = 1
|
237
|
+
begin
|
184
238
|
yield
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
239
|
+
rescue Seriamp::Error => exc
|
240
|
+
if try <= retries
|
241
|
+
logger&.warn("Error during operation: #{exc.class}: #{exc} - will retry")
|
242
|
+
try += 1
|
243
|
+
@device = nil
|
244
|
+
retry
|
245
|
+
else
|
246
|
+
raise
|
191
247
|
end
|
192
|
-
rv
|
193
248
|
end
|
194
249
|
end
|
195
250
|
|
196
|
-
def dispatch(cmd,
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
251
|
+
def dispatch(cmd, resp_lines_range_or_count = 1)
|
252
|
+
resp_lines_range = if Range === resp_lines_range_or_count || Array === resp_lines_range_or_count
|
253
|
+
resp_lines_range_or_count
|
254
|
+
else
|
255
|
+
1..resp_lines_range_or_count
|
256
|
+
end
|
257
|
+
|
258
|
+
with_retry do
|
259
|
+
with_device do
|
260
|
+
with_timeout do
|
261
|
+
@io.syswrite("#{cmd}\x0d")
|
262
|
+
end
|
263
|
+
resp = resp_lines_range.map do
|
264
|
+
read_line(@io, cmd)
|
265
|
+
end
|
266
|
+
|
267
|
+
if @io && IO.select([@io.io], nil, nil, 0)
|
268
|
+
logger&.warn("Serial device readable after completely reading status response - concurrent access?")
|
269
|
+
end
|
270
|
+
|
271
|
+
if resp_lines_range_or_count == 1
|
272
|
+
resp.first
|
273
|
+
else
|
274
|
+
resp
|
275
|
+
end
|
208
276
|
end
|
209
277
|
end
|
210
278
|
end
|
@@ -266,7 +334,7 @@ module Seriamp
|
|
266
334
|
dispatch_assert(cmd, expected)
|
267
335
|
end
|
268
336
|
|
269
|
-
def get_zone_value(cmd_prefix, zone, boolize: false)
|
337
|
+
def get_zone_value(cmd_prefix, zone, boolize: false, include_all: false)
|
270
338
|
if zone
|
271
339
|
if zone < 1 || zone > 4
|
272
340
|
raise ArgumentError, "Zone must be between 1 and 4: #{zone}"
|
@@ -274,22 +342,23 @@ module Seriamp
|
|
274
342
|
resp = dispatch(":#{cmd_prefix}#{zone}?")
|
275
343
|
typecast_value(resp[cmd_prefix.length + 1..], boolize)
|
276
344
|
else
|
277
|
-
|
345
|
+
range = include_all ? [1, 2, 3, 4, 'A'] : (1..4).to_a
|
346
|
+
hashize_query_result(dispatch(":#{cmd_prefix}G?", range), cmd_prefix, boolize, range)
|
278
347
|
end
|
279
348
|
end
|
280
349
|
|
281
|
-
def hashize_query_result(resp_lines, cmd_prefix, boolize)
|
350
|
+
def hashize_query_result(resp_lines, cmd_prefix, boolize, range)
|
282
351
|
index = 1
|
283
352
|
Hash[resp_lines.map do |resp|
|
284
|
-
value = typecast_value(extract_suffix(resp, "#{cmd_prefix}#{index}"), boolize)
|
353
|
+
value = typecast_value(extract_suffix(resp, "#{cmd_prefix}#{range.to_a[index-1]}"), boolize)
|
285
354
|
[index, value].tap do
|
286
355
|
index += 1
|
287
356
|
end
|
288
357
|
end]
|
289
358
|
end
|
290
359
|
|
291
|
-
def get_zone_state(cmd_prefix, zone)
|
292
|
-
get_zone_value(cmd_prefix, zone, boolize: true)
|
360
|
+
def get_zone_state(cmd_prefix, zone, include_all: false)
|
361
|
+
get_zone_value(cmd_prefix, zone, boolize: true, include_all: include_all)
|
293
362
|
end
|
294
363
|
|
295
364
|
def set_channel_value(cmd_prefix, channel, value)
|
@@ -309,7 +378,7 @@ module Seriamp
|
|
309
378
|
typecast_value(dispatch_extract_suffix(":#{cmd_prefix}#{channel}?", "#{cmd_prefix}#{channel}"), boolize)
|
310
379
|
else
|
311
380
|
index = 1
|
312
|
-
hashize_query_result(dispatch(":#{cmd_prefix}G?", 8), cmd_prefix, boolize)
|
381
|
+
hashize_query_result(dispatch(":#{cmd_prefix}G?", 8), cmd_prefix, boolize, 1..8)
|
313
382
|
end
|
314
383
|
end
|
315
384
|
|
data/lib/seriamp/version.rb
CHANGED
data/lib/seriamp/yamaha/app.rb
CHANGED
@@ -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,
|
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
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
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
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
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
|
-
|
270
|
-
|
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
|
-
|
280
|
-
|
281
|
-
model_code:
|
282
|
-
model_name: MODEL_NAMES[
|
283
|
-
firmware_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
|
-
|
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(
|
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
|
330
|
-
|
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
|
-
|
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
|
-
|
343
|
-
|
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
|
-
|
349
|
-
|
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
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.
|
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-
|
11
|
+
date: 2023-01-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: serialport
|