lib/artoo/robot.rb in artoo-0.1.3 vs lib/artoo/robot.rb in artoo-0.2.0
- old
+ new
@@ -1,10 +1,12 @@
require 'celluloid/io'
require 'multi_json'
+require 'artoo/basic'
require 'artoo/connection'
require 'artoo/device'
+require 'artoo/events'
require 'artoo/api'
require 'artoo/master'
require 'artoo/port'
require 'artoo/utility'
@@ -12,30 +14,35 @@
# The most important class used by Artoo is Robot. This represents the primary
# interface for interacting with a collection of physical computing capabilities.
class Robot
include Celluloid
include Celluloid::Notifications
+ extend Artoo::Basic
include Artoo::Utility
+ include Artoo::Events
attr_reader :connections, :devices, :name
def initialize(params={})
@name = params[:name] || "Robot #{random_string}"
initialize_connections(params[:connections] || {})
initialize_devices(params[:devices] || {})
end
class << self
- attr_accessor :connection_types, :device_types, :working_code,
+ attr_accessor :device_types, :working_code,
:use_api, :api_host, :api_port
+ def connection_types
+ @@connection_types ||= []
+ end
+
# connection to some hardware that has one or more devices via some specific protocol
# Example:
# connection :arduino, :adaptor => :firmata, :port => '/dev/tty.usbmodemxxxxx'
def connection(name, params = {})
Celluloid::Logger.info "Registering connection '#{name}'..."
- self.connection_types ||= []
self.connection_types << {:name => name}.merge(params)
end
# device that uses a connection to communicate
# Example:
@@ -90,85 +97,10 @@
end
def test?
ENV["ARTOO_TEST"] == 'true'
end
-
- # Taken from Sinatra codebase
- # Sets an option to the given value. If the value is a proc,
- # the proc will be called every time the option is accessed.
- def set(option, value = (not_set = true), ignore_setter = false, &block)
- raise ArgumentError if block and !not_set
- value, not_set = block, false if block
-
- if not_set
- raise ArgumentError unless option.respond_to?(:each)
- option.each { |k,v| set(k, v) }
- return self
- end
-
- if respond_to?("#{option}=") and not ignore_setter
- return __send__("#{option}=", value)
- end
-
- setter = proc { |val| set option, val, true }
- getter = proc { value }
-
- case value
- when Proc
- getter = value
- when Symbol, Fixnum, FalseClass, TrueClass, NilClass
- getter = value.inspect
- when Hash
- setter = proc do |val|
- val = value.merge val if Hash === val
- set option, val, true
- end
- end
-
- define_singleton_method("#{option}=", setter) if setter
- define_singleton_method(option, getter) if getter
- define_singleton_method("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
- self
- end
-
- # Taken from Sinatra codebase
- CALLERS_TO_IGNORE = [ # :nodoc:
- /lib\/artoo.*\.rb$/, # artoo code
- /^\(.*\)$/, # generated code
- /rubygems\/(custom|core_ext\/kernel)_require\.rb$/, # rubygems require hacks
- /active_support/, # active_support require hacks
- /bundler(\/runtime)?\.rb/, # bundler require hacks
- /<internal:/, # internal in ruby >= 1.9.2
- /src\/kernel\/bootstrap\/[A-Z]/ # maglev kernel files
- ]
-
- # Taken from Sinatra codebase
- # Like Kernel#caller but excluding certain magic entries and without
- # line / method information; the resulting array contains filenames only.
- def caller_files
- cleaned_caller(1).flatten
- end
-
- private
-
- # Taken from Sinatra codebase
- def define_singleton_method(name, content = Proc.new)
- # replace with call to singleton_class once we're 1.9 only
- (class << self; self; end).class_eval do
- undef_method(name) if method_defined? name
- String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
- end
- end
-
- # Taken from Sinatra codebase
- # Like Kernel#caller but excluding certain magic entries
- def cleaned_caller(keep = 3)
- caller(1).
- map { |line| line.split(/:(?=\d|in )/, 3)[0,keep] }.
- reject { |file, *_| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
- end
end
def safe_name
name.gsub(' ', '_').downcase
end
@@ -182,46 +114,24 @@
end
# start doing the work
def work
Logger.info "Starting work..."
- make_connections
- start_devices
- current_instance.instance_eval(&working_code)
- rescue Exception => e
- Logger.error e.message
- Logger.error e.backtrace.inspect
+ execute_startup(connections) {|c| c.future.connect}
+ execute_startup(devices) {|d| d.future.start_device}
+ execute_working_code
end
- def make_connections
- result = false
- future_connections = []
- # block until all connections done
- connections.each {|k, c| future_connections << c.future.connect}
- future_connections.each {|v| result = v.value}
- result
- end
-
- def start_devices
- result = false
- future_devices = []
- # block until all devices done
- devices.each {|k, d| future_devices << d.future.start_device}
- future_devices.each {|v| result = v.value}
- result
- end
-
def disconnect
connections.each {|k, c| c.async.disconnect}
end
def default_connection
connections[connections.keys.first]
end
def connection_types
- current_class.connection_types ||= []
current_class.connection_types
end
def device_types
current_class.device_types ||= []
@@ -230,41 +140,10 @@
def working_code
current_class.working_code ||= proc {puts "No work defined."}
end
- # Subscribe to an event from a device
- def on(device, events={})
- events.each do |k, v|
- subscribe("#{safe_name}_#{device.name}_#{k}", create_proxy_method(k, v))
- end
- end
-
- # Create an anonymous subscription method so we can wrap the
- # subscription method fire into a valid method regardless
- # of where it is defined
- def create_proxy_method(k, v)
- proxy_method_name(k).tap do |name|
- self.class.send :define_method, name do |*args|
- case v
- when Symbol
- self.send v.to_sym, *args
- when Proc
- v.call(*args)
- end
- end
- end
- end
-
- # A simple loop to create a 'fake' anonymous method
- def proxy_method_name(k)
- begin
- meth = "#{k}_#{Random.rand(999)}"
- end while respond_to?(meth)
- meth
- end
-
def to_hash
{:name => name,
:connections => connections.each_value.collect {|c|c.to_hash},
:devices => devices.each_value.collect {|d|d.to_hash}
}
@@ -293,7 +172,19 @@
d = Device.new(d.merge(:parent => current_instance))
instance_eval("def #{d.name}; return devices[:#{d.name}]; end")
@devices[d.name.intern] = d
}
end
+
+ def execute_startup(things, &block)
+ future_things = things.each_value.collect {|t| yield(t)}
+ future_things.each {|v| result = v.value}
+ end
+
+ def execute_working_code
+ current_instance.instance_eval(&working_code)
+ rescue Exception => e
+ Logger.error e.message
+ Logger.error e.backtrace.inspect
+ end
end
-end
\ No newline at end of file
+end