smartware 0.1.23 → 0.1.24
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/bin/smartware +7 -0
- data/bin/smartware-ppp-helper +39 -0
- data/lib/smartware/clients/cash_acceptor.rb +4 -0
- data/lib/smartware/clients/modem.rb +4 -0
- data/lib/smartware/clients/printer.rb +6 -0
- data/lib/smartware/drivers/cash_acceptor/ccnet.rb +5 -0
- data/lib/smartware/drivers/cash_acceptor/dummy.rb +5 -0
- data/lib/smartware/drivers/modem/dummy.rb +10 -3
- data/lib/smartware/drivers/modem/standard.rb +185 -57
- data/lib/smartware/interfaces/cash_acceptor.rb +5 -0
- data/lib/smartware/interfaces/modem.rb +22 -14
- data/lib/smartware/interfaces/printer.rb +5 -0
- data/lib/smartware/service.rb +54 -4
- data/lib/smartware/version.rb +1 -1
- data/smartware.gemspec +1 -1
- metadata +20 -3
data/bin/smartware
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
trap 'TTIN' do
|
4
4
|
Thread.list.each do |thread|
|
5
|
+
puts "Thread #{thread}:"
|
5
6
|
if thread.backtrace
|
6
7
|
puts thread.backtrace.join("\n")
|
7
8
|
else
|
@@ -10,6 +11,12 @@ trap 'TTIN' do
|
|
10
11
|
end
|
11
12
|
end
|
12
13
|
|
14
|
+
require "rubygems"
|
15
|
+
begin
|
16
|
+
require "bundler/setup"
|
17
|
+
rescue
|
18
|
+
end
|
19
|
+
|
13
20
|
require 'smartware/service'
|
14
21
|
require 'dante'
|
15
22
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "bundler/setup"
|
5
|
+
require "cmux"
|
6
|
+
|
7
|
+
device, apn = ARGV
|
8
|
+
|
9
|
+
io = CMUX::IO::open_tty device
|
10
|
+
begin
|
11
|
+
chatter = CMUX::ModemChatter.new io
|
12
|
+
|
13
|
+
complete = false
|
14
|
+
|
15
|
+
chatter.command("+CGDCONT=1,\"IP\",\"#{apn}\";D*99***1#", 30) do |resp|
|
16
|
+
if resp.failure?
|
17
|
+
warn "GPRS activation failed: #{resp.error}"
|
18
|
+
|
19
|
+
exit 1
|
20
|
+
else
|
21
|
+
complete = true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
until complete
|
26
|
+
CMUX::ModemChatter.poll [ chatter ]
|
27
|
+
end
|
28
|
+
ensure
|
29
|
+
io.close
|
30
|
+
end
|
31
|
+
|
32
|
+
Process.exec "/usr/sbin/pppd",
|
33
|
+
device, "115200",
|
34
|
+
"nodetach", "file", "/etc/ppp/options",
|
35
|
+
pgroup: true,
|
36
|
+
in: :close,
|
37
|
+
out: :close,
|
38
|
+
err: :close,
|
39
|
+
close_others: true
|
@@ -5,14 +5,18 @@ module Smartware
|
|
5
5
|
|
6
6
|
class Dummy
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
@port =
|
8
|
+
def initialize(config)
|
9
|
+
@port = config
|
10
10
|
end
|
11
11
|
|
12
12
|
def model
|
13
13
|
'Generic modem'
|
14
14
|
end
|
15
15
|
|
16
|
+
def version
|
17
|
+
'8 Ultimate'
|
18
|
+
end
|
19
|
+
|
16
20
|
def error
|
17
21
|
false
|
18
22
|
end
|
@@ -22,10 +26,13 @@ module Smartware
|
|
22
26
|
"#{res} dbm"
|
23
27
|
end
|
24
28
|
|
25
|
-
def
|
29
|
+
def balance
|
26
30
|
"Сервис временно недоступен"
|
27
31
|
end
|
28
32
|
|
33
|
+
def tick
|
34
|
+
sleep 10
|
35
|
+
end
|
29
36
|
end
|
30
37
|
|
31
38
|
end
|
@@ -1,85 +1,213 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
require '
|
2
|
+
require 'cmux'
|
3
3
|
|
4
4
|
module Smartware
|
5
5
|
module Driver
|
6
6
|
module Modem
|
7
7
|
|
8
8
|
class Standard
|
9
|
+
attr_reader :error, :model, :balance, :version
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
@
|
19
|
-
@
|
11
|
+
def initialize(config)
|
12
|
+
@config = config
|
13
|
+
@state = :closed
|
14
|
+
@error = "not initialized yet"
|
15
|
+
@mux = nil
|
16
|
+
@status_channel = nil
|
17
|
+
@info_requested = false
|
18
|
+
@model = "GSM modem"
|
19
|
+
@version = ""
|
20
|
+
@signal = "+CSQ: 99,99"
|
21
|
+
@ussd_interval = 0
|
22
|
+
@balance = nil
|
23
|
+
@ppp_state = :stopped
|
24
|
+
@ppp_pid = nil
|
20
25
|
end
|
21
26
|
|
22
|
-
def
|
23
|
-
@
|
27
|
+
def signal_level
|
28
|
+
value = @signal.gsub("+CSQ: ",'').split(',')[0].to_i
|
29
|
+
"#{(-113 + value * 2)} dbm"
|
24
30
|
end
|
25
31
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
32
|
+
def tick
|
33
|
+
begin
|
34
|
+
modem_tick
|
35
|
+
ppp_tick
|
36
|
+
|
37
|
+
wait_for_event
|
38
|
+
rescue => e
|
39
|
+
Smartware::Logging.logger.error "uncatched exception in modem monitor: #{e}"
|
40
|
+
end
|
33
41
|
end
|
34
42
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
value = res[1].gsub("+CSQ: ",'').split(',')[0].to_i
|
41
|
-
"#{(-113 + value * 2)} dbm"
|
42
|
-
rescue
|
43
|
-
@error = ERRORS["21"]
|
44
|
-
return false
|
43
|
+
def unsolicited(type, fields)
|
44
|
+
case type
|
45
|
+
when "CUSD"
|
46
|
+
ussd *fields
|
47
|
+
end
|
45
48
|
end
|
46
49
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
if res
|
58
|
-
ussd_body = res.split(",")[1].gsub('"','') # Parse USSD message body
|
59
|
-
ussd_body.scan(/\w{4}/).map{|i| [i.hex].pack("U") }.join.strip # Encode USSD message from broken ucs2 to utf-8
|
50
|
+
def ussd(mode, string = nil, dcs = nil)
|
51
|
+
if mode != "0"
|
52
|
+
Smartware::Logging.logger.warn "USSD completed with mode #{mode}, expected 0"
|
53
|
+
end
|
54
|
+
|
55
|
+
if string
|
56
|
+
@balance = string.scan(/\w{4}/)
|
57
|
+
.map! { |i| [ i.hex ].pack("U") }
|
58
|
+
.join
|
59
|
+
.strip
|
60
60
|
else
|
61
|
-
@
|
62
|
-
|
61
|
+
@balance = nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def modem_tick
|
68
|
+
case @state
|
69
|
+
when :closed
|
70
|
+
Smartware::Logging.logger.info "trying to open modem"
|
71
|
+
|
72
|
+
begin
|
73
|
+
@mux = CMUX::MUX.new @config["device"]
|
74
|
+
@state = :open
|
75
|
+
@status_channel = @mux.allocate(@config["status_channel"]).open
|
76
|
+
@chatter = CMUX::ModemChatter.new @status_channel
|
77
|
+
@chatter.subscribe "CUSD", self
|
78
|
+
@error = false
|
79
|
+
@ussd_interval = 0
|
80
|
+
Smartware::Logging.logger.info "modem ready"
|
81
|
+
rescue => e
|
82
|
+
close_modem "unable to open modem: #{e}"
|
83
|
+
end
|
84
|
+
|
85
|
+
when :open
|
86
|
+
modem_works = nil
|
87
|
+
|
88
|
+
begin
|
89
|
+
@chatter.command("+CGMM;+CGMR;+CSQ", 3) do |resp|
|
90
|
+
modem_works = resp.success?
|
91
|
+
|
92
|
+
if modem_works
|
93
|
+
resp.response.reject! &:empty?
|
94
|
+
@model, @version, @signal = resp.response
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
while modem_works.nil?
|
99
|
+
CMUX::ModemChatter.poll [ @chatter ]
|
100
|
+
end
|
101
|
+
rescue => e
|
102
|
+
modem_works = false
|
103
|
+
end
|
104
|
+
|
105
|
+
if modem_works
|
106
|
+
if @config.include?("balance_ussd") && @ussd_interval == 0
|
107
|
+
@ussd_interval = @config["balance_interval"]
|
108
|
+
begin
|
109
|
+
@chatter.command("+CUSD=1,\"#{@config["balance_ussd"]}\",15", 1)
|
110
|
+
rescue => e
|
111
|
+
close_modem "USSD request failed: #{e}"
|
112
|
+
end
|
113
|
+
else
|
114
|
+
@ussd_interval -= 1
|
115
|
+
end
|
116
|
+
else
|
117
|
+
close_modem "modem is not responding"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def ppp_tick
|
123
|
+
case @ppp_state
|
124
|
+
when :stopped
|
125
|
+
if @state == :open
|
126
|
+
Smartware::Logging.logger.info "trying to start pppd"
|
127
|
+
begin
|
128
|
+
@ppp_channel = @mux.allocate @config["ppp_channel"]
|
129
|
+
|
130
|
+
@ppp_pid = Process.spawn "smartware-ppp-helper",
|
131
|
+
@ppp_channel.device,
|
132
|
+
@config["apn"]
|
133
|
+
@ppp_state = :running
|
134
|
+
|
135
|
+
Smartware::Logging.logger.info "started pppd, PID #{@ppp_pid}"
|
136
|
+
Smartware::ProcessManager.track @ppp_pid, method(:ppp_died)
|
137
|
+
|
138
|
+
rescue => e
|
139
|
+
begin
|
140
|
+
@ppp_channel.close
|
141
|
+
rescue
|
142
|
+
end
|
143
|
+
|
144
|
+
@ppp_channel = nil
|
145
|
+
@ppp_pid = nil
|
146
|
+
@ppp_state = :stopped
|
147
|
+
|
148
|
+
Smartware::Logging.logger.warn "cannot start pppd: #{e.to_s}"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
when :running
|
153
|
+
if @ppp_pid.nil?
|
154
|
+
Smartware::Logging.logger.warn "pppd died"
|
155
|
+
begin
|
156
|
+
@ppp_channel.close
|
157
|
+
rescue
|
158
|
+
end
|
159
|
+
|
160
|
+
@ppp_channel = nil
|
161
|
+
@ppp_state = :stopped
|
162
|
+
end
|
63
163
|
end
|
64
164
|
end
|
65
165
|
|
66
|
-
def
|
67
|
-
|
68
|
-
@
|
69
|
-
read_port(@sp)
|
166
|
+
def ppp_died(pid)
|
167
|
+
# will be handled by the event loop a moment later
|
168
|
+
@ppp_pid = nil
|
70
169
|
end
|
71
170
|
|
72
|
-
def
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
171
|
+
def wait_for_event
|
172
|
+
if @state == :open
|
173
|
+
begin
|
174
|
+
CMUX::ModemChatter.poll [ @chatter ], @config["poll_interval"]
|
175
|
+
rescue => e
|
176
|
+
close_modem "modem poll failed: #{e}"
|
177
|
+
end
|
178
|
+
else
|
179
|
+
sleep @config["poll_interval"]
|
78
180
|
end
|
79
|
-
|
181
|
+
|
80
182
|
end
|
81
|
-
end
|
82
183
|
|
184
|
+
def close_modem(reason)
|
185
|
+
Smartware::Logging.logger.warn "#{reason}"
|
186
|
+
|
187
|
+
@error = reason
|
188
|
+
|
189
|
+
begin
|
190
|
+
@mux.close
|
191
|
+
rescue
|
192
|
+
end
|
193
|
+
|
194
|
+
begin
|
195
|
+
@chatter.unsubscribe "CUSD", self
|
196
|
+
rescue
|
197
|
+
end
|
198
|
+
|
199
|
+
@info_requested = false
|
200
|
+
@mux = nil
|
201
|
+
@chatter = nil
|
202
|
+
@status_channel = nil
|
203
|
+
@ppp_state = :stopped
|
204
|
+
unless @ppp_pid.nil?
|
205
|
+
Smartware::ProcessManager.untrack @ppp_pid
|
206
|
+
@ppp_pid = nil # PPP will die by itself, because we closed the mux.
|
207
|
+
end
|
208
|
+
@state = :closed
|
209
|
+
end
|
210
|
+
end
|
83
211
|
end
|
84
212
|
end
|
85
213
|
end
|
@@ -70,6 +70,10 @@ module Smartware
|
|
70
70
|
@status[:model]
|
71
71
|
end
|
72
72
|
|
73
|
+
def self.version
|
74
|
+
@status[:version]
|
75
|
+
end
|
76
|
+
|
73
77
|
def self.cashsum
|
74
78
|
@banknotes.inject(0){ |result, (key, value)| result + key.to_i*value.to_i }
|
75
79
|
end
|
@@ -120,6 +124,7 @@ module Smartware
|
|
120
124
|
when 'monitor'
|
121
125
|
@status[:error] = @device.error || ''
|
122
126
|
@status[:model] = @device.model
|
127
|
+
@status[:version] = @device.version
|
123
128
|
@status[:cassette] = @device.cassette?
|
124
129
|
@commands.shift
|
125
130
|
else
|
@@ -1,19 +1,20 @@
|
|
1
1
|
require 'drb'
|
2
|
-
require 'smartware/drivers/modem/standard'
|
3
2
|
require 'smartware/drivers/modem/dummy'
|
3
|
+
require 'smartware/drivers/modem/standard'
|
4
4
|
|
5
5
|
module Smartware
|
6
6
|
module Interface
|
7
7
|
|
8
8
|
module Modem
|
9
|
-
|
10
9
|
@configured = false
|
11
10
|
@status = {}
|
12
11
|
|
13
|
-
def self.configure!
|
12
|
+
def self.configure!
|
13
|
+
Smartware::Logging.logger.debug "Creating modem"
|
14
14
|
@device = Smartware::Driver::Modem.const_get(
|
15
15
|
Smartware::Service.config['modem_driver']).new(
|
16
|
-
Smartware::Service.config['
|
16
|
+
Smartware::Service.config['modem_config'])
|
17
|
+
Smartware::Logging.logger.debug "Starting modem"
|
17
18
|
@session.kill if @session
|
18
19
|
@session = self.poll_status!
|
19
20
|
@configured = true
|
@@ -37,6 +38,10 @@ module Smartware
|
|
37
38
|
@status[:model]
|
38
39
|
end
|
39
40
|
|
41
|
+
def self.version
|
42
|
+
@status[:version]
|
43
|
+
end
|
44
|
+
|
40
45
|
def self.balance
|
41
46
|
@status[:balance]
|
42
47
|
end
|
@@ -48,16 +53,19 @@ module Smartware
|
|
48
53
|
private
|
49
54
|
def self.poll_status!
|
50
55
|
t = Thread.new do
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
56
|
+
begin
|
57
|
+
loop do
|
58
|
+
@device.tick
|
59
|
+
|
60
|
+
@status[:signal_level] = @device.signal_level
|
61
|
+
@status[:model] = @device.model
|
62
|
+
@status[:version] = @device.version
|
63
|
+
@status[:error] = @device.error || ''
|
64
|
+
@status[:balance] = @device.balance
|
65
|
+
end
|
66
|
+
rescue => e
|
67
|
+
Smartware::Logging.logger.error e.message
|
68
|
+
Smartware::Logging.logger.error e.backtrace.join("\n")
|
61
69
|
end
|
62
70
|
end
|
63
71
|
end
|
@@ -35,6 +35,10 @@ module Smartware
|
|
35
35
|
@status[:model]
|
36
36
|
end
|
37
37
|
|
38
|
+
def self.version
|
39
|
+
@status[:version]
|
40
|
+
end
|
41
|
+
|
38
42
|
def self.print(filepath)
|
39
43
|
@queue << filepath unless filepath.nil?
|
40
44
|
end
|
@@ -50,6 +54,7 @@ module Smartware
|
|
50
54
|
if @queue.empty?
|
51
55
|
@status[:error] = @device.error || ''
|
52
56
|
@status[:model] = @device.model
|
57
|
+
@status[:version] = @device.version
|
53
58
|
else
|
54
59
|
begin
|
55
60
|
`lpr #{@queue[0]}`
|
data/lib/smartware/service.rb
CHANGED
@@ -11,15 +11,27 @@ module Smartware
|
|
11
11
|
|
12
12
|
def self.start(config_file)
|
13
13
|
$stdout.sync = true
|
14
|
-
|
14
|
+
|
15
15
|
@config = YAML.load File.read(File.expand_path(config_file))
|
16
16
|
|
17
17
|
Smartware::Logging.logger = Logger.new($stdout)
|
18
18
|
Smartware::Logging.logger.info "Smartware started at #{Time.now}"
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
|
21
|
+
@threads = %w(smartware/interfaces/modem
|
22
|
+
smartware/interfaces/cash_acceptor
|
23
|
+
smartware/interfaces/printer
|
24
|
+
).inject([]) do |arr, iface|
|
25
|
+
arr << Thread.new do
|
26
|
+
begin
|
27
|
+
require iface
|
28
|
+
rescue => e
|
29
|
+
Smartware::Logging.logger.fatal "During startup of #{iface}:"
|
30
|
+
Smartware::Logging.logger.fatal e.message
|
31
|
+
Smartware::Logging.logger.fatal e.backtrace.join("\n")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
23
35
|
|
24
36
|
@threads.map(&:join)
|
25
37
|
rescue => e
|
@@ -34,5 +46,43 @@ module Smartware
|
|
34
46
|
end
|
35
47
|
|
36
48
|
end
|
49
|
+
|
50
|
+
module ProcessManager
|
51
|
+
def self.track(pid, method)
|
52
|
+
@tracked ||= {}
|
53
|
+
@tracked[pid] = method
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.untrack(pid)
|
57
|
+
@tracked ||= {}
|
58
|
+
@tracked.delete pid
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.handle_sigchld(signal)
|
62
|
+
pids = []
|
63
|
+
@tracked ||= {}
|
64
|
+
|
65
|
+
begin
|
66
|
+
loop do
|
67
|
+
pid = Process.wait(-1, Process::WNOHANG)
|
68
|
+
|
69
|
+
break if pid == 0
|
70
|
+
|
71
|
+
pids << pid
|
72
|
+
end
|
73
|
+
rescue
|
74
|
+
end
|
75
|
+
|
76
|
+
pids.each do |pid|
|
77
|
+
tracker = @tracked[pid]
|
78
|
+
unless tracker.nil?
|
79
|
+
@tracked.delete pid
|
80
|
+
tracker.call pid
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
trap 'CHLD', ProcessManager.method(:handle_sigchld)
|
37
87
|
end
|
38
88
|
|
data/lib/smartware/version.rb
CHANGED
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.1.
|
4
|
+
version: 0.1.24
|
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-13 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: serialport
|
@@ -44,12 +44,29 @@ dependencies:
|
|
44
44
|
- - ! '>='
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: cmux
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
47
63
|
description: Smartware is the Smartkiosk hardware control daemon
|
48
64
|
email:
|
49
65
|
- e.sudarchikov@roundlake.ru
|
50
66
|
- boris@roundlake.ru
|
51
67
|
executables:
|
52
68
|
- smartware
|
69
|
+
- smartware-ppp-helper
|
53
70
|
extensions: []
|
54
71
|
extra_rdoc_files: []
|
55
72
|
files:
|
@@ -59,6 +76,7 @@ files:
|
|
59
76
|
- README.md
|
60
77
|
- Rakefile
|
61
78
|
- bin/smartware
|
79
|
+
- bin/smartware-ppp-helper
|
62
80
|
- lib/smartware.rb
|
63
81
|
- lib/smartware/clients/cash_acceptor.rb
|
64
82
|
- lib/smartware/clients/modem.rb
|
@@ -101,4 +119,3 @@ signing_key:
|
|
101
119
|
specification_version: 3
|
102
120
|
summary: Smartware is the Smartkiosk hardware control daemon
|
103
121
|
test_files: []
|
104
|
-
has_rdoc:
|