tamashii-agent 0.1.11 → 0.2.0

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.
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
-