Class: OroGen::TypekitMarshallers::ROS::Plugin

Inherits:
Object
  • Object
show all
Defined in:
lib/orogen/marshallers/ros.rb

Overview

Typekit generation plugin to handle ROS types

Constant Summary collapse

ARRAY_TYPES =
[Typelib::ContainerType, Typelib::ArrayType]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(typekit) ⇒ Plugin


75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/orogen/marshallers/ros.rb', line 75

def initialize(typekit)
    @typekit = typekit
    @type_to_msg = DEFAULT_TYPE_TO_MSG.dup
    @boxed_msg_mappings = DEFAULT_BOXED_MSG_MAPPINGS.dup
    self.enabled = true

    Typelib::Type.extend TypeExtension
    Typelib::OpaqueType.extend OpaqueTypeExtension
    Typelib::ContainerType.extend ContainerTypeExtension
    Typelib::ArrayType.extend ArrayTypeExtension
    Typelib::CompoundType.extend CompoundTypeExtension
end

Instance Attribute Details

#boxed_msg_mappingsObject (readonly)

Mappings from ROS boxed messages to the base type they are wrapping

Base ROS types cannot be used as messages. One has to first box them into a std_msgs message. This registers these mappings. oroGen then always generate a convertion function for the boxed type whenever the type_to_msg specify a message that should be boxed.

See Also:


107
108
109
# File 'lib/orogen/marshallers/ros.rb', line 107

def boxed_msg_mappings
  @boxed_msg_mappings
end

#type_to_msgObject (readonly)

Mappings from oroGen types to ROS message names

See Also:


95
96
97
# File 'lib/orogen/marshallers/ros.rb', line 95

def type_to_msg
  @type_to_msg
end

#typekitObject (readonly)

The underlying typekit


182
183
184
# File 'lib/orogen/marshallers/ros.rb', line 182

def typekit
  @typekit
end

Class Method Details

.nameObject


72
# File 'lib/orogen/marshallers/ros.rb', line 72

def self.name; "ros" end

Instance Method Details

#boxed_ros_msg?(msg_name) ⇒ Boolean

Checks whether a ROS message name is a boxing type


116
117
118
# File 'lib/orogen/marshallers/ros.rb', line 116

def boxed_ros_msg?(msg_name)
    !!boxed_msg_mappings[msg_name]
end

#dependenciesObject

Generates the set of compilation dependencies for the ROS transport


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
# File 'lib/orogen/marshallers/ros.rb', line 149

def dependencies
    result = []
    typekit.used_typekits.each do |tk|
        next if tk.virtual?
        build_dep = Gen::RTT_CPP::BuildDependency.new(
            tk.name.upcase + "_TRANSPORT_ROS",
            tk.pkg_transport_name('ros'))
            build_dep.in_context('ros', 'include')
            build_dep.in_context('ros', 'link')
            result << build_dep
    end
    typekit.used_libraries.each do |pkg|
        needs_link = typekit.linked_used_libraries.include?(pkg)
        result << Gen::RTT_CPP::BuildDependency.new(pkg.name.upcase, pkg.name).
            in_context('ros', 'include')
        if needs_link
            result.last.in_context('ros', 'link')
        end
    end
    build_dep = Gen::RTT_CPP::BuildDependency.new(
        "OROCOS_RTT_ROS",
        "orocos-rtt-ros-${OROCOS_TARGET}")
    build_dep.in_context('ros', 'include')
    build_dep.in_context('ros', 'link')
    result << build_dep
    result
end

#enabled?Boolean


90
# File 'lib/orogen/marshallers/ros.rb', line 90

attr_predicate :enabled?, true

#generate(typesets) ⇒ Object

Do the code generation for this ROS transport


359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
# File 'lib/orogen/marshallers/ros.rb', line 359

