tamashii-agent 0.1.11 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0ce0f567235e489eae3966f715bcc87bba005ce1
4
- data.tar.gz: 4717c3ae733bd52914b4d1de710e5f231c3083ee
3
+ metadata.gz: bfc8a55dfec1ccbc0d4d7f6c26fb1331120b9924
4
+ data.tar.gz: 5ecc7b988ce09c76058a48c410d1b3d7d5bf1afa
5
5
  SHA512:
6
- metadata.gz: 408c8942b660d449076376cfd794e92417d3b364d81ee568bade4ef3287ae1db6debee661cb4eb7031ee244bec129590b8d6019b0d016f18b1f0b19ea3f312a4
7
- data.tar.gz: dccdd1ae3140365b90e695d293d954786916d82714ea570c6457668e4f08c813b3475df771bb3a52230e21c8bf086ea91d87e9a8d4f58d868392fe3c9bb089a3
6
+ metadata.gz: 8689f7b4ed689a28abe1137bab775e80e78a6ec01f8958c2bce1fe3e0f812a2d06b421deb32ac4337283d719d698a1df41ac72147e8d4ef822aef6f97b2723d3
7
+ data.tar.gz: 7ba6892db8cd54b5a8eb1333302ee470552a65c8b40f093625fa29b7b800a5a4eac3dc57d3f63d97bf6c1b0988e6245f0720445ab2eef12c76c5b6e5cb716a29
data/bin/console CHANGED
@@ -7,8 +7,8 @@ require "tamashii/agent"
7
7
  # with your gem easier. You can also use a different console, if you like.
8
8
 
9
9
  # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
10
+ require "pry"
11
+ Pry.start
12
12
 
13
- require "irb"
14
- IRB.start
13
+ #require "irb"
14
+ #IRB.start
@@ -0,0 +1,22 @@
1
+ require 'tamashii/agent/adapter/base'
2
+ require 'tamashii/agent/device/lcd'
3
+ require 'tamashii/agent/device/fake_lcd'
4
+
5
+ module Tamashii
6
+ module Agent
7
+ module Adapter
8
+ # :nodoc:
9
+ class LCD < Base
10
+ class << self
11
+ def real_class
12
+ Device::LCD
13
+ end
14
+
15
+ def fake_class
16
+ Device::FakeLCD
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,4 +1,5 @@
1
1
  require 'tamashii/agent/component'
2
+ require 'tamashii/agent/event'
2
3
  require 'tamashii/agent/adapter/buzzer'
3
4
 
4
5
  module Tamashii
@@ -10,11 +11,11 @@ module Tamashii
10
11
  logger.debug "Using buzzer instance: #{@buzzer.class}"
11
12
  end
12
13
 
13
- def process_event(ev_type, ev_body)
14
- case ev_type
15
- when EVENT_BEEP
16
- logger.debug "Beep: #{ev_body}"
17
- case ev_body
14
+ def process_event(event)
15
+ case event.type
16
+ when Event::BEEP
17
+ logger.debug "Beep: #{event.body}"
18
+ case event.body
18
19
  when "ok"
19
20
  @buzzer.play_ok
20
21
  when "no"
@@ -1,6 +1,7 @@
1
1
  require 'mfrc522'
2
2
 
3
3
  require 'tamashii/agent/component'
4
+ require 'tamashii/agent/event'
4
5
  require 'tamashii/agent/adapter/card_reader'
5
6
 
6
7
 
@@ -17,19 +18,23 @@ module Tamashii
17
18
  # override
18
19
  def worker_loop
19
20
  loop do
20
- handle_io
21
- handle_card
21
+ if !handle_new_event(true)
22
+ # no event available
23
+ sleep 0.1
24
+ end
25
+ if handle_card
26
+ # card is sent, sleep to prevent duplicate sent
27
+ sleep 1.0
28
+ else
29
+ # no card available
30
+ sleep 0.1
31
+ end
22
32
  end
23
33
  end
24
34
 
25
- def handle_io
26
- ready = @selector.select(0.1)
27
- ready.each { |m| m.value.call } if ready
28
- end
29
-
30
35
  def handle_card
31
36
  # read card
32
- return unless @reader.picc_request(MFRC522::PICC_REQA)
37
+ return false unless @reader.picc_request(MFRC522::PICC_REQA)
33
38
 
34
39
  begin
35
40
  uid, sak = @reader.picc_select
@@ -40,15 +45,16 @@ module Tamashii
40
45
  logger.error "GemError when selecting card: #{e.message}"
41
46
  end
42
47
  @reader.picc_halt
48
+ true
43
49
  end
44
50
 
45
51
  def process_uid(uid)
46
52
  logger.info "New card detected, UID: #{uid}"
47
- @master.send_event(EVENT_CARD_DATA, uid)
53
+ @master.send_event(Event.new(Event::CARD_DATA, uid))
48
54
  end
49
55
 
50
56
  # override
51
- def process_event(ev_type, ev_body)
57
+ def process_event(event)
52
58
  # silent is gold
53
59
  end
54
60
  end
