smartware 0.4.5 → 0.4.6

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.
@@ -21,11 +21,11 @@ begin
21
21
 
22
22
  Smartware::Logging.logger.info "Initializing Smartware"
23
23
 
24
- service.start
25
-
26
- Smartware::Logging.logger.info "Smartware ready"
24
+ [ 'INT', 'TERM' ].each do |sig|
25
+ trap(sig) { service.stop }
26
+ end
27
27
 
28
- service.join
28
+ service.start
29
29
  rescue => e
30
30
  Smartware::Logging.logger.error e.message
31
31
  Smartware::Logging.logger.error e.backtrace.join("\n")
@@ -5,8 +5,16 @@ require 'bundler/setup'
5
5
  require 'smartware'
6
6
 
7
7
  EventMachine.run do
8
- Smartware.subscribe do |key, *value|
9
- puts "#{key}: #{value.map(&:inspect).join(", ")}"
8
+ Smartware.subscribe do |message|
9
+ print "#{message.key}"
10
+
11
+ if message.reliable?
12
+ print " (reliable, #{message.id})"
13
+ end
14
+
15
+ puts ": #{message.args.map(&:inspect).join(", ")}"
16
+
17
+ message.acknowlege if ARGV[0] == "ack" && message.reliable?
10
18
  end
11
19
 
12
20
  puts "Receiving Smartware events."
@@ -29,11 +29,16 @@ ensure
29
29
  io.close
30
30
  end
31
31
 
32
- Process.exec "/usr/sbin/pppd",
33
- device, "115200",
34
- "nodetach", "file", "/etc/ppp/options",
32
+ cmdline = [
33
+ "/usr/sbin/pppd", device, "115200",
34
+ "nodetach", "logfd", "2", "file", "/etc/ppp/options"
35
+ ]
36
+
37
+ cmdline.unshift "sudo" if Process.euid != 0
38
+
39
+ Process.exec *cmdline,
35
40
  pgroup: true,
36
41
  in: :close,
37
42
  out: :close,
38
- err: :close,
43
+ err: :err,
39
44
  close_others: true
@@ -8,6 +8,7 @@ require 'eventmachine'
8
8
  require 'serialport'
9
9
  require 'json'
10
10
  require 'set'
11
+ require 'redis'
11
12
 
12
13
  require 'smartkiosk/common'
13
14
 
@@ -6,7 +6,38 @@ module Smartware
6
6
  module Modem
7
7
 
8
8
  class Standard
9
+ class LogConnection < EventMachine::Connection
10
+ include EventMachine::Protocols::LineProtocol
11
+
12
+ def initialize(device)
13
+ @device = device
14
+ end
15
+
16
+ def receive_line(line)
17
+ line.rstrip!
18
+
19
+ Logging.logger.debug "pppd: #{line}"
20
+
21
+ if line =~ /^Sent ([0-9]+) bytes, received ([0-9]+) bytes.$/
22
+ @sent = $1.to_i
23
+ @received = $2.to_i
24
+ elsif line =~ /^Connect time ([0-9.]+) minutes.$/
25
+ @time = ($1.to_f * 60).round
26
+ elsif line == "Connection terminated."
27
+ begin
28
+ @device.account.call @sent, @received, @time
29
+ ensure
30
+ @sent = nil
31
+ @received = nil
32
+ @time = nil
33
+ end
34
+ end
35
+
36
+ end
37
+ end
38
+
9
39
  attr_reader :error, :model, :balance, :version
40
+ attr_accessor :account
10
41
 
11
42
  def initialize(config)
12
43
  @port = config["port"]
@@ -29,6 +60,25 @@ module Smartware
29
60
  @balance = nil
30
61
  @ppp_state = :stopped
31
62
  @ppp_pid = nil
