lib/tamashii/agent/lcd.rb in tamashii-agent-0.2.5 vs lib/tamashii/agent/lcd.rb in tamashii-agent-0.2.6
- old
+ new
@@ -3,72 +3,185 @@
require 'tamashii/agent/common'
require 'tamashii/agent/event'
require 'tamashii/agent/adapter/lcd'
-
module Tamashii
module Agent
class LCD < Component
+
+ class LineAnimator
+ include Common::Loggable
+ attr_reader :text
+
+ def self.line_width=(value)
+ @@line_width = value
+ end
+
+ def self.handler_print_line=(value)
+ @@handler_print_line = value
+ end
+
+ def initialize(line)
+ @line = line
+ @text = ""
+ @pos = -1
+ end
+
+ def set_text(text)
+ return if text == @text
+ stop_animation
+ @text = text || ""
+ if @text.size > @@line_width
+ start_animation
+ else
+ print_text(@text)
+ end
+ end
+
+ def animation_show_text
+ text = @text[@pos, @@line_width]
+ @pos += 1
+ @pos = 0 if @pos > @max_pos
+ print_text(text)
+ end
+
+ def start_animation
+ @pos = 0
+ @max_pos = @text.size - @@line_width
+ logger.debug "Start animation for line #{@line}: #{@text}"
+ @animation_thread = Thread.new { animation_loop }
+ end
+
+ def animation_loop
+ loop do
+ sleep Config.lcd_animation_delay
+ animation_show_text
+ sleep 1 if @pos == 0 || @pos == @max_pos
+ end
+ end
+
+ def stop_animation
+ @animation_thread.exit if @animation_thread
+ @animation_thread = nil
+ end
+
+ def print_text(text)
+ @@handler_print_line&.call(text, @line)
+ end
+ end
+
def initialize(master)
super
load_lcd_device
+ @device_line_count = @lcd.class::LINE_COUNT
@device_lock = Mutex.new
- @idle_message = "[Tamashii]\nIdle..."
+ create_line_animators
+ set_idle_text("[Tamashii]\nIdle...")
logger.debug "Using LCD instance: #{@lcd.class}"
- @lcd.print_message("Initializing\nPlease wait...")
+ print_message("Initializing\nPlease wait...")
schedule_to_print_idle
end
+ def create_line_animators
+ LineAnimator.line_width = @lcd.class::WIDTH
+ LineAnimator.handler_print_line = method(:print_line)
+ @line_animators = []
+ @device_line_count.times {|i| @line_animators << LineAnimator.new(i)}
+ end
+
def load_lcd_device
@lcd = Adapter::LCD.object
rescue => e
logger.error "Unable to load LCD instance: #{Adapter::LCD.current_class}"
logger.error "Use #{Adapter::LCD.fake_class} instead"
@lcd = Adapter::LCD.fake_class.new
end
+ def print_message(message)
+ lines = message.lines.map{|l| l.delete("\n")}
+ @device_line_count.times do |line_count|
+ @line_animators[line_count].set_text(lines[line_count])
+ end
+ end
+
+ def print_line(*args)
+ @device_lock.synchronize do
+ @lcd.print_line(*args)
+ end
+ end
+
+ def set_idle_text(text)
+ @idle_text_raw = text
+ @auto_update_interval = 0
+ # Time hint
+ if @idle_text_raw.include?(Tamashii::AgentHint::TIME)
+ @has_time_hint = true
+ @auto_update_interval = [@auto_update_interval, 30].max
+ else
+ @has_time_hint = false
+ end
+ # clear auto update timer
+ @idle_text_timer_task.shutdown if @idle_text_timer_task
+ if @auto_update_interval > 0
+ setup_idle_text_auto_update
+ else
+ # one-time setup
+ compute_idle_text
+ end
+ end
+
+ def setup_idle_text_auto_update
+ @idle_text_timer_task = Concurrent::TimerTask.new(run_now: true) do
+ compute_idle_text
+ print_idle
+ end
+ @idle_text_timer_task.execution_interval = @auto_update_interval
+ @idle_text_timer_task.timeout_interval = @auto_update_interval
+ @idle_text_timer_task.execute
+ end
+
+ def compute_idle_text
+ result = @idle_text_raw.clone
+ if @has_time_hint
+ result.gsub!(Tamashii::AgentHint::TIME, Time.now.localtime(Config.localtime).strftime("%m/%d(%a) %H:%M"))
+ end
+ @idle_text = result
+ logger.debug "Idle text updated to #{@idle_text}"
+ end
+
def schedule_to_print_idle(delay = 5)
@back_to_idle_task = Concurrent::ScheduledTask.execute(delay, &method(:print_idle))
end
def print_idle
- @device_lock.synchronize do
- @lcd.print_message(@idle_message)
- end
+ print_message(@idle_text)
end
def process_event(event)
case event.type
when Event::LCD_MESSAGE
logger.debug "Show message: #{event.body}"
@back_to_idle_task&.cancel
+ print_message(event.body)
@device_lock.synchronize do
- @lcd.print_message(event.body)
schedule_to_print_idle
end
when Event::LCD_SET_IDLE_TEXT
logger.debug "Idle text set to #{event.body}"
- @idle_message = event.body
- @device_lock.synchronize do
- @lcd.print_message(event.body)
- end
+ set_idle_text(event.body)
+ print_idle
end
end
def clear_screen
- @device_lock.synchronize do
- @lcd.print_message("")
- end
+ print_message("")
end
def clean_up
clear_screen
super
end
end
end
end
-
-
-