@@ -4,10 +4,5 @@ module Tamashii
4
4
  module Agent
5
5
  module Common
6
6
  end
7
- EVENT_BEEP = 1
8
- EVENT_SYSTEM_COMMAND = 2
9
- EVENT_AUTH_RESULT = 3
10
- EVENT_CARD_DATA = 4
11
- EVENT_CONNECTION_NOT_READY = 255
12
7
  end
13
8
  end
@@ -1,5 +1,6 @@
1
- require 'nio'
2
1
  require 'tamashii/agent/common'
2
+ require 'tamashii/agent/event'
3
+
3
4
 
4
5
  module Tamashii
5
6
  module Agent
@@ -7,27 +8,33 @@ module Tamashii
7
8
  include Common::Loggable
8
9
 
9
10
  def initialize
10
- @pipe_r, @pipe_w = IO.pipe
11
+ @event_queue = Queue.new
11
12
  end
12
13
 
13
- def send_event(type, body)
14
- str = [type, body.bytesize].pack("Cn") + body
15
- @pipe_w.write(str)
14
+ def send_event(event)
15
+ @event_queue.push(event)
16
16
  end
17
17
 
18
- def receive_event
19
- ev_type, ev_size = @pipe_r.read(3).unpack("Cn")
20
- ev_body = @pipe_r.read(ev_size)
21
- process_event(ev_type, ev_body)
18
+ def check_new_event(non_block = false)
19
+ @event_queue.pop(non_block)
20
+ rescue ThreadError => e
21
+ nil
22
+ end
23
+
24
+ def handle_new_event(non_block = false)
25
+ if ev = check_new_event(non_block)
26
+ process_event(ev)
27
+ end
28
+ ev
22
29
  end
23
30
 
24
- def process_event(ev_type, ev_body)
25
- logger.debug "Got event: #{ev_type}, #{ev_body}"
31
+ def process_event(event)
32
+ logger.debug "Got event: #{event.type}, #{event.body}"
26
33
  end
27
34
 
28
35
  # worker
29
36
  def run
30
- @thr = Thread.start { run_worker_loop }
37
+ @worker_thr = Thread.start { run_worker_loop }
31
38
  end
32
39
 
33
40
  def run!
@@ -36,36 +43,31 @@ module Tamashii
36
43
 
37
44
  def stop
38
45
  logger.info "Stopping component"
39
- @thr.exit if @thr
40
- @thr = nil
46
+ stop_threads
41
47
  clean_up
42
48
  end
43
49
 
50
+ def stop_threads
51
+ @worker_thr.exit if @worker_thr
52
+ @worker_thr = nil
53
+ end
54
+
44
55
  def clean_up
45
56
  end
46
57
 
47
58
  def run_worker_loop
48
- create_selector
49
- register_event_io
50
59
  worker_loop
51
60
  end
52
61
 
53
62
  # a default implementation
54
63
  def worker_loop
55
64
  loop do
56
- ready = @selector.select
57
- ready.each { |m| m.value.call } if ready
65
+ if !handle_new_event
66
+ logger.error "Thread error. Worker loop terminated"
67
+ break
68
+ end
58
69
  end
59
70
  end
60
-
61
- def register_event_io
62
- _monitor = @selector.register(@pipe_r, :r)
63
- _monitor.value = method(:receive_event)
64
- end
65
-
66
- def create_selector
67
- @selector = NIO::Selector.new
68
- end
69
71
  end
70
72
  end
71
73
  end
@@ -10,6 +10,10 @@ module Tamashii
10
10
  register :entry_point, "/tamashii"
11
11
  register :manager_host, "localhost"
12
12
  register :manager_port, 3000
13
+ register :connection_timeout, 3
14
+
15
+ register :lcd_path, '/dev/i2c-1'
16
+ register :lcd_address, 0x27
13
17
 
14
18
  def auth_type(type = nil)
15
19
  return @auth_type ||= :none if type.nil?
@@ -2,18 +2,52 @@ require 'socket'
2
2
  require 'websocket/driver'
3
3
  require 'aasm'
4
4
  require 'openssl'
5
+ require 'json'
6
+ require 'concurrent'
7
+ require 'nio'
5
8
 
6
9
  require 'tamashii/common'
7
10
 
8
11
  require 'tamashii/agent/config'
12
+ require 'tamashii/agent/event'
9
13
  require 'tamashii/agent/component'
10
- require 'tamashii/agent/request_pool'
11
14
 
12
15
  require 'tamashii/agent/handler'
13
16
 
14
17
  module Tamashii
15
18
  module Agent
16
19
  class Connection < Component
