roby 0.7

Sign up to get free protection for your applications and to get access to all the features.
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
+