Class: Syskit::Robot::RobotDefinition

Inherits:
Object
  • Object
show all
Includes:
MetaRuby::DSLs::FindThroughMethodMissing
Defined in:
lib/syskit/robot/robot_definition.rb

Overview

RobotDefinition objects describe a robot through the devices that are available on it.

Direct Known Subclasses

Actions::Profile::RobotDefinition

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeRobotDefinition

Returns a new instance of RobotDefinition



6
7
8
# File 'lib/syskit/robot/robot_definition.rb', line 6

def initialize
    @devices = Hash.new
end

Instance Attribute Details

#devicesObject (readonly)

The devices that are available on this robot



11
12
13
# File 'lib/syskit/robot/robot_definition.rb', line 11

def devices
  @devices
end

Instance Method Details

#clearObject



21
22
23
24
# File 'lib/syskit/robot/robot_definition.rb', line 21

def clear
    invalidate_dependency_injection
    devices.clear
end

#com_bus(type, options = Hash.new) ⇒ Object

Declares a new communication bus



61
62
63
# File 'lib/syskit/robot/robot_definition.rb', line 61

def com_bus(type, options = Hash.new)
    device(type, options.merge(expected_model: Syskit::ComBus, class: ComBus))
end

#device(device_model, doc: nil, **options) ⇒ Object

Adds a new device to this robot definition.

device_model is either the device type or its name. It is implicitely declared by the use of driver_for in component classes, or by using SystemModel#device_type.

For instance, if a Hokuyo orogen component is available that can drive Hokuyo laser scanners, then one would declare the driver with:

class Hokuyo
    driver_for 'Devices::Hokuyo'
end

the newly declared device type can then be accessed as a constant with Devices::Hokuyo. I.e.

Devices::Hokuyo

is the subclass of DeviceModel that describes this device type. It can then be used to declare devices on a robot with

Robot.devices do
  device Devices::Hokuyo
end

This method returns the MasterDeviceInstance instance that describes the actual device



135
136
137
138
139
140
141
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/syskit/robot/robot_definition.rb', line 135

def device(device_model, doc: nil, **options)
    options, device_options = Kernel.filter_options options,
        as: nil,
        using: nil,
        expected_model: Syskit::Device,
        class: MasterDeviceInstance
    device_options, root_task_arguments = Kernel.filter_options device_options,
        MasterDeviceInstance::KNOWN_PARAMETERS

    # Check for duplicates
    if !options[:as]
        raise ArgumentError, "no name given, please provide the :as option"
    end
    name = options[:as]
    if existing = devices[name]
        raise ArgumentError, "device '#{name}' is already defined: #{existing}"
    end

    # Verify that the provided device model matches what we expect
    if !(device_model < options[:expected_model])
        raise ArgumentError, "#{device_model} is not a #{options[:expected_model].short_name}"
    end

    # If the user gave us an explicit selection, honor it
    driver_model =
        begin options[:using] || device_model.default_driver
        rescue Ambiguous => e
            raise e, "#{e.message}, select one explicitely with the using: option of the 'device' statement", e.backtrace
        end

    if driver_model.respond_to?(:find_data_service_from_type)
        driver_model =
            begin driver_model.find_data_service_from_type(device_model)
            rescue Syskit::AmbiguousServiceSelection => e
                raise e, "#{e.message}, select one explicitly with the using: option of the 'device' statement", e.backtrace
            end
        if !driver_model
            raise ArgumentError, "#{options[:using]}, given as the using: option to create #{self}, is not a driver for #{device_model}"
        end
    end

    driver_model = driver_model.to_instance_requirements
    device_instance = options[:class].new(
        self, name, device_model, device_options,
        driver_model, root_task_arguments)
    invalidate_dependency_injection
    device_model.apply_device_configuration_extensions(device_instance)
    device_instance.doc(doc || MetaRuby::DSLs.parse_documentation_block(->(file) { Roby.app.app_file?(file) }, /^device$/))
    register_device(name, device_instance)

    # And register all the slave services there is on the driver
    driver_model.service.each_slave_data_service do |slave_service|
        slave_device = SlaveDeviceInstance.new(device_instance, slave_service)
        device_instance.slaves[slave_service.name] = slave_device
        register_device("#{name}.#{slave_service.name}", slave_device)
    end

    device_instance
end

#each_device(&block) ⇒ Object



199
200
201
# File 'lib/syskit/robot/robot_definition.rb', line 199

def each_device(&block)
    devices.each_value(&block)
end

#each_master_deviceObject

Enumerates all master devices that are available on this robot