20
+
21
+ class RequestTimeoutError < RuntimeError; end
22
+
23
+ class RequestObserver
24
+ include Common::Loggable
25
+ def initialize(connection, id, ev_type, ev_body, future)
26
+ @connection = connection
27
+ @id = id
28
+ @ev_type = ev_type
29
+ @ev_body = ev_body
30
+ @future = future
31
+ end
32
+
33
+ def update(time, ev_data, reason)
34
+ if @future.fulfilled?
35
+ res_ev_type = ev_data[:ev_type]
36
+ res_ev_body = ev_data[:ev_body]
37
+ case res_ev_type
38
+ when Type::RFID_RESPONSE_JSON
39
+ logger.debug "Handled: #{res_ev_type}: #{res_ev_body}"
40
+ @connection.handle_card_result(JSON.parse(res_ev_body))
41
+ else
42
+ logger.warn "Unhandled packet result: #{res_ev_type}: #{res_ev_body}"
43
+ end
44
+ else
45
+ logger.error "#{@id} Failed with #{reason}"
46
+ @connection.on_request_timeout(@ev_type, @ev_body)
47
+ end
48
+ end
49
+ end
50
+
17
51
  include AASM
18
52
 
19
53
  aasm do
@@ -41,7 +75,6 @@ module Tamashii
41
75
 
42
76
  attr_reader :url
43
77
  attr_reader :master
44
- attr_reader :request_pool
45
78
 
46
79
  def initialize(master, host, port)
47
80
  super()
@@ -53,57 +86,70 @@ module Tamashii
53
86
  @port = port
54
87
  @tag = 0
55
88
 
56
- @request_pool = RequestPool.new
57
- @request_pool.set_handler(:request_timedout, method(:handle_request_timedout))
58
- @request_pool.set_handler(:request_meet, method(:handle_request_meet))
59
- @request_pool.set_handler(:send_request, method(:handle_send_request))
89
+ @future_ivar_pool = Concurrent::Map.new
90
+ @driver_lock = Mutex.new
60
91
 
92
+ setup_resolver
93
+ end
94
+
95
+ def create_selector
96
+ @selector = NIO::Selector.new
97
+ end
98
+
99
+ def setup_resolver
61
100
  env_data = {connection: self}
62
101
  Resolver.config do
63
102
  [Type::REBOOT, Type::POWEROFF, Type::RESTART, Type::UPDATE].each do |type|
64
103
  handle type, Handler::System, env_data
65
104
  end
105
+ [Type::LCD_MESSAGE, Type::LCD_SET_IDLE_TEXT].each do |type|
106
+ handle type, Handler::LCD, env_data
107
+ end
66
108
  handle Type::BUZZER_SOUND, Handler::Buzzer, env_data
67
- handle Type::RFID_RESPONSE_JSON, Handler::RequestPoolResponse, env_data
109
+ handle Type::RFID_RESPONSE_JSON, Handler::RemoteResponse, env_data
68
110
  end
69
111
  end
70
112
 
71
- def handle_request_timedout(req)
72
- @master.send_event(EVENT_CONNECTION_NOT_READY, "Connection not ready for #{req.ev_type}:#{req.ev_body}")
73
- end
74
-
75
- def handle_request_meet(req, res)
76
- logger.debug "Got packet: #{res.ev_type}: #{res.ev_body}"
77
- case res.ev_type
78
- when Type::RFID_RESPONSE_JSON
79
- json = JSON.parse(res.ev_body)
80
- handle_card_result(json)
81
- else
82
- logger.warn "Unhandled packet result: #{res.ev_type}: #{res.ev_body}"
83
- end
113
+ def on_request_timeout(ev_type, ev_body)
114
+ @master.send_event(Event.new(Event::CONNECTION_NOT_READY, "Connection not ready for #{ev_type}:#{ev_body}"))
84
115
  end
85
116
 
86
117
  def handle_card_result(result)
87
118
  if result["auth"]
88
- @master.send_event(EVENT_BEEP, "ok")
119
+ @master.send_event(Event.new(Event::BEEP, "ok"))
89
120
  else
90
- @master.send_event(EVENT_BEEP, "no")
121
+ @master.send_event(Event.new(Event::BEEP, "no"))
122
+ end
123
+ if result["message"]
124
+ @master.send_event(Event.new(Event::LCD_MESSAGE, result["message"]))
91
125
  end
92
126
  end
93
127
 
94
- def handle_send_request(req)
128
+ def try_send_request(ev_type, ev_body)
95
129
  if self.ready?
96
- @driver.binary(Packet.new(req.ev_type, @tag, req.wrap_body).dump)
130
+ @driver_lock.synchronize do
131
+ @driver.binary(Packet.new(ev_type, @tag, ev_body).dump)
132
+ end
97
133
  true
98
134
  else
99
135
  false
100
136
  end
101
137
  end
102
138
 
103
- # override
104
- def worker_loop
139
+ def stop_threads
140
+ super
141
+ @websocket_thr.exit if @websocket_thr
142
+ @websocket_thr = nil
143
+ end
144
+
145
+ def run
146
+ super
147
+ @websocket_thr = Thread.start { run_websocket_loop }
148
+ end
149
+
150
+ def run_websocket_loop
151
+ create_selector
105
152
  loop do
106
- @request_pool.update
107
153
  ready = @selector.select(1)
108
154
  ready.each { |m| m.value.call } if ready
109
155
  if @io.nil?
@@ -218,11 +264,75 @@ module Tamashii
218
264
  end