def generate(typesets)
    load_ros_mappings

    @typekit = typekit

    rosmsg_registry = typesets.minimal_registry.dup

    # All the generated messages. Used to generate the CMake
    # file
    all_messages = Array.new
    typesets.converted_types.each do |type|
        # We are reusing an existing ROS message
        next if type_to_msg.has_key?(type.name)
        type_name = type.name
        msg_name  = ros_message_name(type)

        if ros_exported_type?(type) && !typekit.m_type?(type)
            if type.opaque?
                type = typekit.intermediate_type_for(type)
            end

            msg = Gen::RTT_CPP.render_template "typekit", "ros", "Type.msg", binding
            typekit.save_automatic("transports", "ros", "msg", "#{msg_name}.msg", msg)
            all_messages << msg_name
        end
    end

    all_messages = all_messages.sort

    convert_types = generate_type_convertion_list(
        typesets.converted_types.
            find_all { |t| !type_to_msg[t.name] && !typekit.m_type?(t)})
    convert_array_types = generate_type_convertion_list(
        typesets.array_types.map(&:deference).
            find_all { |t| !typekit.m_type?(t)})
    user_converted_types = generate_type_convertion_list(
        typesets.converted_types.
            find_all { |t| type_to_msg[t.name] })
    user_converted_types.delete_if do |type, ros_type|
        ros_cxx_type(type) == type.cxx_name.gsub(/^::/, '')
    end

    if !enabled?
        return generate_disabled(typesets, convert_types, convert_array_types, user_converted_types)
    end

    # Have a look the user_converted_types whether we need to
    # generate some unboxing functions automatically
    convert_boxed_types = user_converted_types.find_all do |type, ros_type|
        boxed_ros_msg?(ros_message_name(ros_type, true))
    end.to_set

    rejected = []
    code_snippets = typesets.interface_types.map do |type|
        if !ros_exported_type?(type)
            rejected << type
            next
        end

        if (mapped = type_to_msg[type.name]) && boxed_ros_msg?(mapped)
            convert_boxed_types << [type, type]
        end
        code  = Gen::RTT_CPP.render_template "typekit", "ros", "Type.cpp", binding
        [type, code]
    end.compact

    headers, impl = Array.new, Array.new

    convert_boxed_types = convert_boxed_types.sort_by { |t1, t2| [t1.name, t2.name] }
    code  = Gen::RTT_CPP.render_template "typekit", "ros", "Convertions.hpp", binding
    headers << typekit.save_automatic("transports", "ros",
                                      "Convertions.hpp", code)
    code  = Gen::RTT_CPP.render_template "typekit", "ros", "Convertions.cpp", binding
    impl << typekit.save_automatic("transports", "ros",
                                   "Convertions.cpp", code)

    code  = Gen::RTT_CPP.render_template "typekit", "ros", "TransportPlugin.hpp", binding
    headers << typekit.save_automatic("transports", "ros",
                                      "TransportPlugin.hpp", code)
    code  = Gen::RTT_CPP.render_template "typekit", "ros", "TransportPlugin.cpp", binding
    impl << typekit.save_automatic("transports", "ros",
                                   "TransportPlugin.cpp", code)
    if !user_converted_types.empty?
        # We need to generate a user part with the convertion
        # functions. Reuse the templates !
        
        code  = Gen::RTT_CPP.render_template "typekit", "ros", "ROSConvertions.hpp", binding
        headers << typekit.save_user("ROSConvertions.hpp", code)
        code  = Gen::RTT_CPP.render_template "typekit", "ros", "ROSConvertions.cpp", binding
        impl << typekit.save_user("ROSConvertions.cpp", code)
        Gen::RTT_CPP.create_or_update_symlink(
            headers.last, File.join(typekit.automatic_dir, "transports", "ros", "ROSConvertions.hpp"))
    end

    impl += typekit.render_typeinfo_snippets(code_snippets, "transports", "ros")

    if !rejected.empty?
        ROS.warn "#{rejected.size} types cannot be exported to ROS, you will not able to publish/subscribe ports of these types to/from ROS: #{rejected.map(&:name).sort.join(", ")}"
    end

    code  = Gen::RTT_CPP.render_template "typekit", "ros", "Registration.hpp", binding
    typekit.save_automatic("transports", "ros", "Registration.hpp", code)

    impl = impl.map do |path|
        typekit.cmake_relative_path(path, "transports", "ros")
    end.sort
    headers = headers.map do |path|
        typekit.cmake_relative_path(path, "transports", "ros")
    end.sort

    cmake_config = Gen::RTT_CPP.render_template "typekit", "ros", "config.cmake.in", binding
    typekit.save_automatic("transports", "ros", "orogen_#{typekit.name}_msgs-config.cmake.in", cmake_config)

    rosmap = Gen::RTT_CPP.render_template "typekit", "ros", "rosmap", binding
    rosmap = typekit.save_automatic("transports", "ros", "#{typekit.name}.rosmap", rosmap)

    pkg_config = Gen::RTT_CPP.render_template "typekit", "ros", "transport-ros.pc", binding
    typekit.save_automatic("transports", "ros", "#{typekit.name}-transport-ros.pc.in", pkg_config)

    cmake = Gen::RTT_CPP.render_template "typekit", "ros", "CMakeLists.txt", binding
    typekit.save_automatic("transports", "ros", "CMakeLists.txt", cmake)

    return [], []