63
+ @shutdown = false
64
+ @account = nil
65
+
66
+ logpipe_smartware, @logpipe_ppp = IO.pipe
67
+ EventMachine.attach logpipe_smartware, LogConnection, self
68
+ end
69
+
70
+ def shutdown(callback)
71
+ @shutdown_callback = callback
72
+
73
+ Logging.logger.info "stopping modem gracefully"
74
+ @shutdown = true
75
+ @shutdown_timer = EventMachine.add_periodic_timer(0.5) do
76
+ if @state == :closed && @ppp_state == :stopped
77
+ Logging.logger.info "modem stopped"
78
+ EventMachine.cancel_timer @shutdown_timer
79
+ @shutdown_callback.call
80
+ end
81
+ end
32
82
  end
33
83
 
34
84
  def signal_level
@@ -43,7 +93,7 @@ module Smartware
43
93
 
44
94
  wait_for_event
45
95
  rescue => e
46
- Smartware::Logging.logger.error "uncatched exception in modem monitor: #{e}"
96
+ Logging.logger.error "uncatched exception in modem monitor: #{e}"
47
97
  end
48
98
  end
49
99
 
@@ -56,7 +106,7 @@ module Smartware
56
106
 
57
107
  def ussd(mode, string = nil, dcs = nil)
58
108
  if mode != "0"
59
- Smartware::Logging.logger.warn "USSD completed with mode #{mode}, expected 0"
109
+ Logging.logger.warn "USSD completed with mode #{mode}, expected 0"
60
110
  end
61
111
 
62
112
  if string
@@ -74,19 +124,21 @@ module Smartware
74
124
  def modem_tick
75
125
  case @state
76
126
  when :closed
77
- Smartware::Logging.logger.info "trying to open modem"
127
+ if !@shutdown
128
+ Logging.logger.info "trying to open modem"
78
129
 
79
- begin
80
- @mux = CMUX::MUX.new @port
81
- @state = :open
82
- @status_channel = @mux.allocate(@status_channel_id).open
83
- @chatter = CMUX::ModemChatter.new @status_channel
84
- @chatter.subscribe "CUSD", self
85
- @error = nil
86
- @balance_timer = 0
87
- Smartware::Logging.logger.info "modem ready"
88
- rescue => e
89
- close_modem "unable to open modem: #{e}"
130
+ begin
131
+ @mux = CMUX::MUX.new @port
132
+ @state = :open
133
+ @status_channel = @mux.allocate(@status_channel_id).open
134
+ @chatter = CMUX::ModemChatter.new @status_channel
135
+ @chatter.subscribe "CUSD", self
136
+ @error = nil
137
+ @balance_timer = 0
138
+ Logging.logger.info "modem ready"
139
+ rescue => e
140
+ close_modem "unable to open modem: #{e}"
141
+ end
90
142
  end
91
143
 
92
144
  when :open
@@ -110,7 +162,9 @@ module Smartware
110
162
  end
111
163
 
112
164
  if modem_works
113
- if !@balance_ussd.nil? && @balance_timer == 0
165
+ if @shutdown && @ppp_state == :stopped
166
+ close_modem "service shutdown"
167
+ elsif !@balance_ussd.nil? && @balance_timer == 0
114
168
  @balance_timer = @balance_interval
115
169
  begin
116
170
  @chatter.command("+CUSD=1,\"#{@balance_ussd}\",15", 1)
@@ -129,43 +183,45 @@ module Smartware
129
183
  def ppp_tick
130
184
  case @ppp_state
131
185
  when :stopped
132
- if @state == :open
133
- Smartware::Logging.logger.info "trying to start pppd"
186
+ if @state == :open && !@shutdown
187
+ Logging.logger.info "trying to start pppd"
134
188
  begin
135
189
  @ppp_channel = @mux.allocate @ppp_channel_id
136
190
 
137
191
  @ppp_pid = Process.spawn "smartware-ppp-helper",
138
192
  @ppp_channel.device,
139
- @apn
193
+ @apn,
194
+ err: @logpipe_ppp
140
195
  @ppp_state = :running
141
196
 
142
- Smartware::Logging.logger.info "started pppd, PID #{@ppp_pid}"
143
- Smartware::ProcessManager.track @ppp_pid, method(:ppp_died)
197
+ Logging.logger.info "started pppd, PID #{@ppp_pid}"
198
+ ProcessManager.track @ppp_pid, method(:ppp_died)
144
199
 
145
200
  rescue => e