219
265
  end
220
266
 
221
- def process_event(ev_type, ev_body)
222
- case ev_type
223
- when EVENT_CARD_DATA
224
- req = RequestPool::Request.new(Type::RFID_NUMBER , ev_body, ev_body)
225
- @request_pool.add_request(req)
267
+ # override
268
+ def process_event(event)
269
+ case event.type
270
+ when Event::CARD_DATA
271
+ id = event.body
272
+ wrapped_body = {
273
+ id: id,
274
+ ev_body: event.body
275
+ }.to_json
276
+ new_remote_request(id, Type::RFID_NUMBER, wrapped_body)
277
+ end
278
+ end
279
+
280
+ def schedule_task_runner(id, ev_type, ev_body, start_time, times)
281
+ logger.debug "Schedule send attemp #{id} : #{times + 1} time(s)"
282
+ if try_send_request(ev_type, ev_body)
283
+ # Request sent, do nothing
284
+ logger.debug "Request sent for id = #{id}"
285
+ else
286
+ if Time.now - start_time < Config.connection_timeout
287
+ # Re-schedule self
288
+ logger.warn "Reschedule #{id} after 1 sec"
289
+ schedule_next_task(1, id, ev_type, ev_body, start_time, times + 1)
290
+ else
291
+ # This job is expired. Do nothing
292
+ logger.warn "Abort scheduling #{id}"
293
+ end
294
+ end
295
+ end
296
+
297
+ def schedule_next_task(interval, id, ev_type, ev_body, start_time, times)
298
+ Concurrent::ScheduledTask.execute(interval, args: [id, ev_type, ev_body, start_time, times], &method(:schedule_task_runner))
299
+ end
300
+
301
+ def create_request_scheduler_task(id, ev_type, ev_body)
302
+ schedule_next_task(0, id, ev_type, ev_body, Time.now, 0)
303
+ end
304
+
305
+ def create_request_async(id, ev_type, ev_body)
306
+ req = Concurrent::Future.new do
307
+ # Create IVar for store result
308
+ ivar = Concurrent::IVar.new
309
+ @future_ivar_pool[id] = ivar
310
+ # Schedule to get the result
311
+ create_request_scheduler_task(id, ev_type, ev_body)
312
+ # Wait for result
313
+ if result = ivar.value(Config.connection_timeout)
314
+ # IVar is already removed from pool
315
+ result
316
+ else
317
+ # Manually remove IVar
318
+ # Any fulfill at this point is useless
319
+ logger.error "Timeout when getting IVar for #{id}"
320
+ @future_ivar_pool.delete(id)
321
+ raise RequestTimeoutError, "Request Timeout"
322
+ end
323
+ end
324
+ req.add_observer(RequestObserver.new(self, id, ev_type, ev_body, req))
325
+ req.execute
326
+ req
327
+ end
328
+
329
+ def new_remote_request(id, ev_type, ev_body)
330
+ # enqueue if not exists
331
+ if !@future_ivar_pool[id]
332
+ create_request_async(id, ev_type, ev_body)
333
+ logger.debug "Request created: #{id}"
334
+ else
335
+ logger.warn "Duplicated id: #{id}, ignored"
226
336
  end
227
337
  end
228
338
 
@@ -235,6 +345,20 @@ module Tamashii
235
345
  rescue => e
236
346
  logger.warn "Error occured when clean up: #{e.to_s}"
237
347
  end
348
+
349
+ # When data is back
350
+ def handle_remote_response(ev_type, wrapped_ev_body)
351
+ logger.debug "Remote packet back: #{ev_type} #{wrapped_ev_body}"
352
+ result = JSON.parse(wrapped_ev_body)
353
+ id = result["id"]
354
+ ev_body = result["ev_body"]
355
+ # fetch ivar and delete it
356
+ if ivar = @future_ivar_pool.delete(id)
357
+ ivar.set(ev_type: ev_type, ev_body: ev_body)
358
+ else
359
+ logger.warn "IVar #{id} not in pool"
360
+ end
361
+ end
238
362
  end
239
363
  end
240
364
  end
