Class: Roby::DRoby::Marshal

Inherits:
Object show all
Defined in:
lib/roby/droby/marshal.rb

Overview

Handles marshalling and demarshalling objects for a given peer

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(object_manager = ObjectManager.new(nil), peer_id = nil, auto_create_plans: false) ⇒ Marshal

Returns a new instance of Marshal.



31
32
33
34
35
36
# File 'lib/roby/droby/marshal.rb', line 31

def initialize(object_manager = ObjectManager.new(nil), peer_id = nil, auto_create_plans: false)
    @object_manager = object_manager
    @peer_id = peer_id
    @context_objects = {}
    @auto_create_plans = auto_create_plans
end

Instance Attribute Details

#context_objectsObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Objects that are temporarily referenced by IDs

This is used by #dump_groups and #load_groups



16
17
18
# File 'lib/roby/droby/marshal.rb', line 16

def context_objects
  @context_objects
end

#object_managerObject (readonly)

The object that allows to match objects known locally with the objects transmitted by the peer



9
10
11
# File 'lib/roby/droby/marshal.rb', line 9

def object_manager
  @object_manager
end

#peer_idPeerID (readonly)

The ID of the peer that self handles

Returns:



21
22
23
# File 'lib/roby/droby/marshal.rb', line 21

def peer_id
  @peer_id
end

Instance Method Details

#dump(object) ⇒ Object

Dump an object for transmition to the peer



115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/roby/droby/marshal.rb', line 115

def dump(object)
    if droby_id = context_objects[object]
        droby_id
    elsif object.respond_to?(:droby_dump)
        if sibling = object_manager.registered_sibling_on(object, peer_id)
            RemoteDRobyID.new(peer_id, sibling)
        else
            object.droby_dump(self)
        end
    else
        object
    end
end

#dump_groups(*groups) ⇒ Object

Temporarily register sets of objects

Use this method to marshal sets of objects that could be referencing each other. Using this method ensures that the cross-references are marshalled using IDs instead of full objects



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/roby/droby/marshal.rb', line 55

def dump_groups(*groups)
    current_context = context_objects.dup
    mappings = groups.map do |collection|
        mapping = []
        collection.each do |obj|
            context_objects[obj] = obj.droby_id
            mapping << [obj.droby_id, obj]
        end
        mapping
    end

    marshalled = mappings.map do |collection|
        collection.flat_map do |obj_id, obj|
            [obj_id, obj.droby_dump(self)]
        end
    end

    if block_given?
        yield(*marshalled)
    else
        marshalled
    end
ensure
    context_objects.replace(current_context)
end

#dump_model(object) ⇒ Object



129
130
131
132
133
134
135
# File 'lib/roby/droby/marshal.rb', line 129

def dump_model(object)
    marshalled = dump(object)
    if !marshalled.kind_of?(RemoteDRobyID) && object.respond_to?(:droby_dump)
        register_model(object)
    end
    marshalled
end

#find_local_model(marshalled, name: marshalled.name) ⇒ Object

Find a known model matching the given name

It is first resolved among the models registered with #register_model and then resolved in the process constant hierarchy



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/roby/droby/marshal.rb', line 213

def find_local_model(marshalled, name: marshalled.name)
    resolved, local_model = find_local_object(marshalled)
    if resolved
        return local_model
    elsif name && (local_model = object_manager.find_model_by_name(name))
        return local_model
    elsif !name
        return
    end

    names = name.split("::")

    # Look locally for the constant listed in the name
    local_object = Object
    while subname = names.shift
        if subname =~ /^[A-Z]\w*$/ && local_object.const_defined_here?(subname)
            local_object = local_object.const_get(subname)
        else
            return
        end
    end
    local_object
end

#find_local_object(marshalled) ⇒ (Boolean,Object)

Finds a local object that matches the object transmitted by our peer

Returns:

  • ((Boolean,Object))

    whether the object was resolved and the unmarshalled object



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/roby/droby/marshal.rb', line 142

def find_local_object(marshalled)
    if local_object = context_objects[marshalled]
        [true, local_object]
    elsif marshalled.kind_of?(DRobyID)
        [true, object_manager.fetch_by_id(peer_id, marshalled)]
    elsif marshalled.kind_of?(RemoteDRobyID)
        [true, object_manager.fetch_by_id(marshalled.peer_id, marshalled.droby_id)]
    elsif marshalled.respond_to?(:remote_siblings)
        marshalled.remote_siblings.each do |peer_id, droby_id|
            if local_object = object_manager.find_by_id(peer_id, droby_id)
                # In case the remote siblings got updated since
                # last time
                object_manager.register_siblings(local_object, marshalled.remote_siblings)
                if marshalled.respond_to?(:update)
                    marshalled.update(self, local_object)
                end
                return true, local_object
            end
        end
        [false, nil]
    elsif !marshalled.respond_to?(:proxy)
        [true, marshalled]
    else
        [false, nil]
    end