204
205
206
207
208
# File 'lib/syskit/robot/robot_definition.rb', line 204

def each_master_device
    return enum_for(__method__) if !block_given?
    devices.find_all { |name, instance| instance.kind_of?(MasterDeviceInstance) }.
        each { |_, instance| yield(instance) }
end

#each_slave_deviceObject

Enumerates all slave devices that are available on this robot



211
212
213
214
215
# File 'lib/syskit/robot/robot_definition.rb', line 211

def each_slave_device
    return enum_for(__method__) if !block_given?
    devices.find_all { |name, instance| instance.kind_of?(SlaveDeviceInstance) }.
        each { |_, instance| yield(instance) }
end

#empty?Boolean

Returns:

  • (Boolean)


17
18
19
# File 'lib/syskit/robot/robot_definition.rb', line 17

def empty?
    devices.empty?
end

#find_device(name) ⇒ DeviceInstance?

Returns a device by its name

Returns:



74
75
76
# File 'lib/syskit/robot/robot_definition.rb', line 74

def find_device(name)
    devices[name]
end

#find_through_method_missing(m, args) ⇒ Object



244
245
246
247
# File 'lib/syskit/robot/robot_definition.rb', line 244

def find_through_method_missing(m, args)
    MetaRuby::DSLs.find_through_method_missing(
        self, m, args, '_dev'.freeze => :find_device) || super
end

#has_device?(name) ⇒ Boolean

Returns true if name is the name of a device registered on this robot model

Returns:

  • (Boolean)


67
68
69
# File 'lib/syskit/robot/robot_definition.rb', line 67

def has_device?(name)
    devices.has_key?(name.to_str)
end

#has_through_method_missing?(m) ⇒ Boolean

Returns:

  • (Boolean)


239
240
241
242
# File 'lib/syskit/robot/robot_definition.rb', line 239

def has_through_method_missing?(m)
    MetaRuby::DSLs.has_through_method_missing?(
        self, m, '_dev'.freeze => :has_device?) || super
end

#inject_di_context(requirements) ⇒ Object

Modify the raw requirements to add context information



14
15
# File 'lib/syskit/robot/robot_definition.rb', line 14

def inject_di_context(requirements)
end

#invalidate_dependency_injectionObject



217
218
219
# File 'lib/syskit/robot/robot_definition.rb', line 217

def invalidate_dependency_injection
    @di = nil
end

#register_device(name, device_instance) ⇒ Object



195
196
197
# File 'lib/syskit/robot/robot_definition.rb', line 195

def register_device(name, device_instance)
    devices[name] = device_instance
end

#through(com_bus, &block) ⇒ Object

Makes all devices declared in the provided block are using the given bus

For instance:

through 'can0' do
    device 'motors'
end

is equivalent to

device('motors').
    attach_to('can0')


92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/syskit/robot/robot_definition.rb', line 92

def through(com_bus, &block)
    if com_bus.respond_to?(:to_str)
        bus = find_device(com_bus.to_str)
        if !bus
            raise ArgumentError, "communication bus #{com_bus} does not exist"
        end
    end

    if !bus.respond_to?(:through)
        raise ArgumentError, "#{bus} is not a communication bus"
    end
    bus.through(&block)
    bus
end

#to_dependency_injectionObject

Returns a dependency injection object that maps names to devices



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/syskit/robot/robot_definition.rb', line 222

def to_dependency_injection
    if !@di
        result = DependencyInjection.new
        device_model_to_instance = Hash.new
        devices.each_value do |instance|
            if !device_model_to_instance.delete(instance.device_model)
                device_model_to_instance[instance.device_model] = instance
            end
        end
        # Register name-to-device mappings
        result.add(device_model_to_instance)
        result.resolve
        @di = result
    end
    return @di.dup
end

#use_robot(robot) ⇒ Object

Add the devices defined in the given robot to the ones defined in self.

If robot and self have devices with the same names, the ones in self take precedence



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/syskit/robot/robot_definition.rb', line 31

def use_robot(robot)
    return [] if robot.empty?

    if devices.empty?
        # Optimize the 'self is empty' codepath because it's
        # actually very common ... and it allows us to reuse the
        # caller's DI object
        @di = robot.to_dependency_injection
        @devices = robot.devices.dup
        return devices.values
    end

    new_devices = []
    robot.devices.each do |device_name, device|
        if !devices.has_key?(device_name)
            devices[device_name] = device
            new_devices << device
        end
    end
    if !new_devices.empty?
        invalidate_dependency_injection
    end
    new_devices

rescue Exception
    invalidate_dependency_injection
    raise
end