end

#generate_disabled(typesets, convert_types, convert_array_types, user_converted_types) ⇒ Object


346
347
348
349
350
351
352
353
354
355
356
# File 'lib/orogen/marshallers/ros.rb', line 346

def generate_disabled(typesets, convert_types, convert_array_types, user_converted_types)
    rosmap = Gen::RTT_CPP.render_template "typekit", "ros", "rosmap", binding
    rosmap = typekit.save_automatic("transports", "ros", "#{typekit.name}.rosmap", rosmap)

    pkg_config = Gen::RTT_CPP.render_template "typekit", "ros", "transport-ros-disabled.pc", binding
    typekit.save_automatic("transports", "ros", "#{typekit.name}-transport-ros.pc.in", pkg_config)

    cmake = Gen::RTT_CPP.render_template "typekit", "ros", "CMakeLists-disabled.txt", binding
    typekit.save_automatic("transports", "ros", "CMakeLists.txt", cmake)
    return [], []
end

#generate_type_convertion_list(typeset) ⇒ Object


332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/orogen/marshallers/ros.rb', line 332

def generate_type_convertion_list(typeset)
    convert_types = Array.new
    typeset.each do |type|
        next if !ros_converted_type?(type)
        convert_types << [type, type]

        target_type = typekit.intermediate_type_for(type)
        if target_type != type
            convert_types << [target_type, type]
        end
    end
    convert_types.to_set.to_a.sort_by { |t0, t1| [t0.name, t1.name] }
end

#has_custom_convertions?Boolean

True if some custom oroGen-to-ROS convertions have been registered


111
112
113
# File 'lib/orogen/marshallers/ros.rb', line 111

def has_custom_convertions?
    !type_to_msg.empty?
end

#load_ros_mappingsObject


133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/orogen/marshallers/ros.rb', line 133

def load_ros_mappings
    typekit.used_typekits.each do |tk|
        next if tk.virtual?
        begin
            # Yuk ! Not dependent on the architecture, and
            # hardcoded pkg-config stuff behaviour
            raw_mapping = ROS.load_rosmap_by_package_name(tk.name)
            raw_mapping.delete_if { |type_name, _| !typekit.registry.include?(type_name) }
            ros_mappings(raw_mapping)
        rescue Utilrb::PkgConfig::NotFound
        end
    end
end

#multidimensional_array?(type) ⇒ Boolean


295
296
297
298
# File 'lib/orogen/marshallers/ros.rb', line 295

def multidimensional_array?(type)
    ARRAY_TYPES.any? { |array_t| type < array_t } &&
        ARRAY_TYPES.any? { |array_t| type.deference < array_t }
end

#nameObject


73
# File 'lib/orogen/marshallers/ros.rb', line 73

def name; "ros" end

#ros_arg_type(type, unbox = true) ⇒ Object

Returns the argument signature the C++ type for the ROS message that represents type


289
290
291
# File 'lib/orogen/marshallers/ros.rb', line 289

def ros_arg_type(type, unbox = true)
    "#{ros_cxx_type(type, unbox)} const&"
end

#ros_base_type?(type) ⇒ Boolean

Returns true if type is a base type in ROS


185
186
187
# File 'lib/orogen/marshallers/ros.rb', line 185

def ros_base_type?(type)
    type < Typelib::NumericType || type < Typelib::EnumType
end

#ros_converted_type?(type) ⇒ Boolean

Returns whether type should be exported as a ROS message


301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/orogen/marshallers/ros.rb', line 301

def ros_converted_type?(type)
    type = typekit.intermediate_type_for(type)
    return true if type_to_msg.has_key?(type.name)
    return if ros_base_type?(type)

    if type < Typelib::CompoundType
        ros_exported_type?(type)
    elsif multidimensional_array?(type)
        return false
    elsif ARRAY_TYPES.any? { |array_type| type < array_type }
        return (ros_converted_type?(type.deference) || type.deference <= Typelib::EnumType)
    else true
    end
end

#ros_cxx_type(type, do_unboxing = true) ⇒ Object

Returns the C++ type name for the ROS message that represents type

unboxed version of the message (i.e. std_msgs/Time would map to ros::Time). If false, the base message type name is kept


248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/orogen/marshallers/ros.rb', line 248