end

#find_model_by_name(name) ⇒ Object?

Attempts to resolve a registered model by its name

In addition to ID-based resolution, models registered with #register_model can also be resolved by name.

This attempts a name-based resolution

Parameters:

  • name (String)

    the name of the model to resolve

Returns:



244
245
246
# File 'lib/roby/droby/marshal.rb', line 244

def find_model_by_name(name)
    object_manager.find_model_by_name(name)
end

#known_siblings_for(object) ⇒ Hash

The set of IDs known for this object

This returns a mapping from peer IDs to the ID of the provided object on this peer. The list of siblings is maintained by #register_object and #deregister_object

Parameters:

Returns:

  • (Hash)

    the siblings. A hash that announces the local ID is returned if the object is not registered, and an empty hash if it is not DRoby-addressable



259
260
261
# File 'lib/roby/droby/marshal.rb', line 259

def known_siblings_for(object)
    object_manager.known_siblings_for(object)
end

#load_groups(*groups) ⇒ Object

Load groups marshalled with #dump_groups



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/roby/droby/marshal.rb', line 82

def load_groups(*groups)
    current_context = context_objects.dup

    updates = []
    local_objects = groups.map do |collection|
        collection.each_slice(2).map do |obj_id, marshalled_obj|
            proxy = local_object(marshalled_obj)
            context_objects[obj_id] = proxy

            if marshalled_obj.respond_to?(:remote_siblings)
                object_manager.register_object(proxy, marshalled_obj.remote_siblings)
            end
            if marshalled_obj.respond_to?(:update)
                updates << [marshalled_obj, proxy]
            end
            proxy
        end
    end

    updates.each do |marshalled, local|
        marshalled.update(self, local, fresh_proxy: true)
    end

    if block_given?
        yield(*local_objects)
    else
        local_objects
    end
ensure
    context_objects.replace(current_context)
end

#local_model(marshalled, create: true) ⇒ Object



237
238
239
240
241
# File 'lib/roby/droby/marshal.rb', line 237

def local_model(marshalled, create: true)
    model = local_object(marshalled, create: create)
    object_manager.register_model(model)
    model
end

#local_object(marshalled, create: true) ⇒ Object

Resolves a marshalled object into a local object

Unlike #find_local_object, it raises if the object cannot be resolved



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/roby/droby/marshal.rb', line 173

def local_object(marshalled, create: true)
    resolved, local_object = find_local_object(marshalled)
    if resolved
        local_object
    elsif marshalled.respond_to?(:remote_siblings)
        unless create
            raise NoLocalObject, "#{marshalled} cannot be resolved into a local object and create is false"
        end

        local_object = marshalled.proxy(self)
        if local_object.respond_to?(:droby_id)
            object_manager.register_object(local_object, marshalled.remote_siblings)
        end
        if marshalled.respond_to?(:update)
            marshalled.update(self, local_object, fresh_proxy: true)
        end
        local_object
    elsif marshalled.respond_to?(:proxy)
        marshalled.proxy(self)
    else
        raise NoLocalObject, "#{marshalled} cannot be resolved into a local object"
    end
end

#local_plan(marshalled) ⇒ Object

Resolve an ID that is known to represent a plan

It calls #local_object by default, but can be overriden for e.g. environments where rebuilding a plan structure is not important (e.g. the shell)



202
203
204
205
206
# File 'lib/roby/droby/marshal.rb', line 202

def local_plan(marshalled)
    local_object(marshalled)
rescue UnknownSibling
    Plan.new
end

#register_model(local_model, known_siblings = {}, name: local_model.name) ⇒ Object

Register a model by name and a list of known siblings for it

In addition to ID-based resolution, models can also be resolved by name through #find_model_by_name. This registers the name mapping and then calls #register_object



254
255
256
# File 'lib/roby/droby/marshal.rb', line 254

def register_model(local_model, known_siblings = {}, name: local_model.name)
    object_manager.register_model(local_model, known_siblings, name: name)
end

#register_object(object, known_siblings = {}) ⇒ Object

Registers the mappings from object IDs to the corresponding local object

This registers the mapping for the local process (local_id => local_object.droby_id), along with known siblings if provided



249
250
251
# File 'lib/roby/droby/marshal.rb', line 249

def register_object(object, known_siblings = {})
    object_manager.register_object(object, known_siblings)
end

#with_object(id_to_object) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
# File 'lib/roby/droby/marshal.rb', line 38

def with_object(id_to_object)
    current_context = context_objects.dup
    id_to_object.each do |id, object|
        context_objects[id] = object
        context_objects[RemoteDRobyID.new(peer_id, id)] = object
    end

    yield
ensure
    context_objects.replace(current_context)
end