Class: Roby::Application

Inherits:
Object show all
Includes:
Singleton
Defined in:
lib/roby/app.rb

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

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeApplication

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_pluginsObject (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

#controlObject (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

#discoveryObject (readonly)

The discovery options in multi-robot mode



84
85
86
# File 'lib/roby/app.rb', line 84

def discovery
  @discovery
end

#drobyObject (readonly)

The robot’s dRoby options

period

the period of neighbour discovery

max_errors

disconnect from a peer if there is more than max_errors consecutive errors detected



89
90
91
# File 'lib/roby/app.rb', line 89

def droby
  @droby
end

#executiveObject

Returns the value of attribute executive.



558
559
560
# File 'lib/roby/app.rb', line 558

def executive
  @executive
end

#logObject (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_serverObject (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_sourcesObject (readonly)

Returns the value of attribute log_sources.



620
621
622
# File 'lib/roby/app.rb', line 620

def log_sources
  @log_sources
end

#optionsObject (readonly)

The plain option hash saved in config/app.yml



61
62
63
# File 'lib/roby/app.rb', line 61

def options
  @options
end

#plugin_dirsObject (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

#pluginsObject (readonly)

An [name, module] array of the loaded plugins



81
82
83
# File 'lib/roby/app.rb', line 81

def plugins
  @plugins
end

#robot_nameObject (readonly)

The robot name



266
267
268
# File 'lib/roby/app.rb', line 266

def robot_name
  @robot_name
end

#robot_typeObject (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

Raises:

  • (Errno::ENOENT)


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

Returns:

  • (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

Returns:

  • (Boolean)


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

Returns:

  • (Boolean)


111
# File 'lib/roby/app.rb', line 111

def filter_backtraces?; log['filter_backtraces'] end

#framework_file?(path) ⇒ Boolean

Returns:

  • (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(options, names)
    names.each do |optname|
  if options[optname]
      send(optname).merge! options[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(options)
    options = options.dup

    if robot_name && (robot_config = options['robots'])
  if robot_config = robot_config[robot_name]
      robot_config.each do |section, values|
    if options[section]
        options[section].merge! values
    else
        options[section] = values
    end
      end
      options.delete('robots')
  end
    end

    @options = options

    load_option_hashes(options, %w{log control discovery droby})
    call_plugins(:load, self, options)
end

#loaded_plugin?(name) ⇒ Boolean

Returns true if name is a loaded plugin

Returns:

  • (Boolean)


151
152
153
# File 'lib/roby/app.rb', line 151

def loaded_plugin?(name)
    plugins.any? { |plugname, _| plugname == name }
end

#log_dirObject

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.expand_path(log['dir'] || 'log', APP_DIR)
end

#model?(model) ⇒ Boolean

Returns:

  • (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.expand_path(dir)
    @plugin_dirs << dir
    $LOAD_PATH.unshift File.expand_path(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

#reloadObject



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

#reload_model?(model) ⇒ Boolean

Returns:

  • (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_modelsObject

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

#resetObject



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_dirObject

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.expand_path(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
    options = { :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 options

    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.full_message
           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.message, e.backtrace
    else
  raise
    end
end

#setupObject



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_dirsObject



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_loggersObject

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

#shellObject



737
# File 'lib/roby/app.rb', line 737

def shell; self.shell = true end

#simulationObject



732
# File 'lib/roby/app.rb', line 732

def simulation; self.simulation = true end

#singleObject



739
# File 'lib/roby/app.rb', line 739

def single;  @single = true end

#single?Boolean

Returns:

  • (Boolean)


738
# File 'lib/roby/app.rb', line 738

def single?; @single || discovery.empty? end

#start_distributedObject

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_serverObject

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 $!.full_message
      end
  end
    end

    call_plugins(:start_server, self)
end

#stopObject



573
# File 'lib/roby/app.rb', line 573

def stop; call_plugins(:stop, self) end

#stop_distributedObject

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_serverObject

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

#testingObject



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) && options
      mod.load(self, options)
  end
    end
end