roby 0.7

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.
Files changed (240) hide show
  1. data/.gitignore +29 -0
  2. data/History.txt +4 -0
  3. data/License-fr.txt +519 -0
  4. data/License.txt +515 -0
  5. data/Manifest.txt +245 -0
  6. data/NOTES +4 -0
  7. data/README.txt +163 -0
  8. data/Rakefile +161 -0
  9. data/TODO.txt +146 -0
  10. data/app/README.txt +24 -0
  11. data/app/Rakefile +8 -0
  12. data/app/config/ROBOT.rb +5 -0
  13. data/app/config/app.yml +91 -0
  14. data/app/config/init.rb +7 -0
  15. data/app/config/roby.yml +3 -0
  16. data/app/controllers/.gitattributes +0 -0
  17. data/app/controllers/ROBOT.rb +2 -0
  18. data/app/data/.gitattributes +0 -0
  19. data/app/planners/ROBOT/main.rb +6 -0
  20. data/app/planners/main.rb +5 -0
  21. data/app/scripts/distributed +3 -0
  22. data/app/scripts/generate/bookmarks +3 -0
  23. data/app/scripts/replay +3 -0
  24. data/app/scripts/results +3 -0
  25. data/app/scripts/run +3 -0
  26. data/app/scripts/server +3 -0
  27. data/app/scripts/shell +3 -0
  28. data/app/scripts/test +3 -0
  29. data/app/tasks/.gitattributes +0 -0
  30. data/app/tasks/ROBOT/.gitattributes +0 -0
  31. data/bin/roby +210 -0
  32. data/bin/roby-log +168 -0
  33. data/bin/roby-shell +25 -0
  34. data/doc/images/event_generalization.png +0 -0
  35. data/doc/images/exception_propagation_1.png +0 -0
  36. data/doc/images/exception_propagation_2.png +0 -0
  37. data/doc/images/exception_propagation_3.png +0 -0
  38. data/doc/images/exception_propagation_4.png +0 -0
  39. data/doc/images/exception_propagation_5.png +0 -0
  40. data/doc/images/replay_handler_error.png +0 -0
  41. data/doc/images/replay_handler_error_0.png +0 -0
  42. data/doc/images/replay_handler_error_1.png +0 -0
  43. data/doc/images/roby_cycle_overview.png +0 -0
  44. data/doc/images/roby_replay_02.png +0 -0
  45. data/doc/images/roby_replay_03.png +0 -0
  46. data/doc/images/roby_replay_04.png +0 -0
  47. data/doc/images/roby_replay_event_representation.png +0 -0
  48. data/doc/images/roby_replay_first_state.png +0 -0
  49. data/doc/images/roby_replay_relations.png +0 -0
  50. data/doc/images/roby_replay_startup.png +0 -0
  51. data/doc/images/task_event_generalization.png +0 -0
  52. data/doc/papers.rdoc +11 -0
  53. data/doc/styles/allison.css +314 -0
  54. data/doc/styles/allison.js +316 -0
  55. data/doc/styles/allison.rb +276 -0
  56. data/doc/styles/jamis.rb +593 -0
  57. data/doc/tutorials/01-GettingStarted.rdoc +86 -0
  58. data/doc/tutorials/02-GoForward.rdoc +220 -0
  59. data/doc/tutorials/03-PlannedPath.rdoc +268 -0
  60. data/doc/tutorials/04-EventPropagation.rdoc +236 -0
  61. data/doc/tutorials/05-ErrorHandling.rdoc +319 -0
  62. data/doc/tutorials/06-Overview.rdoc +40 -0
  63. data/doc/videos.rdoc +69 -0
  64. data/ext/droby/dump.cc +175 -0
  65. data/ext/droby/extconf.rb +3 -0
  66. data/ext/graph/algorithm.cc +746 -0
  67. data/ext/graph/extconf.rb +7 -0
  68. data/ext/graph/graph.cc +529 -0
  69. data/ext/graph/graph.hh +183 -0
  70. data/ext/graph/iterator_sequence.hh +102 -0
  71. data/ext/graph/undirected_dfs.hh +226 -0
  72. data/ext/graph/undirected_graph.hh +421 -0
  73. data/lib/roby.rb +41 -0
  74. data/lib/roby/app.rb +870 -0
  75. data/lib/roby/app/rake.rb +56 -0
  76. data/lib/roby/app/run.rb +14 -0
  77. data/lib/roby/app/scripts/distributed.rb +13 -0
  78. data/lib/roby/app/scripts/generate/bookmarks.rb +162 -0
  79. data/lib/roby/app/scripts/replay.rb +31 -0
  80. data/lib/roby/app/scripts/results.rb +15 -0
  81. data/lib/roby/app/scripts/run.rb +26 -0
  82. data/lib/roby/app/scripts/server.rb +18 -0
  83. data/lib/roby/app/scripts/shell.rb +88 -0
  84. data/lib/roby/app/scripts/test.rb +40 -0
  85. data/lib/roby/basic_object.rb +151 -0
  86. data/lib/roby/config.rb +5 -0
  87. data/lib/roby/control.rb +747 -0
  88. data/lib/roby/decision_control.rb +17 -0
  89. data/lib/roby/distributed.rb +32 -0
  90. data/lib/roby/distributed/base.rb +440 -0
  91. data/lib/roby/distributed/communication.rb +871 -0
  92. data/lib/roby/distributed/connection_space.rb +592 -0
  93. data/lib/roby/distributed/distributed_object.rb +206 -0
  94. data/lib/roby/distributed/drb.rb +62 -0
  95. data/lib/roby/distributed/notifications.rb +539 -0
  96. data/lib/roby/distributed/peer.rb +550 -0
  97. data/lib/roby/distributed/protocol.rb +529 -0
  98. data/lib/roby/distributed/proxy.rb +343 -0
  99. data/lib/roby/distributed/subscription.rb +311 -0
  100. data/lib/roby/distributed/transaction.rb +498 -0
  101. data/lib/roby/event.rb +897 -0
  102. data/lib/roby/exceptions.rb +234 -0
  103. data/lib/roby/executives/simple.rb +30 -0
  104. data/lib/roby/graph.rb +166 -0
  105. data/lib/roby/interface.rb +390 -0
  106. data/lib/roby/log.rb +3 -0
  107. data/lib/roby/log/chronicle.rb +303 -0
  108. data/lib/roby/log/console.rb +72 -0
  109. data/lib/roby/log/data_stream.rb +197 -0
  110. data/lib/roby/log/dot.rb +279 -0
  111. data/lib/roby/log/event_stream.rb +151 -0
  112. data/lib/roby/log/file.rb +340 -0
  113. data/lib/roby/log/gui/basic_display.ui +83 -0
  114. data/lib/roby/log/gui/chronicle.rb +26 -0
  115. data/lib/roby/log/gui/chronicle_view.rb +40 -0
  116. data/lib/roby/log/gui/chronicle_view.ui +70 -0
  117. data/lib/roby/log/gui/data_displays.rb +172 -0
  118. data/lib/roby/log/gui/data_displays.ui +155 -0
  119. data/lib/roby/log/gui/notifications.rb +26 -0
  120. data/lib/roby/log/gui/relations.rb +248 -0
  121. data/lib/roby/log/gui/relations.ui +123 -0
  122. data/lib/roby/log/gui/relations_view.rb +185 -0
  123. data/lib/roby/log/gui/relations_view.ui +149 -0
  124. data/lib/roby/log/gui/replay.rb +327 -0
  125. data/lib/roby/log/gui/replay_controls.rb +200 -0
  126. data/lib/roby/log/gui/replay_controls.ui +259 -0
  127. data/lib/roby/log/gui/runtime.rb +130 -0
  128. data/lib/roby/log/hooks.rb +185 -0
  129. data/lib/roby/log/logger.rb +202 -0
  130. data/lib/roby/log/notifications.rb +244 -0
  131. data/lib/roby/log/plan_rebuilder.rb +470 -0
  132. data/lib/roby/log/relations.rb +1056 -0
  133. data/lib/roby/log/server.rb +550 -0
  134. data/lib/roby/log/sqlite.rb +47 -0
  135. data/lib/roby/log/timings.rb +164 -0
  136. data/lib/roby/plan-object.rb +247 -0
  137. data/lib/roby/plan.rb +762 -0
  138. data/lib/roby/planning.rb +13 -0
  139. data/lib/roby/planning/loops.rb +302 -0
  140. data/lib/roby/planning/model.rb +906 -0
  141. data/lib/roby/planning/task.rb +151 -0
  142. data/lib/roby/propagation.rb +562 -0
  143. data/lib/roby/query.rb +619 -0
  144. data/lib/roby/relations.rb +583 -0
  145. data/lib/roby/relations/conflicts.rb +70 -0
  146. data/lib/roby/relations/ensured.rb +20 -0
  147. data/lib/roby/relations/error_handling.rb +23 -0
  148. data/lib/roby/relations/events.rb +9 -0
  149. data/lib/roby/relations/executed_by.rb +193 -0
  150. data/lib/roby/relations/hierarchy.rb +239 -0
  151. data/lib/roby/relations/influence.rb +10 -0
  152. data/lib/roby/relations/planned_by.rb +63 -0
  153. data/lib/roby/robot.rb +7 -0
  154. data/lib/roby/standard_errors.rb +218 -0
  155. data/lib/roby/state.rb +5 -0
  156. data/lib/roby/state/events.rb +221 -0
  157. data/lib/roby/state/information.rb +55 -0
  158. data/lib/roby/state/pos.rb +110 -0
  159. data/lib/roby/state/shapes.rb +32 -0
  160. data/lib/roby/state/state.rb +353 -0
  161. data/lib/roby/support.rb +92 -0
  162. data/lib/roby/task-operations.rb +182 -0
  163. data/lib/roby/task.rb +1618 -0
  164. data/lib/roby/test/common.rb +399 -0
  165. data/lib/roby/test/distributed.rb +214 -0
  166. data/lib/roby/test/tasks/empty_task.rb +9 -0
  167. data/lib/roby/test/tasks/goto.rb +36 -0
  168. data/lib/roby/test/tasks/simple_task.rb +23 -0
  169. data/lib/roby/test/testcase.rb +519 -0
  170. data/lib/roby/test/tools.rb +160 -0
  171. data/lib/roby/thread_task.rb +87 -0
  172. data/lib/roby/transactions.rb +462 -0
  173. data/lib/roby/transactions/proxy.rb +292 -0
  174. data/lib/roby/transactions/updates.rb +139 -0
  175. data/plugins/fault_injection/History.txt +4 -0
  176. data/plugins/fault_injection/README.txt +37 -0
  177. data/plugins/fault_injection/Rakefile +18 -0
  178. data/plugins/fault_injection/TODO.txt +0 -0
  179. data/plugins/fault_injection/app.rb +52 -0
  180. data/plugins/fault_injection/fault_injection.rb +89 -0
  181. data/plugins/fault_injection/test/test_fault_injection.rb +84 -0
  182. data/plugins/subsystems/README.txt +40 -0
  183. data/plugins/subsystems/Rakefile +18 -0
  184. data/plugins/subsystems/app.rb +171 -0
  185. data/plugins/subsystems/test/app/README +24 -0
  186. data/plugins/subsystems/test/app/Rakefile +8 -0
  187. data/plugins/subsystems/test/app/config/app.yml +71 -0
  188. data/plugins/subsystems/test/app/config/init.rb +9 -0
  189. data/plugins/subsystems/test/app/config/roby.yml +3 -0
  190. data/plugins/subsystems/test/app/planners/main.rb +20 -0
  191. data/plugins/subsystems/test/app/scripts/distributed +3 -0
  192. data/plugins/subsystems/test/app/scripts/replay +3 -0
  193. data/plugins/subsystems/test/app/scripts/results +3 -0
  194. data/plugins/subsystems/test/app/scripts/run +3 -0
  195. data/plugins/subsystems/test/app/scripts/server +3 -0
  196. data/plugins/subsystems/test/app/scripts/shell +3 -0
  197. data/plugins/subsystems/test/app/scripts/test +3 -0
  198. data/plugins/subsystems/test/app/tasks/services.rb +15 -0
  199. data/plugins/subsystems/test/test_subsystems.rb +71 -0
  200. data/test/distributed/test_communication.rb +178 -0
  201. data/test/distributed/test_connection.rb +282 -0
  202. data/test/distributed/test_execution.rb +373 -0
  203. data/test/distributed/test_mixed_plan.rb +341 -0
  204. data/test/distributed/test_plan_notifications.rb +238 -0
  205. data/test/distributed/test_protocol.rb +516 -0
  206. data/test/distributed/test_query.rb +102 -0
  207. data/test/distributed/test_remote_plan.rb +491 -0
  208. data/test/distributed/test_transaction.rb +463 -0
  209. data/test/mockups/tasks.rb +27 -0
  210. data/test/planning/test_loops.rb +380 -0
  211. data/test/planning/test_model.rb +427 -0
  212. data/test/planning/test_task.rb +106 -0
  213. data/test/relations/test_conflicts.rb +42 -0
  214. data/test/relations/test_ensured.rb +38 -0
  215. data/test/relations/test_executed_by.rb +149 -0
  216. data/test/relations/test_hierarchy.rb +158 -0
  217. data/test/relations/test_planned_by.rb +54 -0
  218. data/test/suite_core.rb +24 -0
  219. data/test/suite_distributed.rb +9 -0
  220. data/test/suite_planning.rb +3 -0
  221. data/test/suite_relations.rb +8 -0
  222. data/test/test_bgl.rb +508 -0
  223. data/test/test_control.rb +399 -0
  224. data/test/test_event.rb +894 -0
  225. data/test/test_exceptions.rb +592 -0
  226. data/test/test_interface.rb +37 -0
  227. data/test/test_log.rb +114 -0
  228. data/test/test_log_server.rb +132 -0
  229. data/test/test_plan.rb +584 -0
  230. data/test/test_propagation.rb +210 -0
  231. data/test/test_query.rb +266 -0
  232. data/test/test_relations.rb +180 -0
  233. data/test/test_state.rb +414 -0
  234. data/test/test_support.rb +16 -0
  235. data/test/test_task.rb +938 -0
  236. data/test/test_testcase.rb +122 -0
  237. data/test/test_thread_task.rb +73 -0
  238. data/test/test_transactions.rb +569 -0
  239. data/test/test_transactions_proxy.rb +198 -0
  240. metadata +570 -0
