smartware 0.1

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.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in smartware.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 ya.jeks
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # Smartware
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'smartware'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install smartware
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ trap 'TTIN' do
4
+ Thread.list.each do |thread|
5
+ if thread.backtrace
6
+ puts thread.backtrace.join("\n")
7
+ else
8
+ puts "<no backtrace available>"
9
+ end
10
+ end
11
+ end
12
+
13
+ require 'smartware/service'
14
+ require 'dante'
15
+
16
+ runner = Dante::Runner.new('smartware')
17
+ runner.description = "Smartkiosk hardware control daemon"
18
+
19
+ runner.execute do |opts|
20
+ begin
21
+ Smartware::Service.start
22
+ rescue => e
23
+ raise e if $DEBUG
24
+ STDERR.puts e.message
25
+ STDERR.puts e.backtrace.join("\n")
26
+ exit 1
27
+ end
28
+ end
@@ -0,0 +1,25 @@
1
+ require 'smartware/clients/cash_acceptor'
2
+ require 'smartware/clients/printer'
3
+ require 'smartware/clients/modem'
4
+
5
+ module Smartware
6
+
7
+ def self.devices
8
+ yield self
9
+ end
10
+
11
+ def self.cash_acceptor
12
+ Smartware::Client::CashAcceptor
13
+ end
14
+
15
+ def self.printer
16
+ Smartware::Client::Printer
17
+ end
18
+
19
+ def self.modem
20
+ Smartware::Client::Modem
21
+ end
22
+
23
+ end
24
+
25
+
@@ -0,0 +1,40 @@
1
+ require 'drb'
2
+
3
+ module Smartware
4
+ module Client
5
+
6
+ module CashAcceptor
7
+
8
+ def self.configure(port, driver)
9
+ DRb.start_service
10
+ @device = DRbObject.new_with_uri('druby://localhost:6001')
11
+ @device.configure!(port, driver)
12
+ end
13
+
14
+ def self.open(limit_min = nil, limit_max = nil)
15
+ @device.open_session(limit_min, limit_max)
16
+ end
17
+
18
+ def self.close
19
+ @device.close_session
20
+ end
21
+
22
+ def self.status
23
+ @device.status
24
+ end
25
+
26
+ def self.model
27
+ @device.model
28
+ end
29
+
30
+ def self.banknotes
31
+ @device.banknotes
32
+ end
33
+
34
+ def self.sum
35
+ @device.cashsum
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,50 @@
1
+ require 'drb'
2
+
3
+ module Smartware
4
+ module Client
5
+
6
+ module Modem
7
+
8
+ def self.configure(port, driver)
9
+ DRb.start_service
10
+ @device = DRbObject.new_with_uri('druby://localhost:6002')
11
+ @device.configure!(port, driver)
12
+ end
13
+
14
+ def self.error
15
+ @device.error
16
+ end
17
+
18
+ def self.model
19
+ @device.model
20
+ end
21
+
22
+ def self.balance
23
+ @device.balance
24
+ end
25
+
26
+ def self.signal_level
27
+ @device.signal_level
28
+ end
29
+
30
+ def self.d_model
31
+ @device.d_model
32
+ end
33
+
34
+ def self.d_signal
35
+ @device.d_signal
36
+ end
37
+
38
+ def self.d_balance
39
+ @device.d_balance
40
+ end
41
+
42
+ def self.d_stop
43
+ @device.d_stop
44
+ end
45
+
46
+
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,40 @@
1
+ require 'drb'
2
+
3
+ module Smartware
4
+ module Client
5
+
6
+ module Printer
7
+
8
+ def self.configure(port, driver)
9
+ DRb.start_service
10
+ @device = DRbObject.new_with_uri('druby://localhost:6005')
11
+ @device.configure!(port, driver)
12
+ end
13
+
14
+ def self.error
15
+ @device.error
16
+ rescue => e
17
+ 'No device'
18
+ end
19
+
20
+ def self.model
21
+ @device.model
22
+ rescue => e
23
+ 'No device'
24
+ end
25
+
26
+ def self.test
27
+ @device.test
28
+ rescue => e
29
+ 'No device'
30
+ end
31
+
32
+ def self.print(filepath)
33
+ @device.print filepath
34
+ rescue => e
35
+ 'No device'
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,199 @@
1
+ # coding: utf-8
2
+ require 'serialport'
3
+
4
+ module Smartware
5
+ module Driver
6
+ module CashAcceptor
7
+
8
+ class CCNET
9
+
10
+ RESET = 0x30
11
+ GET_STATUS = 0x31
12
+ SET_SECURITY = 0x32
13
+ POLL = 0x33
14
+ ENABLE_BILL_TYPES = 0x34
15
+ STACK = 0x35
16
+ RETURN = 0x36
17
+ IDENTIFICATION = 0x37
18
+ HOLD = 0x38
19
+ SET_BARCODE_PARAMETERS = 0x39
20
+ EXTRACT_BARCODE_DATA = 0x3A
21
+ GET_BILL_TABLE = 0x41
22
+ GET_CRC32_OF_THE_CODE = 0x51
23
+ DOWNLOAD = 0x50
24
+ REQUEST_STATISTICS = 0x60
25
+ ACK = 0x00
26
+ DISPENCE = 0x3C
27
+
28
+ STATUSES = {
29
+ "10" => "10", # 'Power up',
30
+ "11" => "11", # 'Power Up with Bill in Validator',
31
+ "12" => "12", # 'Power Up with Bill in Stacker',
32
+ "13" => "13", # 'Initialize',
33
+ "14" => "14", # 'Idling',
34
+ "15" => "15", # 'Accepting',
35
+ "17" => "16", # 'Stacking',
36
+ "18" => "17", # 'Returning',
37
+ "19" => "18", # 'Unit Disabled',
38
+ "1a" => "19", # 'Holding',
39
+ "1b" => "20", # 'Device Busy',
40
+ "1c" => "21", # 'Rejecting',
41
+ "82" => "22" # 'Bill returned'
42
+ }
43
+
44
+ ERRORS = {
45
+ "41" => "23", # 'Drop Cassette Full',
46
+ "42" => "24", # 'Drop Cassette out of position. The Bill Validator has detected the drop cassette to be open or removed.',
47
+ "43" => "25", # 'Validator Jammed. A bill(s) has jammed in the acceptance path.',
48
+ "44" => "26", # 'Drop Cassette Jammed. A bill has jammed in drop cassette.',
49
+ "45" => "27", # 'Cheated. Intentions of the user to deceive the Bill Validator are detected',
50
+ "46" => "28", # 'Pause. Bill Validator stops motion of the second bill until the second bill is removed.',
51
+ "47" => "29", # 'Bill validator failure',
52
+ "50" => "30", # 'Stack Motor Failure',
53
+ "51" => "31", # 'Transport Motor Speed Failure',
54
+ "52" => "32", # 'Transport Motor Failure',
55
+ "53" => "33", # 'Aligning Motor Failure',
56
+ "54" => "34", # 'Initial Cassette Status Failure',
57
+ "55" => "35", # 'Optic Canal Failure',
58
+ "56" => "36", # 'Magnetic Canal Failure',
59
+ "5f" => "37" # 'Capacitance Canal Failure'
60
+ }
61
+
62
+ NOMINALS = { "2" => 10, "3" => 50, "4" => 100, "5" => 500, "6" => 1000, "7" => 5000 }
63
+
64
+ def initialize(port)
65
+ @port = port
66
+ end
67
+
68
+ def cassette?
69
+ return false if error == "24"
70
+ true
71
+ end
72
+
73
+ def model
74
+ if answer = send([IDENTIFICATION], false)
75
+ answer = answer[2..answer.length]
76
+ return "#{answer[0..15]} #{answer[16..27]} #{answer[28..34].unpack("C*")}"
77
+ else
78
+ return "Unknown device answer"
79
+ end
80
+ rescue
81
+ -1
82
+ end
83
+
84
+ def error
85
+ res = poll
86
+ ack
87
+ return false if res != nil and CCNET::STATUSES.keys.include?(res[3])
88
+ return false if res == nil
89
+
90
+ result = check_error(res)
91
+ rescue
92
+ -1
93
+ end
94
+
95
+ def current_banknote
96
+ poll
97
+ ack
98
+ hold
99
+ res = poll
100
+ ack
101
+
102
+ result = check_error(res)
103
+ if res != nil and res[2] == "7" and res[3] == "80" and CCNET::NOMINALS.keys.include?(res[4]) # has money?
104
+ result = CCNET::NOMINALS[res[4]]
105
+ end
106
+ result
107
+ end
108
+
109
+ def get_status
110
+ send([GET_STATUS])
111
+ end
112
+
113
+ def reset
114
+ send([RESET])
115
+ end
116
+
117
+ def ack
118
+ send([ACK])
119
+ end
120
+
121
+ def stack
122
+ send([STACK])
123
+ end
124
+
125
+ def return
126
+ send([RETURN])
127
+ end
128
+
129
+ def hold
130
+ send([])
131
+ end
132
+
133
+ def poll
134
+ send([POLL])
135
+ end
136
+
137
+ def accept
138
+ send([ENABLE_BILL_TYPES,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF])
139
+ end
140
+
141
+ def cancel_accept
142
+ send([ENABLE_BILL_TYPES,0x00,0x00,0x00,0x00,0x00,0x00])
143
+ get_status
144
+ end
145
+
146
+ private
147
+ def check_error(res)
148
+ if CCNET::ERRORS.keys.include? res[3]
149
+ res[3] == "47" ? CCNET::ERRORS[res[4]] : CCNET::ERRORS[res[3]] # More details for 47 error
150
+ else
151
+ false
152
+ end
153
+ end
154
+
155
+ def crc_for(msg)
156
+ polynom = 0x8408.to_i
157
+ msg = msg.collect{|o| o.to_i}
158
+ res = 0
159
+ tmpcrc = 0
160
+ 0.upto(msg.length-1){|i|
161
+ tmpcrc = res ^ msg[i]
162
+ 0.upto(7){
163
+ if tmpcrc & 1 != 0
164
+ tmpcrc = tmpcrc >> 1
165
+ tmpcrc = tmpcrc ^ polynom
166
+ else
167
+ tmpcrc = tmpcrc >> 1
168
+ end
169
+ }
170
+ res = tmpcrc
171
+ }
172
+ crc = tmpcrc
173
+ crc = ("%02x" % crc).rjust(4,"0")
174
+ crc = [Integer("0x"+crc[2..3]), Integer("0x"+crc[0..1])]
175
+ end
176
+
177
+ def send(msg, parse_answer = true)
178
+ sp = SerialPort.new(@port, 9600, 8, 1, SerialPort::NONE)
179
+ sp.read_timeout = 100
180
+ message = [0x02, 0x03, 5 + msg.length]
181
+ message += msg
182
+ crc = crc_for(message)
183
+ message += crc
184
+ message = message.pack("C*")
185
+ sp.write message
186
+ ans = sp.gets
187
+ if ans
188
+ parse_answer ? res = ans.unpack("C*").map{|e| e.to_s(16) } : res = ans
189
+ else
190
+ res = nil
191
+ end
192
+ sp.close
193
+ res
194
+ end
195
+ end
196
+
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,52 @@
1
+ # coding: utf-8
2
+ module Smartware
3
+ module Driver
4
+ module CashAcceptor
5
+
6
+ class Dummy
7
+
8
+ def initialize(port)
9
+ @port = port
10
+ end
11
+
12
+ def model
13
+ "Dummy cash acceptor v1.0"
14
+ end
15
+
16
+ def cassette?
17
+ true
18
+ end
19
+
20
+ def error
21
+ false
22
+ end
23
+
24
+ def current_banknote
25
+ return false if ( rand(9) < 6 )
26
+ [20, 40, 60, 80].sample
27
+ end
28
+
29
+ def accept
30
+
31
+ end
32
+
33
+ def cancel_accept
34
+
35
+ end
36
+
37
+ def stack
38
+
39
+ end
40
+
41
+ def return
42
+
43
+ end
44
+
45
+ def reset
46
+
47
+ end
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ module Smartware
3
+ module Driver
4
+ module Modem
5
+
6
+ class Dummy
7
+
8
+ def initialize(port)
9
+ @port = port
10
+ end
11
+
12
+ def model
13
+ 'Dummy modem v1.0'
14
+ end
15
+
16
+ def error
17
+ false
18
+ end
19
+
20
+ def signal_level
21
+ "-#{rand(90)*10+rand(9)} dbm"
22
+ end
23
+
24
+ #
25
+ # Method send ussd to operator and return only valid answer body
26
+ # Don`t call with method synchronously from app, method waits USSD answer 3 sec,
27
+ # Use some scheduler and buffer for balance value
28
+ #
29
+ def ussd(code="*100#")
30
+ sleep 3
31
+ "#{rand(90)*100+rand(9)} " + %w(руб. dollars тэньге).sample
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,74 @@
1
+ # coding: utf-8
2
+ require 'serialport'
3
+
4
+ module Smartware
5
+ module Driver
6
+ module Modem
7
+
8
+ class Standart
9
+
10
+ def initialize(port)
11
+ # @port Class variable needs for ussd request
12
+ @port = port
13
+ @sp = SerialPort.new(@port, 115200, 8, 1, SerialPort::NONE)
14
+ end
15
+
16
+ def error
17
+ false
18
+ end
19
+
20
+ def model
21
+ res = send 'ATI' rescue -1
22
+ return -1 unless res.last == "OK"
23
+
24
+ res.shift
25
+ res.pop
26
+ res.join(' ')
27
+ rescue
28
+ -1
29
+ end
30
+
31
+ #
32
+ # Returns signal level in dbm
33
+ #
34
+ def signal_level
35
+ res = send 'AT+CSQ'
36
+ return -1 if res.last != "OK"
37
+ value = res[1].gsub("+CSQ: ",'').split(',')[0].to_i
38
+ "#{(-113 + value * 2)} dbm"
39
+ rescue
40
+ -1
41
+ end
42
+
43
+ #
44
+ # Method send ussd to operator and return only valid answer body
45
+ # Don`t call with method synchronously from app, method waits USSD answer 3 sec,
46
+ # Use some scheduler and buffer for balance value
47
+ #
48
+ def ussd(code="*100#")
49
+ port = SerialPort.new(@port, 115200, 8, 1, SerialPort::NONE)
50
+ port.read_timeout = 3000
51
+ port.write "AT+CUSD=1,\"#{code}\",15\r\n"
52
+ ussd_body = port.read.split(/[\r\n]+/).last.split(",")[1].gsub('"','') # Get only USSD message body
53
+ port.close
54
+ ussd_body.scan(/\w{4}/).map{|i| [i.hex].pack("U") }.join # Encode USSD message from broken ucs2 to utf-8
55
+ rescue
56
+ -1
57
+ end
58
+
59
+ private
60
+ def send cmd
61
+ @sp.write "#{ cmd }\r\n"
62
+ answer = ''
63
+ while IO.select [@sp], [], [], 0.25
64
+ chr = @sp.getc.chr
65
+ answer << chr
66
+ end
67
+ answer.split(/[\r\n]+/)
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ module Smartware
3
+ module Driver
4
+ module Printer
5
+
6
+ class Dummy
7
+
8
+ def initialize(port)
9
+
10
+ end
11
+
12
+ def error
13
+ false
14
+ end
15
+
16
+ def model
17
+ 'Dummy printer'
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+
@@ -0,0 +1,57 @@
1
+ # coding: utf-8
2
+ require 'serialport'
3
+
4
+ module Smartware
5
+ module Driver
6
+ module Printer
7
+
8
+ class TG24XX
9
+
10
+ def initialize(port)
11
+ @sp = SerialPort.new(port, 115200, 8, 1, SerialPort::NONE)
12
+ @sp.read_timeout = 100
13
+ end
14
+
15
+ def error
16
+ res = send "\x1Dr1"
17
+ return "1" unless res # No answer
18
+ return "" if res == "0" # No error
19
+ return "2" if res == "f" # Paper not present
20
+ return "3" if res == "3" # Paper near end
21
+ rescue
22
+ -1
23
+ end
24
+
25
+ def model
26
+ model_code = send "\x1DI1"
27
+ rom_version = send "\x1DI3", false
28
+ model_name = case model_code
29
+ when "a7" then 'Custom TG2460H'
30
+ when "a8" then 'Custom TG2480H'
31
+ when "ac" then 'Custom TL80'
32
+ when "ad" then 'Custom TL60'
33
+ else 'Unknown printer'
34
+ end
35
+ "#{model_name}, ROM v#{rom_version}"
36
+ rescue
37
+ -1
38
+ end
39
+
40
+ private
41
+ def send(message, parse_answer = true)
42
+ @sp.write message
43
+ ans = @sp.gets
44
+ if ans
45
+ parse_answer ? res = ans.unpack("C*").map{|e| e.to_s(16) } : res = ans
46
+ else
47
+ res = nil
48
+ end
49
+ res.is_a?(Array) ? res[0] : res
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+ end
56
+ end
57
+
@@ -0,0 +1,131 @@
1
+ require 'drb'
2
+ require 'smartware/drivers/cash_acceptor/ccnet'
3
+ require 'smartware/drivers/cash_acceptor/dummy'
4
+
5
+ module Smartware
6
+ module Interface
7
+
8
+ module CashAcceptor
9
+
10
+ #
11
+ # Init vars
12
+ #
13
+ @configured = false
14
+ @no_limit = true
15
+ @banknotes = {}
16
+ @commands = %w(monitor)
17
+ @status = { :cassette => true }
18
+
19
+ #
20
+ # (re)configure device
21
+ #
22
+ def self.configure!(port, driver)
23
+ @device = Smartware::Driver::CashAcceptor.const_get(driver).new(port)
24
+ @session.kill if @session and @session.alive?
25
+ @device.cancel_accept
26
+ @session = self.start_poll!
27
+ Smartware::Logging.logger.info "Cash acceptor monitor started"
28
+ @configured = true
29
+ rescue => e
30
+ @configured = false
31
+ Smartware::Logging.logger.error e.message
32
+ Smartware::Logging.logger.error e.backtrace.join("\n")
33
+ end
34
+
35
+ def self.open_session(limit_min, limit_max)
36
+ @commands << 'open'
37
+ @banknotes = {}
38
+ if !limit_min.nil? && !limit_max.nil?
39
+ @no_limit = false
40
+ @limit_min = limit_min
41
+ @limit_max = limit_max
42
+ else
43
+ @no_limit = true
44
+ end
45
+ end
46
+
47
+ def self.close_session
48
+ @commands << 'close'
49
+ @limit_min = nil
50
+ @limit_max = nil
51
+ @no_limit = true
52
+ end
53
+
54
+ def self.configured?
55
+ @configured
56
+ end
57
+
58
+ def self.status
59
+ @status
60
+ end
61
+
62
+ def self.model
63
+ @status[:model]
64
+ end
65
+
66
+ def self.cashsum
67
+ @banknotes.inject(0){ |result, (key, value)| result + key.to_i*value.to_i }
68
+ end
69
+
70
+ def self.banknotes
71
+ @banknotes
72
+ end
73
+
74
+ #
75
+ # Session private method
76
+ #
77
+ private
78
+ def self.start_poll!
79
+ t = Thread.new do
80
+ loop do
81
+ case @commands[0]
82
+ when 'open'
83
+ @device.reset
84
+ @device.accept
85
+ Smartware::Logging.logger.info "Cash acceptor open: #{@commands}"
86
+
87
+ @commands.shift
88
+ @commands << 'get_money'
89
+ when 'get_money'
90
+ res = @device.current_banknote
91
+ if res.is_a? Integer # Have a banknote
92
+ if @no_limit or (@limit_min..@limit_max).include?(self.cashsum + res)
93
+ @device.stack
94
+ @banknotes[res] = (@banknotes[res]||0) + 1
95
+ Smartware::Logging.logger.info "Cash acceptor bill stacked, #{res}"
96
+ else
97
+ @device.return
98
+ Smartware::Logging.logger.info "Cash acceptor limit violation, return #{res}"
99
+ end
100
+ elsif res.is_a? String # Have a error, errors always as String
101
+ Smartware::Logging.logger.error "Cash acceptor error #{res}"
102
+ @status[:error] = res
103
+ end
104
+ @device.cancel_accept if !@no_limit && (@limit_max == self.cashsum and @limit_max > 0) # Close cash acceptor if current cashsum equal max-limit
105
+
106
+ @commands.shift
107
+ @commands << 'get_money' if @commands.empty?
108
+ when 'close'
109
+ @device.cancel_accept
110
+ Smartware::Logging.logger.info "Cash acceptor close: #{@commands}"
111
+
112
+ @commands.shift
113
+ when 'monitor'
114
+ @status[:error] = @device.error
115
+ @status[:model] = @device.model
116
+ @status[:cassette] = @device.cassette?
117
+ @commands.shift
118
+ else
119
+ @commands << 'monitor'
120
+ end
121
+ sleep 0.5
122
+ end
123
+ end
124
+ end
125
+
126
+ end
127
+ end
128
+ end
129
+
130
+ DRb.start_service('druby://localhost:6001', Smartware::Interface::CashAcceptor)
131
+ DRb.thread.join
@@ -0,0 +1,80 @@
1
+ require 'drb'
2
+ require 'smartware/drivers/modem/standart'
3
+ require 'smartware/drivers/modem/dummy'
4
+
5
+ module Smartware
6
+ module Interface
7
+
8
+ module Modem
9
+
10
+ @configured = false
11
+ @status = {}
12
+
13
+ def self.configure!(port, driver)
14
+ @device = Smartware::Driver::Modem.const_get(driver).new(port)
15
+ @session.kill if @session
16
+ @session = self.poll_status!
17
+ @configured = true
18
+ @status = {}
19
+ rescue => e
20
+ Smartware::Logging.logger.error e.message
21
+ Smartware::Logging.logger.error e.backtrace.join("\n")
22
+ @configured = false
23
+ end
24
+
25
+ def self.configured?
26
+ @configured
27
+ end
28
+
29
+ def self.error
30
+ @status[:error] || ''
31
+ end
32
+
33
+ def self.model
34
+ @status[:model]
35
+ end
36
+
37
+ def self.balance
38
+ @status[:balance]
39
+ end
40
+
41
+ def self.signal_level
42
+ @status[:signal_level]
43
+ end
44
+
45
+ def self.d_model
46
+ @device.model
47
+ end
48
+
49
+ def self.d_signal
50
+ @device.signal_level
51
+ end
52
+
53
+ def self.d_balance
54
+ @device.ussd
55
+ end
56
+
57
+ def self.d_stop
58
+ @session.kill
59
+ end
60
+
61
+ private
62
+ def self.poll_status!
63
+ t = Thread.new do
64
+ loop do
65
+ @status[:signal_level] = @device.signal_level
66
+ @status[:model] = @device.model
67
+ @status[:error] = @device.error
68
+ @status[:balance] = @device.ussd('*100#')
69
+ sleep 30
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ end
76
+ end
77
+
78
+ DRb.start_service('druby://localhost:6002', Smartware::Interface::Modem)
79
+ DRb.thread.join
80
+
@@ -0,0 +1,73 @@
1
+ require 'drb'
2
+ require 'smartware/drivers/printer/dummy'
3
+ require 'smartware/drivers/printer/tg24xx'
4
+
5
+ module Smartware
6
+ module Interface
7
+
8
+ module Printer
9
+
10
+ @configured = false
11
+ @status = {:error => ''}
12
+ @queue = []
13
+
14
+ def self.configure!(port, driver)
15
+ @device = Smartware::Driver::Printer.const_get(driver).new(port)
16
+ @session.kill if @session and @session.alive?
17
+ @session = self.start_monitor!
18
+ Smartware::Logging.logger.info 'Printer monitor started'
19
+ @configured = true
20
+ rescue => e
21
+ @configured = false
22
+ end
23
+
24
+ def self.configured?
25
+ @configured
26
+ end
27
+
28
+ def self.error
29
+ @status[:error]
30
+ end
31
+
32
+ def self.model
33
+ @status[:model]
34
+ end
35
+
36
+ def self.print(filepath)
37
+ @queue << filepath
38
+ end
39
+
40
+ def self.test
41
+ @queue << '/usr/share/cups/data/testprint'
42
+ end
43
+
44
+ private
45
+ def self.start_monitor!
46
+ t = Thread.new do
47
+ loop do
48
+ if @queue.empty?
49
+ @status[:error] = @device.error || ''
50
+ @status[:model] = @device.model
51
+ else
52
+ begin
53
+ `lpr #{@queue[0]} >> #{File.expand_path(Smartware::Logging.logfile)} 2>&1` # Turn lpr-log from STDOUT to smartware
54
+ Smartware::Logging.logger.info "Printed #{@queue[0]}"
55
+ @queue.shift
56
+ sleep 5
57
+ rescue => e
58
+ Smartware::Logging.logger.error e.message
59
+ Smartware::Logging.logger.error e.backtrace.join("\n")
60
+ end
61
+ end
62
+ sleep 0.2
63
+ end
64
+ end
65
+ end
66
+
67
+ end
68
+ end
69
+ end
70
+
71
+ DRb.start_service('druby://localhost:6005', Smartware::Interface::Printer)
72
+ DRb.thread.join
73
+
@@ -0,0 +1,31 @@
1
+ require 'logger'
2
+
3
+ module Smartware
4
+ module Logging
5
+
6
+ def self.logdir=(dir)
7
+ @logdir = dir
8
+ end
9
+
10
+ def self.logger
11
+ @logger ||= begin
12
+ log = Logger.new(STDOUT)
13
+ log.level = Logger::INFO
14
+ log
15
+ end
16
+ end
17
+
18
+ def self.logger=(val)
19
+ @logger = (val ? val : Logger.new('/dev/null'))
20
+ end
21
+
22
+ def self.logfile=(val)
23
+ @logfile = val
24
+ end
25
+
26
+ def self.logfile
27
+ @logfile
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,29 @@
1
+ require 'thread'
2
+ require 'smartware/logging'
3
+
4
+ module Smartware
5
+ module Service
6
+ def self.start
7
+ $stdout.sync = true
8
+
9
+ Smartware::Logging.logger = Logger.new($stdout)
10
+ Smartware::Logging.logger.info "Smartware started at #{Time.now}"
11
+
12
+ @threads = %w(smartware/interfaces/cash_acceptor
13
+ smartware/interfaces/printer
14
+ smartware/interfaces/modem).inject([]){|arr, iface| arr << Thread.new{ require iface } }
15
+
16
+ @threads.map(&:join)
17
+ rescue => e
18
+ Smartware::Logging.logger.fatal e.message
19
+ Smartware::Logging.logger.fatal e.backtrace.join("\n")
20
+ end
21
+
22
+ def self.stop
23
+ @threads.map(&:kill)
24
+ Smartware::Logging.logger.info "Smartware shutdown at #{Time.now}"
25
+ exit 0
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,3 @@
1
+ module Smartware
2
+ VERSION = 0.1
3
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'smartware/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "smartware"
8
+ gem.version = Smartware::VERSION
9
+ gem.authors = ["Evgeni Sudarchikov", "Boris Staal"]
10
+ gem.email = ["e.sudarchikov@roundlake.ru", "boris@roundlake.ru"]
11
+ gem.description = %q{Smartware is the Smartkiosk hardware control daemon}
12
+ gem.summary = gem.description
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'serialport'
21
+ gem.add_dependency 'dante'
22
+
23
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: smartware
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Evgeni Sudarchikov
9
+ - Boris Staal
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-12-17 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: serialport
17
+ requirement: &70261534685920 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *70261534685920
26
+ - !ruby/object:Gem::Dependency
27
+ name: dante
28
+ requirement: &70261534685280 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *70261534685280
37
+ description: Smartware is the Smartkiosk hardware control daemon
38
+ email:
39
+ - e.sudarchikov@roundlake.ru
40
+ - boris@roundlake.ru
41
+ executables:
42
+ - smartware
43
+ extensions: []
44
+ extra_rdoc_files: []
45
+ files:
46
+ - .gitignore
47
+ - Gemfile
48
+ - LICENSE.txt
49
+ - README.md
50
+ - Rakefile
51
+ - bin/smartware
52
+ - lib/smartware.rb
53
+ - lib/smartware/clients/cash_acceptor.rb
54
+ - lib/smartware/clients/modem.rb
55
+ - lib/smartware/clients/printer.rb
56
+ - lib/smartware/drivers/cash_acceptor/ccnet.rb
57
+ - lib/smartware/drivers/cash_acceptor/dummy.rb
58
+ - lib/smartware/drivers/modem/dummy.rb
59
+ - lib/smartware/drivers/modem/standart.rb
60
+ - lib/smartware/drivers/printer/dummy.rb
61
+ - lib/smartware/drivers/printer/tg24xx.rb
62
+ - lib/smartware/interfaces/cash_acceptor.rb
63
+ - lib/smartware/interfaces/modem.rb
64
+ - lib/smartware/interfaces/printer.rb
65
+ - lib/smartware/logging.rb
66
+ - lib/smartware/service.rb
67
+ - lib/smartware/version.rb
68
+ - smartware.gemspec
69
+ homepage: ''
70
+ licenses: []
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 1.8.15
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: Smartware is the Smartkiosk hardware control daemon
93
+ test_files: []