@@ -0,0 +1,30 @@
1
+ module Tamashii
2
+ module Agent
3
+ module Device
4
+ # :nodoc:
5
+ class FakeLCD
6
+ WIDTH = 16
7
+
8
+ attr_accessor :backlight
9
+
10
+ def initialize
11
+ @backlight = true
12
+ end
13
+
14
+ def print_message(message)
15
+ lines = message.lines.map{|l| l.delete("\n")}
16
+ puts "LCD Display(BACKLIGHT: #{@backlight}):"
17
+ puts lines.take(2).map { |line| print_line(line) }.join("\n")
18
+ end
19
+
20
+ private
21
+
22
+ def print_line(message)
23
+ message = '' unless message
24
+ message = message.ljust(WIDTH, ' ')
25
+ message.split('').take(WIDTH).join('')
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,86 @@
1
+ require 'i2c'
2
+
3
+ module Tamashii
4
+ module Agent
5
+ module Device
6
+ # :nodoc:
7
+ class LCD
8
+ WIDTH = 16
9
+
10
+ OP_CHR = 1
11
+ OP_CMD = 0
12
+
13
+ LINES = [
14
+ 0x80,
15
+ 0xC0
16
+ ].freeze
17
+
18
+ BACKLIGHT_ON = 0x08
19
+ BACKLIGHT_OFF = 0x00
20
+
21
+ ENABLE = 0b00000100
22
+
23
+ PULSE = 0.0005
24
+ DELAY = 0.0005
25
+
26
+ attr_accessor :backlight
27
+
28
+ def initialize
29
+ @lcd = I2C.create(Config.lcd_path)
30
+ @address = Config.lcd_address
31
+ @backlight = true
32
+
33
+ byte(0x33, OP_CMD)
34
+ byte(0x32, OP_CMD)
35
+ byte(0x06, OP_CMD)
36
+ byte(0x0C, OP_CMD)
37
+ byte(0x28, OP_CMD)
38
+ byte(0x01, OP_CMD)
39
+ sleep(DELAY)
40
+ end
41
+
42
+ def print_message(message)
43
+ lines = message.lines.map{|l| l.delete("\n")}
44
+ 2.times.each { |line| print_line(lines[line], LINES[line]) }
45
+ end
46
+
47
+ private
48
+
49
+ def backlight_mode
50
+ return BACKLIGHT_ON if @backlight
51
+ BACKLIGHT_OFF
52
+ end
53
+
54
+ def print_line(message, line)
55
+ message = '' unless message
56
+ message = message.ljust(WIDTH, ' ')
57
+ byte(line, OP_CMD)
58
+ WIDTH.times.each { |pos| byte(message[pos].ord, OP_CHR) }
59
+ end
60
+
61
+ def write(bits)
62
+ @lcd.write(@address, bits)
63
+ end
64
+
65
+ def byte(bits, mode)
66
+ high = mode | (bits & 0xF0) | backlight_mode
67
+ low = mode | (bits << 4) & 0xF0 | backlight_mode
68
+
69
+ write(high)
70
+ toggle(high)
71
+
72
+ write(low)
73
+ toggle(low)
74
+ end
75
+
76
+ def toggle(bits)
77
+ sleep(DELAY)
78
+ write(bits | ENABLE)
79
+ sleep(PULSE)
80
+ write(bits & ~ENABLE)
81
+ sleep(DELAY)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,27 @@
1
+ module Tamashii
2
+ module Agent
3
+ class Event
4
+
5
+ BEEP = 1
6
+ SYSTEM_COMMAND = 2
7
+ AUTH_RESULT = 3
8
+ CARD_DATA = 4
9
+ LCD_MESSAGE = 5
10
+ LCD_SET_IDLE_TEXT = 6
11
+
12
+ CONNECTION_NOT_READY = 255
13
+
14
+ attr_reader :type, :body
15
+
16
+ def initialize(type, body)
17
+ @type = type
18
+ @body = body
19
+ self.freeze
20
+ end
21
+
22
+ def ==(other)
23
+ @type == other.type && @body == other.body
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,4 +1,4 @@
1
- require 'tamashii/agent/common'
1
+ require 'tamashii/agent/event'
2
2
  require 'tamashii/agent/handler/base'
3
3
 
4
4
  module Tamashii
@@ -6,7 +6,7 @@ module Tamashii
6
6
  module Handler
7
7
  class Buzzer < Base
8
8
  def resolve(data)
9
- @master.send_event(EVENT_BEEP, data)
9
+ @master.send_event(Event.new(Event::BEEP, data))
10
10
  end
11
11
  end
12
12
  end
@@ -0,0 +1,19 @@
1
+ require 'tamashii/agent/event'
2
+ require 'tamashii/agent/handler/base'
3
+
4
+ module Tamashii
5
+ module Agent
6
+ module Handler
7
+ class LCD < Base
8
+ def resolve(data)
9
+ case type
10
+ when Type::LCD_MESSAGE
11
+ @master.send_event(Event.new(Event::LCD_MESSAGE, data))
12
+ when Type::LCD_SET_IDLE_TEXT
13
+ @master.send_event(Event.new(Event::LCD_SET_IDLE_TEXT, data))
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ require 'tamashii/agent/handler/base'
2
+
3
+ module Tamashii
4
+ module Agent
5
+ module Handler
6
+ class RemoteResponse < Base
7
+ def resolve(data)
8
+ @connection.handle_remote_response(self.type, data)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,4 +1,4 @@
1
- require 'tamashii/agent/common'
1
+ require 'tamashii/agent/event'
2
2
  require 'tamashii/agent/handler/base'
3
3
 
4
4
  module Tamashii
@@ -6,7 +6,7 @@ module Tamashii
6
6
  module Handler
7
7
  class System < Base
8
8
  def resolve(data)
9
- @master.send_event(EVENT_SYSTEM_COMMAND, type.to_s)
9
+ @master.send_event(Event.new(Event::SYSTEM_COMMAND, type.to_s))
10
10
  end
11
11
  end
