smartware 0.2.8 → 0.3
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.
- data/lib/smartware.rb +2 -0
- data/lib/smartware/drivers/card_reader/ict3_k5.rb +48 -313
- data/lib/smartware/drivers/cash_acceptor/ccnet.rb +212 -144
- data/lib/smartware/drivers/cash_acceptor/dummy.rb +11 -33
- data/lib/smartware/drivers/common/ccnet_connection.rb +135 -0
- data/lib/smartware/drivers/common/command_based_device.rb +101 -0
- data/lib/smartware/drivers/common/sankyo_connection.rb +154 -0
- data/lib/smartware/interfaces/cash_acceptor.rb +53 -101
- data/lib/smartware/service.rb +17 -12
- data/lib/smartware/version.rb +1 -1
- data/smartware.gemspec +1 -0
- metadata +21 -2
data/lib/smartware.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
|
-
require "
|
2
|
-
require "digest/crc16_ccitt"
|
1
|
+
require "smartware/drivers/common/sankyo_connection"
|
3
2
|
|
4
3
|
module Smartware
|
5
|
-
|
6
4
|
module Driver
|
7
5
|
module CardReader
|
8
6
|
class ICT3K5
|
@@ -43,55 +41,15 @@ module Smartware
|
|
43
41
|
0xB0 => Interface::CardReader::COMMUNICATION_ERROR # Not received initialize
|
44
42
|
}
|
45
43
|
|
46
|
-
class CRC < Digest::CRC16CCITT
|
47
|
-
INIT_CRC = 0x0000
|
48
|
-
end
|
49
|
-
|
50
|
-
class CommandResponse
|
51
|
-
def initialize(response)
|
52
|
-
@response = response
|
53
|
-
end
|
54
|
-
|
55
|
-
def error?
|
56
|
-
@response.nil? || (@response[0] != "P" && @response[0] != "N")
|
57
|
-
end
|
58
|
-
|
59
|
-
def positive?
|
60
|
-
@response[0] == "P"
|
61
|
-
end
|
62
|
-
|
63
|
-
def negative?
|
64
|
-
@response[0] == "N"
|
65
|
-
end
|
66
|
-
|
67
|
-
def response
|
68
|
-
@response[1..-1]
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
STX = 0xF2
|
73
|
-
ACK = 0x06
|
74
|
-
NAK = 0x15
|
75
|
-
|
76
44
|
def initialize(config)
|
77
|
-
@config = config
|
78
|
-
|
79
|
-
@port = SerialPort.new(config["port"], 115200, 8, 1, SerialPort::EVEN)
|
80
|
-
|
81
|
-
@state = :not_ready
|
82
|
-
@event_read, @event_write = IO.pipe
|
83
|
-
@read_buf = ""
|
84
|
-
@write_buf = ""
|
85
|
-
@command_queue = Queue.new
|
86
|
-
@status_mutex = Mutex.new
|
87
|
-
@active_command = nil
|
88
|
-
@active_block = nil
|
89
|
-
@start_time = nil
|
90
|
-
@retries = nil
|
91
45
|
@ready = false
|
92
46
|
@accepting = false
|
93
47
|
|
94
|
-
|
48
|
+
@port = SerialPort.new(config["port"], 115200, 8, 1, SerialPort::EVEN)
|
49
|
+
@port.flow_control = SerialPort::HARD
|
50
|
+
|
51
|
+
@connection = EventMachine.attach @port, SankyoConnection
|
52
|
+
@connection.initialize_device = method :initialize_device
|
95
53
|
end
|
96
54
|
|
97
55
|
def model
|
@@ -103,36 +61,36 @@ module Smartware
|
|
103
61
|
end
|
104
62
|
|
105
63
|
def ready?
|
106
|
-
@
|
64
|
+
@ready
|
107
65
|
end
|
108
66
|
|
109
67
|
def accepting?
|
110
|
-
@
|
68
|
+
@accepting
|
111
69
|
end
|
112
70
|
|
113
71
|
def accepting=(accepting)
|
114
72
|
if accepting
|
115
73
|
set_led :green
|
116
|
-
resp = command 0x3A, 0x30
|
74
|
+
resp = @connection.command 0x3A, 0x30
|
117
75
|
else
|
118
76
|
set_led :red
|
119
|
-
resp = command 0x3A, 0x31
|
77
|
+
resp = @connection.command 0x3A, 0x31
|
120
78
|
end
|
121
79
|
|
122
80
|
translate_response resp
|
123
81
|
|
124
|
-
@
|
82
|
+
@accepting = accepting
|
125
83
|
end
|
126
84
|
|
127
85
|
def eject
|
128
|
-
resp = command 0x33, 0x30
|
86
|
+
resp = @connection.command 0x33, 0x30
|
129
87
|
translate_response resp
|
130
88
|
|
131
89
|
self
|
132
90
|
end
|
133
91
|
|
134
92
|
def capture
|
135
|
-
resp = command 0x33, 0x31
|
93
|
+
resp = @connection.command 0x33, 0x31
|
136
94
|
translate_response resp
|
137
95
|
|
138
96
|
self
|
@@ -141,7 +99,7 @@ module Smartware
|
|
141
99
|
def status
|
142
100
|
return :not_ready if !ready?
|
143
101
|
|
144
|
-
resp = command 0x31, 0x30
|
102
|
+
resp = @connection.command 0x31, 0x30
|
145
103
|
translate_response resp
|
146
104
|
|
147
105
|
case resp.response[2..3]
|
@@ -161,8 +119,8 @@ module Smartware
|
|
161
119
|
|
162
120
|
def read_magstrip
|
163
121
|
[ 0x31, 0x32, 0x33, 0x34 ].map! do |track|
|
164
|
-
resp = command 0x36, track
|
165
|
-
translate_response resp if resp.
|
122
|
+
resp = @connection.command 0x36, track
|
123
|
+
translate_response resp if resp.nil?
|
166
124
|
|
167
125
|
if resp.positive?
|
168
126
|
resp.response[4..-1]
|
@@ -174,36 +132,27 @@ module Smartware
|
|
174
132
|
|
175
133
|
private
|
176
134
|
|
177
|
-
def
|
178
|
-
if
|
179
|
-
|
180
|
-
|
135
|
+
def complete_init(response)
|
136
|
+
if response.nil?
|
137
|
+
Smartware::Logging.logger.warn "ICT3K5: initialization error"
|
138
|
+
elsif response.negative?
|
139
|
+
Smartware::Logging.logger.warn "ICT3K5: initialization negative: #{response.response}"
|
181
140
|
else
|
182
|
-
|
183
|
-
|
184
|
-
|
141
|
+
Smartware::Logging.logger.info "ICT3K5: initialization: #{response.response}"
|
142
|
+
@ready = true
|
143
|
+
set_led :red
|
185
144
|
end
|
186
145
|
end
|
187
146
|
|
188
|
-
def
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
translated_error = ERRORS[error]
|
198
|
-
else
|
199
|
-
translated_error = Interface::CardReader::HARDWARE_ERROR
|
200
|
-
end
|
201
|
-
|
202
|
-
raise Interface::CardReader::CardReaderError.new(
|
203
|
-
"command failed: #{error}",
|
204
|
-
translated_error
|
205
|
-
)
|
206
|
-
end
|
147
|
+
def initialize_device
|
148
|
+
@connection.command 0x30, # Initialize
|
149
|
+
0x30, # Eject card,
|
150
|
+
0x33, 0x32, 0x34, 0x31, 0x30, # Compatibility nonsense
|
151
|
+
0x30, # Power down card
|
152
|
+
0x31, # Identify reader
|
153
|
+
0x30, # Eject card on DTR low
|
154
|
+
0x30, # Turn off capture counter
|
155
|
+
&method(:complete_init)
|
207
156
|
end
|
208
157
|
|
209
158
|
def set_led(color)
|
@@ -223,241 +172,27 @@ module Smartware
|
|
223
172
|
code = 0x33
|
224
173
|
end
|
225
174
|
|
226
|
-
command(0x35, code)
|
227
|
-
|
228
|
-
end
|
175
|
+
@connection.command(0x35, code) {}
|
229
176
|
end
|
230
177
|
|
231
|
-
def
|
232
|
-
if response.
|
233
|
-
|
178
|
+
def translate_response(response)
|
179
|
+
if response.nil?
|
180
|
+
raise Interface::CardReader::CardReaderError.new(
|
181
|
+
"communication error",
|
182
|
+
Interface::CardReader::COMMUNICATION_ERROR
|
183
|
+
)
|
234
184
|
elsif response.negative?
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
@status_mutex.synchronize { @ready = true }
|
239
|
-
set_led :red
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
def run_periodic
|
244
|
-
if @start_time.nil?
|
245
|
-
elapsed = nil
|
246
|
-
else
|
247
|
-
elapsed = Time.now - @start_time
|
248
|
-
end
|
249
|
-
|
250
|
-
case @state
|
251
|
-
when :not_ready
|
252
|
-
if @port.dsr == 1
|
253
|
-
|
254
|
-
Smartware::Logging.logger.info "ICT3K5: DSR active, initializing"
|
255
|
-
|
256
|
-
@state = :accepting
|
257
|
-
|
258
|
-
flushed = []
|
259
|
-
|
260
|
-
command 0x30, # Initialize
|
261
|
-
0x30, # Eject card,
|
262
|
-
0x33, 0x32, 0x34, 0x31, 0x30, # Compatibility nonsense
|
263
|
-
0x30, # Power down card
|
264
|
-
0x31, # Identify reader
|
265
|
-
0x30, # Eject card on DTR low
|
266
|
-
0x30, # Turn off capture counter
|
267
|
-
&method(:complete_init)
|
268
|
-
|
269
|
-
flushed.each do |(command, block)|
|
270
|
-
block.call CommandResponse.new(error: "timeout")
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
|
-
when :waiting_ack
|
275
|
-
if elapsed > 0.3
|
276
|
-
Smartware::Logging.logger.info "ICT3K5: ACK timeout"
|
277
|
-
retry_or_fail
|
278
|
-
end
|
279
|
-
|
280
|
-
when :reading_response
|
281
|
-
if elapsed > 20
|
282
|
-
Smartware::Logging.logger.info "ICT3K5: command timeout"
|
283
|
-
retry_or_fail
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
|
-
if @port.dsr == 0
|
288
|
-
@status_mutex.synchronize do
|
289
|
-
@ready = false
|
290
|
-
@accepting = false
|
291
|
-
end
|
292
|
-
|
293
|
-
if !@active_command.nil?
|
294
|
-
Smartware::Logging.logger.info "ICT3K5: DSR fall"
|
295
|
-
|
296
|
-
fail_command
|
297
|
-
end
|
298
|
-
|
299
|
-
until @command_queue.empty?
|
300
|
-
unpacked, block = @command_queue.pop(true)
|
301
|
-
|
302
|
-
block.call CommandResponse.new(nil)
|
303
|
-
end
|
304
|
-
end
|
305
|
-
end
|
306
|
-
|
307
|
-
def start_execution
|
308
|
-
@event_read.readbyte
|
309
|
-
|
310
|
-
unpacked, @active_block = @command_queue.pop
|
311
|
-
@active_command = frame_command(unpacked)
|
312
|
-
|
313
|
-
@write_buf << @active_command
|
314
|
-
@start_time = Time.now
|
315
|
-
@state = :waiting_ack
|
316
|
-
@retries = 8
|
317
|
-
end
|
318
|
-
|
319
|
-
def complete_command(response)
|
320
|
-
block = @active_block
|
321
|
-
|
322
|
-
@state = :accepting
|
323
|
-
@active_command = nil
|
324
|
-
@active_block = nil
|
325
|
-
|
326
|
-
Smartware::Logging.logger.info "ICT3K5: completing command"
|
327
|
-
|
328
|
-
block.call CommandResponse.new(response)
|
329
|
-
end
|
330
|
-
|
331
|
-
def fail_command
|
332
|
-
block = @active_block
|
333
|
-
|
334
|
-
@state = :accepting
|
335
|
-
@active_command = nil
|
336
|
-
@active_block = nil
|
337
|
-
|
338
|
-
Smartware::Logging.logger.info "ICT3K5: failing command"
|
339
|
-
|
340
|
-
block.call CommandResponse.new(nil)
|
341
|
-
end
|
342
|
-
|
343
|
-
def retry_or_fail
|
344
|
-
if @retries == 0
|
345
|
-
fail_command
|
346
|
-
else
|
347
|
-
@retries -= 1
|
348
|
-
@start_time = Time.now
|
349
|
-
@state = :waiting_ack
|
350
|
-
@write_buf << @active_command
|
351
|
-
end
|
352
|
-
end
|
353
|
-
|
354
|
-
def frame_command(bytes)
|
355
|
-
data = [ STX, bytes.length + 1 ].pack("Cn")
|
356
|
-
data << "C"
|
357
|
-
data << bytes.pack("C*")
|
358
|
-
|
359
|
-
crc = CRC.new
|
360
|
-
crc << data
|
361
|
-
data << [ crc.checksum ].pack("n")
|
362
|
-
|
363
|
-
data
|
364
|
-
end
|
365
|
-
|
366
|
-
def read_chunk
|
367
|
-
@read_buf << @port.read_nonblock(8192)
|
368
|
-
rescue IO::WaitReadable
|
369
|
-
end
|
370
|
-
|
371
|
-
def write_chunk
|
372
|
-
bytes = @port.write_nonblock @write_buf
|
373
|
-
@write_buf.slice! 0, bytes
|
374
|
-
|
375
|
-
rescue IO::WaitWritable
|
376
|
-
end
|
377
|
-
|
378
|
-
def handle_input
|
379
|
-
until @read_buf.empty? do
|
380
|
-
case @state
|
381
|
-
when :waiting_ack
|
382
|
-
initial_byte = @read_buf.slice!(0, 1).ord
|
383
|
-
|
384
|
-
case initial_byte
|
385
|
-
when ACK
|
386
|
-
Smartware::Logging.logger.info "ICT3K5: ACK"
|
387
|
-
|
388
|
-
@state = :reading_response
|
389
|
-
@start_time = Time.now
|
390
|
-
|
391
|
-
when NAK
|
392
|
-
Smartware::Logging.logger.info "ICT3K5: NAK"
|
393
|
-
|
394
|
-
retry_or_fail
|
395
|
-
|
396
|
-
else
|
397
|
-
Smartware::Logging.logger.info "ICT3K5: garbage on line: #{initial_byte}"
|
398
|
-
end
|
399
|
-
|
400
|
-
when :reading_response
|
401
|
-
break if @read_buf.length < 5
|
402
|
-
|
403
|
-
leading_byte, length = @read_buf[0..2].unpack("Cn")
|
404
|
-
if leading_byte != STX
|
405
|
-
Smartware::Logging.logger.info "ICT3K5: garbage on line: #{leading_byte}"
|
406
|
-
|
407
|
-
@read_buf.slice! 0, 1
|
408
|
-
next
|
409
|
-
end
|
410
|
-
|
411
|
-
full_length = 5 + length
|
412
|
-
|
413
|
-
break if @read_buf.length < full_length
|
414
|
-
|
415
|
-
message = @read_buf.slice! 0, full_length
|
416
|
-
sum, = message.slice!(full_length - 2, 2).unpack("n")
|
417
|
-
crc = CRC.new
|
418
|
-
crc << message
|
419
|
-
if sum == crc.checksum
|
420
|
-
Smartware::Logging.logger.info "ICT3K5: message checksum ok, ACK and process"
|
421
|
-
@write_buf << ACK.chr
|
422
|
-
complete_command message[3..-1]
|
423
|
-
else
|
424
|
-
Smartware::Logging.logger.info "ICT3K5: message checksum invalid, NAK"
|
425
|
-
@write_buf << NAK.chr
|
426
|
-
end
|
427
|
-
|
185
|
+
error = response.response[0..1].to_i(16)
|
186
|
+
if ERRORS.include? error
|
187
|
+
translated_error = ERRORS[error]
|
428
188
|
else
|
429
|
-
|
189
|
+
translated_error = Interface::CardReader::HARDWARE_ERROR
|
430
190
|
end
|
431
|
-
end
|
432
|
-
end
|
433
|
-
|
434
|
-
def dispatch
|
435
|
-
loop do
|
436
|
-
begin
|
437
|
-
run_periodic
|
438
|
-
|
439
|
-
read_set = [ @port ]
|
440
|
-
write_set = []
|
441
191
|
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
unless read_set.nil?
|
448
|
-
start_execution if read_set.include? @event_read
|
449
|
-
read_chunk if read_set.include? @port
|
450
|
-
write_chunk if write_set.include? @port
|
451
|
-
end
|
452
|
-
|
453
|
-
handle_input
|
454
|
-
rescue => e
|
455
|
-
Smartware::Logging.logger.error "Error in ICT3K5 dispatch:"
|
456
|
-
Smartware::Logging.logger.error e.to_s
|
457
|
-
e.backtrace.each do |line|
|
458
|
-
Smartware::Logging.logger.error line
|
459
|
-
end
|
460
|
-
end
|
192
|
+
raise Interface::CardReader::CardReaderError.new(
|
193
|
+
"command failed: #{error}",
|
194
|
+
translated_error
|
195
|
+
)
|
461
196
|
end
|
462
197
|
end
|
463
198
|
end
|