Class: Roby::Application
Overview
Roby Applications
There is one and only one Application object, which holds mainly the system-wide configuration and takes care of file loading and system-wide setup (#setup). A Roby application can be started in multiple modes. The first and most important mode is the runtime mode (scripts/run). Other modes are the testing mode (#testing? returns true, entered through scripts/test) and the shell mode (#shell? returns true, entered through scripts/shell). Usually, user code does not have to take the modes into account, but it is sometime useful.
Finally, in both testing and runtime mode, the code can be started in simulation or live setups (see #simulation?). Specific plugins can for instance start and set up a simulation system in simulation mode, and as well set up some simulation-specific configuration for the functional layer of the architecture.
Configuration files
In all modes, a specific set of configuration files are loaded. The files that are actually loaded are defined by the robot name and type, as specified to #robot. The loaded files are, in order, the following:
- config/app.yml
-
the application configuration as a YAML file. See the comments in that file for more details.
- config/init.rb
-
Ruby code for the common configuration of all robots
- config/ROBOT_NAME.rb or config/ROBOT_TYPE.rb
-
Ruby code for the configuration of either all robots of the same type, or a specific robot. It is one or the other. If a given robot needs to inherit the configuration of its type, explicitely require the ROBOT_TYPE.rb file in config/ROBOT_NAME.rb.
Runtime mode (scripts/run)
Then, in runtime mode the robot controller controller/ROBOT_NAME.rb or controller/ROBOT_TYPE.rb is loaded. The same rules than for the configuration file config/ROBOT_NAME.rb apply.
Testing mode (scripts/test)
This mode is used to run test suites in the test directory. See Roby::Test::TestCase for a description of Roby-specific tests.
Constant Summary collapse
- DISCOVERY_TEMPLATE =
[:droby, nil, nil]
- @@reload_model_filter =
[]
Instance Attribute Summary collapse
-
#available_plugins ⇒ Object
readonly
A [name, dir, file, module] array of available plugins, where ‘name’ is the plugin name, ‘dir’ the directory in which it is installed, ‘file’ the file which should be required to load the plugin and ‘module’ the Application-compatible module for configuration of the plug-in.
-
#control ⇒ Object
readonly
- Configuration of the control loop abort_on_exception
-
if the control loop should abort if an uncaught task or event exception is received.
-
#discovery ⇒ Object
readonly
The discovery options in multi-robot mode.
-
#droby ⇒ Object
readonly
- The robot’s dRoby options period
- the period of neighbour discovery max_errors
-
disconnect from a peer if there is more than
max_errorsconsecutive errors detected.
-
#executive ⇒ Object
Returns the value of attribute executive.
-
#log ⇒ Object
readonly
Logging options.
-
#log_server ⇒ Object
readonly
Returns the value of attribute log_server.
-
#log_sources ⇒ Object
readonly
Returns the value of attribute log_sources.
-
#options ⇒ Object
readonly
The plain option hash saved in config/app.yml.
-
#plugin_dirs ⇒ Object
readonly
An array of directories in which to search for plugins.
-
#plugins ⇒ Object
readonly
An [name, module] array of the loaded plugins.
-
#robot_name ⇒ Object
readonly
The robot name.
-
#robot_type ⇒ Object
readonly
The robot type.
Class Method Summary collapse
-
.filter_reloaded_models(&block) ⇒ Object
Add a filter to model reloading.
- .find_data(name) ⇒ Object
- .register_plugin(name, mod, &init) ⇒ Object
-
.unique_dirname(base_dir, path_spec) ⇒ Object
Returns a unique directory name as a subdirectory of
base_dir, based onpath_spec.
Instance Method Summary collapse
- #app_file?(path) ⇒ Boolean
-
#call_plugins(method, *args) ⇒ Object
Call
methodon each loaded extension module which define it, with argumentsargs. -
#data_streams(log_dir = nil) ⇒ Object
Returns the list of data streams suitable for data display known to the application.
-
#data_streams_of(filenames) ⇒ Object
Guesses the type of
filenameif it is a source suitable for data display in this application. -
#defined_plugin?(name) ⇒ Boolean
True if
nameis a plugin known to us. - #each_plugin(on_available = false) ⇒ Object
-
#each_responding_plugin(method, on_available = false) ⇒ Object
Yields each extension modules that respond to
method. - #filter_backtraces=(value) ⇒ Object
-
#filter_backtraces? ⇒ Boolean
True if we should remove the framework code from the error backtraces.
- #framework_file?(path) ⇒ Boolean
-
#initialize ⇒ Application
constructor
A new instance of Application.
- #load_option_hashes(options, names) ⇒ Object
-
#load_robotfile(pattern) ⇒ Object
Loads the first file found matching
pattern. -
#load_yaml(options) ⇒ Object
Load configuration from the given option hash.
-
#loaded_plugin?(name) ⇒ Boolean
Returns true if
nameis a loaded plugin. -
#log_dir ⇒ Object
The directory in which logs are to be saved Defaults to APP_DIR/log.
- #model?(model) ⇒ Boolean
-
#plugin_definition(name) ⇒ Object
Returns the [name, dir, file, module] array definition of the plugin
name, or nil ifnameis not a known plugin. -
#plugin_dir(dir) ⇒ Object
Adds
dirin the list of directories searched for plugins. - #reload ⇒ Object
- #reload_model?(model) ⇒ Boolean
-
#require_dir(dirname) ⇒ Object
Require all files in
dirname. -
#require_models ⇒ Object
Loads the models, based on the given robot name and robot type.
-
#require_robotdir(pattern) ⇒ Object
Require all files in the directories matching
pattern. -
#require_robotfile(pattern, method = :require) ⇒ Object
Requires or loads (according to the value of
method) the first file found matchingpattern. - #reset ⇒ Object
-
#results_dir ⇒ Object
The directory in which results should be saved Defaults to APP_DIR/results.
-
#robot(name, type = name) ⇒ Object
Sets up the name and type of the robot.
- #run(&block) ⇒ Object
- #run_plugins(mods, &block) ⇒ Object
- #setup ⇒ Object
- #setup_dirs ⇒ Object
-
#setup_loggers ⇒ Object
Sets up all the default loggers.
- #shell ⇒ Object
- #simulation ⇒ Object
- #single ⇒ Object
- #single? ⇒ Boolean
-
#start_distributed ⇒ Object
Starts services needed for distributed operations.
-
#start_server ⇒ Object
Start services that should exist for every robot in the system.
- #stop ⇒ Object
-
#stop_distributed ⇒ Object
Stop services needed for distributed operations.
-
#stop_server ⇒ Object
Stop server.
- #testing ⇒ Object
-
#using(*names) ⇒ Object
Loads the plugins whose name are listed in
names.
Constructor Details
#initialize ⇒ Application
Returns a new instance of Application.
114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/roby/app.rb', line 114 def initialize @plugins = Array.new @available_plugins = Array.new @log = Hash['events' => 'stats', 'levels' => Hash.new, 'filter_backtraces' => true] @discovery = Hash.new @droby = Hash['period' => 0.5, 'max_errors' => 1] @control = Hash[ 'abort_on_exception' => false, 'abort_on_application_exception' => true ] @automatic_testing = true @testing_keep_logs = false @plugin_dirs = [] end |
Instance Attribute Details
#available_plugins ⇒ Object (readonly)
A [name, dir, file, module] array of available plugins, where ‘name’ is the plugin name, ‘dir’ the directory in which it is installed, ‘file’ the file which should be required to load the plugin and ‘module’ the Application-compatible module for configuration of the plug-in
79 80 81 |
# File 'lib/roby/app.rb', line 79 def available_plugins @available_plugins end |
#control ⇒ Object (readonly)
Configuration of the control loop
- abort_on_exception
-
if the control loop should abort if an uncaught task or event exception is received. Defaults to false
- abort_on_application_exception
-
if the control should abort if an uncaught application exception (not originating from a task or event) is caught. Defaults to true.
96 97 98 |
# File 'lib/roby/app.rb', line 96 def control @control end |
#discovery ⇒ Object (readonly)
The discovery options in multi-robot mode
84 85 86 |
# File 'lib/roby/app.rb', line 84 def discovery @discovery end |
#droby ⇒ Object (readonly)
The robot’s dRoby options
- period
-
the period of neighbour discovery
- max_errors
-
disconnect from a peer if there is more than
max_errorsconsecutive errors detected
89 90 91 |
# File 'lib/roby/app.rb', line 89 def droby @droby end |
#executive ⇒ Object
Returns the value of attribute executive.
558 559 560 |
# File 'lib/roby/app.rb', line 558 def executive @executive end |
#log ⇒ Object (readonly)
Logging options.
- events
-
save a log of all events in the system. This log can be read using scripts/replay If this value is ‘stats’, only the data necessary for timing statistics is saved.
- levels
-
a component => level hash of the minimum level of the messages that
should be displayed on the console. The levels are DEBUG, INFO, WARN and FATAL.
Roby: FATAL
Roby::Distributed: INFO
- dir
-
the log directory. Uses APP_DIR/log if not set
- filter_backtraces
-
true if the framework code should be removed from the error backtraces
72 73 74 |
# File 'lib/roby/app.rb', line 72 def log @log end |
#log_server ⇒ Object (readonly)
Returns the value of attribute log_server.
619 620 621 |
# File 'lib/roby/app.rb', line 619 def log_server @log_server end |
#log_sources ⇒ Object (readonly)
Returns the value of attribute log_sources.
620 621 622 |
# File 'lib/roby/app.rb', line 620 def log_sources @log_sources end |
#options ⇒ Object (readonly)
The plain option hash saved in config/app.yml
61 62 63 |
# File 'lib/roby/app.rb', line 61 def end |
#plugin_dirs ⇒ Object (readonly)
An array of directories in which to search for plugins
99 100 101 |
# File 'lib/roby/app.rb', line 99 def plugin_dirs @plugin_dirs end |
#plugins ⇒ Object (readonly)
An [name, module] array of the loaded plugins
81 82 83 |
# File 'lib/roby/app.rb', line 81 def plugins @plugins end |
#robot_name ⇒ Object (readonly)
The robot name
266 267 268 |
# File 'lib/roby/app.rb', line 266 def robot_name @robot_name end |
#robot_type ⇒ Object (readonly)
The robot type
268 269 270 |
# File 'lib/roby/app.rb', line 268 def robot_type @robot_type end |
Class Method Details
.filter_reloaded_models(&block) ⇒ Object
Add a filter to model reloading. A task or planner model is reinitialized only if all filter blocks return true for it
798 799 800 |
# File 'lib/roby/app.rb', line 798 def self.filter_reloaded_models(&block) @@reload_model_filter << block end |
.find_data(name) ⇒ Object
781 782 783 784 785 786 787 |
# File 'lib/roby/app.rb', line 781 def self.find_data(name) Roby::State.datadirs.each do |dir| path = File.join(dir, name) return path if File.exists?(path) end raise Errno::ENOENT, "no file #{name} found in #{Roby::State.datadirs.join(":")}" end |
.register_plugin(name, mod, &init) ⇒ Object
789 790 791 792 793 |
# File 'lib/roby/app.rb', line 789 def self.register_plugin(name, mod, &init) caller(1)[0] =~ /^([^:]+):\d/ dir = File.(File.dirname($1)) Roby.app.available_plugins << [name, dir, mod, init] end |
.unique_dirname(base_dir, path_spec) ⇒ Object
Returns a unique directory name as a subdirectory of base_dir, based on path_spec. The generated name is of the form
<base_dir>/a/b/c/YYYYMMDD-basename
if path_spec = "a/b/c/basename". A .<number> suffix is appended if the path already exists.
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
# File 'lib/roby/app.rb', line 304 def self.unique_dirname(base_dir, path_spec) if path_spec =~ /\/$/ basename = "" dirname = path_spec else basename = File.basename(path_spec) dirname = File.dirname(path_spec) end date = Date.today date = "%i%02i%02i" % [date.year, date.month, date.mday] if basename && !basename.empty? basename = date + "-" + basename else basename = date end # Check if +basename+ already exists, and if it is the case add a # .x suffix to it full_path = File.(File.join(dirname, basename), base_dir) base_dir = File.dirname(full_path) unless File.exists?(base_dir) FileUtils.mkdir_p(base_dir) end final_path, i = full_path, 0 while File.exists?(final_path) i += 1 final_path = full_path + ".#{i}" end final_path end |
Instance Method Details
#app_file?(path) ⇒ Boolean
811 812 813 814 |
# File 'lib/roby/app.rb', line 811 def app_file?(path) (path =~ %r{(^|/)#{APP_DIR}(/|$)}) || ((path[0] != ?/) && File.file?(File.join(APP_DIR, path))) end |
#call_plugins(method, *args) ⇒ Object
Call method on each loaded extension module which define it, with arguments args
185 186 187 188 189 |
# File 'lib/roby/app.rb', line 185 def call_plugins(method, *args) each_responding_plugin(method) do |config_extension| config_extension.send(method, *args) end end |
#data_streams(log_dir = nil) ⇒ Object
Returns the list of data streams suitable for data display known to the application
766 767 768 769 770 771 772 773 774 775 776 777 778 779 |
# File 'lib/roby/app.rb', line 766 def data_streams(log_dir = nil) log_dir ||= self.log_dir streams = [] Dir.glob(File.join(log_dir, '*-events.log*')).each do |file| next unless file =~ /-events\.log$/ streams << Roby::Log::EventStream.new($`) end each_responding_plugin(:data_streams, true) do |config| if s = config.data_streams(log_dir) streams += s end end streams end |
#data_streams_of(filenames) ⇒ Object
Guesses the type of filename if it is a source suitable for data display in this application
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 |
# File 'lib/roby/app.rb', line 743 def data_streams_of(filenames) if filenames.size == 1 path = filenames.first path = if path =~ /-(events|timings)\.log$/ $` elsif File.exists?("#{path}-events.log") path end if path return [Roby::Log::EventStream.new(path)] end end each_responding_plugin(:data_streams_of, true) do |config| if streams = config.data_streams_of(filenames) return streams end end nil end |
#defined_plugin?(name) ⇒ Boolean
True if name is a plugin known to us
162 163 164 |
# File 'lib/roby/app.rb', line 162 def defined_plugin?(name) available_plugins.any? { |plugname, *_| plugname == name } end |
#each_plugin(on_available = false) ⇒ Object
166 167 168 169 170 171 172 173 174 |
# File 'lib/roby/app.rb', line 166 def each_plugin(on_available = false) plugins = self.plugins if on_available plugins = available_plugins.map { |name, _, mod, _| [name, mod] } end plugins.each do |_, mod| yield(mod) end end |
#each_responding_plugin(method, on_available = false) ⇒ Object
Yields each extension modules that respond to method
177 178 179 180 181 |
# File 'lib/roby/app.rb', line 177 def each_responding_plugin(method, on_available = false) each_plugin do |mod| yield(mod) if mod.respond_to?(method) end end |
#filter_backtraces=(value) ⇒ Object
112 |
# File 'lib/roby/app.rb', line 112 def filter_backtraces=(value); log['filter_backtraces'] = value end |
#filter_backtraces? ⇒ Boolean
True if we should remove the framework code from the error backtraces
111 |
# File 'lib/roby/app.rb', line 111 def filter_backtraces?; log['filter_backtraces'] end |
#framework_file?(path) ⇒ Boolean
815 816 817 818 819 820 821 822 823 824 |
# File 'lib/roby/app.rb', line 815 def framework_file?(path) if path =~ /roby\/.*\.rb$/ true else Roby.app.plugins.any? do |name, _| _, dir, _, _ = Roby.app.plugin_definition(name) path =~ %r{(^|/)#{dir}(/|$)} end end end |
#load_option_hashes(options, names) ⇒ Object
214 215 216 217 218 219 220 |
# File 'lib/roby/app.rb', line 214 def load_option_hashes(, names) names.each do |optname| if [optname] send(optname).merge! [optname] end end end |
#load_robotfile(pattern) ⇒ Object
Loads the first file found matching pattern
See #require_robotfile
705 706 707 |
# File 'lib/roby/app.rb', line 705 def load_robotfile(pattern) require_robotfile(pattern, :load) end |
#load_yaml(options) ⇒ Object
Load configuration from the given option hash
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/roby/app.rb', line 192 def load_yaml() = .dup if robot_name && (robot_config = ['robots']) if robot_config = robot_config[robot_name] robot_config.each do |section, values| if [section] [section].merge! values else [section] = values end end .delete('robots') end end = load_option_hashes(, %w{log control discovery droby}) call_plugins(:load, self, ) end |
#loaded_plugin?(name) ⇒ Boolean
Returns true if name is a loaded plugin
151 152 153 |
# File 'lib/roby/app.rb', line 151 def loaded_plugin?(name) plugins.any? { |plugname, _| plugname == name } end |
#log_dir ⇒ Object
The directory in which logs are to be saved Defaults to APP_DIR/log
284 285 286 |
# File 'lib/roby/app.rb', line 284 def log_dir File.(log['dir'] || 'log', APP_DIR) end |
#model?(model) ⇒ Boolean
802 803 804 805 |
# File 'lib/roby/app.rb', line 802 def model?(model) (model <= Roby::Task) || (model.kind_of?(Roby::TaskModelTag)) || (model <= Planning::Planner) || (model <= Planning::Library) end |
#plugin_definition(name) ⇒ Object
Returns the [name, dir, file, module] array definition of the plugin name, or nil if name is not a known plugin
157 158 159 |
# File 'lib/roby/app.rb', line 157 def plugin_definition(name) available_plugins.find { |plugname, *_| plugname == name } end |
#plugin_dir(dir) ⇒ Object
Adds dir in the list of directories searched for plugins
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/roby/app.rb', line 130 def plugin_dir(dir) dir = File.(dir) @plugin_dirs << dir $LOAD_PATH.unshift File.(dir) Dir.new(dir).each do |subdir| subdir = File.join(dir, subdir) next unless File.directory?(subdir) appfile = File.join(subdir, "app.rb") next unless File.file?(appfile) begin require appfile rescue Roby.warn "cannot load plugin in #{subdir}: #{$!.full_message}\n" end Roby.info "loaded plugin in #{subdir}" end end |
#reload ⇒ Object
826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 |
# File 'lib/roby/app.rb', line 826 def reload # Always reload this file first. This ensure that one can use #reload # to fix the reload code itself load __FILE__ # Clear all event definitions in task models that are filtered out by # Application.filter_reloaded_models ObjectSpace.each_object(Class) do |model| next unless model?(model) next unless reload_model?(model) model.clear_model end # Remove what we want to reload from LOADED_FEATURES and use # require. Do not use 'load' as the reload order should be the # require order. needs_reload = [] $LOADED_FEATURES.delete_if do |feature| if framework_file?(feature) || app_file?(feature) needs_reload << feature end end needs_reload.each do |feature| begin require feature.gsub(/\.rb$/, '') rescue Exception => e STDERR.puts e. end end end |
#reload_model?(model) ⇒ Boolean
807 808 809 |
# File 'lib/roby/app.rb', line 807 def reload_model?(model) @@reload_model_filter.all? { |filter| filter[model] } end |
#require_dir(dirname) ⇒ Object
Require all files in dirname
682 683 684 685 686 687 688 |
# File 'lib/roby/app.rb', line 682 def require_dir(dirname) Dir.new(dirname).each do |file| file = File.join(dirname, file) file = file.gsub(/^#{Regexp.quote(APP_DIR)}\//, '') require file if file =~ /\.rb$/ && File.file?(file) end end |
#require_models ⇒ Object
Loads the models, based on the given robot name and robot type
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 |
# File 'lib/roby/app.rb', line 397 def require_models # Require all common task models and the task models specific to # this robot require_dir(File.join(APP_DIR, 'tasks')) require_robotdir(File.join(APP_DIR, 'tasks', 'ROBOT')) # Load robot-specific configuration planner_dir = File.join(APP_DIR, 'planners') models_search = [planner_dir] if robot_name load_robotfile(File.join(APP_DIR, 'config', "ROBOT.rb")) models_search << File.join(planner_dir, robot_name) << File.join(planner_dir, robot_type) if !require_robotfile(File.join(APP_DIR, 'planners', 'ROBOT', 'main.rb')) require File.join(APP_DIR, "planners", "main") end else require File.join(APP_DIR, "planners", "main") end # Load the other planners models_search.each do |base_dir| next unless File.directory?(base_dir) Dir.new(base_dir).each do |file| if File.file?(file) && file =~ /\.rb$/ && file !~ 'main\.rb$' require file end end end end |
#require_robotdir(pattern) ⇒ Object
Require all files in the directories matching pattern. If pattern contains the word ROBOT, it is replaced by – in order – the robot name and then the robot type
693 694 695 696 697 698 699 700 |
# File 'lib/roby/app.rb', line 693 def require_robotdir(pattern) return unless robot_name && robot_type [robot_name, robot_type].each do |name| dirname = pattern.gsub(/ROBOT/, name) require_dir(dirname) if File.directory?(dirname) end end |
#require_robotfile(pattern, method = :require) ⇒ Object
Requires or loads (according to the value of method) the first file found matching pattern. pattern can contain the word ROBOT, in which case the file is first checked against the robot name and then against the robot type
713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 |
# File 'lib/roby/app.rb', line 713 def require_robotfile(pattern, method = :require) return unless robot_name && robot_type robot_config = pattern.gsub(/ROBOT/, robot_name) if File.file?(robot_config) Kernel.send(method, robot_config) true else robot_config = pattern.gsub(/ROBOT/, robot_type) if File.file?(robot_config) Kernel.send(method, robot_config) true else false end end end |
#reset ⇒ Object
256 257 258 259 260 261 262 263 |
# File 'lib/roby/app.rb', line 256 def reset if defined? State State.clear else Roby.const_set(:State, StateSpace.new) end call_plugins(:reset, self) end |
#results_dir ⇒ Object
The directory in which results should be saved Defaults to APP_DIR/results
294 295 296 |
# File 'lib/roby/app.rb', line 294 def results_dir File.(log['results'] || 'results', APP_DIR) end |
#robot(name, type = name) ⇒ Object
Sets up the name and type of the robot. This can be called only once in a given Roby controller.
271 272 273 274 275 276 277 278 279 280 |
# File 'lib/roby/app.rb', line 271 def robot(name, type = name) if @robot_name if name != @robot_name && type != @robot_type raise ArgumentError, "the robot is already set to #{name}, of type #{type}" end return end @robot_name = name @robot_type = type end |
#run(&block) ⇒ Object
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 |
# File 'lib/roby/app.rb', line 470 def run(&block) # Set up dRoby, setting an Interface object as front server, for shell access host = droby['host'] || "" if host !~ /:\d+$/ host << ":#{Distributed::DEFAULT_DROBY_PORT}" end if single? || !robot_name host =~ /:(\d+)$/ DRb.start_service "druby://:#{$1 || '0'}", Interface.new(Roby.control) else DRb.start_service "druby://#{host}", Interface.new(Roby.control) droby_config = { :ring_discovery => !!discovery['ring'], :name => robot_name, :plan => Roby.plan, :period => discovery['period'] || 0.5 } if discovery['tuplespace'] droby_config[:discovery_tuplespace] = DRbObject.new_with_uri("druby://#{discovery['tuplespace']}") end Roby::Distributed.state = Roby::Distributed::ConnectionSpace.new(droby_config) if discovery['ring'] Roby::Distributed.publish discovery['ring'] end Roby::Control.every(discovery['period'] || 0.5) do Roby::Distributed.state.start_neighbour_discovery end end @robot_name ||= 'common' @robot_type ||= 'common' control_config = self.control control = Roby.control = { :detach => true, :cycle => control_config['cycle'] || 0.1 } # Add an executive if one is defined if control_config['executive'] self.executive = control_config['executive'] end if log['events'] require 'roby/log/file' logfile = File.join(log_dir, robot_name) logger = Roby::Log::FileLogger.new(logfile) logger.stats_mode = log['events'] == 'stats' Roby::Log.add_logger logger end control.abort_on_exception = control_config['abort_on_exception'] control.abort_on_application_exception = control_config['abort_on_application_exception'] control.run plugins = self.plugins.map { |_, mod| mod if mod.respond_to?(:run) }.compact run_plugins(plugins, &block) rescue Exception => e if e.respond_to?(:pretty_print) pp e else pp e. end end |
#run_plugins(mods, &block) ⇒ Object
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 |
# File 'lib/roby/app.rb', line 535 def run_plugins(mods, &block) control = Roby.control if mods.empty? yield control.join else mod = mods.shift mod.run(self) do run_plugins(mods, &block) end end rescue Exception => e if Roby.control.running? control.quit control.join raise e, e., e.backtrace else raise end end |
#setup ⇒ Object
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 |
# File 'lib/roby/app.rb', line 428 def setup reset $LOAD_PATH.unshift(APP_DIR) unless $LOAD_PATH.include?(APP_DIR) # Get the application-wide configuration file = File.join(APP_DIR, 'config', 'app.yml') file = YAML.load(File.open(file)) load_yaml(file) if File.exists?(initfile = File.join(APP_DIR, 'config', 'init.rb')) load initfile end setup_dirs setup_loggers # Import some constants directly at toplevel before loading the # user-defined models unless Object.const_defined?(:Application) Object.const_set(:Application, Roby::Application) Object.const_set(:State, Roby::State) end require_models # MainPlanner is always included in the planner list Roby.control.planners << MainPlanner # Set up the loaded plugins call_plugins(:setup, self) # If we are in test mode, import the test extensions from plugins if testing? require 'roby/test/testcase' each_plugin do |mod| if mod.const_defined?(:Test) Roby::Test::TestCase.include mod.const_get(:Test) end end end end |
#setup_dirs ⇒ Object
381 382 383 384 385 386 387 388 389 390 391 392 393 394 |
# File 'lib/roby/app.rb', line 381 def setup_dirs Dir.mkdir(log_dir) unless File.exists?(log_dir) if File.directory?(libdir = File.join(APP_DIR, 'lib')) if !$LOAD_PATH.include?(libdir) $LOAD_PATH.unshift File.join(APP_DIR, 'lib') end end Roby::State.datadirs = [] datadir = File.join(APP_DIR, "data") if File.directory?(datadir) Roby::State.datadirs << datadir end end |
#setup_loggers ⇒ Object
Sets up all the default loggers. It creates the logger for the Robot module (accessible through Robot.logger), and sets up log levels as specified in the config/app.yml file.
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 |
# File 'lib/roby/app.rb', line 342 def setup_loggers # Create the robot namespace STDOUT.sync = true Robot.logger = Logger.new(STDOUT) Robot.logger.level = Logger::INFO Robot.logger.formatter = Roby.logger.formatter Robot.logger.progname = robot_name # Set up log levels log['levels'].each do |name, value| name = name.camelize if value =~ /^(\w+):(.+)$/ level, file = $1, $2 level = Logger.const_get(level) file = file.gsub('ROBOT', robot_name) if robot_name else level = Logger.const_get(value) end new_logger = if file path = File.(file, log_dir) io = (log_files[path] ||= File.open(path, 'w')) Logger.new(io) else Logger.new(STDOUT) end new_logger.level = level new_logger.formatter = Roby.logger.formatter if (mod = name.constantize rescue nil) if robot_name new_logger.progname = "#{name} #{robot_name}" else new_logger.progname = name end mod.logger = new_logger end end end |
#shell ⇒ Object
737 |
# File 'lib/roby/app.rb', line 737 def shell; self.shell = true end |
#simulation ⇒ Object
732 |
# File 'lib/roby/app.rb', line 732 def simulation; self.simulation = true end |
#single ⇒ Object
739 |
# File 'lib/roby/app.rb', line 739 def single; @single = true end |
#single? ⇒ Boolean
738 |
# File 'lib/roby/app.rb', line 738 def single?; @single || discovery.empty? end |
#start_distributed ⇒ Object
Starts services needed for distributed operations. These services are supposed to be started only once for a whole system
If you have external servers to start for every robot, plug it into #start_server
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 |
# File 'lib/roby/app.rb', line 582 def start_distributed Thread.abort_on_exception = true if !File.exists?(log_dir) Dir.mkdir(log_dir) end unless single? || !discovery['tuplespace'] ts = Rinda::TupleSpace.new discovery['tuplespace'] =~ /(:\d+)$/ DRb.start_service "druby://#{$1}", ts new_db = ts.notify('write', DISCOVERY_TEMPLATE) take_db = ts.notify('take', DISCOVERY_TEMPLATE) Thread.start do new_db.each { |_, t| STDERR.puts "new host #{t[1]}" } end Thread.start do take_db.each { |_, t| STDERR.puts "host #{t[1]} has disconnected" } end Roby.warn "Started service discovery on #{discovery['tuplespace']}" end call_plugins(:start_distributed, self) end |
#start_server ⇒ Object
Start services that should exist for every robot in the system. Services that are needed only once for all robots should be started in #start_distributed
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 |
# File 'lib/roby/app.rb', line 624 def start_server Thread.abort_on_exception = true # Start a log server if needed, and poll the log directory for new # data sources if log_server = (log.has_key?('server') ? log['server'] : true) require 'roby/log/server' port = if log_server.kind_of?(Hash) && log_server['port'] Integer(log_server['port']) end @log_server = Log::Server.new(port ||= Log::Server::RING_PORT) Roby::Log::Server.info "log server published on port #{port}" @log_streams = [] @log_streams_poll = Thread.new do begin loop do Thread.exclusive do known_streams = @log_server.streams streams = data_streams (streams - known_streams).each do |s| Roby::Log::Server.info "new stream found #{s.name} [#{s.type}]" s.open @log_server.added_stream(s) end (known_streams - streams).each do |s| Roby::Log::Server.info "end of stream #{s.name} [#{s.type}]" s.close @log_server.removed_stream(s) end end sleep(5) end rescue Interrupt rescue Roby::Log::Server.fatal $!. end end end call_plugins(:start_server, self) end |
#stop ⇒ Object
573 |
# File 'lib/roby/app.rb', line 573 def stop; call_plugins(:stop, self) end |
#stop_distributed ⇒ Object
Stop services needed for distributed operations. See #start_distributed
612 613 614 615 616 617 |
# File 'lib/roby/app.rb', line 612 def stop_distributed DRb.stop_service call_plugins(:stop_distributed, self) rescue Interrupt end |
#stop_server ⇒ Object
Stop server. See #start_server
669 670 671 672 673 674 675 676 677 678 679 |
# File 'lib/roby/app.rb', line 669 def stop_server if @log_server @log_streams_poll.raise Interrupt, "quitting" @log_streams_poll.join @log_server.quit @log_streams.clear end call_plugins(:stop_server, self) end |
#testing ⇒ Object
735 |
# File 'lib/roby/app.rb', line 735 def testing; self.testing = true end |
#using(*names) ⇒ Object
Loads the plugins whose name are listed in names
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/roby/app.rb', line 223 def using(*names) names.each do |name| name = name.to_s unless plugin = plugin_definition(name) raise ArgumentError, "#{name} is not a known plugin (#{available_plugins.map { |n, *_| n }.join(", ")})" end name, dir, mod, init = *plugin if plugins.find { |n, m| n == name && m == mod } next end if init begin $LOAD_PATH.unshift dir init.call mod.reset(self) if mod.respond_to?(:reset) rescue Exception => e Roby.fatal "cannot load plugin #{name}: #{e.full_message}" exit(1) ensure $LOAD_PATH.shift end end plugins << [name, mod] extend mod # If +load+ has already been called, call it on the module if mod.respond_to?(:load) && mod.load(self, ) end end end |