12
12
  end
@@ -1,6 +1,7 @@
1
- require 'tamashii/agent/handler/request_pool_response'
1
+ require 'tamashii/agent/handler/remote_response'
2
2
  require 'tamashii/agent/handler/system'
3
3
  require 'tamashii/agent/handler/buzzer'
4
+ require 'tamashii/agent/handler/lcd'
4
5
 
5
6
  module Tamashii
6
7
  module Agent
@@ -0,0 +1,72 @@
1
+ require 'concurrent'
2
+
3
+ require 'tamashii/agent/common'
4
+ require 'tamashii/agent/event'
5
+ require 'tamashii/agent/adapter/lcd'
6
+
7
+
8
+
9
+ module Tamashii
10
+ module Agent
11
+ class LCD < Component
12
+ def initialize
13
+ super
14
+ load_lcd_device
15
+ @device_lock = Mutex.new
16
+ @idle_message = "[Tamashii]\nIdle..."
17
+ logger.debug "Using LCD instance: #{@lcd.class}"
18
+ @lcd.print_message("Initializing\nPlease wait...")
19
+ schedule_to_print_idle
20
+ end
21
+
22
+ def load_lcd_device
23
+ @lcd = Adapter::LCD.object
24
+ rescue => e
25
+ logger.error "Unable to load LCD instance: #{Adapter::LCD.current_class}"
26
+ logger.error "Use #{Adapter::LCD.fake_class} instead"
27
+ @lcd = Adapter::LCD.fake_class.new
28
+ end
29
+
30
+ def schedule_to_print_idle(delay = 5)
31
+ @back_to_idle_task = Concurrent::ScheduledTask.execute(delay) do
32
+ @device_lock.synchronize do
33
+ @lcd.print_message(@idle_message)
34
+ end
35
+ end
36
+ end
37
+
38
+ def process_event(event)
39
+ case event.type
40
+ when Event::LCD_MESSAGE
41
+ logger.debug "Show message: #{event.body}"
42
+ @back_to_idle_task&.cancel
43
+ @device_lock.synchronize do
44
+ @lcd.print_message(event.body)
45
+ schedule_to_print_idle
46
+ end
47
+ when Event::LCD_SET_IDLE_TEXT
48
+ logger.debug "Idle text set to #{event.body}"
49
+ @idle_message = event.body
50
+ @device_lock.synchronize do
51
+ @lcd.print_message(event.body)
52
+ end
53
+ end
54
+ end
55
+
56
+ def clear_screen
57
+ @device_lock.synchronize do
58
+ @lcd.print_message("")
59
+ end
60
+ end
61
+
62
+ def clean_up
63
+ clear_screen
64
+ super
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+
71
+
72
+
@@ -1,6 +1,9 @@
1
+ require 'tamashii/agent/common'
1
2
  require 'tamashii/agent/connection'
3
+ require 'tamashii/agent/lcd'
2
4
  require 'tamashii/agent/buzzer'
3
5
  require 'tamashii/agent/card_reader'
6
+ require 'tamashii/agent/event'
4
7
 
5
8
  require 'thread'
6
9
 
@@ -41,6 +44,7 @@ module Tamashii
41
44
  @components = {}
42
45
  @components[:connection] = create_component(Connection, self, @host, @port)
43
46
  @components[:buzzer] = create_component(Buzzer)
47
+ @components[:lcd] = create_component(LCD)
44
48
  @components[:card_reader] = create_component(CardReader, self)
45
49
  end
46
50
 
@@ -53,12 +57,12 @@ module Tamashii
53
57
  end
54
58
 
55
59
  # override
56
- def process_event(ev_type, ev_body)
60
+ def process_event(event)
57
61
  super
58
- case ev_type
59
- when EVENT_SYSTEM_COMMAND
60
- logger.info "System command code: #{ev_body}"
61
- case ev_body.to_i
62
+ case event.type
63
+ when Event::SYSTEM_COMMAND
64
+ logger.info "System command code: #{event.body}"
65
+ case event.body.to_i
62
66
  when Tamashii::Type::REBOOT
63
67
  system_reboot
64
68
  when Tamashii::Type::POWEROFF
@@ -68,30 +72,35 @@ module Tamashii
68
72
  when Tamashii::Type::UPDATE
69
73
  system_update
70
74
  end
71
- when EVENT_CONNECTION_NOT_READY
72
- broadcast_event(EVENT_BEEP, "error")
75
+ when Event::CONNECTION_NOT_READY
76
+ broadcast_event(Event.new(Event::BEEP, "error"))
73
77
  else
74
- broadcast_event(ev_type, ev_body)
78
+ broadcast_event(event)
75
79
  end
76
80
  end
77
81
 
82
+ def show_message(message)
83
+ logger.info message
84
+ broadcast_event(Event.new(Event::LCD_MESSAGE, message))
85
+ end
86
+
78
87
  def system_reboot
79
- logger.info "Rebooting..."
88
+ show_message "Rebooting"
80
89
  system("reboot &")
81
90
  end
82
91
 
83
92
  def system_poweroff
