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,206 @@
|
|
|
1
|
+
module Roby
|
|
2
|
+
module Distributed
|
|
3
|
+
# Module included in objects distributed across multiple pDBs
|
|
4
|
+
module DistributedObject
|
|
5
|
+
attribute(:mutex) { Mutex.new }
|
|
6
|
+
attribute(:synchro_call) { ConditionVariable.new }
|
|
7
|
+
|
|
8
|
+
# Makes this object owned by the local DB. This is equivalent to
|
|
9
|
+
# object.self_owned = true
|
|
10
|
+
def self_owned; self.self_owned = true end
|
|
11
|
+
|
|
12
|
+
# Adds or removes the local DB from the list of owners. This is
|
|
13
|
+
# equivalent to calling add_peer(Distributed) and
|
|
14
|
+
# remove_peer(Distributed)
|
|
15
|
+
def self_owned=(flag)
|
|
16
|
+
if flag then add_owner(Distributed)
|
|
17
|
+
else remove_owner(Distributed)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Add the Peer +peer+ to the list of owners
|
|
22
|
+
def add_owner(peer, distributed = true)
|
|
23
|
+
return if owners.include?(peer)
|
|
24
|
+
if distributed
|
|
25
|
+
if !self_owned?
|
|
26
|
+
raise OwnershipError, "not object owner"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
call_siblings(:add_owner, self, peer)
|
|
30
|
+
added_owner(peer)
|
|
31
|
+
else
|
|
32
|
+
owners << peer
|
|
33
|
+
if plan
|
|
34
|
+
plan.task_index.add_owner(self, peer)
|
|
35
|
+
end
|
|
36
|
+
Distributed.debug { "added owner to #{self}: #{owners.to_a}" }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
def added_owner(peer); super if defined? super end
|
|
40
|
+
|
|
41
|
+
# Removes +peer+ from the list of owners. Raises OwnershipError if
|
|
42
|
+
# there are modified tasks in this transaction which are owned by
|
|
43
|
+
# +peer+
|
|
44
|
+
def remove_owner(peer, distributed = true)
|
|
45
|
+
return unless owners.include?(peer)
|
|
46
|
+
|
|
47
|
+
if distributed
|
|
48
|
+
results = call_siblings(:prepare_remove_owner, self, peer)
|
|
49
|
+
if error = results.values.find { |error| error }
|
|
50
|
+
raise error
|
|
51
|
+
end
|
|
52
|
+
call_siblings(:remove_owner, self, peer)
|
|
53
|
+
else
|
|
54
|
+
owners.delete(peer)
|
|
55
|
+
removed_owner(peer)
|
|
56
|
+
if plan
|
|
57
|
+
plan.task_index.remove_owner(self, peer)
|
|
58
|
+
end
|
|
59
|
+
Distributed.debug { "removed owner to #{self}: #{owners.to_a}" }
|
|
60
|
+
end
|
|
61
|
+
nil
|
|
62
|
+
end
|
|
63
|
+
def prepare_remove_owner(peer); super if defined? super end
|
|
64
|
+
def removed_owner(peer); super if defined? super end
|
|
65
|
+
|
|
66
|
+
def owner=(peer)
|
|
67
|
+
add_owner(peer)
|
|
68
|
+
owners.each do |owner|
|
|
69
|
+
remove_owner(owner) unless owner == peer
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def call_siblings(m, *args)
|
|
75
|
+
Distributed.call_peers(updated_peers.dup << Distributed, m, *args)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def call_owners(*args) # :nodoc:
|
|
79
|
+
raise OwnershipError, "not owner" if !self_owned?
|
|
80
|
+
|
|
81
|
+
if owners.any? { |peer| !has_sibling_on?(peer) }
|
|
82
|
+
raise InvalidRemoteOperation, "cannot do #{args} if the object is not distributed on all its owners"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
Distributed.call_peers(owners, *args)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Calls +args+ on all peers and returns a { peer => return_value } hash
|
|
90
|
+
# of all the values returned by each peer
|
|
91
|
+
def self.call_peers(calling, m, *args)
|
|
92
|
+
Distributed.debug { "distributed call of #{m}(#{args}) on #{calling}" }
|
|
93
|
+
|
|
94
|
+
# This is a tricky procedure. Let's describe what is done here:
|
|
95
|
+
# * we send the required message to the peers listed in +calling+,
|
|
96
|
+
# and wait for all of them to have finished
|
|
97
|
+
# * since there is a coordination requirement, once a peer have
|
|
98
|
+
# processed its call we stop processing any of the messages it
|
|
99
|
+
# sends. We therefore block the RX thread of this peer using
|
|
100
|
+
# the block_communication condition variable
|
|
101
|
+
|
|
102
|
+
result = Hash.new
|
|
103
|
+
call_local = calling.include?(Distributed)
|
|
104
|
+
synchro, mutex = Roby.condition_variable(true)
|
|
105
|
+
|
|
106
|
+
mutex.synchronize do
|
|
107
|
+
waiting_for = calling.size
|
|
108
|
+
waiting_for -= 1 if call_local
|
|
109
|
+
|
|
110
|
+
calling.each do |peer|
|
|
111
|
+
next if peer == Distributed
|
|
112
|
+
|
|
113
|
+
callback = Proc.new do |peer_result|
|
|
114
|
+
mutex.synchronize do
|
|
115
|
+
result[peer] = peer.local_object(peer_result)
|
|
116
|
+
waiting_for -= 1
|
|
117
|
+
Distributed.debug { "reply for #{m}(#{args.join(", ")}) from #{peer}, #{waiting_for} remaining" }
|
|
118
|
+
if waiting_for == 0
|
|
119
|
+
synchro.broadcast
|
|
120
|
+
end
|
|
121
|
+
peer.disable_rx
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
peer.queue_call false, m, args, callback, Thread.current
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
unless waiting_for == 0
|
|
128
|
+
Distributed.debug "waiting for our peers to complete the call"
|
|
129
|
+
synchro.wait(mutex)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
if call_local
|
|
134
|
+
Distributed.debug "processing locally ..."
|
|
135
|
+
result[Distributed] = Distributed.call(m, *args)
|
|
136
|
+
end
|
|
137
|
+
result
|
|
138
|
+
|
|
139
|
+
ensure
|
|
140
|
+
for peer in calling
|
|
141
|
+
peer.enable_rx if peer != Distributed
|
|
142
|
+
end
|
|
143
|
+
Roby.return_condition_variable(synchro, mutex)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
class PeerServer
|
|
147
|
+
# Message sent when our remote peer requests that we create a local
|
|
148
|
+
# representation for one of its objects. It therefore creates a
|
|
149
|
+
# sibling for +marshalled_object+, which is a representation of a
|
|
150
|
+
# distributed object present on our peer.
|
|
151
|
+
#
|
|
152
|
+
# It calls #created_sibling on +marshalled_object+ with the new
|
|
153
|
+
# created sibling, to allow for specific operations to be done on
|
|
154
|
+
# it.
|
|
155
|
+
def create_sibling(marshalled_object)
|
|
156
|
+
object_remote_id = peer.remote_object(marshalled_object)
|
|
157
|
+
if sibling = peer.proxies[object_remote_id]
|
|
158
|
+
raise ArgumentError, "#{marshalled_object} has already a sibling (#{sibling})"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
sibling = marshalled_object.sibling(peer)
|
|
162
|
+
peer.subscriptions << object_remote_id
|
|
163
|
+
marshalled_object.created_sibling(peer, sibling)
|
|
164
|
+
nil
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Message received when +owner+ is a peer which now owns +object+
|
|
168
|
+
def add_owner(object, owner)
|
|
169
|
+
peer.local_object(object).add_owner(peer.local_object(owner), false)
|
|
170
|
+
nil
|
|
171
|
+
end
|
|
172
|
+
# Message received when +owner+ does not own +object+ anymore
|
|
173
|
+
def remove_owner(object, owner)
|
|
174
|
+
peer.local_object(object).remove_owner(peer.local_object(owner), false)
|
|
175
|
+
nil
|
|
176
|
+
end
|
|
177
|
+
# Message received before #remove_owner, to verify if the removal
|
|
178
|
+
# operation can be done or not.
|
|
179
|
+
def prepare_remove_owner(object, owner)
|
|
180
|
+
peer.local_object(object).prepare_remove_owner(peer.local_object(owner))
|
|
181
|
+
nil
|
|
182
|
+
rescue
|
|
183
|
+
$!
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
class Peer
|
|
188
|
+
# Creates a sibling for +object+ on the peer, and returns the corresponding
|
|
189
|
+
# DRbObject
|
|
190
|
+
def create_sibling(object)
|
|
191
|
+
unless object.kind_of?(DistributedObject)
|
|
192
|
+
raise TypeError, "cannot create a sibling for a non-distributed object"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
call(:create_sibling, object)
|
|
196
|
+
subscriptions << object.sibling_on(self)
|
|
197
|
+
Roby::Control.synchronize do
|
|
198
|
+
local_server.subscribe(object)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
synchro_point
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require 'rinda/rinda'
|
|
2
|
+
require 'rinda/ring'
|
|
3
|
+
|
|
4
|
+
require 'roby/distributed/protocol'
|
|
5
|
+
|
|
6
|
+
# This file contains extension to dRuby and Rinda classes which are needed to
|
|
7
|
+
# make Distributed Roby work
|
|
8
|
+
#
|
|
9
|
+
# Some are direct modification of the standard library (through reopening classes),
|
|
10
|
+
# others are made by subclassing the standard library.
|
|
11
|
+
|
|
12
|
+
module Rinda
|
|
13
|
+
class NotifyTemplateEntry
|
|
14
|
+
def pop(nonblock = false)
|
|
15
|
+
raise RequestExpiredError if @done
|
|
16
|
+
it = @queue.pop(nonblock) rescue nil
|
|
17
|
+
@done = true if it && it[0] == 'close'
|
|
18
|
+
return it
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module Roby::Distributed
|
|
24
|
+
# Reimplements Rinda::RingServer, removing the tuplespace intermediate and
|
|
25
|
+
# the creation of most threads. This is done for performance reasons.
|
|
26
|
+
class RingServer < Rinda::RingServer
|
|
27
|
+
attr_reader :bind, :port
|
|
28
|
+
|
|
29
|
+
# Added a :bind option
|
|
30
|
+
def initialize(ts, options = {})
|
|
31
|
+
options = validate_options options, :bind => Socket.gethostname, :port => Rinda::Ring_PORT
|
|
32
|
+
|
|
33
|
+
@bind = options[:bind]
|
|
34
|
+
@port = options[:port]
|
|
35
|
+
|
|
36
|
+
@ts = ts
|
|
37
|
+
@soc = UDPSocket.new
|
|
38
|
+
@soc.bind options[:bind], options[:port]
|
|
39
|
+
@service = service
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def service
|
|
43
|
+
Thread.new do
|
|
44
|
+
Thread.current.priority = 0
|
|
45
|
+
begin
|
|
46
|
+
loop do
|
|
47
|
+
msg = @soc.recv(1024)
|
|
48
|
+
tuple, timeout = Marshal.load(msg)
|
|
49
|
+
tuple[1].call(@ts) rescue nil
|
|
50
|
+
end
|
|
51
|
+
rescue Interrupt
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def close
|
|
57
|
+
@service.raise Interrupt, "interrupting RingServer"
|
|
58
|
+
@soc.close
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
module Roby
|
|
2
|
+
module Distributed
|
|
3
|
+
# Returns the set of edges for which both sides are in +objects+. The
|
|
4
|
+
# set if formatted as [object, relations, ...] where +relations+ is the
|
|
5
|
+
# output of relations_of
|
|
6
|
+
def self.subgraph_of(objects)
|
|
7
|
+
return [] if objects.size < 2
|
|
8
|
+
|
|
9
|
+
relations = []
|
|
10
|
+
|
|
11
|
+
objects = objects.dup
|
|
12
|
+
objects.delete_if do |obj|
|
|
13
|
+
obj_relations = relations_of(obj) do |related_object|
|
|
14
|
+
objects.include?(related_object)
|
|
15
|
+
end
|
|
16
|
+
relations << obj << obj_relations
|
|
17
|
+
true
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
relations
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# call-seq:
|
|
24
|
+
# relations_of(object) => relations
|
|
25
|
+
# relations_of(object) { |object| ... } => relations
|
|
26
|
+
#
|
|
27
|
+
# Relations to be sent to the remote host if +object+ is in a plan. The
|
|
28
|
+
# returned array if formatted as
|
|
29
|
+
# [ [graph, parents, children], [graph, ..] ]
|
|
30
|
+
# where +parents+ is the set of parents of +objects+ in +graph+ and
|
|
31
|
+
# +children+ the set of children
|
|
32
|
+
#
|
|
33
|
+
# +parents+ and +children+ are formatted as
|
|
34
|
+
# [object, info, object, info, ...]
|
|
35
|
+
#
|
|
36
|
+
# If a block is given, a new parent or child is added only if the block
|
|
37
|
+
# returns true
|
|
38
|
+
def self.relations_of(object)
|
|
39
|
+
result = []
|
|
40
|
+
# For transaction proxies, never send non-discovered relations to
|
|
41
|
+
# remote hosts
|
|
42
|
+
Roby::Distributed.each_object_relation(object) do |graph|
|
|
43
|
+
next unless graph.distribute?
|
|
44
|
+
parents = []
|
|
45
|
+
object.each_parent_object(graph) do |parent|
|
|
46
|
+
next unless parent.distribute?
|
|
47
|
+
next unless yield(parent) if block_given?
|
|
48
|
+
parents << parent << parent[object, graph]
|
|
49
|
+
end
|
|
50
|
+
children = []
|
|
51
|
+
object.each_child_object(graph) do |child|
|
|
52
|
+
next unless child.distribute?
|
|
53
|
+
next unless yield(child) if block_given?
|
|
54
|
+
children << child << object[child, graph]
|
|
55
|
+
end
|
|
56
|
+
result << graph << parents << children
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
result
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Set of hooks which send Plan updates to remote hosts
|
|
63
|
+
module PlanModificationHooks
|
|
64
|
+
# Hook called when a new task is marked as mission. It sends a
|
|
65
|
+
# PeerServer#plan_set_mission message to the remote host.
|
|
66
|
+
#
|
|
67
|
+
# Note that plan will have called the #discovered_tasks hook
|
|
68
|
+
# beforehand
|
|
69
|
+
def inserted(task)
|
|
70
|
+
super if defined? super
|
|
71
|
+
return unless task.distribute? && task.self_owned?
|
|
72
|
+
|
|
73
|
+
unless Distributed.updating?(self) || Distributed.updating?(task)
|
|
74
|
+
Distributed.each_updated_peer(self, task) do |peer|
|
|
75
|
+
peer.transmit(:plan_set_mission, self, task, true)
|
|
76
|
+
end
|
|
77
|
+
Distributed.trigger(task)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Hook called when a new task is not a mission anymore. It sends a
|
|
82
|
+
# PeerServer#plan_set_mission message to the remote host.
|
|
83
|
+
def discarded(task)
|
|
84
|
+
super if defined? super
|
|
85
|
+
return unless task.distribute? && task.self_owned?
|
|
86
|
+
|
|
87
|
+
unless Distributed.updating?(self) || Distributed.updating?(task)
|
|
88
|
+
Distributed.each_updated_peer(self, task) do |peer|
|
|
89
|
+
peer.transmit(:plan_set_mission, self, task, false)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Common implementation for the #discovered_events and
|
|
95
|
+
# #discovered_tasks hooks. It sends PeerServer#plan_discover for
|
|
96
|
+
# all tasks which can be shared among plan managers
|
|
97
|
+
def self.discovered_objects(plan, objects)
|
|
98
|
+
unless Distributed.updating?(plan)
|
|
99
|
+
relations = nil
|
|
100
|
+
Distributed.each_updated_peer(plan) do |peer|
|
|
101
|
+
# Compute +objects+ and +relations+ only if there is a
|
|
102
|
+
# peer to update
|
|
103
|
+
unless relations
|
|
104
|
+
objects = objects.find_all { |t| t.distribute? && t.self_owned? && t.root_object? && !Distributed.updating?(t) }
|
|
105
|
+
return if objects.empty?
|
|
106
|
+
relations = Distributed.subgraph_of(objects)
|
|
107
|
+
end
|
|
108
|
+
peer.transmit(:plan_discover, plan, objects, relations)
|
|
109
|
+
end
|
|
110
|
+
Distributed.trigger(*objects)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
# New tasks have been discovered in the plan.
|
|
114
|
+
#
|
|
115
|
+
# See PlanModificationHooks.discovered_objects
|
|
116
|
+
def discovered_tasks(tasks)
|
|
117
|
+
super if defined? super
|
|
118
|
+
PlanModificationHooks.discovered_objects(self, tasks)
|
|
119
|
+
end
|
|
120
|
+
# New free events have been discovered in the plan.
|
|
121
|
+
#
|
|
122
|
+
# See PlanModificationHooks.discovered_objects
|
|
123
|
+
def discovered_events(events)
|
|
124
|
+
super if defined? super
|
|
125
|
+
PlanModificationHooks.discovered_objects(self, events)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Hook called when +from+ has been replaced by +to+ in the plan.
|
|
129
|
+
# It sends a PeerServer#plan_replace message
|
|
130
|
+
def replaced(from, to)
|
|
131
|
+
super if defined? super
|
|
132
|
+
if (from.distribute? && to.distribute?) && (to.self_owned? || from.self_owned?)
|
|
133
|
+
unless Distributed.updating?(self) || Distributed.updating_all?([from, to])
|
|
134
|
+
Distributed.each_updated_peer(from) do |peer|
|
|
135
|
+
peer.transmit(:plan_replace, self, from, to)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Common implementation for the #finalized_task and
|
|
142
|
+
# PeerServer#finalized_event hooks. It sends the plan_remove_object message.
|
|
143
|
+
def self.finalized_object(plan, object)
|
|
144
|
+
return unless object.distribute? && object.root_object?
|
|
145
|
+
|
|
146
|
+
Distributed.keep.delete(object)
|
|
147
|
+
|
|
148
|
+
if object.self_owned?
|
|
149
|
+
Distributed.clean_triggered(object)
|
|
150
|
+
|
|
151
|
+
if !Distributed.updating?(plan)
|
|
152
|
+
Distributed.peers.each_value do |peer|
|
|
153
|
+
if peer.connected?
|
|
154
|
+
peer.transmit(:plan_remove_object, plan, object)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
if object.remotely_useful?
|
|
160
|
+
Distributed.removed_objects << object
|
|
161
|
+
end
|
|
162
|
+
else
|
|
163
|
+
object.remote_siblings.keys.each do |peer|
|
|
164
|
+
object.forget_peer(peer) unless peer == Roby::Distributed
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
# Hook called when a task has been removed from the plan.
|
|
169
|
+
#
|
|
170
|
+
# See PlanModificationHooks.finalized_object
|
|
171
|
+
def finalized_task(task)
|
|
172
|
+
super if defined? super
|
|
173
|
+
PlanModificationHooks.finalized_object(self, task)
|
|
174
|
+
end
|
|
175
|
+
# Hook called when a free event has been removed from the plan.
|
|
176
|
+
#
|
|
177
|
+
# See PlanModificationHooks.finalized_object
|
|
178
|
+
def finalized_event(event)
|
|
179
|
+
super if defined? super
|
|
180
|
+
PlanModificationHooks.finalized_object(self, event)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
Plan.include PlanModificationHooks
|
|
184
|
+
|
|
185
|
+
class PeerServer
|
|
186
|
+
# Message received when +task+ has become a mission (flag = true),
|
|
187
|
+
# or has become a non-mission (flag = false) in +plan+.
|
|
188
|
+
def plan_set_mission(plan, task, flag)
|
|
189
|
+
plan = peer.local_object(plan)
|
|
190
|
+
task = peer.local_object(task)
|
|
191
|
+
if plan.owns?(task)
|
|
192
|
+
if flag
|
|
193
|
+
plan.insert(task)
|
|
194
|
+
else
|
|
195
|
+
plan.discard(task)
|
|
196
|
+
end
|
|
197
|
+
else
|
|
198
|
+
task.mission = flag
|
|
199
|
+
end
|
|
200
|
+
nil
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Message received when the set of tasks +m_tasks+ has been
|
|
204
|
+
# discovered by the remote plan. +m_relations+ describes the
|
|
205
|
+
# internal relations between elements of +m_tasks+. It is in a
|
|
206
|
+
# format suitable for PeerServer#set_relations.
|
|
207
|
+
def plan_discover(plan, m_tasks, m_relations)
|
|
208
|
+
Distributed.update(plan = peer.local_object(plan)) do
|
|
209
|
+
tasks = peer.local_object(m_tasks).to_value_set
|
|
210
|
+
Distributed.update_all(tasks) do
|
|
211
|
+
plan.discover(tasks)
|
|
212
|
+
m_relations.each_slice(2) do |obj, rel|
|
|
213
|
+
set_relations(obj, rel)
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
nil
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Message received when +m_from+ has been replaced by +m_to+ in the
|
|
221
|
+
# plan
|
|
222
|
+
def plan_replace(plan, m_from, m_to)
|
|
223
|
+
Distributed.update(plan = peer.local_object(plan)) do
|
|
224
|
+
from, to = peer.local_object(m_from), peer.local_object(m_to)
|
|
225
|
+
|
|
226
|
+
Distributed.update_all([from, to]) { plan.replace(from, to) }
|
|
227
|
+
|
|
228
|
+
# Subscribe to the new task if the old task was subscribed
|
|
229
|
+
# +from+ will be unsubscribed when it is finalized
|
|
230
|
+
if peer.subscribed?(from) && !peer.subscribed?(to)
|
|
231
|
+
peer.subscribe(to)
|
|
232
|
+
nil
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
nil
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Message received when +object+ has been removed from +plan+
|
|
239
|
+
def plan_remove_object(plan, object)
|
|
240
|
+
if local = peer.local_object(object, false)
|
|
241
|
+
# Beware, transaction proxies have no 'plan' attribute
|
|
242
|
+
plan = peer.local_object(plan)
|
|
243
|
+
Distributed.update(plan) do
|
|
244
|
+
Distributed.update(local) do
|
|
245
|
+
plan.remove_object(local)
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
local.forget_peer(peer)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
rescue ArgumentError => e
|
|
252
|
+
if e.message =~ /has not been included in this plan/
|
|
253
|
+
Roby::Distributed.warn "filtering the 'not included in this plan bug'"
|
|
254
|
+
else
|
|
255
|
+
raise
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Message received when a relation graph has been updated. +op+ is
|
|
260
|
+
# either +add_child_object+ or +remove_child_object+ and describes
|
|
261
|
+
# what relation modification should be done. The two plan objects
|
|
262
|
+
# +m_from+ and +m_to+ are respectively linked or unlinked in the
|
|
263
|
+
# relation +m_rel+, with the given information object in case of a
|
|
264
|
+
# new relation.
|
|
265
|
+
def update_relation(plan, m_from, op, m_to, m_rel, m_info = nil)
|
|
266
|
+
if plan
|
|
267
|
+
Roby::Distributed.update(peer.local_object(plan)) { update_relation(nil, m_from, op, m_to, m_rel, m_info) }
|
|
268
|
+
else
|
|
269
|
+
from, to =
|
|
270
|
+
peer.local_object(m_from, false),
|
|
271
|
+
peer.local_object(m_to, false)
|
|
272
|
+
|
|
273
|
+
if !from
|
|
274
|
+
return unless to && (to.self_owned? || to.subscribed?)
|
|
275
|
+
from = peer.local_object(m_from)
|
|
276
|
+
elsif !to
|
|
277
|
+
return unless from && (from.self_owned? || from.subscribed?)
|
|
278
|
+
to = peer.local_object(m_to)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
rel = peer.local_object(m_rel)
|
|
282
|
+
Roby::Distributed.update_all([from.root_object, to.root_object]) do
|
|
283
|
+
if op == :add_child_object
|
|
284
|
+
from.add_child_object(to, rel, peer.local_object(m_info))
|
|
285
|
+
elsif op == :remove_child_object
|
|
286
|
+
from.remove_child_object(to, rel)
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
nil
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# This module defines the hooks needed to notify our peers of relation
|
|
295
|
+
# modifications. It is included in plan objects.
|
|
296
|
+
module RelationModificationHooks
|
|
297
|
+
# Hook called when a new relation is added. It sends the
|
|
298
|
+
# PeerServer#update_relation message.
|
|
299
|
+
def added_child_object(child, relations, info)
|
|
300
|
+
super if defined? super
|
|
301
|
+
|
|
302
|
+
return if Distributed.updating?(plan)
|
|
303
|
+
return if Distributed.updating_all?([self.root_object, child.root_object])
|
|
304
|
+
return unless Distributed.state
|
|
305
|
+
|
|
306
|
+
# Remove all relations that should not be distributed, and if
|
|
307
|
+
# there is a relation remaining, notify our peer only of the
|
|
308
|
+
# first one: this is the child of all others
|
|
309
|
+
if notified_relation = relations.find { |rel| rel.distribute? }
|
|
310
|
+
Distributed.each_updated_peer(self.root_object, child.root_object) do |peer|
|
|
311
|
+
peer.transmit(:update_relation, plan, self, :add_child_object, child, notified_relation, info)
|
|
312
|
+
end
|
|
313
|
+
Distributed.trigger(self, child)
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# Hook called when a relation is removed. It sends the
|
|
318
|
+
# PeerServer#update_relation message.
|
|
319
|
+
def removed_child_object(child, relations)
|
|
320
|
+
super if defined? super
|
|
321
|
+
return unless Distributed.state
|
|
322
|
+
|
|
323
|
+
# If our peer is pushing a distributed transaction, children
|
|
324
|
+
# can be removed Avoid sending unneeded updates by testing on
|
|
325
|
+
# plan update
|
|
326
|
+
return if Distributed.updating?(plan)
|
|
327
|
+
return if Distributed.updating_all?([self.root_object, child.root_object])
|
|
328
|
+
|
|
329
|
+
# Remove all relations that should not be distributed, and if
|
|
330
|
+
# there is a relation remaining, notify our peer only of the
|
|
331
|
+
# first one: this is the child of all others
|
|
332
|
+
if notified_relation = relations.find { |rel| rel.distribute? }
|
|
333
|
+
Distributed.each_updated_peer(self.root_object, child.root_object) do |peer|
|
|
334
|
+
peer.transmit(:update_relation, plan, self, :remove_child_object, child, notified_relation)
|
|
335
|
+
end
|
|
336
|
+
Distributed.trigger(self, child)
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
PlanObject.include RelationModificationHooks
|
|
341
|
+
|
|
342
|
+
# This module includes the hooks needed to notify our peers of event
|
|
343
|
+
# propagation (fired, forwarding and signalling)
|
|
344
|
+
module EventNotifications
|
|
345
|
+
# Hook called when an event has been emitted. It sends the
|
|
346
|
+
# PeerServer#event_fired message.
|
|
347
|
+
def fired(event)
|
|
348
|
+
super if defined? super
|
|
349
|
+
if self_owned? && !Distributed.updating?(root_object)
|
|
350
|
+
Distributed.each_updated_peer(root_object) do |peer|
|
|
351
|
+
peer.transmit(:event_fired, self, event.object_id, event.time, event.context)
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
# Hook called when an event is being forwarded. It sends the
|
|
356
|
+
# PeerServer#event_add_propagation message.
|
|
357
|
+
def forwarding(event, to)
|
|
358
|
+
super if defined? super
|
|
359
|
+
if self_owned? && !Distributed.updating?(root_object)
|
|
360
|
+
Distributed.each_updated_peer(root_object, to.root_object) do |peer|
|
|
361
|
+
peer.transmit(:event_add_propagation, true, self, to, event.object_id, event.time, event.context)
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
# Hook called when an event is being forwarded. It sends the
|
|
366
|
+
# PeerServer#event_add_propagation message.
|
|
367
|
+
def signalling(event, to)
|
|
368
|
+
super if defined? super
|
|
369
|
+
if self_owned? && !Distributed.updating?(root_object)
|
|
370
|
+
Distributed.each_updated_peer(root_object, to.root_object) do |peer|
|
|
371
|
+
peer.transmit(:event_add_propagation, false, self, to, event.object_id, event.time, event.context)
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# This module define hooks on Roby::Plan to manage the event fired
|
|
377
|
+
# cache. It is required by the receiving side of the event
|
|
378
|
+
# propagation distribution.
|
|
379
|
+
#
|
|
380
|
+
# See PeerServer#pending_events
|
|
381
|
+
module PlanCacheCleanup
|
|
382
|
+
# Removes events generated by +generator+ from the Event object
|
|
383
|
+
# cache, PeerServer#pending_events. This cache is used by
|
|
384
|
+
# PeerServer#event_for on behalf of PeerServer#event_fired and
|
|
385
|
+
# PeerServer#event_add_propagation
|
|
386
|
+
def finalized_event(generator)
|
|
387
|
+
super if defined? super
|
|
388
|
+
Distributed.peers.each_value do |peer|
|
|
389
|
+
peer.local_server.pending_events.delete(generator)
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
Roby::Plan.include PlanCacheCleanup
|
|
394
|
+
end
|
|
395
|
+
Roby::EventGenerator.include EventNotifications
|
|
396
|
+
|
|
397
|
+
class PeerServer
|
|
398
|
+
# A set of events which have been received by #event_fired. This
|
|
399
|
+
# cache in cleaned up by PlanCacheCleanup#finalized_event when the
|
|
400
|
+
# associated generator is finalized.
|
|
401
|
+
#
|
|
402
|
+
# This cache is used to merge the events between the firing step
|
|
403
|
+
# (event_fired) and the propagation steps (add_event_propagation).
|
|
404
|
+
# Without it, different Event objects at the various method calls.
|
|
405
|
+
attribute(:pending_events) { Hash.new }
|
|
406
|
+
|
|
407
|
+
# Creates an Event object for +generator+, with the given argument
|
|
408
|
+
# as parameters, or returns an already existing one
|
|
409
|
+
def event_for(generator, event_id, time, context)
|
|
410
|
+
id, event = pending_events[generator]
|
|
411
|
+
if id && id == event_id
|
|
412
|
+
return event
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
event = generator.new(context)
|
|
416
|
+
event.send(:time=, time)
|
|
417
|
+
if generator.respond_to?(:task)
|
|
418
|
+
generator.task.update_task_status(event)
|
|
419
|
+
end
|
|
420
|
+
pending_events[generator] = [event_id, event]
|
|
421
|
+
event
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
# Message received when the +marshalled_from+ generator fired an
|
|
425
|
+
# event, with the given event id, time and context.
|
|
426
|
+
def event_fired(marshalled_from, event_id, time, context)
|
|
427
|
+
from_generator = peer.local_object(marshalled_from)
|
|
428
|
+
context = peer.local_object(context)
|
|
429
|
+
|
|
430
|
+
event = event_for(from_generator, event_id, time, context)
|
|
431
|
+
|
|
432
|
+
event.send(:propagation_id=, Propagation.propagation_id)
|
|
433
|
+
from_generator.instance_variable_set("@happened", true)
|
|
434
|
+
from_generator.fired(event)
|
|
435
|
+
from_generator.call_handlers(event)
|
|
436
|
+
|
|
437
|
+
nil
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
# Message received when the +marshalled_from+ generator has either
|
|
441
|
+
# been forwarded (only_forward = true) or signals (only_forward =
|
|
442
|
+
# false) the +marshalled_to+ generator. The remaining information
|
|
443
|
+
# describes the event itself.
|
|
444
|
+
def event_add_propagation(only_forward, marshalled_from, marshalled_to, event_id, time, context)
|
|
445
|
+
from_generator = peer.local_object(marshalled_from)
|
|
446
|
+
to_generator = peer.local_object(marshalled_to)
|
|
447
|
+
context = peer.local_object(context)
|
|
448
|
+
|
|
449
|
+
event = event_for(from_generator, event_id, time, context)
|
|
450
|
+
|
|
451
|
+
# Only add the signalling if we own +to+
|
|
452
|
+
if to_generator.self_owned?
|
|
453
|
+
Propagation.add_event_propagation(only_forward, [event], to_generator, event.context, nil)
|
|
454
|
+
else
|
|
455
|
+
# Call #signalling or #forwarding to make
|
|
456
|
+
# +from_generator+ look like as if the event was really
|
|
457
|
+
# fired locally ...
|
|
458
|
+
Distributed.update_all([from_generator.root_object, to_generator.root_object]) do
|
|
459
|
+
if only_forward then from_generator.forwarding(event, to_generator)
|
|
460
|
+
else from_generator.signalling(event, to_generator)
|
|
461
|
+
end
|
|
462
|
+
end
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
nil
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
# This module defines the hooks required by dRoby on Roby::Task
|
|
470
|
+
module TaskNotifications
|
|
471
|
+
# Hook called when the internal task data is modified. It sends
|
|
472
|
+
# PeerServer#updated_data
|
|
473
|
+
def updated_data
|
|
474
|
+
super if defined? super
|
|
475
|
+
|
|
476
|
+
unless Distributed.updating?(self)
|
|
477
|
+
Distributed.each_updated_peer(self) do |peer|
|
|
478
|
+
peer.transmit(:updated_data, self, data)
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
Roby::Task.include TaskNotifications
|
|
484
|
+
|
|
485
|
+
# This module defines the hooks required by dRoby on Roby::TaskArguments
|
|
486
|
+
module TaskArgumentsNotifications
|
|
487
|
+
# Hook called when the task argumensts are modified. It sends
|
|
488
|
+
# the PeerServer#updated_arguments message.
|
|
489
|
+
def updated
|
|
490
|
+
super if defined? super
|
|
491
|
+
|
|
492
|
+
unless Distributed.updating?(task)
|
|
493
|
+
Distributed.each_updated_peer(task) do |peer|
|
|
494
|
+
peer.transmit(:updated_arguments, task, task.arguments)
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
end
|
|
499
|
+
TaskArguments.include TaskArgumentsNotifications
|
|
500
|
+
|
|
501
|
+
class PeerServer
|
|
502
|
+
# Message received to announce that the internal data of +task+ is
|
|
503
|
+
# now +data+.
|
|
504
|
+
def updated_data(task, data)
|
|
505
|
+
proxy = peer.local_object(task)
|
|
506
|
+
proxy.instance_variable_set("@data", peer.proxy(data))
|
|
507
|
+
nil
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
# Message received to announce that the arguments of +task+ have
|
|
511
|
+
# been modified. +arguments+ is a hash containing only the new
|
|
512
|
+
# values.
|
|
513
|
+
def updated_arguments(task, arguments)
|
|
514
|
+
proxy = peer.local_object(task)
|
|
515
|
+
arguments = peer.proxy(arguments)
|
|
516
|
+
Distributed.update(proxy) do
|
|
517
|
+
proxy.arguments.merge!(arguments || {})
|
|
518
|
+
end
|
|
519
|
+
nil
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
class PeerServer
|
|
524
|
+
# Message received to update our view of the remote robot state.
|
|
525
|
+
def state_update(new_state)
|
|
526
|
+
peer.state = new_state
|
|
527
|
+
nil
|
|
528
|
+
end
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
Roby::Control.at_cycle_end do
|
|
532
|
+
peers.each_value do |peer|
|
|
533
|
+
if peer.connected?
|
|
534
|
+
peer.transmit(:state_update, Roby::State)
|
|
535
|
+
end
|
|
536
|
+
end
|
|
537
|
+
end
|
|
538
|
+
end
|
|
539
|
+
end
|