smartware 0.2.8 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
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