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,70 @@
1
+
2
+ module Roby
3
+ module TaskStructure
4
+ Roby::Task.inherited_enumerable(:conflicting_model, :conflicting_models) { ValueSet.new }
5
+ module ModelConflicts
6
+ def conflicts_with(model)
7
+ conflicting_models << model
8
+ model.conflicting_models << self
9
+ end
10
+
11
+ def conflicts_with?(model)
12
+ each_conflicting_model do |m|
13
+ return true if m == model
14
+ end
15
+ false
16
+ end
17
+ end
18
+ Roby::Task.extend ModelConflicts
19
+
20
+ relation :Conflicts, :noinfo => true do
21
+ def conflicts_with(task)
22
+ task.event(:stop).add_precedence event(:start)
23
+ add_conflicts(task)
24
+ end
25
+
26
+ def self.included(klass) # :nodoc:
27
+ klass.extend ModelConflicts
28
+ super
29
+ end
30
+ end
31
+ end
32
+
33
+ module ConflictEventHandling
34
+ def calling(context)
35
+ super if defined? super
36
+ return unless symbol == :start
37
+
38
+ # Check for conflicting tasks
39
+ result = nil
40
+ task.each_conflicts do |conflicting_task|
41
+ result ||= ValueSet.new
42
+ result << conflicting_task
43
+ end
44
+
45
+ if result
46
+ Roby.decision_control.conflict(task, result)
47
+ end
48
+
49
+ # Add the needed conflict relations
50
+ models = task.class.conflicting_models
51
+ for model in models
52
+ if candidates = plan.task_index.by_model[model]
53
+ for t in candidates
54
+ t.conflicts_with task if t.pending?
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ def fired(event)
61
+ super if defined? super
62
+
63
+ if symbol == :stop
64
+ TaskStructure::Conflicts.remove(task)
65
+ end
66
+ end
67
+ end
68
+ Roby::TaskEventGenerator.include ConflictEventHandling
69
+ end
70
+
@@ -0,0 +1,20 @@
1
+ require 'roby/event'
2
+ module Roby::EventStructure
3
+ relation :EnsuredEvent, :noinfo => true do
4
+ def calling(context)
5
+ super if defined? super
6
+ each_ensured_event do |ev|
7
+ if !ev.happened?
8
+ postpone(ev, "waiting for ensured event #{ev}") do
9
+ ev.call(context) if ev.controlable?
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ def ensure(event)
16
+ add_ensured_event event
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,23 @@
1
+
2
+ module Roby::TaskStructure
3
+ class Roby::TaskEventGenerator
4
+ # Mark this event as being handled by the task +task+
5
+ def handle_with(repairing_task)
6
+ if !task.child_object?(repairing_task, ErrorHandling)
7
+ task.add_error_handler repairing_task, ValueSet.new
8
+ end
9
+
10
+ task[repairing_task, ErrorHandling] << symbol
11
+ end
12
+ end
13
+
14
+ relation :ErrorHandling, :child_name => :error_handler do
15
+ def failed_task
16
+ each_parent_object(ErrorHandling) do |task|
17
+ return task
18
+ end
19
+ nil
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,9 @@
1
+ require 'roby/event'
2
+
3
+ module Roby::EventStructure
4
+ relation :Signal, :noinfo => true
5
+ relation :Forwarding, :noinfo => true
6
+ relation :CausalLink, :subsets => [Signal, Forwarding], :noinfo => true
7
+ relation :Precedence, :subsets => [CausalLink], :noinfo => true
8
+ end
9
+
@@ -0,0 +1,193 @@
1
+ require 'roby/task'
2
+
3
+ module Roby::TaskStructure
4
+ # This module defines model-level definition of execution agent, for
5
+ # instance to Roby::Task
6
+ module ModelLevelExecutionAgent
7
+ # The model of execution agent for this class
8
+ def execution_agent
9
+ for klass in ancestors
10
+ if klass.instance_variable_defined?(:@execution_agent)
11
+ return klass.instance_variable_get(:@execution_agent)
12
+ end
13
+ end
14
+ nil
15
+ end
16
+
17
+ # Defines a model of execution agent. Doing
18
+ #
19
+ # TaskModel.executed_by ExecutionAgentModel
20
+ #
21
+ # is equivalent to
22
+ #
23
+ # task = TaskModel.new
24
+ # exec = <find a suitable ExecutionAgentModel instance in the plan or
25
+ # create a new one>
26
+ # task.executed_by exec
27
+ #
28
+ # for all instances of TaskModel. The actual job is done in the
29
+ # ExecutionAgentSpawn module
30
+ def executed_by(agent)
31
+ @execution_agent = agent
32
+ end
33
+ end
34
+
35
+ # The execution_agent defines an agent (process or otherwise) a given
36
+ # task is executed by. It allows to define a class of these execution agent,
37
+ # so that the specific agents are managed externally (load-balancing, ...)
38
+ relation :ExecutionAgent, :parent_name => :executed_task, :child_name => :execution_agent,
39
+ :noinfo => true, :distribute => false, :single_child => true do
40
+
41
+ # When ExecutionAgent support is included in a model (for instance Roby::Task), add
42
+ # the model-level classes
43
+ def self.included(klass) # :nodoc:
44
+ klass.extend Roby::TaskStructure::ModelLevelExecutionAgent
45
+ super
46
+ end
47
+
48
+ # Defines a new execution agent for this task.
49
+ def executed_by(agent)
50
+ return if execution_agent == agent
51
+ if !agent.event(:start).controlable? && !agent.running?
52
+ raise ArgumentError, "the start event of #{self}'s execution agent #{agent} is not controlable"
53
+ end
54
+ # Check that agent defines the :ready event
55
+ if !agent.has_event?(:ready)
56
+ raise ArgumentError, "execution agent tasks should define the :ready event"
57
+ end
58
+
59
+ old_agent = execution_agent
60
+ if old_agent && old_agent != agent
61
+ Roby.debug "an agent is already defined for this task"
62
+ remove_execution_agent old_agent
63
+ end
64
+
65
+ unless old_agent
66
+ # If the task did have an agent already, these event handlers
67
+ # are already set up
68
+ if running?
69
+ Roby::Distributed.update(self) do
70
+ agent.forward(:stop, self, :aborted)
71
+ end
72
+ else
73
+ on(:start) do
74
+ # The event handler will be called even if the
75
+ # execution agent has been removed. Check that there is
76
+ # actually an execution agent
77
+ if execution_agent
78
+ Roby::Distributed.update(self) do
79
+ execution_agent.forward(:stop, self, :aborted)
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ on(:stop) do
86
+ if execution_agent
87
+ Roby::Distributed.update(self) do
88
+ execution_agent.event(:stop).remove_forwarding event(:aborted)
89
+ remove_execution_agent execution_agent
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ add_execution_agent(agent)
96
+ end
97
+
98
+ end
99
+
100
+ class ExecutionAgentSpawningFailed < Roby::LocalizedError
101
+ attr_reader :agent_model, :error
102
+ def initialize(task, agent_model, error)
103
+ super(task)
104
+ @agent_model, @error = agent_model, error
105
+ end
106
+ end
107
+
108
+ # Add a suitable execution agent to +task+ if its model has a execution
109
+ # agent model (see ModelLevelExecutionAgent), either by reusing one
110
+ # that is already in the plan, or by creating a new one.
111
+ def ExecutionAgent.spawn(task)
112
+ agent_model = task.model.execution_agent
113
+ candidates = task.plan.find_tasks.
114
+ with_model(agent_model).
115
+ self_owned.
116
+ not_finished
117
+
118
+ agent = nil
119
+
120
+ if candidates.empty?
121
+ begin
122
+ agent = agent_model.new
123
+ agent.on(:stop) do
124
+ agent.each_executed_task do |task|
125
+ if task.running?
126
+ task.emit(:aborted, "execution agent #{self} failed")
127
+ elsif task.pending?
128
+ task.remove_execution_agent agent
129
+ spawn(task)
130
+ end
131
+ end
132
+ end
133
+ rescue Exception => e
134
+ Roby::Propagation.add_error(ExecutionAgentSpawningFailed.new(task, agent_model, e))
135
+ end
136
+ else
137
+ running, pending = candidates.partition { |t| t.running? }
138
+ agent = if running.empty? then pending.first
139
+ else running.first
140
+ end
141
+ end
142
+ task.executed_by agent
143
+ agent
144
+ end
145
+
146
+ # This module is hooked in Roby::TaskEventGenerator to check that a task
147
+ # which is being started has a suitable execution agent, and to start it if
148
+ # it's not the case
149
+ module ExecutionAgentStart
150
+ def calling(context)
151
+ super if defined? super
152
+ return unless symbol == :start
153
+ return unless agent = task.execution_agent
154
+
155
+ if agent.finished? || agent.finishing?
156
+ raise CommandFailed.new(nil, self), "task #{task} has an execution agent but it is dead"
157
+ elsif !agent.event(:ready).happened? && !agent.depends_on?(task)
158
+ postpone(agent.event(:ready), "spawning execution agent #{agent} for #{self}") do
159
+ if agent.pending?
160
+ agent.event(:ready).if_unreachable(true) do |reason|
161
+ self.emit_failed "execution agent #{agent} failed to initialize: #{reason}"
162
+ end
163
+ agent.start!
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
169
+ Roby::TaskEventGenerator.include ExecutionAgentStart
170
+
171
+ # This module is included in Roby::Plan to automatically add execution agents
172
+ # to tasks that require it and are discovered in the executable plan.
173
+ module ExecutionAgentSpawn
174
+ # Hook into plan discovery to add execution agents to new tasks.
175
+ # See ExecutionAgentSpawn.spawn
176
+ def discovered_tasks(tasks)
177
+ # For now, settle on adding the execution agents only in the
178
+ # main plan. Otherwise, it is possible that two transactions
179
+ # will try to add two different agents
180
+ #
181
+ # Note that it would be solved by plan merging ...
182
+ return unless executable?
183
+
184
+ for task in tasks
185
+ if !task.execution_agent && task.model.execution_agent && task.self_owned?
186
+ ExecutionAgent.spawn(task)
187
+ end
188
+ end
189
+ end
190
+ end
191
+ Roby::Plan.include ExecutionAgentSpawn
192
+ end
193
+
@@ -0,0 +1,239 @@
1
+ require 'roby/task'
2
+ require 'roby/control'
3
+ require 'set'
4
+
5
+ module Roby::TaskStructure
6
+ # Document-module: Hierarchy
7
+ relation :Hierarchy, :child_name => :child, :parent_name => :parent_task do
8
+ # True if +obj+ is a parent of this object in the hierarchy relation
9
+ # (+obj+ is realized by +self+)
10
+ def realizes?(obj); parent_object?(obj, Hierarchy) end
11
+ # True if +obj+ is a child of this object in the hierarchy relation
12
+ def realized_by?(obj); child_object?(obj, Hierarchy) end
13
+ # True if +obj+ can be reached through the Hierarchy relation by
14
+ # starting from this object
15
+ def depends_on?(obj)
16
+ generated_subgraph(Hierarchy).include?(obj)
17
+ end
18
+ # The set of parent objects in the Hierarchy relation
19
+ def parents; parent_objects(Hierarchy) end
20
+ # The set of child objects in the Hierarchy relation
21
+ def children; child_objects(Hierarchy) end
22
+
23
+
24
+ # Adds +task+ as a child of +self+ in the Hierarchy relation. The
25
+ # following options are allowed:
26
+ #
27
+ # success:: the list of success events. The default is [:success]
28
+ # failure:: the list of failing events. The default is [:failed]
29
+ # model:: a <tt>[task_model, arguments]</tt> pair which defines the task model the parent is expecting.
30
+ # The default value is to get these parameters from +task+
31
+ #
32
+ # The +success+ set describes the events of the child task that are
33
+ # _required_ by the parent task. More specifically, the child task
34
+ # remains useful for the parent task as long as none of these events are
35
+ # emitted. By default, it is the +success+ event. Of course, an error
36
+ # condition is encountered when all events of +success+ become
37
+ # unreachable. In addition, the relation is removed if the
38
+ # +remove_when_done+ flag is set to true (false by default).
39
+ #
40
+ # The +failure+ set describes the events of the child task which are an
41
+ # error condition from the parent task point of view.
42
+ #
43
+ # In both error cases, a +ChildFailedError+ exception is raised.
44
+ def realized_by(task, options = {})
45
+ options = validate_options options,
46
+ :model => [task.model, task.meaningful_arguments],
47
+ :success => [:success],
48
+ :failure => [:failed],
49
+ :remove_when_done => false
50
+
51
+ options[:success] = Array[*options[:success]]
52
+ options[:failure] = Array[*options[:failure]]
53
+
54
+ # Validate failure and success event names
55
+ options[:success].each { |ev| task.event(ev) }
56
+ options[:failure].each { |ev| task.event(ev) }
57
+
58
+ options[:model] = [options[:model], {}] unless Array === options[:model]
59
+ required_model, required_args = *options[:model]
60
+ if !task.fullfills?(required_model, required_args)
61
+ raise ArgumentError, "task #{task} does not fullfills the provided model #{options[:model]}"
62
+ end
63
+
64
+ add_child(task, options)
65
+ self
66
+ end
67
+
68
+ # Set up the event gathering needed by Hierarchy.check_structure
69
+ def added_child_object(child, relations, info) # :nodoc:
70
+ super if defined? super
71
+ if relations.include?(Hierarchy) && !respond_to?(:__getobj__) && !child.respond_to?(:__getobj__)
72
+ events = info[:success].map { |ev| child.event(ev) }
73
+ events.concat info[:failure].map { |ev| child.event(ev) }
74
+ Roby::EventGenerator.gather_events(Hierarchy.interesting_events, events)
75
+ end
76
+ end
77
+
78
+ # Return the set of this task children for which the :start event has
79
+ # no parent in CausalLinks
80
+ def first_children
81
+ result = ValueSet.new
82
+
83
+ generated_subgraph(Hierarchy).each do |task|
84
+ next if task == self
85
+ if task.event(:start).root?(Roby::EventStructure::CausalLink)
86
+ result << task
87
+ end
88
+ end
89
+ result
90
+ end
91
+
92
+ # The set of events that are needed by the parent tasks
93
+ def fullfilled_events
94
+ needed = ValueSet.new
95
+ each_parent_task do |parent|
96
+ needed.merge(parent[self, Hierarchy][:success])
97
+ end
98
+ needed
99
+ end
100
+
101
+ # Return [tags, arguments] where +tags+ is a list of task models which
102
+ # are required by the parent tasks of this task, and arguments the
103
+ # required arguments
104
+ #
105
+ # If there is a task class in the required models, it is always the
106
+ # first element of +tags+
107
+ def fullfilled_model
108
+ model, tags, arguments = Roby::Task, [], {}
109
+
110
+ each_parent_task do |parent|
111
+ m, a = parent[self, Hierarchy][:model]
112
+ if m.instance_of?(Roby::TaskModelTag)
113
+ tags << m
114
+ elsif m.has_ancestor?(model)
115
+ model = m
116
+ elsif !model.has_ancestor?(m)
117
+ raise "inconsistency in fullfilled models: #{model} and #{m} are incompatible"
118
+ end
119
+ a.merge!(arguments) do |old, new|
120
+ if old != new
121
+ raise "inconsistency in fullfilled models: #{old} and #{new}"
122
+ end
123
+ end
124
+ end
125
+
126
+ tags.unshift(model)
127
+ [tags, arguments]
128
+ end
129
+
130
+ # Remove all children that have successfully finished
131
+ def remove_finished_children
132
+ # We call #to_a to get a copy of children, since we will remove
133
+ # children in the block. Note that we can't use #delete_if here
134
+ # since #children is a relation enumerator (not the relation list
135
+ # itself)
136
+ children.to_a.each do |child|
137
+ success_events = self[child, Hierarchy][:success]
138
+ if success_events.any? { |ev| child.event(ev).happened? }
139
+ remove_child(child)
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ # Checks the structure of +plan+ w.r.t. the constraints of the hierarchy
146
+ # relations. It returns an array of ChildFailedError for all failed
147
+ # hierarchy relations
148
+ def Hierarchy.check_structure(plan)
149
+ result = []
150
+
151
+ events = Hierarchy.interesting_events
152
+ return result if events.empty? && failing_tasks.empty?
153
+
154
+ # Get the set of tasks for which a possible failure has been
155
+ # registered The tasks that are failing the hierarchy requirements
156
+ # are registered in Hierarchy.failing_tasks. The interesting_events
157
+ # set is cleared at cycle end (see below)
158
+ tasks = events.inject(failing_tasks) { |set, event| set << event.generator.task }
159
+ @failing_tasks = ValueSet.new
160
+ tasks.each do |child|
161
+ # Check if the task has been removed from the plan
162
+ next unless child.plan
163
+
164
+ has_error = false
165
+ child.each_parent_task do |parent|
166
+ next unless parent.self_owned?
167
+ next if parent.finished? || parent.finishing?
168
+
169
+ options = parent[child, Hierarchy]
170
+ success = options[:success]
171
+ failure = options[:failure]
172
+
173
+ if success.any? { |e| child.event(e).happened? }
174
+ if options[:remove_when_done]
175
+ parent.remove_child child
176
+ end
177
+ elsif failing_event = failure.find { |e| child.event(e).happened? }
178
+ result << Roby::ChildFailedError.new(parent, child.event(failing_event).last)
179
+ failing_tasks << child
180
+ end
181
+ end
182
+ end
183
+
184
+ events.clear
185
+ result
186
+ end
187
+
188
+ class << Hierarchy
189
+ # The set of events that have been fired in this cycle and are involved in a Hierarchy relation
190
+ attribute(:interesting_events) { Array.new }
191
+
192
+ # The set of tasks that are currently failing
193
+ attribute(:failing_tasks) { ValueSet.new }
194
+ end
195
+ end
196
+
197
+ module Roby
198
+ # This exception is raised when a {hierarchy relation}[classes/Roby/TaskStructure/Hierarchy.html] fails
199
+ class ChildFailedError < LocalizedError
200
+ # The parent in the relation
201
+ attr_reader :parent
202
+ # The child in the relation
203
+ def child; failed_task end
204
+ # The relation parameters (i.e. the hash given to #realized_by)
205
+ attr_reader :relation
206
+
207
+ # The event which is the cause of this error. This is either the task
208
+ # source of a failure event, or the reason why a positive event has
209
+ # become unreachable (if there is one)
210
+ def initialize(parent, event)
211
+ super(event.task_sources.find { true })
212
+ @parent = parent
213
+ @relation = parent[child, TaskStructure::Hierarchy]
214
+ end
215
+
216
+ def pretty_print(pp) # :nodoc:
217
+ super
218
+ pp.breakable
219
+ pp.breakable
220
+ pp.text "The failed relation is"
221
+ pp.breakable
222
+ pp.nest(2) do
223
+ pp.text " "
224
+ parent.pretty_print pp
225
+ pp.breakable
226
+ pp.text "realized_by "
227
+ child.pretty_print pp
228
+ end
229
+ end
230
+ def backtrace; [] end
231
+
232
+ # True if +obj+ is involved in this exception
233
+ def involved_plan_object?(obj)
234
+ super || obj == parent
235
+ end
236
+ end
237
+ Control.structure_checks << TaskStructure::Hierarchy.method(:check_structure)
238
+ end
239
+