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.
- data/.gitignore +29 -0
- data/History.txt +4 -0
- data/License-fr.txt +519 -0
- data/License.txt +515 -0
- data/Manifest.txt +245 -0
- data/NOTES +4 -0
- data/README.txt +163 -0
- data/Rakefile +161 -0
- data/TODO.txt +146 -0
- data/app/README.txt +24 -0
- data/app/Rakefile +8 -0
- data/app/config/ROBOT.rb +5 -0
- data/app/config/app.yml +91 -0
- data/app/config/init.rb +7 -0
- data/app/config/roby.yml +3 -0
- data/app/controllers/.gitattributes +0 -0
- data/app/controllers/ROBOT.rb +2 -0
- data/app/data/.gitattributes +0 -0
- data/app/planners/ROBOT/main.rb +6 -0
- data/app/planners/main.rb +5 -0
- data/app/scripts/distributed +3 -0
- data/app/scripts/generate/bookmarks +3 -0
- data/app/scripts/replay +3 -0
- data/app/scripts/results +3 -0
- data/app/scripts/run +3 -0
- data/app/scripts/server +3 -0
- data/app/scripts/shell +3 -0
- data/app/scripts/test +3 -0
- data/app/tasks/.gitattributes +0 -0
- data/app/tasks/ROBOT/.gitattributes +0 -0
- data/bin/roby +210 -0
- data/bin/roby-log +168 -0
- data/bin/roby-shell +25 -0
- data/doc/images/event_generalization.png +0 -0
- data/doc/images/exception_propagation_1.png +0 -0
- data/doc/images/exception_propagation_2.png +0 -0
- data/doc/images/exception_propagation_3.png +0 -0
- data/doc/images/exception_propagation_4.png +0 -0
- data/doc/images/exception_propagation_5.png +0 -0
- data/doc/images/replay_handler_error.png +0 -0
- data/doc/images/replay_handler_error_0.png +0 -0
- data/doc/images/replay_handler_error_1.png +0 -0
- data/doc/images/roby_cycle_overview.png +0 -0
- data/doc/images/roby_replay_02.png +0 -0
- data/doc/images/roby_replay_03.png +0 -0
- data/doc/images/roby_replay_04.png +0 -0
- data/doc/images/roby_replay_event_representation.png +0 -0
- data/doc/images/roby_replay_first_state.png +0 -0
- data/doc/images/roby_replay_relations.png +0 -0
- data/doc/images/roby_replay_startup.png +0 -0
- data/doc/images/task_event_generalization.png +0 -0
- data/doc/papers.rdoc +11 -0
- data/doc/styles/allison.css +314 -0
- data/doc/styles/allison.js +316 -0
- data/doc/styles/allison.rb +276 -0
- data/doc/styles/jamis.rb +593 -0
- data/doc/tutorials/01-GettingStarted.rdoc +86 -0
- data/doc/tutorials/02-GoForward.rdoc +220 -0
- data/doc/tutorials/03-PlannedPath.rdoc +268 -0
- data/doc/tutorials/04-EventPropagation.rdoc +236 -0
- data/doc/tutorials/05-ErrorHandling.rdoc +319 -0
- data/doc/tutorials/06-Overview.rdoc +40 -0
- data/doc/videos.rdoc +69 -0
- data/ext/droby/dump.cc +175 -0
- data/ext/droby/extconf.rb +3 -0
- data/ext/graph/algorithm.cc +746 -0
- data/ext/graph/extconf.rb +7 -0
- data/ext/graph/graph.cc +529 -0
- data/ext/graph/graph.hh +183 -0
- data/ext/graph/iterator_sequence.hh +102 -0
- data/ext/graph/undirected_dfs.hh +226 -0
- data/ext/graph/undirected_graph.hh +421 -0
- data/lib/roby.rb +41 -0
- data/lib/roby/app.rb +870 -0
- data/lib/roby/app/rake.rb +56 -0
- data/lib/roby/app/run.rb +14 -0
- data/lib/roby/app/scripts/distributed.rb +13 -0
- data/lib/roby/app/scripts/generate/bookmarks.rb +162 -0
- data/lib/roby/app/scripts/replay.rb +31 -0
- data/lib/roby/app/scripts/results.rb +15 -0
- data/lib/roby/app/scripts/run.rb +26 -0
- data/lib/roby/app/scripts/server.rb +18 -0
- data/lib/roby/app/scripts/shell.rb +88 -0
- data/lib/roby/app/scripts/test.rb +40 -0
- data/lib/roby/basic_object.rb +151 -0
- data/lib/roby/config.rb +5 -0
- data/lib/roby/control.rb +747 -0
- data/lib/roby/decision_control.rb +17 -0
- data/lib/roby/distributed.rb +32 -0
- data/lib/roby/distributed/base.rb +440 -0
- data/lib/roby/distributed/communication.rb +871 -0
- data/lib/roby/distributed/connection_space.rb +592 -0
- data/lib/roby/distributed/distributed_object.rb +206 -0
- data/lib/roby/distributed/drb.rb +62 -0
- data/lib/roby/distributed/notifications.rb +539 -0
- data/lib/roby/distributed/peer.rb +550 -0
- data/lib/roby/distributed/protocol.rb +529 -0
- data/lib/roby/distributed/proxy.rb +343 -0
- data/lib/roby/distributed/subscription.rb +311 -0
- data/lib/roby/distributed/transaction.rb +498 -0
- data/lib/roby/event.rb +897 -0
- data/lib/roby/exceptions.rb +234 -0
- data/lib/roby/executives/simple.rb +30 -0
- data/lib/roby/graph.rb +166 -0
- data/lib/roby/interface.rb +390 -0
- data/lib/roby/log.rb +3 -0
- data/lib/roby/log/chronicle.rb +303 -0
- data/lib/roby/log/console.rb +72 -0
- data/lib/roby/log/data_stream.rb +197 -0
- data/lib/roby/log/dot.rb +279 -0
- data/lib/roby/log/event_stream.rb +151 -0
- data/lib/roby/log/file.rb +340 -0
- data/lib/roby/log/gui/basic_display.ui +83 -0
- data/lib/roby/log/gui/chronicle.rb +26 -0
- data/lib/roby/log/gui/chronicle_view.rb +40 -0
- data/lib/roby/log/gui/chronicle_view.ui +70 -0
- data/lib/roby/log/gui/data_displays.rb +172 -0
- data/lib/roby/log/gui/data_displays.ui +155 -0
- data/lib/roby/log/gui/notifications.rb +26 -0
- data/lib/roby/log/gui/relations.rb +248 -0
- data/lib/roby/log/gui/relations.ui +123 -0
- data/lib/roby/log/gui/relations_view.rb +185 -0
- data/lib/roby/log/gui/relations_view.ui +149 -0
- data/lib/roby/log/gui/replay.rb +327 -0
- data/lib/roby/log/gui/replay_controls.rb +200 -0
- data/lib/roby/log/gui/replay_controls.ui +259 -0
- data/lib/roby/log/gui/runtime.rb +130 -0
- data/lib/roby/log/hooks.rb +185 -0
- data/lib/roby/log/logger.rb +202 -0
- data/lib/roby/log/notifications.rb +244 -0
- data/lib/roby/log/plan_rebuilder.rb +470 -0
- data/lib/roby/log/relations.rb +1056 -0
- data/lib/roby/log/server.rb +550 -0
- data/lib/roby/log/sqlite.rb +47 -0
- data/lib/roby/log/timings.rb +164 -0
- data/lib/roby/plan-object.rb +247 -0
- data/lib/roby/plan.rb +762 -0
- data/lib/roby/planning.rb +13 -0
- data/lib/roby/planning/loops.rb +302 -0
- data/lib/roby/planning/model.rb +906 -0
- data/lib/roby/planning/task.rb +151 -0
- data/lib/roby/propagation.rb +562 -0
- data/lib/roby/query.rb +619 -0
- data/lib/roby/relations.rb +583 -0
- data/lib/roby/relations/conflicts.rb +70 -0
- data/lib/roby/relations/ensured.rb +20 -0
- data/lib/roby/relations/error_handling.rb +23 -0
- data/lib/roby/relations/events.rb +9 -0
- data/lib/roby/relations/executed_by.rb +193 -0
- data/lib/roby/relations/hierarchy.rb +239 -0
- data/lib/roby/relations/influence.rb +10 -0
- data/lib/roby/relations/planned_by.rb +63 -0
- data/lib/roby/robot.rb +7 -0
- data/lib/roby/standard_errors.rb +218 -0
- data/lib/roby/state.rb +5 -0
- data/lib/roby/state/events.rb +221 -0
- data/lib/roby/state/information.rb +55 -0
- data/lib/roby/state/pos.rb +110 -0
- data/lib/roby/state/shapes.rb +32 -0
- data/lib/roby/state/state.rb +353 -0
- data/lib/roby/support.rb +92 -0
- data/lib/roby/task-operations.rb +182 -0
- data/lib/roby/task.rb +1618 -0
- data/lib/roby/test/common.rb +399 -0
- data/lib/roby/test/distributed.rb +214 -0
- data/lib/roby/test/tasks/empty_task.rb +9 -0
- data/lib/roby/test/tasks/goto.rb +36 -0
- data/lib/roby/test/tasks/simple_task.rb +23 -0
- data/lib/roby/test/testcase.rb +519 -0
- data/lib/roby/test/tools.rb +160 -0
- data/lib/roby/thread_task.rb +87 -0
- data/lib/roby/transactions.rb +462 -0
- data/lib/roby/transactions/proxy.rb +292 -0
- data/lib/roby/transactions/updates.rb +139 -0
- data/plugins/fault_injection/History.txt +4 -0
- data/plugins/fault_injection/README.txt +37 -0
- data/plugins/fault_injection/Rakefile +18 -0
- data/plugins/fault_injection/TODO.txt +0 -0
- data/plugins/fault_injection/app.rb +52 -0
- data/plugins/fault_injection/fault_injection.rb +89 -0
- data/plugins/fault_injection/test/test_fault_injection.rb +84 -0
- data/plugins/subsystems/README.txt +40 -0
- data/plugins/subsystems/Rakefile +18 -0
- data/plugins/subsystems/app.rb +171 -0
- data/plugins/subsystems/test/app/README +24 -0
- data/plugins/subsystems/test/app/Rakefile +8 -0
- data/plugins/subsystems/test/app/config/app.yml +71 -0
- data/plugins/subsystems/test/app/config/init.rb +9 -0
- data/plugins/subsystems/test/app/config/roby.yml +3 -0
- data/plugins/subsystems/test/app/planners/main.rb +20 -0
- data/plugins/subsystems/test/app/scripts/distributed +3 -0
- data/plugins/subsystems/test/app/scripts/replay +3 -0
- data/plugins/subsystems/test/app/scripts/results +3 -0
- data/plugins/subsystems/test/app/scripts/run +3 -0
- data/plugins/subsystems/test/app/scripts/server +3 -0
- data/plugins/subsystems/test/app/scripts/shell +3 -0
- data/plugins/subsystems/test/app/scripts/test +3 -0
- data/plugins/subsystems/test/app/tasks/services.rb +15 -0
- data/plugins/subsystems/test/test_subsystems.rb +71 -0
- data/test/distributed/test_communication.rb +178 -0
- data/test/distributed/test_connection.rb +282 -0
- data/test/distributed/test_execution.rb +373 -0
- data/test/distributed/test_mixed_plan.rb +341 -0
- data/test/distributed/test_plan_notifications.rb +238 -0
- data/test/distributed/test_protocol.rb +516 -0
- data/test/distributed/test_query.rb +102 -0
- data/test/distributed/test_remote_plan.rb +491 -0
- data/test/distributed/test_transaction.rb +463 -0
- data/test/mockups/tasks.rb +27 -0
- data/test/planning/test_loops.rb +380 -0
- data/test/planning/test_model.rb +427 -0
- data/test/planning/test_task.rb +106 -0
- data/test/relations/test_conflicts.rb +42 -0
- data/test/relations/test_ensured.rb +38 -0
- data/test/relations/test_executed_by.rb +149 -0
- data/test/relations/test_hierarchy.rb +158 -0
- data/test/relations/test_planned_by.rb +54 -0
- data/test/suite_core.rb +24 -0
- data/test/suite_distributed.rb +9 -0
- data/test/suite_planning.rb +3 -0
- data/test/suite_relations.rb +8 -0
- data/test/test_bgl.rb +508 -0
- data/test/test_control.rb +399 -0
- data/test/test_event.rb +894 -0
- data/test/test_exceptions.rb +592 -0
- data/test/test_interface.rb +37 -0
- data/test/test_log.rb +114 -0
- data/test/test_log_server.rb +132 -0
- data/test/test_plan.rb +584 -0
- data/test/test_propagation.rb +210 -0
- data/test/test_query.rb +266 -0
- data/test/test_relations.rb +180 -0
- data/test/test_state.rb +414 -0
- data/test/test_support.rb +16 -0
- data/test/test_task.rb +938 -0
- data/test/test_testcase.rb +122 -0
- data/test/test_thread_task.rb +73 -0
- data/test/test_transactions.rb +569 -0
- data/test/test_transactions_proxy.rb +198 -0
- metadata +570 -0
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
require 'drb'
|
|
2
|
+
require 'set'
|
|
3
|
+
require 'rinda/tuplespace'
|
|
4
|
+
require 'utilrb/value_set'
|
|
5
|
+
require 'roby_marshalling'
|
|
6
|
+
|
|
7
|
+
require 'roby'
|
|
8
|
+
|
|
9
|
+
class NilClass
|
|
10
|
+
def droby_dump(dest); nil end
|
|
11
|
+
end
|
|
12
|
+
class Array
|
|
13
|
+
def proxy(peer) # :nodoc:
|
|
14
|
+
map do |element|
|
|
15
|
+
catch(:ignore_this_call) { peer.proxy(element) }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
class Hash
|
|
20
|
+
def proxy(peer) # :nodoc:
|
|
21
|
+
inject({}) do |h, (k, v)|
|
|
22
|
+
h[peer.proxy(k)] = catch(:ignore_this_call) { peer.proxy(v) }
|
|
23
|
+
h
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
class Set
|
|
28
|
+
def proxy(peer) # :nodoc:
|
|
29
|
+
map do |element|
|
|
30
|
+
catch(:ignore_this_call) { peer.proxy(element) }
|
|
31
|
+
end.to_set
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
class ValueSet
|
|
35
|
+
def proxy(peer) # :nodoc:
|
|
36
|
+
map do |element|
|
|
37
|
+
catch(:ignore_this_call) { peer.proxy(element) }
|
|
38
|
+
end.to_value_set
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class Module
|
|
43
|
+
def droby_dump(dest)
|
|
44
|
+
raise "can't dump modules"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
class Class
|
|
48
|
+
def droby_dump(dest)
|
|
49
|
+
raise "can't dump class #{self}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
module Roby
|
|
54
|
+
class TaskModelTag
|
|
55
|
+
@@local_to_remote = Hash.new
|
|
56
|
+
@@remote_to_local = Hash.new
|
|
57
|
+
def self.local_to_remote; @@local_to_remote end
|
|
58
|
+
def self.remote_to_local; @@remote_to_local end
|
|
59
|
+
|
|
60
|
+
class DRoby
|
|
61
|
+
attr_reader :tagdef
|
|
62
|
+
def initialize(tagdef); @tagdef = tagdef end
|
|
63
|
+
def _dump(lvl); @__droby_marshalled__ ||= Marshal.dump(tagdef) end
|
|
64
|
+
def self._load(str); DRoby.new(Marshal.load(str)) end
|
|
65
|
+
|
|
66
|
+
def proxy(peer)
|
|
67
|
+
including = []
|
|
68
|
+
tagdef.each do |name, remote_tag|
|
|
69
|
+
tag = DRoby.local_tag(name, remote_tag) do |tag|
|
|
70
|
+
including.each { |mod| tag.include mod }
|
|
71
|
+
end
|
|
72
|
+
including << tag
|
|
73
|
+
end
|
|
74
|
+
including.last
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def self.local_tag(name, remote_tag)
|
|
78
|
+
if !remote_tag.kind_of?(Distributed::RemoteID)
|
|
79
|
+
remote_tag
|
|
80
|
+
elsif local_model = TaskModelTag.remote_to_local[remote_tag]
|
|
81
|
+
local_model
|
|
82
|
+
else
|
|
83
|
+
if name && !name.empty?
|
|
84
|
+
local_model = constant(name) rescue nil
|
|
85
|
+
end
|
|
86
|
+
unless local_model
|
|
87
|
+
local_model = Roby::TaskModelTag.new do
|
|
88
|
+
define_method(:name) { name }
|
|
89
|
+
end
|
|
90
|
+
TaskModelTag.remote_to_local[remote_tag] = local_model
|
|
91
|
+
TaskModelTag.local_to_remote[local_model] = [name, remote_tag]
|
|
92
|
+
yield(local_model) if block_given?
|
|
93
|
+
end
|
|
94
|
+
local_model
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def ==(other)
|
|
99
|
+
other.kind_of?(DRoby) &&
|
|
100
|
+
tagdef.zip(other.tagdef).all? { |a, b| a == b }
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def droby_dump(dest)
|
|
105
|
+
unless @__droby_marshalled__
|
|
106
|
+
tagdef = ancestors.map do |mod|
|
|
107
|
+
if mod.instance_of?(Roby::TaskModelTag)
|
|
108
|
+
unless id = TaskModelTag.local_to_remote[mod]
|
|
109
|
+
id = [mod.name, mod.remote_id]
|
|
110
|
+
end
|
|
111
|
+
id
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
tagdef.compact!
|
|
115
|
+
@__droby_marshalled__ = DRoby.new(tagdef.reverse)
|
|
116
|
+
end
|
|
117
|
+
@__droby_marshalled__
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
class TaskMatcher
|
|
122
|
+
# An intermediate representation of TaskMatcher objects suitable to be
|
|
123
|
+
# sent to our peers.
|
|
124
|
+
class DRoby
|
|
125
|
+
attr_reader :args
|
|
126
|
+
def initialize(args); @args = args end
|
|
127
|
+
def _dump(lvl) # :nodoc:
|
|
128
|
+
Marshal.dump(args)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def self._load(str) # :nodoc:
|
|
132
|
+
setup_matcher(TaskMatcher.new, Marshal.load(str))
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Common initialization of a TaskMatcher object from the given
|
|
136
|
+
# argument set. This is to be used by DRoby-dumped versions of
|
|
137
|
+
# subclasses of TaskMatcher.
|
|
138
|
+
def self.setup_matcher(matcher, args)
|
|
139
|
+
model, args, improves, needs, predicates, neg_predicates, owners = *args
|
|
140
|
+
model = model.proxy(nil) if model
|
|
141
|
+
owners = owners.map { |peer| peer.proxy(nil) } if owners
|
|
142
|
+
args = args
|
|
143
|
+
|
|
144
|
+
matcher = matcher.with_model(model).with_arguments(args || {}).
|
|
145
|
+
which_improves(*improves).which_needs(*needs)
|
|
146
|
+
matcher.predicates.merge(predicates)
|
|
147
|
+
matcher.owners.concat(owners)
|
|
148
|
+
matcher
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Returns an intermediate representation of +self+ suitable to be sent
|
|
153
|
+
# to the +dest+ peer. +klass+ is the actual class of the intermediate
|
|
154
|
+
# representation. It is used for code reuse by subclasses of
|
|
155
|
+
# TaskMatcher.
|
|
156
|
+
def droby_dump(dest, klass = DRoby)
|
|
157
|
+
args = [model, arguments, improved_information, needed_information, predicates, neg_predicates, owners]
|
|
158
|
+
klass.new args.droby_dump(dest)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
class Query
|
|
162
|
+
# An intermediate representation of Query objects suitable to be sent
|
|
163
|
+
# to our peers.
|
|
164
|
+
class DRoby # :nodoc:
|
|
165
|
+
attr_reader :plan_predicates, :neg_plan_predicates, :matcher
|
|
166
|
+
def initialize(plan_predicates, neg_plan_predicates, matcher)
|
|
167
|
+
@plan_predicates, @neg_plan_predicates, @matcher =
|
|
168
|
+
plan_predicates, neg_plan_predicates, matcher
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def _dump(lvl) # :nodoc:
|
|
172
|
+
Marshal.dump([plan_predicates, neg_plan_predicates, matcher])
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def self._load(str) # :nodoc:
|
|
176
|
+
DRoby.new(*Marshal.load(str))
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def to_query(plan)
|
|
180
|
+
query = TaskMatcher::DRoby.setup_matcher(plan.find_tasks, matcher)
|
|
181
|
+
query.plan_predicates.concat(plan_predicates)
|
|
182
|
+
query.neg_plan_predicates.concat(neg_plan_predicates)
|
|
183
|
+
query
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def proxy(peer)
|
|
187
|
+
to_query(peer.connection_space.plan)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Returns an intermediate representation of +self+ suitable to be sent
|
|
192
|
+
# to the +dest+ peer.
|
|
193
|
+
def droby_dump(dest)
|
|
194
|
+
marshalled_matcher = super
|
|
195
|
+
DRoby.new(plan_predicates, neg_plan_predicates, marshalled_matcher.args)
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
class OrTaskMatcher
|
|
200
|
+
# An intermediate representation of OrTaskMatcher objects suitable to
|
|
201
|
+
# be sent to our peers.
|
|
202
|
+
class DRoby < TaskMatcher::DRoby
|
|
203
|
+
def self._load(str) # :nodoc:
|
|
204
|
+
args = Marshal.load(str)
|
|
205
|
+
ops = args.pop
|
|
206
|
+
setup_matcher(OrTaskMatcher.new(*ops), args)
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Returns an intermediate representation of +self+ suitable to be sent
|
|
211
|
+
# to the +dest+ peer.
|
|
212
|
+
def droby_dump(dest)
|
|
213
|
+
m = super(dest, OrTaskMatcher::DRoby)
|
|
214
|
+
m.args << @ops
|
|
215
|
+
m
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
class AndTaskMatcher
|
|
219
|
+
# An intermediate representation of AndTaskMatcher objects suitable to
|
|
220
|
+
# be sent to our peers.
|
|
221
|
+
class DRoby < TaskMatcher::DRoby
|
|
222
|
+
def self._load(str) # :nodoc:
|
|
223
|
+
args = Marshal.load(str)
|
|
224
|
+
ops = args.pop
|
|
225
|
+
setup_matcher(AndTaskMatcher.new(*ops), args)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Returns an intermediate representation of +self+ suitable to be sent
|
|
230
|
+
# to the +dest+ peer.
|
|
231
|
+
def droby_dump(dest)
|
|
232
|
+
m = super(dest, AndTaskMatcher::DRoby)
|
|
233
|
+
m.args << @ops
|
|
234
|
+
m
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
class NegateTaskMatcher
|
|
238
|
+
# An intermediate representation of NegateTaskMatcher objects suitable to
|
|
239
|
+
# be sent to our peers.
|
|
240
|
+
class DRoby < TaskMatcher::DRoby
|
|
241
|
+
def self._load(str) # :nodoc:
|
|
242
|
+
args = Marshal.load(str)
|
|
243
|
+
op = args.pop
|
|
244
|
+
setup_matcher(NegateTaskMatcher.new(op), args)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Returns an intermediate representation of +self+ suitable to be sent
|
|
249
|
+
# to the +dest+ peer.
|
|
250
|
+
def droby_dump(dest)
|
|
251
|
+
m = super(dest, NegateTaskMatcher::DRoby)
|
|
252
|
+
m.args << @op
|
|
253
|
+
m
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
module Roby
|
|
259
|
+
module Distributed
|
|
260
|
+
# If set to true, enable some consistency-checking code in the
|
|
261
|
+
# communication code.
|
|
262
|
+
DEBUG_MARSHALLING = false
|
|
263
|
+
|
|
264
|
+
class Peer
|
|
265
|
+
# An intermediate representation of Peer objects suitable to be
|
|
266
|
+
# sent to our peers.
|
|
267
|
+
class DRoby # :nodoc:
|
|
268
|
+
attr_reader :name, :peer_id
|
|
269
|
+
def initialize(name, peer_id); @name, @peer_id = name, peer_id end
|
|
270
|
+
def hash; peer_id.hash end
|
|
271
|
+
def eql?(obj); obj.respond_to?(:peer_id) && peer_id == obj.peer_id end
|
|
272
|
+
alias :== :eql?
|
|
273
|
+
|
|
274
|
+
def to_s; "#<dRoby:Peer #{name} #{peer_id}>" end
|
|
275
|
+
def proxy(peer)
|
|
276
|
+
if peer = Distributed.peer(peer_id)
|
|
277
|
+
peer
|
|
278
|
+
else
|
|
279
|
+
raise "unknown peer ID #{peer_id}, known peers are #{Distributed.peers}"
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# Returns an intermediate representation of +self+ suitable to be sent
|
|
285
|
+
# to the +dest+ peer.
|
|
286
|
+
def droby_dump(dest = nil)
|
|
287
|
+
@__droby_marshalled__ ||= DRoby.new(remote_name, remote_id)
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# Dumps a constant by using its name. On reload, #proxy searches for a
|
|
292
|
+
# constant with the same name, and raises ArgumentError if none exists.
|
|
293
|
+
class DRobyConstant
|
|
294
|
+
@@valid_constants = Hash.new
|
|
295
|
+
def self.valid_constants; @@valid_constants end
|
|
296
|
+
def to_s; "#<dRoby:Constant #{name}>" end
|
|
297
|
+
|
|
298
|
+
# Generic implementation of the constant-dumping method. This is to
|
|
299
|
+
# be included in all kind of classes which should be dumped by their
|
|
300
|
+
# constant name (for intance RelationGraph).
|
|
301
|
+
module Dump
|
|
302
|
+
# Returns a DRobyConstant object which references +self+. It
|
|
303
|
+
# checks that +self+ can actually be referenced locally by
|
|
304
|
+
# calling <tt>constant(name)</tt>, or raises ArgumentError if
|
|
305
|
+
# it is not the case.
|
|
306
|
+
def droby_dump(dest)
|
|
307
|
+
unless DRobyConstant.valid_constants[self]
|
|
308
|
+
if const_obj = (constant(name) rescue nil)
|
|
309
|
+
DRobyConstant.valid_constants[self] = DRobyConstant.new(name)
|
|
310
|
+
else
|
|
311
|
+
raise ArgumentError, "invalid constant name #{obj.name}"
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
DRobyConstant.valid_constants[self]
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
# The constant name
|
|
319
|
+
attr_reader :name
|
|
320
|
+
def initialize(name); @name = name end
|
|
321
|
+
# Returns the local object which can be referenced by this name, or
|
|
322
|
+
# raises ArgumentError.
|
|
323
|
+
def proxy(peer); constant(name) end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
class Roby::RelationGraph
|
|
327
|
+
include Roby::Distributed::DRobyConstant::Dump
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Dumps a model (an event, task or planner class). When unmarshalling,
|
|
331
|
+
# it tries to search for the same model. If it does not find it, it
|
|
332
|
+
# rebuilds the same hierarchy using anonymous classes, basing itself on
|
|
333
|
+
# the less abstract class known to both the remote and local sides.
|
|
334
|
+
class DRobyModel
|
|
335
|
+
@@remote_to_local = Hash.new
|
|
336
|
+
@@local_to_remote = Hash.new
|
|
337
|
+
|
|
338
|
+
# A name -> class map which maps remote models to local anonymous classes
|
|
339
|
+
# Remote models are always identified by their name
|
|
340
|
+
def self.remote_to_local; @@remote_to_local end
|
|
341
|
+
# A class => ID object which maps the anonymous classes we built for remote
|
|
342
|
+
# models to the remote ID of these remote models
|
|
343
|
+
def self.local_to_remote; @@local_to_remote end
|
|
344
|
+
def to_s # :nodoc:
|
|
345
|
+
"#<dRoby:Model #{ancestors.first.first}"
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# Generic implementation of #droby_dump for all classes which
|
|
349
|
+
# should be marshalled as DRobyModel.
|
|
350
|
+
module Dump
|
|
351
|
+
# Creates a DRobyModel object which can be used to reference
|
|
352
|
+
# +self+ in the communication protocol. It properly takes into
|
|
353
|
+
# account the anonymous models we have created to map remote
|
|
354
|
+
# unknown models.
|
|
355
|
+
def droby_dump(dest)
|
|
356
|
+
unless @__droby_marshalled__
|
|
357
|
+
formatted = ancestors.map do |klass|
|
|
358
|
+
if klass.instance_of?(Class) && !klass.is_singleton?
|
|
359
|
+
if result = DRobyModel.local_to_remote[klass]
|
|
360
|
+
result
|
|
361
|
+
else
|
|
362
|
+
[klass.name, klass.remote_id]
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
formatted.compact!
|
|
367
|
+
@__droby_marshalled__ = DRobyModel.new(formatted)
|
|
368
|
+
end
|
|
369
|
+
@__droby_marshalled__
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# The set of ancestors for the model, as a [name, remote_id] array
|
|
374
|
+
attr_reader :ancestors
|
|
375
|
+
|
|
376
|
+
# Initialize a DRobyModel object with the given set of ancestors
|
|
377
|
+
def initialize(ancestors); @ancestors = ancestors end
|
|
378
|
+
def _dump(lvl) # :nodoc:
|
|
379
|
+
@__droby_marshalled__ ||= Marshal.dump(@ancestors)
|
|
380
|
+
end
|
|
381
|
+
def self._load(str) # :nodoc:
|
|
382
|
+
DRobyModel.new(Marshal.load(str))
|
|
383
|
+
end
|
|
384
|
+
# Returns a local Class object which maps the given model.
|
|
385
|
+
#
|
|
386
|
+
# See DRobyModel.local_model
|
|
387
|
+
def proxy(peer)
|
|
388
|
+
DRobyModel.local_model(ancestors.map { |name, id| [name, id.local_object] })
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
# True if the two objects reference the same model
|
|
392
|
+
def ==(other)
|
|
393
|
+
other.kind_of?(DRobyModel) &&
|
|
394
|
+
ancestors == other.ancestors
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
# Returns a local representation of the given model. If the model
|
|
398
|
+
# itself is known to us (i.e. there is a constant with the same
|
|
399
|
+
# name), it is returned. Otherwise, the model hierarchy is
|
|
400
|
+
# re-created using anonymous classes, branching the inheritance
|
|
401
|
+
# chain at a point commonly known between the local plan manager
|
|
402
|
+
# and the remote one.
|
|
403
|
+
def self.local_model(ancestors)
|
|
404
|
+
name, id = ancestors.shift
|
|
405
|
+
if !id.kind_of?(Distributed::RemoteID)
|
|
406
|
+
# this is a local task model
|
|
407
|
+
id
|
|
408
|
+
elsif !name.empty? && model = (constant(name) rescue nil)
|
|
409
|
+
model
|
|
410
|
+
elsif model = @@remote_to_local[id]
|
|
411
|
+
model
|
|
412
|
+
elsif !ancestors.empty?
|
|
413
|
+
parent_model = local_model(ancestors)
|
|
414
|
+
model = Class.new(parent_model) do
|
|
415
|
+
singleton_class.class_eval do
|
|
416
|
+
define_method(:remote_name) { name }
|
|
417
|
+
define_method(:name) { "AnonModel(#{remote_name})" }
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
@@remote_to_local[id] = model
|
|
421
|
+
@@local_to_remote[model] = [name, id]
|
|
422
|
+
|
|
423
|
+
model
|
|
424
|
+
else
|
|
425
|
+
raise ArgumentError, "cannot find a root class"
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
Roby::EventGenerator.extend Distributed::DRobyModel::Dump
|
|
430
|
+
Roby::Planning::Planner.extend Distributed::DRobyModel::Dump
|
|
431
|
+
|
|
432
|
+
# Dumping intermediate for Task classes. This dumps both the ancestor
|
|
433
|
+
# list via DRobyModel and the list of task tags.
|
|
434
|
+
class DRobyTaskModel < DRobyModel
|
|
435
|
+
# Set of task tags the task model was referring to
|
|
436
|
+
attr_reader :tags
|
|
437
|
+
# Create a DRobyTaskModel with the given tags and ancestor list
|
|
438
|
+
def initialize(tags, ancestors)
|
|
439
|
+
super(ancestors)
|
|
440
|
+
@tags = tags
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
# Generic implementation of #droby_dump for all classes which
|
|
444
|
+
# should be marshalled as DRobyTaskModel.
|
|
445
|
+
module Dump
|
|
446
|
+
include DRobyModel::Dump
|
|
447
|
+
|
|
448
|
+
# This augments DRobyModel::Dump#droby_dump by taking into
|
|
449
|
+
# account TaskModelTag modules in the ancestors list.
|
|
450
|
+
def droby_dump(dest)
|
|
451
|
+
unless @__droby_marshalled__
|
|
452
|
+
formatted_class = super
|
|
453
|
+
tags = ancestors.map do |mod|
|
|
454
|
+
if mod.instance_of?(Roby::TaskModelTag)
|
|
455
|
+
mod.droby_dump(dest)
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
tags.compact!
|
|
459
|
+
@__droby_marshalled__ = DRobyTaskModel.new(tags.reverse, formatted_class.ancestors)
|
|
460
|
+
end
|
|
461
|
+
@__droby_marshalled__
|
|
462
|
+
end
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
# True if +other+ describes the same task model than +self+
|
|
466
|
+
def ==(other)
|
|
467
|
+
super &&
|
|
468
|
+
tags == other.tags
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
def _dump(lvl) # :nodoc:
|
|
472
|
+
@__droby_marshalled__ ||= Marshal.dump([tags, ancestors])
|
|
473
|
+
end
|
|
474
|
+
def self._load(str) # :nodoc:
|
|
475
|
+
DRobyTaskModel.new(*Marshal.load(str))
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
# Returns or creates a Task-subclass which matches the task model
|
|
479
|
+
# described by this object
|
|
480
|
+
def proxy(peer)
|
|
481
|
+
model = super
|
|
482
|
+
tags.each do |tag|
|
|
483
|
+
tag = tag.proxy(nil)
|
|
484
|
+
model.include tag unless model < tag
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
model
|
|
488
|
+
end
|
|
489
|
+
end
|
|
490
|
+
Roby::Task.extend Distributed::DRobyTaskModel::Dump
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
Exception.extend Roby::Distributed::DRobyModel::Dump
|
|
495
|
+
class Exception
|
|
496
|
+
# An intermediate representation of Exception objects suitable to
|
|
497
|
+
# be sent to our peers.
|
|
498
|
+
class DRoby
|
|
499
|
+
attr_reader :model, :message
|
|
500
|
+
def initialize(model, message); @model, @message = model, message end
|
|
501
|
+
|
|
502
|
+
# Returns a local representation of the exception object +self+
|
|
503
|
+
# describes. If the real exception message is not available, it reuses
|
|
504
|
+
# the more-specific exception class which is available.
|
|
505
|
+
def proxy(peer)
|
|
506
|
+
error_model = model.proxy(peer)
|
|
507
|
+
error_model.exception(self.message)
|
|
508
|
+
|
|
509
|
+
rescue ArgumentError
|
|
510
|
+
# try to get a less-specific error model which does allow a simple
|
|
511
|
+
# message. In the worst case, we will fall back to Exception itself
|
|
512
|
+
#
|
|
513
|
+
# However, include the real model name in the message
|
|
514
|
+
message = "#{self.message} (#{model.ancestors.first.first})"
|
|
515
|
+
for model in error_model.ancestors
|
|
516
|
+
next unless model.kind_of?(Class)
|
|
517
|
+
begin
|
|
518
|
+
return model.exception(message)
|
|
519
|
+
rescue ArgumentError
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
end
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
# Returns an intermediate representation of +self+ suitable to be sent to
|
|
526
|
+
# the +dest+ peer.
|
|
527
|
+
def droby_dump(dest); DRoby.new(self.class.droby_dump(dest), message) end
|
|
528
|
+
end
|
|
529
|
+
|