84
- logger.info "Powering Off..."
93
+ show_message "Powering Off"
85
94
  system("poweroff &")
86
95
  end
87
96
 
88
97
  def system_restart
89
- logger.info "Restarting..."
98
+ show_message "Restarting"
90
99
  system("systemctl restart tamashii-agent.service &")
91
100
  end
92
101
 
93
102
  def system_update
94
- logger.info "Updating..."
103
+ show_message("Updating")
95
104
  system("gem update tamashii-agent")
96
105
  system_restart
97
106
  end
@@ -105,9 +114,9 @@ module Tamashii
105
114
  end
106
115
 
107
116
 
108
- def broadcast_event(ev_type, ev_body)
117
+ def broadcast_event(event)
109
118
  @components.each_value do |c|
110
- c.send_event(ev_type, ev_body)
119
+ c.send_event(event)
111
120
  end
112
121
  end
113
122
  end
@@ -1,5 +1,5 @@
1
1
  module Tamashii
2
2
  module Agent
3
- VERSION = "0.1.11"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -1,4 +1,3 @@
1
- require "tamashii/common"
2
1
  require "tamashii/agent/version"
3
2
  require "tamashii/agent/master"
4
3
  require "tamashii/agent/config"
@@ -35,12 +35,15 @@ Gem::Specification.new do |spec|
35
35
  spec.add_development_dependency "simplecov"
36
36
  spec.add_development_dependency "guard"
37
37
  spec.add_development_dependency "guard-rspec"
38
+ spec.add_development_dependency "pry"
38
39
 
39
40
 
40
- spec.add_runtime_dependency "tamashii-common"
41
+ spec.add_runtime_dependency "tamashii-common"
41
42
  spec.add_runtime_dependency "websocket-driver"
42
43
  spec.add_runtime_dependency "nio4r"
43
44
  spec.add_runtime_dependency "pi_piper"
44
45
  spec.add_runtime_dependency "mfrc522"
46
+ spec.add_runtime_dependency "i2c"
45
47
  spec.add_runtime_dependency "aasm"
48
+ spec.add_runtime_dependency "concurrent-ruby"
46
49
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tamashii-agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.11
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - 蒼時弦也
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2017-04-26 00:00:00.000000000 Z
13
+ date: 2017-07-27 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -96,6 +96,20 @@ dependencies:
96
96
  - - ">="
97
97
  - !ruby/object:Gem::Version
98
98
  version: '0'
99
+ - !ruby/object:Gem::Dependency
100
+ name: pry
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
99
113
  - !ruby/object:Gem::Dependency
100
114
  name: tamashii-common
101
115
  requirement: !ruby/object:Gem::Requirement
@@ -166,6 +180,20 @@ dependencies:
166
180
  - - ">="
167
181
  - !ruby/object:Gem::Version
168
182
  version: '0'
183
+ - !ruby/object:Gem::Dependency
184
+ name: i2c
185
+ requirement: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ type: :runtime
191
+ prerelease: false
192
+ version_requirements: !ruby/object:Gem::Requirement
193
+ requirements:
194
+ - - ">="
195
+ - !ruby/object:Gem::Version
196
+ version: '0'
169
197
  - !ruby/object:Gem::Dependency
170
198
  name: aasm
171
199
  requirement: !ruby/object:Gem::Requirement
@@ -180,6 +208,20 @@ dependencies:
180
208
  - - ">="
181
209
  - !ruby/object:Gem::Version
182
210
  version: '0'
211
+ - !ruby/object:Gem::Dependency
212
+ name: concurrent-ruby
213
+ requirement: !ruby/object:Gem::Requirement
214
+ requirements:
215
+ - - ">="
216
+ - !ruby/object:Gem::Version
217
+ version: '0'
218
+ type: :runtime
219
+ prerelease: false
220
+ version_requirements: !ruby/object:Gem::Requirement
221
+ requirements:
222
+ - - ">="
223
+ - !ruby/object:Gem::Version
224
+ version: '0'
183
225
  description: The agent module for RubyConfTW checkin system.
184
226
  email:
185
227
  - elct9620@frost.tw
@@ -205,6 +247,7 @@ files:
205
247
  - lib/tamashii/agent/adapter/base.rb
206
248
  - lib/tamashii/agent/adapter/buzzer.rb
207
249
  - lib/tamashii/agent/adapter/card_reader.rb
250
+ - lib/tamashii/agent/adapter/lcd.rb
208
251
  - lib/tamashii/agent/buzzer.rb
209
252
  - lib/tamashii/agent/card_reader.rb
210
253
  - lib/tamashii/agent/common.rb
@@ -214,16 +257,18 @@ files:
214
257
  - lib/tamashii/agent/connection.rb
215
258
  - lib/tamashii/agent/device/fake_buzzer.rb
216
259
  - lib/tamashii/agent/device/fake_card_reader.rb
260
+ - lib/tamashii/agent/device/fake_lcd.rb
261
+ - lib/tamashii/agent/device/lcd.rb
217
262
  - lib/tamashii/agent/device/pi_buzzer.rb
