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 CHANGED
@@ -4,6 +4,8 @@ require 'active_support/core_ext/string/inflections'
4
4
  require 'drb'
5
5
  require 'redcarpet'
6
6
  require 'stringio'
7
+ require 'eventmachine'
8
+ require 'serialport'
7
9
 
8
10
  require 'smartkiosk/common'
9
11
 
@@ -1,8 +1,6 @@
1
- require "serialport"
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
- Thread.new &method(:dispatch)
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
- @status_mutex.synchronize { @ready }
64
+ @ready
107
65
  end
108
66
 
109
67
  def accepting?
110
- @status_mutex.synchronize { @accepting }
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
- @status_mutex.synchronize { @accepting = accepting }
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.error?
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 command(*args, &block)
178
- if block_given?
179
- @command_queue.push [ args, block ]
180
- @event_write.write "\x01"
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
- queue = Queue.new
183
- command(*args) { |response| queue.push response }
184
- queue.pop
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 translate_response(response)
189
- if response.error?
190
- raise Interface::CardReader::CardReaderError.new(
191
- "communication error",
192
- Interface::CardReader::COMMUNICATION_ERROR
193
- )
194
- elsif response.negative?
195
- error = response.response[0..1].to_i(16)
196
- if ERRORS.include? error
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) do |resp|
227
-
228
- end
175
+ @connection.command(0x35, code) {}
229
176
  end
230
177
 
231
- def complete_init(response)
232
- if response.error?
233
- Smartware::Logging.logger.info "ICT3K5: initialization error"
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
- Smartware::Logging.logger.info "ICT3K5: initialization negative: #{response.response}"
236
- else
237
- Smartware::Logging.logger.info "ICT3K5: initialization: #{response.response}"
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
- break
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
- read_set << @event_read if @state == :accepting
443
- write_set << @port unless @write_buf.empty?
444
-
445
- read_set, write_set, = IO.select read_set, write_set, [], 1
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