smartware 0.2.7 → 0.2.8
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/config/smartware.yml.sample +7 -0
- data/lib/smartware/clients/card_reader.rb +60 -0
- data/lib/smartware/drivers/card_reader/dummy.rb +71 -0
- data/lib/smartware/drivers/card_reader/ict3_k5.rb +466 -0
- data/lib/smartware/drivers/modem/standard.rb +20 -13
- data/lib/smartware/drivers/printer/dummy.rb +21 -1
- data/lib/smartware/interfaces/card_reader.rb +83 -0
- data/lib/smartware/service.rb +1 -1
- data/lib/smartware/version.rb +1 -1
- data/lib/smartware.rb +3 -0
- data/smartware.gemspec +1 -0
- metadata +22 -2
data/config/smartware.yml.sample
CHANGED
@@ -5,6 +5,13 @@ interfaces:
|
|
5
5
|
- name: Modem
|
6
6
|
uri: druby://localhost:6002
|
7
7
|
driver: Dummy
|
8
|
+
- name: Watchdog
|
9
|
+
uri: druby://localhost:6003
|
10
|
+
driver: Dummy
|
11
|
+
- name: CardReader
|
12
|
+
uri: druby://localhost:6004
|
13
|
+
driver: Dummy
|
8
14
|
- name: Printer
|
9
15
|
uri: druby://localhost:6005
|
10
16
|
driver: Dummy
|
17
|
+
connection_timeout: 60
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'drb'
|
2
|
+
|
3
|
+
module Smartware
|
4
|
+
module Client
|
5
|
+
|
6
|
+
module CardReader
|
7
|
+
|
8
|
+
DRb.start_service
|
9
|
+
@device = DRbObject.new_with_uri('druby://localhost:6004')
|
10
|
+
|
11
|
+
def self.open(limit_min = nil, limit_max = nil)
|
12
|
+
@device.open_session(limit_min, limit_max)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.close
|
16
|
+
@device.close_session
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.status
|
20
|
+
@device.status
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.error
|
24
|
+
@device.error
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.model
|
28
|
+
@device.model
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.version
|
32
|
+
@device.version
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.card_inserted?
|
36
|
+
@device.card_inserted?
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.start_accepting
|
40
|
+
@device.start_accepting
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.stop_accepting
|
44
|
+
@device.stop_accepting
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.eject
|
48
|
+
@device.eject
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.capture
|
52
|
+
@device.capture
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.read_magstrip
|
56
|
+
@device.read_magstrip
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Smartware
|
2
|
+
module Driver
|
3
|
+
module CardReader
|
4
|
+
class Dummy
|
5
|
+
def initialize(config)
|
6
|
+
@accepting = false
|
7
|
+
@state = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def model
|
11
|
+
"Dummy card reader"
|
12
|
+
end
|
13
|
+
|
14
|
+
def version
|
15
|
+
""
|
16
|
+
end
|
17
|
+
|
18
|
+
def ready?
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def accepting?
|
23
|
+
@accepting
|
24
|
+
end
|
25
|
+
|
26
|
+
def accepting=(accepting)
|
27
|
+
@state = :accepting if accepting
|
28
|
+
@accepting = accepting
|
29
|
+
end
|
30
|
+
|
31
|
+
def eject
|
32
|
+
@state = :eject
|
33
|
+
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def capture
|
38
|
+
@state = :eject
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def status
|
43
|
+
case @state
|
44
|
+
when nil
|
45
|
+
:ready
|
46
|
+
|
47
|
+
when :accepting
|
48
|
+
@state = :inserted
|
49
|
+
:card_at_gate
|
50
|
+
|
51
|
+
when :inserted
|
52
|
+
:card_inserted
|
53
|
+
|
54
|
+
when :eject
|
55
|
+
@state = nil
|
56
|
+
:card_at_gate
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def read_magstrip
|
61
|
+
[
|
62
|
+
"B4154000000000000^IVANOV/IVAN^1501101000",
|
63
|
+
"4154000000000000=1501101000",
|
64
|
+
nil,
|
65
|
+
nil
|
66
|
+
]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,466 @@
|
|
1
|
+
require "serialport"
|
2
|
+
require "digest/crc16_ccitt"
|
3
|
+
|
4
|
+
module Smartware
|
5
|
+
|
6
|
+
module Driver
|
7
|
+
module CardReader
|
8
|
+
class ICT3K5
|
9
|
+
ERRORS = {
|
10
|
+
0x00 => Interface::CardReader::COMMUNICATION_ERROR, # A given command code is unidentified
|
11
|
+
0x01 => Interface::CardReader::COMMUNICATION_ERROR, # Parameter is not correct
|
12
|
+
0x02 => Interface::CardReader::COMMUNICATION_ERROR, # Command execution is impossible
|
13
|
+
0x03 => Interface::CardReader::COMMUNICATION_ERROR, # Function is not implemented
|
14
|
+
0x04 => Interface::CardReader::COMMUNICATION_ERROR, # Command data error
|
15
|
+
0x06 => Interface::CardReader::COMMUNICATION_ERROR, # Key for decrypting is not received
|
16
|
+
0x10 => Interface::CardReader::CARD_JAM_ERROR,
|
17
|
+
0x11 => Interface::CardReader::CARD_ERROR, # Shutter error
|
18
|
+
0x13 => Interface::CardReader::CARD_ERROR, # Long card
|
19
|
+
0x14 => Interface::CardReader::CARD_ERROR, # Short card
|
20
|
+
0x15 => Interface::CardReader::HARDWARE_ERROR, # Flash Memory Parameter Area CRC error
|
21
|
+
0x16 => Interface::CardReader::CARD_ERROR, # Card position move
|
22
|
+
0x17 => Interface::CardReader::CARD_JAM_ERROR, # Jam error at retrieve
|
23
|
+
0x18 => Interface::CardReader::CARD_ERROR, # Two card error
|
24
|
+
0x20 => Interface::CardReader::MAG_READ_ERROR, # Parity error
|
25
|
+
0x21 => Interface::CardReader::MAG_READ_ERROR, # Sentinel error
|
26
|
+
0x23 => Interface::CardReader::MAG_READ_ERROR, # No data contents
|
27
|
+
0x24 => Interface::CardReader::MAG_READ_ERROR, # No stripe
|
28
|
+
0x30 => Interface::CardReader::HARDWARE_ERROR, # Power loss
|
29
|
+
0x31 => Interface::CardReader::COMMUNICATION_ERROR, # DTR low
|
30
|
+
0x39 => Interface::CardReader::HARDWARE_ERROR, # Fan failure
|
31
|
+
0x40 => Interface::CardReader::CARD_ERROR, # Pull out error
|
32
|
+
0x43 => Interface::CardReader::CARD_ERROR, # IC positioning error
|
33
|
+
0x50 => Interface::CardReader::HARDWARE_ERROR, # Capture counter overflow
|
34
|
+
0x60 => Interface::CardReader::ICC_ERROR, # Abnormal VCC condition
|
35
|
+
0x61 => Interface::CardReader::ICC_ERROR, # ATR error
|
36
|
+
0x62 => Interface::CardReader::ICC_ERROR, # Invalid ATR error
|
37
|
+
0x63 => Interface::CardReader::ICC_ERROR, # No response
|
38
|
+
0x64 => Interface::CardReader::ICC_ERROR, # Communication error
|
39
|
+
0x65 => Interface::CardReader::ICC_ERROR, # Not activated
|
40
|
+
0x66 => Interface::CardReader::ICC_ERROR, # Unsupported card
|
41
|
+
0x69 => Interface::CardReader::ICC_ERROR, # Unsupported card
|
42
|
+
0x73 => Interface::CardReader::HARDWARE_ERROR, # EEPROM error
|
43
|
+
0xB0 => Interface::CardReader::COMMUNICATION_ERROR # Not received initialize
|
44
|
+
}
|
45
|
+
|
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
|
+
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
|
+
@ready = false
|
92
|
+
@accepting = false
|
93
|
+
|
94
|
+
Thread.new &method(:dispatch)
|
95
|
+
end
|
96
|
+
|
97
|
+
def model
|
98
|
+
"ICT3K5"
|
99
|
+
end
|
100
|
+
|
101
|
+
def version
|
102
|
+
""
|
103
|
+
end
|
104
|
+
|
105
|
+
def ready?
|
106
|
+
@status_mutex.synchronize { @ready }
|
107
|
+
end
|
108
|
+
|
109
|
+
def accepting?
|
110
|
+
@status_mutex.synchronize { @accepting }
|
111
|
+
end
|
112
|
+
|
113
|
+
def accepting=(accepting)
|
114
|
+
if accepting
|
115
|
+
set_led :green
|
116
|
+
resp = command 0x3A, 0x30
|
117
|
+
else
|
118
|
+
set_led :red
|
119
|
+
resp = command 0x3A, 0x31
|
120
|
+
end
|
121
|
+
|
122
|
+
translate_response resp
|
123
|
+
|
124
|
+
@status_mutex.synchronize { @accepting = accepting }
|
125
|
+
end
|
126
|
+
|
127
|
+
def eject
|
128
|
+
resp = command 0x33, 0x30
|
129
|
+
translate_response resp
|
130
|
+
|
131
|
+
self
|
132
|
+
end
|
133
|
+
|
134
|
+
def capture
|
135
|
+
resp = command 0x33, 0x31
|
136
|
+
translate_response resp
|
137
|
+
|
138
|
+
self
|
139
|
+
end
|
140
|
+
|
141
|
+
def status
|
142
|
+
return :not_ready if !ready?
|
143
|
+
|
144
|
+
resp = command 0x31, 0x30
|
145
|
+
translate_response resp
|
146
|
+
|
147
|
+
case resp.response[2..3]
|
148
|
+
when "00"
|
149
|
+
:ready
|
150
|
+
|
151
|
+
when "01"
|
152
|
+
:card_at_gate
|
153
|
+
|
154
|
+
when "02"
|
155
|
+
:card_inserted
|
156
|
+
|
157
|
+
else
|
158
|
+
:not_ready
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def read_magstrip
|
163
|
+
[ 0x31, 0x32, 0x33, 0x34 ].map! do |track|
|
164
|
+
resp = command 0x36, track
|
165
|
+
translate_response resp if resp.error?
|
166
|
+
|
167
|
+
if resp.positive?
|
168
|
+
resp.response[4..-1]
|
169
|
+
else
|
170
|
+
nil
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
|
177
|
+
def command(*args, &block)
|
178
|
+
if block_given?
|
179
|
+
@command_queue.push [ args, block ]
|
180
|
+
@event_write.write "\x01"
|
181
|
+
else
|
182
|
+
queue = Queue.new
|
183
|
+
command(*args) { |response| queue.push response }
|
184
|
+
queue.pop
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
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
|
207
|
+
end
|
208
|
+
|
209
|
+
def set_led(color)
|
210
|
+
code = nil
|
211
|
+
|
212
|
+
case color
|
213
|
+
when :off
|
214
|
+
code = 0x30
|
215
|
+
|
216
|
+
when :green
|
217
|
+
code = 0x31
|
218
|
+
|
219
|
+
when :red
|
220
|
+
code = 0x32
|
221
|
+
|
222
|
+
when :orange
|
223
|
+
code = 0x33
|
224
|
+
end
|
225
|
+
|
226
|
+
command(0x35, code) do |resp|
|
227
|
+
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def complete_init(response)
|
232
|
+
if response.error?
|
233
|
+
Smartware::Logging.logger.info "ICT3K5: initialization error"
|
234
|
+
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
|
+
|
428
|
+
else
|
429
|
+
break
|
430
|
+
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
|
+
|
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
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
end
|
@@ -9,7 +9,14 @@ module Smartware
|
|
9
9
|
attr_reader :error, :model, :balance, :version
|
10
10
|
|
11
11
|
def initialize(config)
|
12
|
-
@
|
12
|
+
@port = config["port"]
|
13
|
+
@balance_ussd = config["balance_ussd"]
|
14
|
+
@status_channel_id = config["status_channel"].to_i
|
15
|
+
@ppp_channel_id = config["ppp_channel"].to_i
|
16
|
+
@poll_interval = config["poll_interval"].to_i
|
17
|
+
@balance_interval = config["balance_interval"].to_i
|
18
|
+
@apn = config["apn"]
|
19
|
+
|
13
20
|
@state = :closed
|
14
21
|
@error = Interface::Modem::MODEM_NOT_AVAILABLE
|
15
22
|
@mux = nil
|
@@ -18,7 +25,7 @@ module Smartware
|
|
18
25
|
@model = "GSM modem"
|
19
26
|
@version = ""
|
20
27
|
@signal = "+CSQ: 99,99"
|
21
|
-
@
|
28
|
+
@balance_timer = 0
|
22
29
|
@balance = nil
|
23
30
|
@ppp_state = :stopped
|
24
31
|
@ppp_pid = nil
|
@@ -70,13 +77,13 @@ module Smartware
|
|
70
77
|
Smartware::Logging.logger.info "trying to open modem"
|
71
78
|
|
72
79
|
begin
|
73
|
-
@mux = CMUX::MUX.new @
|
80
|
+
@mux = CMUX::MUX.new @port
|
74
81
|
@state = :open
|
75
|
-
@status_channel = @mux.allocate(@
|
82
|
+
@status_channel = @mux.allocate(@status_channel_id).open
|
76
83
|
@chatter = CMUX::ModemChatter.new @status_channel
|
77
84
|
@chatter.subscribe "CUSD", self
|
78
85
|
@error = nil
|
79
|
-
@
|
86
|
+
@balance_timer = 0
|
80
87
|
Smartware::Logging.logger.info "modem ready"
|
81
88
|
rescue => e
|
82
89
|
close_modem "unable to open modem: #{e}"
|
@@ -103,15 +110,15 @@ module Smartware
|
|
103
110
|
end
|
104
111
|
|
105
112
|
if modem_works
|
106
|
-
if
|
107
|
-
@
|
113
|
+
if !@balance_ussd.nil? && @balance_timer == 0
|
114
|
+
@balance_timer = @balance_interval
|
108
115
|
begin
|
109
|
-
@chatter.command("+CUSD=1,\"#{@
|
116
|
+
@chatter.command("+CUSD=1,\"#{@balance_ussd}\",15", 1)
|
110
117
|
rescue => e
|
111
118
|
close_modem "USSD request failed: #{e}"
|
112
119
|
end
|
113
120
|
else
|
114
|
-
@
|
121
|
+
@balance_timer -= 1
|
115
122
|
end
|
116
123
|
else
|
117
124
|
close_modem "modem is not responding"
|
@@ -125,11 +132,11 @@ module Smartware
|
|
125
132
|
if @state == :open
|
126
133
|
Smartware::Logging.logger.info "trying to start pppd"
|
127
134
|
begin
|
128
|
-
@ppp_channel = @mux.allocate @
|
135
|
+
@ppp_channel = @mux.allocate @ppp_channel_id
|
129
136
|
|
130
137
|
@ppp_pid = Process.spawn "smartware-ppp-helper",
|
131
138
|
@ppp_channel.device,
|
132
|
-
@
|
139
|
+
@apn
|
133
140
|
@ppp_state = :running
|
134
141
|
|
135
142
|
Smartware::Logging.logger.info "started pppd, PID #{@ppp_pid}"
|
@@ -171,12 +178,12 @@ module Smartware
|
|
171
178
|
def wait_for_event
|
172
179
|
if @state == :open
|
173
180
|
begin
|
174
|
-
CMUX::ModemChatter.poll [ @chatter ], @
|
181
|
+
CMUX::ModemChatter.poll [ @chatter ], @poll_interval
|
175
182
|
rescue => e
|
176
183
|
close_modem "modem poll failed: #{e}"
|
177
184
|
end
|
178
185
|
else
|
179
|
-
sleep @
|
186
|
+
sleep @poll_interval
|
180
187
|
end
|
181
188
|
|
182
189
|
end
|
@@ -25,6 +25,13 @@ module Smartware
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def print(data)
|
28
|
+
name = "smartwareprint_#{Time.now.strftime("%Y-%m-%d-%H:%M:%S")}.txt"
|
29
|
+
pathname = File.join(Dir.home, name)
|
30
|
+
|
31
|
+
File.open(pathname, 'w') { |io| io.write data }
|
32
|
+
|
33
|
+
Logging.logger.info "Created #{pathname}"
|
34
|
+
|
28
35
|
true
|
29
36
|
end
|
30
37
|
|
@@ -33,10 +40,23 @@ module Smartware
|
|
33
40
|
end
|
34
41
|
|
35
42
|
def new_render
|
36
|
-
|
43
|
+
DummyRender.new
|
37
44
|
end
|
38
45
|
end
|
39
46
|
|
47
|
+
class DummyRender < Redcarpet::Render::Base
|
48
|
+
def linebreak
|
49
|
+
"\n"
|
50
|
+
end
|
51
|
+
|
52
|
+
def normal_text(text, keep_newlines = false)
|
53
|
+
unless keep_newlines
|
54
|
+
text.gsub! "\n", " "
|
55
|
+
end
|
56
|
+
|
57
|
+
text
|
58
|
+
end
|
59
|
+
end
|
40
60
|
end
|
41
61
|
end
|
42
62
|
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Smartware
|
2
|
+
module Interface
|
3
|
+
class CardReader < Interface
|
4
|
+
COMMUNICATION_ERROR = 1
|
5
|
+
HARDWARE_ERROR = 2
|
6
|
+
CARD_JAM_ERROR = 3
|
7
|
+
CARD_ERROR = 4
|
8
|
+
MAG_READ_ERROR = 5
|
9
|
+
ICC_ERROR = 6
|
10
|
+
|
11
|
+
class CardReaderError < RuntimeError
|
12
|
+
attr_reader :code
|
13
|
+
|
14
|
+
def initialize(message, code)
|
15
|
+
super(message)
|
16
|
+
|
17
|
+
@code = code
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(config)
|
22
|
+
super
|
23
|
+
|
24
|
+
@status[:model] = @device.model
|
25
|
+
@status[:version] = @device.version
|
26
|
+
end
|
27
|
+
|
28
|
+
def card_inserted?
|
29
|
+
@device.status == :card_inserted
|
30
|
+
rescue CardReaderError => e
|
31
|
+
@status[:error] = e.code
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def start_accepting
|
36
|
+
@device.accepting = true
|
37
|
+
@status[:error] = nil
|
38
|
+
true
|
39
|
+
rescue CardReaderError => e
|
40
|
+
@status[:error] = e.code
|
41
|
+
false
|
42
|
+
end
|
43
|
+
|
44
|
+
def stop_accepting
|
45
|
+
@device.accepting = false
|
46
|
+
@status[:error] = nil
|
47
|
+
true
|
48
|
+
rescue CardReaderError => e
|
49
|
+
@status[:error] = e.code
|
50
|
+
false
|
51
|
+
end
|
52
|
+
|
53
|
+
def eject
|
54
|
+
@device.eject
|
55
|
+
|
56
|
+
sleep 0.5 while @device.status == :card_at_gate
|
57
|
+
|
58
|
+
@status[:error] = nil
|
59
|
+
true
|
60
|
+
rescue CardReaderError => e
|
61
|
+
@status[:error] = e.code
|
62
|
+
false
|
63
|
+
end
|
64
|
+
|
65
|
+
def capture
|
66
|
+
@device.capture
|
67
|
+
@status[:error] = nil
|
68
|
+
true
|
69
|
+
rescue CardReaderError => e
|
70
|
+
@status[:error] = e.code
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
def read_magstrip
|
75
|
+
@device.read_magstrip
|
76
|
+
|
77
|
+
rescue CardReaderError => e
|
78
|
+
@status[:error] = e.code
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/smartware/service.rb
CHANGED
data/lib/smartware/version.rb
CHANGED
data/lib/smartware.rb
CHANGED
@@ -3,6 +3,7 @@ require 'yaml'
|
|
3
3
|
require 'active_support/core_ext/string/inflections'
|
4
4
|
require 'drb'
|
5
5
|
require 'redcarpet'
|
6
|
+
require 'stringio'
|
6
7
|
|
7
8
|
require 'smartkiosk/common'
|
8
9
|
|
@@ -14,11 +15,13 @@ require 'smartware/clients/cash_acceptor'
|
|
14
15
|
require 'smartware/clients/printer'
|
15
16
|
require 'smartware/clients/modem'
|
16
17
|
require 'smartware/clients/watchdog'
|
18
|
+
require 'smartware/clients/card_reader'
|
17
19
|
require 'smartware/interfaces/interface'
|
18
20
|
require 'smartware/interfaces/cash_acceptor'
|
19
21
|
require 'smartware/interfaces/modem'
|
20
22
|
require 'smartware/interfaces/printer'
|
21
23
|
require 'smartware/interfaces/watchdog'
|
24
|
+
require 'smartware/interfaces/card_reader'
|
22
25
|
require 'smartware/connection_monitor'
|
23
26
|
|
24
27
|
module Smartware
|
data/smartware.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smartware
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.8
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-01-
|
13
|
+
date: 2013-01-24 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: smartkiosk-common
|
@@ -108,6 +108,22 @@ dependencies:
|
|
108
108
|
- - ! '>='
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: digest-crc
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ! '>='
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :runtime
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ! '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
111
127
|
description: Smartware is the Smartkiosk hardware control daemon
|
112
128
|
email:
|
113
129
|
- e.sudarchikov@roundlake.ru
|
@@ -127,11 +143,14 @@ files:
|
|
127
143
|
- bin/smartware-ppp-helper
|
128
144
|
- config/smartware.yml.sample
|
129
145
|
- lib/smartware.rb
|
146
|
+
- lib/smartware/clients/card_reader.rb
|
130
147
|
- lib/smartware/clients/cash_acceptor.rb
|
131
148
|
- lib/smartware/clients/modem.rb
|
132
149
|
- lib/smartware/clients/printer.rb
|
133
150
|
- lib/smartware/clients/watchdog.rb
|
134
151
|
- lib/smartware/connection_monitor.rb
|
152
|
+
- lib/smartware/drivers/card_reader/dummy.rb
|
153
|
+
- lib/smartware/drivers/card_reader/ict3_k5.rb
|
135
154
|
- lib/smartware/drivers/cash_acceptor/ccnet.rb
|
136
155
|
- lib/smartware/drivers/cash_acceptor/dummy.rb
|
137
156
|
- lib/smartware/drivers/modem/dummy.rb
|
@@ -140,6 +159,7 @@ files:
|
|
140
159
|
- lib/smartware/drivers/printer/esc_pos.rb
|
141
160
|
- lib/smartware/drivers/watchdog/dummy.rb
|
142
161
|
- lib/smartware/drivers/watchdog/watchdog_daemon.rb
|
162
|
+
- lib/smartware/interfaces/card_reader.rb
|
143
163
|
- lib/smartware/interfaces/cash_acceptor.rb
|
144
164
|
- lib/smartware/interfaces/interface.rb
|
145
165
|
- lib/smartware/interfaces/modem.rb
|