@@ -0,0 +1,18 @@
1
+ rdoc_base_dir = Dir.pwd
2
+ if ENV['ROBY_ROOT_DIR']
3
+ $LOAD_PATH << File.join(ENV['ROBY_ROOT_DIR'], 'lib')
4
+ rdoc_base_dir = ENV['ROBY_ROOT_DIR']
5
+ end
6
+ require 'rake/rdoctask'
7
+ require 'roby/app/rake'
8
+
9
+ Rake::RDocTask.new("docs") do |rdoc|
10
+ rdoc.options << "--inline-source" << "--accessor" << "attribute" << "--accessor" << "attr_predicate"
11
+ rdoc.rdoc_dir = "#{rdoc_base_dir}/doc/rdoc/plugins/fault_injection"
12
+ rdoc.title = "Fault injection plugin for Roby"
13
+ rdoc.template = Roby::Rake.rdoc_template
14
+ rdoc.options << '--main' << 'README.txt'
15
+ rdoc.rdoc_files.include('README.txt', 'TODO.txt', 'History.txt')
16
+ rdoc.rdoc_files.include('fault_injection.rb', 'app.rb')
17
+ end
18
+
File without changes
@@ -0,0 +1,52 @@
1
+
2
+ module Roby
3
+ # Fault injection plugin for Roby
4
+ #
5
+ # Roby::FaultInjection::Application#fault_models defines a set of
6
+ # probabilistic models of emission for task events. It maps task model
7
+ # events to emission models, and during execution will randomly emit
8
+ # events.
9
+ #
10
+ # Fault injection applies only on tasks which are running and interruptible.
11
+ module FaultInjection
12
+ # This module gets included in Roby::Application when the plugin is activated
13
+ module Application
14
+ # The set of fault models defined for this application. More specifically,
15
+ # the emission model for an event +ev+ of a task model +model+ is given by
16
+ #
17
+ # emission_model = Roby.app.fault_models[model][ev]
18
+ #
19
+ # where +ev+ is a symbol.
20
+ attribute(:fault_models) do
21
+ Hash.new { |h, k| h[k] = Hash.new }
22
+ end
23
+
24
+ # call-seq:
25
+ # Roby.app.fault_model task_model, emission_model, ev1, ev2, ...
26
+ #
27
+ # Defines +emission_model+ as the emission model for the listed
28
+ # events of task +task_model+. +ev1+, +ev2+ are symbols.
29
+ #
30
+ # Emission models are objects which must respond to #fault?(task).
31
+ # If this predicate returns true for the running task +task+, then
32
+ # the emission of the tested event will be simulated. The emission
33
+ # models are tested every one second.
34
+ #
35
+ # See Roby::FaultInjection::Rate for an example.
36
+ def add_fault_model(task_model, *args)
37
+ fault_model = args.pop
38
+ args.each do |ev|
39
+ fault_models[task_model][ev.to_sym] = fault_model
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ Application.register_plugin('fault_injection', Roby::FaultInjection::Application) do
46
+ require 'fault_injection'
47
+ Roby.every(1) do
48
+ FaultInjection.apply(Roby.app.fault_models)
49
+ end
50
+ end
51
+ end
52
+
@@ -0,0 +1,89 @@
1
+ module Roby
2
+ class Task
3
+ # Returns for how many seconds this task is running. Returns nil if
4
+ # the task is not running.
5
+ def lifetime
6
+ if running?
7
+ Time.now - history.first.time
8
+ end
9
+ end
10
+ end
11
+
12
+ module FaultInjection
13
+ extend Logger::Hierarchy
14
+ extend Logger::Forward
15
+
16
+ # This fault model is based on a constant fault rate, defined upon the
17
+ # mean-time-to-failure of the task.
18
+ #
19
+ # In this model, the probability of having a fault for task t at any
20
+ # given instant is
21
+ #
22
+ # p(fault) = max * (1.0 - exp(- task.lifetime / mttf))
23
+ #
24
+ class Rate
25
+ attr_reader :mttf, :max
26
+ def initialize(mttf, max = 1.0)
27
+ @mttf, @max = mttf, max
28
+ end
29
+
30
+ def fault?(task)
31
+ f = max * (1.0 - Math.exp(- task.lifetime / mttf))
32
+ rand <= f
33
+ end
34
+ end
35
+
36
+ # Apply the given fault models to the main Roby plan
37
+ def self.apply(fault_models)
38
+ injected_faults = Array.new
39
+
40
+ for model, faults in fault_models
41
+ for ev, p in faults
42
+ Roby.plan.find_tasks(model).
43
+ running.not_finishing.
44
+ interruptible.each do |task|
45
+ if p.fault?(task)
46
+ FaultInjection.info "injecting fault #{ev} on #{task}"
47
+ injected_faults << [task, ev, task.inject_fault(ev)]
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ injected_faults
54
+ end
55
+ end
56
+
57
+ class Task
58
+ # Makes the plan behave as if this task had failed by emitting the
59
+ # event +event+ with context +context+.
60
+ #
61
+ # The task must be running and interruptible, and +event+ must be
62
+ # terminal. The default implementation creates a duplicate of this task
63
+ # by using #dup and forwards the +stop+ event of the old task to
64
+ # +event+ on the new one.
65
+ #
66
+ # It returns a new task which replaced this one in the plan. This is
67
+ # the task on which +event+ will actually be emitted.
68
+ def inject_fault(event, context = nil)
69
+ if !running?
70
+ raise ArgumentError, "this task is not running"
71
+ elsif finishing?
72
+ raise ArgumentError, "this task is being stopped"
73
+ elsif !interruptible?
74
+ raise ArgumentError, "fault injection works only on interruptible tasks"
75
+ elsif !event(event).terminal?
76
+ raise ArgumentError, "the injected fault must be a terminal event"
77
+ end
78
+
79
+ new_task = dup
80
+ old_task = self
81
+
82
+ plan.replace_task(old_task, new_task)
83
+ old_task.event(:stop).filter(context).forward new_task.event(event)
84
+
85
+ new_task
86
+ end
87
+ end
88
+ end
89
+
@@ -0,0 +1,84 @@
1
+ $LOAD_PATH.unshift File.expand_path( '..', File.dirname(__FILE__))
2
+ require 'test/unit'
3
+ require 'roby'
4
+ require 'roby/test/common'
5
+ require 'roby/test/tasks/simple_task'
6
+ require 'fault_injection'
7
+
8
+ class TC_FaultInjection < Test::Unit::TestCase
9
+ include Roby::Test
10
+
11
+ def test_inject_fault
12
+ Roby.control.run :detach => true
13
+
14
+ task = nil
15
+ Roby.execute do
16
+ model = Class.new(Roby::Test::SimpleTask) do
17
+ event :specialized_fault
18
+ forward :specialized_fault => :failed
19
+ end
20
+
21
+ plan.permanent(task = model.new)
22
+ assert_raises(ArgumentError) { task.inject_fault(:start) }
23
+
24
+ task.start!
25
+ end
26
+ Roby.wait_one_cycle
27
+
28
+ fake_task = nil
29
+ Roby.execute do
30
+ assert_raises(ArgumentError) { task.inject_fault(:updated_data) }
31
+ assert_nothing_raised { task.inject_fault(:specialized_fault) }
32
+ assert_equal(2, plan.known_tasks.size)
33
+ fake_task = plan.known_tasks.find { |t| t != task }
34
+ end
35
+ Roby.wait_one_cycle
36
+
37
+ assert(fake_task.finished?)
38
+ assert(fake_task.event(:specialized_fault).happened?)
39
+ end
40
+
41
+ def test_apply
42
+ Roby.control.run :detach => true
43
+
44
+ model = Class.new(Roby::Test::SimpleTask) do
45
+ event :specialized_fault
46
+ forward :specialized_fault => :failed
47
+ end
48
+
49
+ fault_models = Hash.new { |h, k| h[k] = Hash.new }
50
+ fault_models[model][:specialized_fault] = FaultInjection::Rate.new(0.01, 1.0)
51
+ fault_models[Roby::Test::SimpleTask][:stop] = FaultInjection::Rate.new(1_000_000, 1.0)
52
+
53
+ simple, specialized = nil
54
+ Roby.execute do
55
+ plan.permanent(simple = Roby::Test::SimpleTask.new)
56
+ plan.permanent(specialized = model.new)
57
+ simple.start!
58
+ specialized.start!
59
+ end
60
+ Roby.wait_one_cycle
61
+
62
+ sleep(0.5)
63
+ fake_specialized = nil
64
+ Roby.execute do
65
+ result = Roby::FaultInjection.apply(fault_models)
66
+ assert_equal(1, result.size)
67
+ fake_specialized = result.first.last
68
+ end
69
+ Roby.wait_one_cycle
70
+
71
+ assert(simple.running?)
72
+ assert(specialized.finished?)
73
+ assert(fake_specialized.finished?)
74
+ assert(fake_specialized.event(:specialized_fault).happened?)
75
+ end
76
+
77
+ def test_task_lifetime
78
+ plan.discover(task = Roby::Test::SimpleTask.new)
79
+ task.start!
80
+ sleep(0.5)
81
+ assert(task.lifetime > 0.5)
82
+ end
83
+ end
84
+
@@ -0,0 +1,40 @@
1
+
2
+ http://rubyforge.org/projects/roby
3
+
4
+ = Subsystems plugin for Roby
5
+
6
+ This is a plugin for the Roby robotic control framework. It allows to generate
7
+ an initial plan in which a set of common subsystems (sensors, localization,
8
+ ...) are already initialized, easing later the writing and management of the
9
+ planning methods for other, more dynamical actions.
10
+
11
+ == Usage
12
+
13
+ A set of services can be defined on <tt>State.services</tt> like this:
14
+
15
+ State.services.localization = 'pom'
16
+ State.services.camera = 'cam'
17
+
18
+ Then, at controller initialization and before the controller file is actually
19
+ loaded, the corresponding planning methods are called to generate a plan in which those
20
+ subsystems are present:
21
+
22
+ planner.localization(:id => 'pom')
23
+ planner.camera(:id => 'cam')
24
+
25
+ The returned tasks are also started, in order. The controller file gets loaded
26
+ only when the subsystems are ready (i.e. their start events are emitted).
27
+
28
+ == Enabling the plugin
29
+
30
+ The plugin must be enabled in the configuration files (i.e. either in
31
+ <tt>config/init.rb</tt> or in one of the robot-specific files) through a call
32
+ to Roby.app.using:
33
+
34
+ Roby.app.using 'subsystems'
35
+
36
+ == License:
37
+
38
+ This work is licensed under the CeCiLL-B license, which is a free software
39
+ license similar to the BSD license. See License.txt for details.
40
+
@@ -0,0 +1,18 @@
1
+ rdoc_base_dir = Dir.pwd
2
+ if ENV['ROBY_ROOT_DIR']
3
+ $LOAD_PATH << File.join(ENV['ROBY_ROOT_DIR'], 'lib')
4
+ rdoc_base_dir = ENV['ROBY_ROOT_DIR']
5
+ end
6
+ require 'rake/rdoctask'
7
+ require 'roby/app/rake'
8
+
9
+ Rake::RDocTask.new("docs") do |rdoc|
10
+ rdoc.options << "--inline-source" << "--accessor" << "attribute" << "--accessor" << "attr_predicate"
11
+ rdoc.rdoc_dir = "#{rdoc_base_dir}/doc/rdoc/plugins/subsystems"
12
+ rdoc.title = "Subsystems plugin for Roby"
13
+ rdoc.template = Roby::Rake.rdoc_template
14
+ rdoc.options << '--main' << 'README.txt'
15
+ rdoc.rdoc_files.include('README.txt')
16
+ rdoc.rdoc_files.include('app.rb')
17
+ end
18
+
@@ -0,0 +1,171 @@
1
+
2
+ module Roby
3
+ # Subsystem plugin for Roby
4
+ #
5
+ # This plugin manages a set of services that should be available at all
6
+ # times (for instance: localization). For now, it makes Roby start them at
7
+ # initialization time, allowing to consider that they are always available
8
+ # during plan generation afterwards.
9
+ #
10
+ # == Subsystems at initialization
11
+ # The set of subsystems that should be considered is to be set through the State.services
12
+ # configuration object:
13
+ #
14
+ # State.services do |sys|
15
+ # sys.localization = 'pom'
16
+ # sys.laser_ranges = 'sick'
17
+ # end
18
+ #
19
+ # The subsystem plugin will then use <tt>MainPlanner#subsystem_name(:id =>
20
+ # 'subsystem_selected')</tt> to generate the subsystems' plans. In the
21
+ # example above, the initial plan will be made using
22
+ #
23
+ # planner.localization(:id => 'pom')
24
+ # planner.laser_ranges(:id => 'sick')
25
+ #
26
+ # The resulting plan is then started one subsystem after the other
27
+ # <b>before</b> the controller is loaded
28
+ #
29
+ # == Subsystems and state update
30
+ #
31
+ # If the main tasks representing the subsystem -- the one returned by the
32
+ # planning methods, respond to update_<sysname>(state), that method is
33
+ # called, provided that the task is running, at every cycle with the
34
+ # system's state representation as an argument.
35
+ #
36
+ # For instance, the Localization task which represents the localization
37
+ # subsystems can have a #update_localization method which takes an
38
+ # argument. This method would update at each cycle the system's position in
39
+ # the state representation.
40
+ module Subsystems
41
+ module Application
42
+ def self.setup_main_planner
43
+ MainPlanner.class_eval do
44
+ Roby::State.services.each_member do |name, value|
45
+ filter(name) do |options, method|
46
+ options[:id] || method.id.to_s == value.to_s
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ # This method generates the initial plan built upon the set of
53
+ # required subsystems.
54
+ def self.initialize_plan
55
+ setup_main_planner
56
+ plan = Transaction.new(Roby.plan)
57
+
58
+ # Event which is emitted when all subsystems are properly
59
+ # initialized
60
+ ready = AndGenerator.new
61
+
62
+ # Create the one task for each subsystem. +ready+ is an event
63
+ # which is fired when all subsystems are properly initialized
64
+ planner = MainPlanner.new(plan)
65
+
66
+ tasks = Array.new
67
+ task_objects = ValueSet.new
68
+ State.services.each_member do |name, value|
69
+ next if name == 'tasks'
70
+ new_task = begin
71
+ planner.send(name)
72
+ rescue Planning::NotFound => e
73
+ raise RuntimeError, e.full_message
74
+ end
75
+
76
+ State.services.tasks.send("#{name}=", new_task)
77
+
78
+ plan.permanent(new_task)
79
+ started_with = if new_task.has_event?(:ready) then :ready
80
+ else :start
81
+ end
82
+
83
+ ready << new_task.event(started_with)
84
+
85
+ new_task.on(started_with) do
86
+ Robot.info "#{name} subsystem started (#{value})"
87
+ end
88
+ new_task.on(:stop) do
89
+ Robot.info "#{name} subsystem stopped (#{value})"
90
+ end
91
+
92
+ tasks << [name, value, new_task]
93
+ task_objects << new_task
94
+ end
95
+
96
+ # We make sure each subsystem is started only when all the
97
+ # other subsystems it depends on are started and ready.
98
+ # +start_with+ is the event which should be called to start
99
+ # everything
100
+ starting_point = EventGenerator.new(true)
101
+ tasks.each do |name, type, task|
102
+ children = task.generated_subgraph(TaskStructure::Hierarchy) & task_objects
103
+ children.delete(task)
104
+
105
+ ev = if children.empty?
106
+ starting_point
107
+ else
108
+ children.inject(AndGenerator.new) do |ev, child|
109
+ started_with = if child.has_event?(:ready) then :ready
110
+ else :start
111
+ end
112
+ ev << child.event(started_with)
113
+ end
114
+ end
115
+
116
+ ev.on { Robot.info "starting subsystem #{name} (#{type})" }
117
+ ev.on task.event(:start)
118
+ end
119
+
120
+ Roby.execute do
121
+ plan.commit_transaction
122
+ end
123
+ [starting_point, ready]
124
+ end
125
+
126
+ # Hook to generate the initial plan and start subsystems when the
127
+ # application starts.
128
+ def self.run(config, &block)
129
+ unless Roby::State.services? && !Roby::State.services.empty?
130
+ Robot.info "No subsystems defined"
131
+ return yield
132
+ end
133
+
134
+ Robot.info "Starting subsystems ..."
135
+
136
+ starting_point, ready = initialize_plan
137
+ # Start the deepest tasks. The signalling order will do the rest.
138
+ # The 'ready' event is emitted when all the subsystem tasks are
139
+ Roby.wait_until(ready) do
140
+ Roby.execute do
141
+ starting_point.call
142
+ end
143
+ end
144
+
145
+ yield
146
+ end
147
+ end
148
+ end
149
+
150
+ Application.register_plugin('subsystems', Roby::Subsystems::Application) do
151
+ # Call the #update_ methods for the subsystems tasks which define it.
152
+ Roby.each_cycle do
153
+ srv = Roby::State.services
154
+ srv.each_member do |name, value|
155
+ task = srv.tasks.send(name)
156
+ next unless task.running?
157
+ if task.has_event?(:ready)
158
+ next unless task.event(:ready).happened?
159
+ end
160
+
161
+ if task.respond_to?("update_#{name}")
162
+ begin
163
+ task.send("update_#{name}", State)
164
+ rescue Exception => e
165
+ Roby.warn "update_#{name} failed on #{task} with #{e}"
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end