146
- begin
147
- @ppp_channel.close
148
- rescue
149
- end
201
+ @ppp_channel.close rescue nil
150
202
 
151
203
  @ppp_channel = nil
152
204
  @ppp_pid = nil
153
205
  @ppp_state = :stopped
154
206
 
155
- Smartware::Logging.logger.warn "cannot start pppd: #{e.to_s}"
207
+ Logging.logger.warn "cannot start pppd: #{e.to_s}"
156
208
  end
157
209
  end
158
210
 
159
211
  when :running
160
212
  if @ppp_pid.nil?
161
- Smartware::Logging.logger.warn "pppd died"
162
- begin
163
- @ppp_channel.close
164
- rescue
165
- end
213
+ Logging.logger.warn "pppd died"
214
+ @ppp_channel.close rescue nil
166
215
 
167
216
  @ppp_channel = nil
168
217
  @ppp_state = :stopped
218
+ elsif @shutdown
219
+ Logging.logger.debug "Terminating pppd #{@ppp_pid}"
220
+ if Process.euid == 0
221
+ Process.kill 'TERM', @ppp_pid
222
+ else
223
+ system "sudo", "kill", "-TERM", @ppp_pid.to_s
224
+ end
169
225
  end
170
226
  end
171
227
  end
@@ -189,19 +245,12 @@ module Smartware
189
245
  end
190
246
 
191
247
  def close_modem(reason)
192
- Smartware::Logging.logger.warn "#{reason}"
248
+ Logging.logger.warn "#{reason}"
193
249
 
194
250
  @error = Interface::Modem::MODEM_NOT_AVAILABLE
195
251
 
196
- begin
197
- @mux.close
198
- rescue
199
- end
200
-
201
- begin
202
- @chatter.unsubscribe "CUSD", self
203
- rescue
204
- end
252
+ @mux.close rescue nil
253
+ @chatter.unsubscribe "CUSD", self rescue nil
205
254
 
206
255
  @info_requested = false
207
256
  @mux = nil
@@ -209,7 +258,7 @@ module Smartware
209
258
  @status_channel = nil
210
259
  @ppp_state = :stopped
211
260
  unless @ppp_pid.nil?
212
- Smartware::ProcessManager.untrack @ppp_pid
261
+ ProcessManager.untrack @ppp_pid
213
262
  @ppp_pid = nil # PPP will die by itself, because we closed the mux.
214
263
  end
215
264
  @state = :closed
@@ -26,6 +26,16 @@ module Smartware
26
26
  .new(config)
27
27
  end
28
28
 
29
+ def shutdown(callback)
30
+ if @device.respond_to? :shutdown
31
+ @device.shutdown callback
32
+
33
+ true
34
+ else
35
+ false
36
+ end
37
+ end
38
+
29
39
  def repush_events(connection)
30
40
  @status_mutex.synchronize do
31
41
  @status.each do |key, value|
@@ -64,6 +74,10 @@ module Smartware
64
74
  def publish_event(key, *data)
65
75
  @service.publish_event "#{@iface_id}.#{key}", *data
66
76
  end
77
+
78
+ def publish_reliable_event(key, *data)
79
+ @service.publish_reliable_event "#{@iface_id}.#{key}", *data
80
+ end
67
81
  end
68
82
  end
69
83
  end
@@ -6,11 +6,12 @@ module Smartware
6
6
  def initialize(config, service)
7
7
  super
8
8
 
9
+ @device.account = method(:account)
10
+
9
11
  update_status :balance, ''
10
12
  update_status :signal_level, ''
11
13
 
12
14
  @session = Thread.new &method(:poll)
13
- Smartware::Logging.logger.info 'Modem monitor started'
14
15
  end
15
16
 
16
17
  def balance
@@ -39,6 +40,12 @@ module Smartware
39
40
  Smartware::Logging.logger.error e.backtrace.join("\n")
40
41
  end
41
42
  end
43
+
44
+ def account(sent, received, time)
45
+ Smartware::Logging.logger.debug "recording session: #{sent} upstream #{received} downstream, #{time} seconds"
46
+
47
+ publish_reliable_event 'accounting', sent, received, time
48
+ end
42
49
  end