263
+ - lib/tamashii/agent/event.rb
218
264
  - lib/tamashii/agent/handler.rb
219
265
  - lib/tamashii/agent/handler/base.rb
220
266
  - lib/tamashii/agent/handler/buzzer.rb
221
- - lib/tamashii/agent/handler/request_pool_response.rb
267
+ - lib/tamashii/agent/handler/lcd.rb
268
+ - lib/tamashii/agent/handler/remote_response.rb
222
269
  - lib/tamashii/agent/handler/system.rb
270
+ - lib/tamashii/agent/lcd.rb
223
271
  - lib/tamashii/agent/master.rb
224
- - lib/tamashii/agent/request_pool.rb
225
- - lib/tamashii/agent/request_pool/request.rb
226
- - lib/tamashii/agent/request_pool/response.rb
227
272
  - lib/tamashii/agent/version.rb
228
273
  - tamashii-agent.gemspec
229
274
  homepage: https://github.com/5xruby/tamashii-agent
@@ -1,14 +0,0 @@
1
- require 'tamashii/agent/handler/base'
2
- require 'tamashii/agent/request_pool'
3
-
4
- module Tamashii
5
- module Agent
6
- module Handler
7
- class RequestPoolResponse < Base
8
- def resolve(data)
9
- @connection.request_pool.add_response(RequestPool::Response.new(self.type, data))
10
- end
11
- end
12
- end
13
- end
14
- end
@@ -1,38 +0,0 @@
1
- require 'json'
2
- module Tamashii
3
- module Agent
4
- class RequestPool
5
- class Request
6
- attr_accessor :id
7
- attr_accessor :ev_type
8
- attr_accessor :ev_body
9
- attr_accessor :state
10
-
11
- STATE_PENDING = :pending
12
- STATE_SENT = :sent
13
-
14
- def initialize(ev_type, ev_body, id)
15
- @ev_type = ev_type
16
- @ev_body = ev_body
17
- @id = id
18
- @state = STATE_PENDING
19
- end
20
-
21
- def wrap_body
22
- {
23
- id: @id,
24
- ev_body: @ev_body
25
- }.to_json
26
- end
27
-
28
- def sent!
29
- @state = STATE_SENT
30
- end
31
-
32
- def sent?
33
- @state == STATE_SENT
34
- end
35
- end
36
- end
37
- end
38
- end
@@ -1,18 +0,0 @@
1
- require 'json'
2
- module Tamashii
3
- module Agent
4
- class RequestPool
5
- class Response
6
- attr_accessor :ev_type, :ev_body, :id
7
-
8
- def initialize(ev_type, wrapped_body)
9
- @ev_type = ev_type
10
- data = JSON.parse(wrapped_body)
11
- @id = data["id"]
12
- @ev_body = data["ev_body"]
13
- end
14
-
15
- end
16
- end
17
- end
18
- end
@@ -1,80 +0,0 @@
1
- require 'tamashii/agent/common'
2
- require 'tamashii/agent/request_pool/request'
3
- require 'tamashii/agent/request_pool/response'
4
-
5
-
6
- module Tamashii
7
- module Agent
8
- class RequestPool
9
- include Common::Loggable
10
- def initialize
11
- @pool = {}
12
- @handlers = {}
13
- end
14
-
15
- def set_handler(sym, method)
16
- @handlers[sym] = method
17
- end
18
-
19
- def call_handler(sym, *args)
20
- if handle?(sym)
21
- @handlers[sym].call(*args)
22
- else
23
- logger.warn "WARN: un-handled event: #{sym}"
24
- end
25
- end
26
-
27
- def handle?(sym)
28
- @handlers.has_key? sym
29
- end
30
-
31
- def add_request(req, timedout = 3)
32
- @pool[req.id] = {req: req, timestamp: Time.now, timedout: timedout}
33
- try_send_request(req)
34
- end
35
-
36
- def add_response(res)
37
- # find the same id
38
- req_data = @pool[res.id]
39
- if req_data
40
- @pool.delete(res.id)
41
- call_handler(:request_meet, req_data[:req], res)
42
- else
43
- # unmatched response
44
- # discard
45
- logger.warn "WARN: un-matched response (id=#{res.id}): #{res.inspect}"
46
- end
47
- end
48
-
49
- def update
50
- process_pending
51
- check_timedout
52
- end
53
-
54
- def check_timedout
55
- now = Time.now
56
- @pool.each do |id, req_data|
57
- if now - req_data[:timestamp] >= req_data[:timedout]
58
- # timedout
59
- @pool.delete(id)
60
- call_handler(:request_timedout, req_data[:req])
61
- end
62
- end
63
- end
64
-
65
- def process_pending
66
- @pool.each_value do |data|
67
- try_send_request(data[:req]) unless data[:req].sent?
68
- end
69
- end
70
-
71
- def try_send_request(req)
72
- if handle?(:send_request)
73
- req.sent! if call_handler(:send_request, req)
74
- end
75
- end
76
- end
77
- end
78
- end
79
-
80
-