def ros_cxx_type(type, do_unboxing = true)
    explicit_mapping = type_to_msg[type.name]
    if !explicit_mapping && (type < Typelib::ArrayType || type < Typelib::ContainerType)
        return "std::vector< #{ros_cxx_type(type.deference, do_unboxing)} >"
    end

    msg_name = ros_message_name(type, true)
    if do_unboxing && (unboxed = boxed_msg_mappings[msg_name])
        msg_name = unboxed
    end

    if type < Typelib::NumericType && do_unboxing
        if type.integer?
            if msg_name == "bool" then "bool"
            else "boost::#{msg_name}_t"
            end
        elsif type.size == 4
            return "float"
        elsif type.size == 8
            return "double"
        else raise ArgumentError, "don't know what to use to represent #{type} on the ROS C++ mapping"
        end
    elsif type < Typelib::EnumType
        return "boost::int32_t"
    elsif msg_name == "time"
        return "ros::Time"
    elsif msg_name == 'string'
        return 'std::string'
    else
        return msg_name.gsub(/\//, '::')
    end
end

#ros_exported_type?(type) ⇒ Boolean

Returns whether type should be exported as a ROS message


317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/orogen/marshallers/ros.rb', line 317

def ros_exported_type?(type)
    type = typekit.intermediate_type_for(type)
    return true if type_to_msg.has_key?(type.name)
    return if ros_base_type?(type)
    return if !(type < Typelib::CompoundType)
    # ROS cannot represent arrays in arrays. Filter those out
    type.recursive_dependencies.each do |t|
        next if type_to_msg.has_key?(t.name)
        if multidimensional_array?(t)
            return
        end
    end
    return true
end

#ros_field_name(type, absolute = false) ⇒ Object

Returns the type name that should be used in a field in another ROS message

For “normal” messages, this is the message name. For boxed messages (e.g. std_msgs/Time), it is the corresponding base type


195
196
197
198
199
200
201
202
# File 'lib/orogen/marshallers/ros.rb', line 195

def ros_field_name(type, absolute = false)
    if !type_to_msg.has_key?(type.name) && (type <= Typelib::ArrayType || type <= Typelib::ContainerType)
        "#{ros_field_name(type.deference, absolute)}[]"
    else
        msg_name = ros_message_name(type, absolute)
        boxed_msg_mappings[msg_name] || msg_name
    end
end

#ros_mappings(mappings) ⇒ Object

Define custom mappings from typegen types to ROS messages

known to oroGen to the ROS message that should be used to represent it on the ROS side. The ROS message name should be fully qualified


126
127
128
129
130
131
# File 'lib/orogen/marshallers/ros.rb', line 126

def ros_mappings(mappings)
    mappings.each do |typename, ros_msg_name|
        type = typekit.find_type(typename)
        type_to_msg[type.name] = ros_msg_name
    end
end

#ros_message_name(type, absolute = false) ⇒ Object

Returns the ROS message name for type

always contain its namespace. Otherwise, it contains its namespace only if not generated by typekit. This is required by the ROS message generator


222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/orogen/marshallers/ros.rb', line 222

def ros_message_name(type, absolute = false)
    if type < Typelib::EnumType
        "std_msgs/Int32"
    elsif msg_name = type_to_msg[type.name]
        return msg_name
    elsif type <= Typelib::ArrayType || type <= Typelib::ContainerType
        "#{ros_message_name(type.deference, absolute)}[]"
    else
        source_typekit = typekit.imported_typekits_for(type.name).first
        if source_typekit || absolute
            prefix = "#{ros_package_name_for_typekit(source_typekit || typekit)}/"
        end
        "#{prefix}" + type.name.
            split("/").
            map { |n| "#{n[0, 1].upcase}#{n[1..-1]}" }.
            join("").
            gsub(/[^\w]/, "")
    end
end

#ros_package_name_for_typekit(typekit) ⇒ String

Returns the ROS package name generated for a given typekit


208
209
210
211
212
213
# File 'lib/orogen/marshallers/ros.rb', line 208

def ros_package_name_for_typekit(typekit)
    if typekit.respond_to?(:name)
        typekit = typekit.name
    end
    "orogen_#{typekit}_msgs"
end

#ros_ref_type(type, unbox = true) ⇒ Object

Returns a reference signature to the C++ type for the ROS message that represents type


283
284
285
# File 'lib/orogen/marshallers/ros.rb', line 283

def ros_ref_type(type, unbox = true)
    "#{ros_cxx_type(type, unbox)}&"
end

#separate_cmake?Boolean

Generates the set of compilation dependencies for the ROS transport


179
# File 'lib/orogen/marshallers/ros.rb', line 179

def separate_cmake?; true end