43
50
  end
44
51
  end
@@ -2,6 +2,39 @@ require "em/protocols/line_protocol"
2
2
 
3
3
  module Smartware
4
4
  class PubSubClient
5
+ class Message
6
+ attr_reader :key, :args
7
+
8
+ def initialize(reliable_id, key, args, callback)
9
+ @reliable_id = reliable_id
10
+ @key = key
11
+ @args = args
12
+ @callback = callback
13
+ end
14
+
15
+ def reliable?
16
+ !@reliable_id.nil?
17
+ end
18
+
19
+ def id
20
+ @reliable_id
21
+ end
22
+
23
+ def acknowlege
24
+ raise "message is not reliable" if @reliable_id.nil?
25
+
26
+ @callback.call @reliable_id
27
+ end
28
+
29
+ def [](index)
30
+ @args[index]
31
+ end
32
+
33
+ def to_a
34
+ @args
35
+ end
36
+ end
37
+
5
38
  class PubSubHandler < EventMachine::Connection
6
39
  include EventMachine::Protocols::LineProtocol
7
40
 
@@ -16,7 +49,11 @@ module Smartware
16
49
  def receive_line(line)
17
50
  data = JSON.load line
18
51
 
19
- @client.deliver data["key"], *data["args"]
52
+ @client.receive data
53
+ end
54
+
55
+ def send_record(data)
56
+ send_data(JSON.dump(data) + "\r\n")
20
57
  end
21
58
  end
22
59
 
@@ -55,8 +92,16 @@ module Smartware
55
92
  @reconnect_timer = EventMachine.add_timer 1, &method(:attempt)
56
93
  end
57
94
 
58
- def deliver(key, *args)
59
- @receiver.call key, *args
95
+ def receive(data)
96
+ message = Message.new data["reliable_id"],
97
+ data["key"],
98
+ data["args"],
99
+ ->(id) do
100
+
101
+ @connection.send_record command: "acknowlege", id: id
102
+ end
103
+
104
+ @receiver.call message
60
105
  end
61
106
 
62
107
  private
@@ -1,6 +1,10 @@
1
+ require "em/protocols/line_protocol"
2
+
1
3
  module Smartware
2
4
  class PubSubServer
3
5
  class PubSubConnection < EventMachine::Connection
6
+ include EventMachine::Protocols::LineProtocol
7
+
4
8
  def initialize(server)
5
9
  @server = server
6
10
  end
@@ -13,13 +17,30 @@ module Smartware
13
17
  @server.remove_connection self
14
18
  end
15
19
 
16
- def receive_data(data)
20
+ def receive_line(line)
21
+ begin
22
+ data = JSON.load line
17
23
 
24
+ case data["command"]
25
+ when "acknowlege"
26
+ @server.acknowlege_reliable data["id"]
27
+
28
+ else
29
+ raise "unsupported command #{data["command"]}"
30
+ end
31
+ rescue => e
32
+ Logging.logger.warn e.to_s
33
+ e.backtrace.each { |line| Logging.logger.warn line }
34
+ end
18
35
  end
19
36
 
20
37
  def publish_event(key, *args)
21
38
  send_data(JSON.dump({ key: key, args: args }) + "\r\n")
22
39
  end
40
+
41
+ def publish_reliable_event(id, key, *args)
42
+ send_data(JSON.dump({ reliable_id: id, key: key, args: args }) + "\r\n")
43
+ end
23
44
  end
24
45
 
25
46
  attr_accessor :repush
@@ -27,6 +48,7 @@ module Smartware
27
48
  def initialize(host, port)
28
49
  @repush = nil
29
50
  @connections = Set.new
51
+ @redis = Redis.new
30
52
 
31
53
  EventMachine.start_server host, port, PubSubConnection, self
32
54
  end
@@ -34,6 +56,12 @@ module Smartware
34
56
  def add_connection(connection)
35
57
  @connections.add connection
36
58
  @repush.call connection
59
+
60
+ @redis.hgetall("smartware:reliable_events").each do |key, data|
61
+ data = JSON.load(data)
62
+
63
+ connection.publish_reliable_event key, data["key"], data["args"]
64
+ end
37
65
  end
38
66
 
39
67
  def remove_connection(connection)
@@ -45,5 +73,19 @@ module Smartware
45
73
  connection.publish_event key, *args
46
74
  end
47
75
  end
76
+
77
+ def publish_reliable_event(key, *args)
78
+ id = (Time.now.to_f * 1000000).round.to_s
79
+
80
+ @redis.hset "smartware:reliable_events", id, JSON.dump({ key: key, args: args })
81
+
82
+ @connections.each do |connection|
83
+ connection.publish_reliable_event id, key, args
84
+ end
85
+ end
86
+
87
+ def acknowlege_reliable(id)
88
+ @redis.hdel "smartware:reliable_events", id
89
+ end
48
90
  end
49
91
  end
@@ -6,6 +6,7 @@ module Smartware
6
6
  @config = YAML.load File.read(config_file)
7
7
  @interfaces = []
8
8
  @devices = []
9
+ @shutdown_holders = 0
9
10
  end
10
11
 
11
12
  def start
@@ -16,6 +17,11 @@ module Smartware
16
17
  @pubsub.publish_event key, *args
17
18
  end
18
19
 
20
+ @reliable_event_channel = EventMachine::Channel.new
21
+ @reliable_event_channel.subscribe do |(key, args)|
22
+ @pubsub.publish_reliable_event key, *args
23
+ end
24
+
19
25
  @pubsub = PubSubServer.new "localhost", (@config["pubsub_port"] || 6100)
20
26
  @pubsub.repush = ->(connection) do
21
27
  @devices.each do |device|
@@ -51,16 +57,30 @@ module Smartware
51
57
  @event_channel.push [key, args]
52
58
  end
53
59
 
60
+ def publish_reliable_event(key, *args)
61
+ @reliable_event_channel.push [key, args]
62
+ end
63
+
54
64
  def stop
55
65
  @interfaces.each &:stop_service
56
66
 
57
- EventMachine.stop
58
- end
67
+ callback = method :decr_shutdown_holders
59
68
 
60
- def join
61
- @interfaces.each do |server|
62
- server.thread.join
69
+ @devices.each do |interface|
70
+ if interface.shutdown callback
71
+ @shutdown_holders += 1
72
+ end
63
73
  end
74
+
75
+ EventMachine.stop if @shutdown_holders == 0
76
+ end
77
+
78
+ private
79
+
80
+ def decr_shutdown_holders
81
+ @shutdown_holders -= 1
82
+
83
+ EventMachine.stop if @shutdown_holders == 0
64
84
  end
65
85
  end
66
86
  end
@@ -1,3 +1,3 @@
1
1
  module Smartware
2
- VERSION = "0.4.5"
2
+ VERSION = "0.4.6"
3
3
  end
@@ -27,4 +27,5 @@ Gem::Specification.new do |gem|
27
27
  gem.add_dependency 'eventmachine'
28
28
  gem.add_dependency 'chunky_png'
29
29
  gem.add_dependency 'oily_png'
30
+ gem.add_dependency 'redis'
30
31
  end
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.4.5
4
+ version: 0.4.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2013-02-15 00:00:00.000000000 Z
14
+ date: 2013-02-16 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: smartkiosk-common
@@ -173,6 +173,22 @@ dependencies:
173
173
  - - ! '>='
174
174
  - !ruby/object:Gem::Version
175
175
  version: '0'
176
+ - !ruby/object:Gem::Dependency
177
+ name: redis
178
+ requirement: !ruby/object:Gem::Requirement
179
+ none: false
180
+ requirements:
181
+ - - ! '>='
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ type: :runtime
185
+ prerelease: false
186
+ version_requirements: !ruby/object:Gem::Requirement
187
+ none: false
188
+ requirements:
189
+ - - ! '>='
190
+ - !ruby/object:Gem::Version
191
+ version: '0'
176
192
  description: Smartware is the Smartkiosk hardware control daemon
177
193
  email:
178
194
  - e.sudarchikov@roundlake.ru