rclrb 1.1.0
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.
- checksums.yaml +7 -0
- data/COPYING +373 -0
- data/README.md +13 -0
- data/lib/rclrb/callback_group.rb +31 -0
- data/lib/rclrb/capi.rb +369 -0
- data/lib/rclrb/client.rb +79 -0
- data/lib/rclrb/clock.rb +17 -0
- data/lib/rclrb/common.rb +43 -0
- data/lib/rclrb/executor.rb +34 -0
- data/lib/rclrb/fields.rb +224 -0
- data/lib/rclrb/future.rb +82 -0
- data/lib/rclrb/guard_condition.rb +18 -0
- data/lib/rclrb/init.rb +59 -0
- data/lib/rclrb/interfaces.rb +270 -0
- data/lib/rclrb/internal.rb +16 -0
- data/lib/rclrb/node.rb +143 -0
- data/lib/rclrb/publisher.rb +40 -0
- data/lib/rclrb/qos.rb +116 -0
- data/lib/rclrb/service.rb +46 -0
- data/lib/rclrb/subscription.rb +45 -0
- data/lib/rclrb/time.rb +32 -0
- data/lib/rclrb/timer.rb +31 -0
- data/lib/rclrb/version.rb +3 -0
- data/lib/rclrb/wait_set.rb +62 -0
- data/lib/rclrb.rb +43 -0
- metadata +98 -0
data/lib/rclrb/fields.rb
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
module Rclrb
|
2
|
+
##
|
3
|
+
# This module contains classes used to handle ROS messages.
|
4
|
+
# The content of Fields is internal API to rclrb and should not be directly called and is subject
|
5
|
+
# to change.
|
6
|
+
module Fields
|
7
|
+
# This function is used for building generic parse_ros_message_array function, that will read array elements one-by-one and call block to parse them
|
8
|
+
def Fields.__generic_parse_array data, length, field_size, &block
|
9
|
+
array = []
|
10
|
+
for i in 0...length
|
11
|
+
ptr = data + i * field_size
|
12
|
+
array.append block.call(ptr)
|
13
|
+
end
|
14
|
+
return array
|
15
|
+
end
|
16
|
+
# This function is used for building generic fill_ros_message_array function, that will write array elements one-by-one and call block to add them
|
17
|
+
def Fields.__generic_fill_ros_message_array array_pointer, value, field_size, &block
|
18
|
+
for i in 0...value.length
|
19
|
+
block.call array_pointer + i * field_size, value[i]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
class AtomicField
|
25
|
+
attr_reader :init_value, :field_name, :ffi_type
|
26
|
+
def initialize(init_value, field_name, ffi_type, array_reader, array_writter)
|
27
|
+
@init_value = init_value
|
28
|
+
@field_name = "Rclrb::Fields::#{field_name}"
|
29
|
+
@ffi_type = ffi_type
|
30
|
+
@memory_size = FFI::type_size @ffi_type
|
31
|
+
@array_reader = array_reader
|
32
|
+
@array_writter = array_writter
|
33
|
+
end
|
34
|
+
def array_field()
|
35
|
+
@array_field = ArrayField.new(self) unless defined?(@array_field)
|
36
|
+
return @array_field
|
37
|
+
end
|
38
|
+
def parse_ros_message(data)
|
39
|
+
return data
|
40
|
+
end
|
41
|
+
def parse_ros_message_array(data, length)
|
42
|
+
if @array_reader
|
43
|
+
return data.send @array_reader, 0, length
|
44
|
+
else
|
45
|
+
return Fields.__generic_parse_array(data, length, self.ffi_size) { |ptr| ptr.get(@ffi_type, 0) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
def fill_ros_message(data, index, value)
|
49
|
+
data[index] = value
|
50
|
+
end
|
51
|
+
def fill_ros_message_array(array_pointer, value)
|
52
|
+
if @array_writter
|
53
|
+
return array_pointer.send @array_writter, 0, value
|
54
|
+
else
|
55
|
+
Fields.__generic_fill_ros_message_array(array_pointer, value, self.ffi_size) { |ptr, value| ptr.put @ffi_type, 0, value }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
def destroy_ros_message(data)
|
59
|
+
end
|
60
|
+
def ffi_size()
|
61
|
+
return @memory_size
|
62
|
+
end
|
63
|
+
def is_atomic
|
64
|
+
return true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
class StringField
|
68
|
+
def StringField.init_value()
|
69
|
+
return "\"\""
|
70
|
+
end
|
71
|
+
def StringField.field_name()
|
72
|
+
return "Rclrb::Fields::StringField"
|
73
|
+
end
|
74
|
+
def StringField.parse_ros_message(data)
|
75
|
+
length = data[:length]
|
76
|
+
return data[:string].get_string(0, length)
|
77
|
+
end
|
78
|
+
def StringField.parse_ros_message_array(data, length)
|
79
|
+
return Fields.__generic_parse_array(data, length, StringField.ffi_size) { |ptr| StringField.parse_ros_message FFIType.new(ptr) }
|
80
|
+
end
|
81
|
+
def StringField.fill_ros_message(data, index, value)
|
82
|
+
st = data[index]
|
83
|
+
str_pointer = Rclrb.rcl_allocate value.length + 1
|
84
|
+
st[:string] = str_pointer
|
85
|
+
str_pointer.put_string(0, value)
|
86
|
+
str_pointer.put_uint8(value.length, 0)
|
87
|
+
st[:length] = value.length
|
88
|
+
st[:capacity] = value.length + 1
|
89
|
+
end
|
90
|
+
def StringField.destroy_ros_message(data)
|
91
|
+
if data.kind_of? FFIType
|
92
|
+
Rclrb.rcl_deallocate data[:string]
|
93
|
+
else
|
94
|
+
return StringField.destroy_ros_message FFIType.new(data)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
def StringField.array_field()
|
98
|
+
@@array_field = ArrayField.new(StringField) unless defined?(@@array_field)
|
99
|
+
return @@array_field
|
100
|
+
end
|
101
|
+
def StringField.ffi_type
|
102
|
+
return FFIType
|
103
|
+
end
|
104
|
+
def StringField.ffi_size
|
105
|
+
return FFIType.size
|
106
|
+
end
|
107
|
+
def StringField.is_atomic
|
108
|
+
return false
|
109
|
+
end
|
110
|
+
class FFIType < FFI::Struct
|
111
|
+
layout :string, :pointer, :length, :size_t, :capacity, :size_t
|
112
|
+
end
|
113
|
+
end
|
114
|
+
class TimeField
|
115
|
+
def TimeField.init_value()
|
116
|
+
return "Rclrb::Time.new()"
|
117
|
+
end
|
118
|
+
def TimeField.field_name()
|
119
|
+
return "Rclrb::Fields::TimeField"
|
120
|
+
end
|
121
|
+
def TimeField.parse_ros_message(data)
|
122
|
+
return Time.from_sec_nsec data[:sec], data[:nsec]
|
123
|
+
end
|
124
|
+
def TimeField.fill_ros_message(data, index, value)
|
125
|
+
st = data[index]
|
126
|
+
s, ns = value.to_sec_nsec
|
127
|
+
st[:sec] = s
|
128
|
+
st[:nsec] = ns
|
129
|
+
end
|
130
|
+
def TimeField.destroy_ros_message(data)
|
131
|
+
end
|
132
|
+
def TimeField.array_field()
|
133
|
+
@@array_field = ArrayField.new(TimeField) unless defined?(@@array_field)
|
134
|
+
return @@array_field
|
135
|
+
end
|
136
|
+
def TimeField.ffi_type
|
137
|
+
return FFIType
|
138
|
+
end
|
139
|
+
def TimeField.ffi_size
|
140
|
+
return FFIType.size
|
141
|
+
end
|
142
|
+
def TimeField.is_atomic
|
143
|
+
return false
|
144
|
+
end
|
145
|
+
class FFIType < FFI::Struct
|
146
|
+
layout :sec, :int32, :nsec, :uint32
|
147
|
+
end
|
148
|
+
end
|
149
|
+
class ArrayField
|
150
|
+
def initialize(field)
|
151
|
+
@field = field
|
152
|
+
end
|
153
|
+
def init_value()
|
154
|
+
return "[]"
|
155
|
+
end
|
156
|
+
def field_name()
|
157
|
+
return "#{@field.field_name}.array_field()"
|
158
|
+
end
|
159
|
+
def parse_ros_message(data)
|
160
|
+
return @field.parse_ros_message_array data[:array], data[:length]
|
161
|
+
end
|
162
|
+
def fill_ros_message(data, index, value)
|
163
|
+
array_pointer = Rclrb.rcl_allocate value.length * @field.ffi_size
|
164
|
+
@field.fill_ros_message_array array_pointer, value
|
165
|
+
arr = data[index]
|
166
|
+
arr[:array] = array_pointer
|
167
|
+
arr[:length] = value.length
|
168
|
+
arr[:capacity] = value.length
|
169
|
+
end
|
170
|
+
SIZE = CApi::POINTER_SIZE + 2 * CApi::SIZE_T_SIZE
|
171
|
+
def ros_size()
|
172
|
+
return SIZE
|
173
|
+
end
|
174
|
+
def destroy_ros_message(data)
|
175
|
+
length = data[:length]
|
176
|
+
unless @field.is_atomic
|
177
|
+
for i in 0...length
|
178
|
+
@field.destroy_ros_message data[:array] + i * @field.ffi_size
|
179
|
+
end
|
180
|
+
end
|
181
|
+
Rclrb.rcl_deallocate data[:array]
|
182
|
+
end
|
183
|
+
def is_atomic
|
184
|
+
return false
|
185
|
+
end
|
186
|
+
def ffi_type
|
187
|
+
return FFIType
|
188
|
+
end
|
189
|
+
class FFIType < FFI::Struct
|
190
|
+
layout :array, :pointer, :length, :size_t, :capacity, :size_t
|
191
|
+
end
|
192
|
+
end
|
193
|
+
Float64Field = AtomicField.new "0.0", "Float64Field", :double, :get_array_of_float64, :put_array_of_float64
|
194
|
+
Float32Field = AtomicField.new "0.0", "Float32Field", :float, :get_array_of_float32, :put_array_of_float32
|
195
|
+
ByteField = AtomicField.new "0", "ByteField", :uint8, :get_array_of_uint8, :put_array_of_uint8
|
196
|
+
CharField = AtomicField.new "0", "CharField", :uint8, :get_array_of_uint8, :put_array_of_uint8
|
197
|
+
BoolField = AtomicField.new "false", "BoolField", :bool, nil, nil
|
198
|
+
Int8Field = AtomicField.new "0", "Int8Field", :int8, :get_array_of_int8, :put_array_of_int8
|
199
|
+
Int16Field = AtomicField.new "0", "Int16Field", :int16, :get_array_of_int16, :put_array_of_int16
|
200
|
+
Int32Field = AtomicField.new "0", "Int32Field", :int32, :get_array_of_int32, :put_array_of_int32
|
201
|
+
Int64Field = AtomicField.new "0", "Int64Field", :int64, :get_array_of_int64, :put_array_of_int64
|
202
|
+
Uint8Field = AtomicField.new "0", "Uint8Field", :uint8, :get_array_of_uint8, :put_array_of_uint8
|
203
|
+
Uint16Field = AtomicField.new "0", "Uint16Field", :uint16, :get_array_of_uint16, :put_array_of_uint16
|
204
|
+
Uint32Field = AtomicField.new "0", "Uint32Field", :uint32, :get_array_of_uint32, :put_array_of_uint32
|
205
|
+
Uint64Field = AtomicField.new "0", "Uint64Field", :uint64, :get_array_of_uint64, :put_array_of_uint64
|
206
|
+
|
207
|
+
AtomicFields = { "float64" => Float64Field,
|
208
|
+
"float32" => Float32Field,
|
209
|
+
"char" => CharField,
|
210
|
+
"byte" => ByteField,
|
211
|
+
"bool" => BoolField,
|
212
|
+
"int8" => Int8Field,
|
213
|
+
"int16" => Int16Field,
|
214
|
+
"int32" => Int32Field,
|
215
|
+
"int64" => Int64Field,
|
216
|
+
"uint8" => Uint8Field,
|
217
|
+
"uint16" => Uint16Field,
|
218
|
+
"uint32" => Uint32Field,
|
219
|
+
"uint64" => Uint64Field,
|
220
|
+
"string" => StringField,
|
221
|
+
"builtin_interfaces/Time" => TimeField }
|
222
|
+
|
223
|
+
end
|
224
|
+
end
|
data/lib/rclrb/future.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
module Rclrb
|
2
|
+
#
|
3
|
+
# Define a future
|
4
|
+
#
|
5
|
+
#
|
6
|
+
class Future
|
7
|
+
##
|
8
|
+
# Create a future with a nil result
|
9
|
+
def initialize
|
10
|
+
@result = nil
|
11
|
+
@observers = []
|
12
|
+
@mutex = Mutex.new
|
13
|
+
@resource = ConditionVariable.new
|
14
|
+
@exception = nil
|
15
|
+
end
|
16
|
+
##
|
17
|
+
# Execute the block in a thread and return a Future.
|
18
|
+
# The block is expected to return a value which is set using set_value.
|
19
|
+
def Future.execute(&block)
|
20
|
+
f = Future.new
|
21
|
+
Thread.new do
|
22
|
+
begin
|
23
|
+
f.set_result block.call
|
24
|
+
rescue => ex
|
25
|
+
f.set_exception ex
|
26
|
+
end
|
27
|
+
end
|
28
|
+
return f
|
29
|
+
end
|
30
|
+
##
|
31
|
+
# Add an observer, which is executed once the result is set.
|
32
|
+
def add_observer(&block)
|
33
|
+
@mutex.synchronize do
|
34
|
+
if @result.nil?
|
35
|
+
@observers.push block
|
36
|
+
else
|
37
|
+
block.call @result
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
##
|
42
|
+
# Wait undefinitely for a result.
|
43
|
+
def wait_for_result()
|
44
|
+
@mutex.synchronize do
|
45
|
+
while @result.nil?
|
46
|
+
raise @exception unless @exception.nil?
|
47
|
+
@resource.wait(@mutex)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
##
|
52
|
+
# Returns true if it already has a result.
|
53
|
+
def has_result?
|
54
|
+
return not(@result.nil?)
|
55
|
+
end
|
56
|
+
##
|
57
|
+
# Set that an exception occurs
|
58
|
+
def set_exception(ex)
|
59
|
+
@mutex.synchronize do
|
60
|
+
@exception = ex
|
61
|
+
@resource.signal
|
62
|
+
end
|
63
|
+
end
|
64
|
+
##
|
65
|
+
# Set the result of the future
|
66
|
+
def set_result(result)
|
67
|
+
raise RclError.new "Result is already set" unless @result.nil?
|
68
|
+
@mutex.synchronize do
|
69
|
+
@result = result
|
70
|
+
@resource.signal
|
71
|
+
end
|
72
|
+
@observers.each() { |o| o.call Time.now, @result }
|
73
|
+
@observers = []
|
74
|
+
end
|
75
|
+
##
|
76
|
+
# Wait for the result and return it
|
77
|
+
def result()
|
78
|
+
wait_for_result()
|
79
|
+
return @result
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Rclrb
|
2
|
+
class GuardCondition
|
3
|
+
attr_reader :guard_condition_handle
|
4
|
+
def initialize
|
5
|
+
@guard_condition_handle = CApi.rcl_get_zero_initialized_guard_condition
|
6
|
+
CApi.handle_result CApi.rcl_guard_condition_init(@guard_condition_handle, Rclrb.rcl_context, CApi.rcl_guard_condition_get_default_options)
|
7
|
+
end
|
8
|
+
rclrb_finalize_with :@guard_condition_handle do |guard_condition_handle|
|
9
|
+
CApi.handle_result CApi.rcl_guard_condition_fini(guard_condition_handle)
|
10
|
+
end
|
11
|
+
def trigger()
|
12
|
+
CApi.handle_result CApi.rcl_trigger_guard_condition(@guard_condition_handle)
|
13
|
+
end
|
14
|
+
def spin(wait_set = nil)
|
15
|
+
wait_set.add self if wait_set
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/rclrb/init.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module Rclrb
|
2
|
+
|
3
|
+
##
|
4
|
+
# Initialise Rclrb with the given arguments
|
5
|
+
def Rclrb.init(arguments: [])
|
6
|
+
@@initOptions = CApi.rcl_get_zero_initialized_init_options()
|
7
|
+
@@context = CApi.rcl_get_zero_initialized_context()
|
8
|
+
@@allocator = CApi.rcutils_get_default_allocator
|
9
|
+
CApi.handle_result(CApi.rcl_init_options_init(@@initOptions, @@allocator))
|
10
|
+
|
11
|
+
# Prepare arguments
|
12
|
+
p_values = nil
|
13
|
+
if arguments.length > 0
|
14
|
+
strings = arguments.map { |k| FFI::MemoryPointer.from_string(k.to_s) }
|
15
|
+
p_values = FFI::MemoryPointer.new(:pointer, arguments.size + 1)
|
16
|
+
p_values.write_array_of_pointer(strings)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Call init
|
20
|
+
CApi.handle_result(CApi.rcl_init(arguments.length, p_values, @@initOptions, @@context))
|
21
|
+
|
22
|
+
# Guard condition to shutdown execution when shutdown is called
|
23
|
+
@@signal_guard_condition = GuardCondition.new
|
24
|
+
@@shutdown_requested = false
|
25
|
+
|
26
|
+
# Setup signal to catch ctr+c
|
27
|
+
Rclrb.setup_signal
|
28
|
+
end
|
29
|
+
def Rclrb.setup_signal
|
30
|
+
Signal.trap("INT") {
|
31
|
+
puts "Interrupts called..."
|
32
|
+
Rclrb.shutdown()
|
33
|
+
}
|
34
|
+
end
|
35
|
+
##
|
36
|
+
# Terminate Rclrb
|
37
|
+
def Rclrb.shutdown
|
38
|
+
@@shutdown_requested = true
|
39
|
+
@@signal_guard_condition.trigger
|
40
|
+
end
|
41
|
+
def Rclrb.rcl_context()
|
42
|
+
return @@context
|
43
|
+
end
|
44
|
+
def Rclrb.rcl_signal_guard_condition
|
45
|
+
return @@signal_guard_condition
|
46
|
+
end
|
47
|
+
def Rclrb.rcl_shutdown_requested?
|
48
|
+
return @@shutdown_requested
|
49
|
+
end
|
50
|
+
def Rclrb.rcl_allocator
|
51
|
+
return @@allocator
|
52
|
+
end
|
53
|
+
def Rclrb.rcl_allocate(size)
|
54
|
+
return @@allocator[:allocate].call size, @@allocator[:state]
|
55
|
+
end
|
56
|
+
def Rclrb.rcl_deallocate(ptr)
|
57
|
+
@@allocator[:deallocate].call ptr, @@allocator[:state]
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,270 @@
|
|
1
|
+
module Rclrb
|
2
|
+
##
|
3
|
+
# This module contains classes used to generate package to handle ROS messages.
|
4
|
+
# The content of Interfaces is internal API to rclrb and should not be directly called and is subject
|
5
|
+
# to change.
|
6
|
+
module Interfaces
|
7
|
+
def Interfaces.__parse_msg_srv_act_file package_name, path, filename, interface_type, interface_type_camel
|
8
|
+
definition = {}
|
9
|
+
definition[:name] = filename[0, filename.length - 4]
|
10
|
+
definition[:class_name] = "#{Rclrb.camelize(package_name)}::#{interface_type_camel}::#{definition[:name]}"
|
11
|
+
line_num = 0
|
12
|
+
fieldss = []
|
13
|
+
fields = []
|
14
|
+
constantss = []
|
15
|
+
constants = []
|
16
|
+
depends = []
|
17
|
+
ext_depends = []
|
18
|
+
File.open("#{path}/#{interface_type}/#{filename}").each do |line|
|
19
|
+
line_num += 1
|
20
|
+
# Remove comment
|
21
|
+
comment_index = line.index('#')
|
22
|
+
line = line[0, comment_index] unless comment_index.nil?
|
23
|
+
if line.index('=') != nil
|
24
|
+
l = line.split('=')
|
25
|
+
if l.length == 2
|
26
|
+
l2 = l[0].split(' ')
|
27
|
+
if l2.length == 2
|
28
|
+
constants.append "#{l2[1].upcase} = #{l[1]}"
|
29
|
+
else
|
30
|
+
raise ParseError.new "Cannot parse '#{path}/#{interface_type}/#{filename}', invalid constant definition on line number #{line_num}"
|
31
|
+
end
|
32
|
+
else
|
33
|
+
raise ParseError.new "Cannot parse '#{path}/#{interface_type}/#{filename}', invalid constant definition on line number #{line_num}"
|
34
|
+
end
|
35
|
+
else
|
36
|
+
l = line.split(' ', ).reject { |c| c.empty?}
|
37
|
+
if l.length == 2 or l.length == 3
|
38
|
+
type = l[0]
|
39
|
+
name = l[1]
|
40
|
+
array_index = type.index(/\[\d*\]/)
|
41
|
+
if array_index.nil?
|
42
|
+
base_type = type
|
43
|
+
is_array = false
|
44
|
+
else
|
45
|
+
base_type = type[0, array_index]
|
46
|
+
is_array = true
|
47
|
+
end
|
48
|
+
|
49
|
+
if Fields::AtomicFields.include? base_type
|
50
|
+
field_def = Fields::AtomicFields[base_type].field_name
|
51
|
+
if l.length == 3
|
52
|
+
field_init_value = l[2]
|
53
|
+
else
|
54
|
+
field_init_value = Fields::AtomicFields[base_type].init_value
|
55
|
+
end
|
56
|
+
else
|
57
|
+
if base_type == "Header"
|
58
|
+
field_def = "StdMsgs::#{interface_type_camel}::Header"
|
59
|
+
field_init_value = "StdMsgs::Header.new"
|
60
|
+
if package_name != "std_msgs"
|
61
|
+
ext_depends.append "std_msgs/msg"
|
62
|
+
end
|
63
|
+
elsif base_type.index("/").nil?
|
64
|
+
field_def = "#{Rclrb.camelize(package_name)}::Msg::#{base_type}"
|
65
|
+
field_init_value = "#{field_def}.new"
|
66
|
+
if interface_type != 'msg'
|
67
|
+
ext_depends.append "#{package_name}/msg"
|
68
|
+
end
|
69
|
+
else
|
70
|
+
base_type_split = base_type.split "/"
|
71
|
+
if base_type_split.length == 2
|
72
|
+
ext_depends.append "#{base_type_split[0]}/msg" unless base_type_split[0] == package_name
|
73
|
+
field_def = "#{Rclrb.camelize(base_type_split[0])}::Msg::#{base_type_split[1]}"
|
74
|
+
field_init_value = "#{field_def}.new"
|
75
|
+
else
|
76
|
+
raise ParseError.new "Cannot parse '#{path}/#{interface_type}/#{filename}', invalid type #{base_type} at line number #{line_num}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
depends.append field_def
|
80
|
+
end
|
81
|
+
if is_array
|
82
|
+
field_def += ".array_field"
|
83
|
+
field_init_value = []
|
84
|
+
end
|
85
|
+
fields.append({ :name => name, :field_def_name => field_def, :field_init_value => field_init_value })
|
86
|
+
elsif l.length == 1 and l[0] = "---"
|
87
|
+
fieldss.append(fields)
|
88
|
+
constantss.append(constants)
|
89
|
+
fields = []
|
90
|
+
constants = []
|
91
|
+
elsif l.length != 0
|
92
|
+
raise ParseError.new "Cannot parse '#{path}/#{interface_type}/#{filename}', invalid line number #{line_num}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
fieldss.append(fields)
|
97
|
+
constantss.append(constants)
|
98
|
+
definition[:fields] = fieldss
|
99
|
+
definition[:constants] = constantss
|
100
|
+
definition[:depends] = depends
|
101
|
+
definition[:ext_depends] = ext_depends.uniq
|
102
|
+
return definition
|
103
|
+
end
|
104
|
+
def Interfaces.__fields_body(index, klass_name)
|
105
|
+
str = <<-EOF
|
106
|
+
attr_accessor <%= mdef[:fields][#{index}].map { |f| ":" + f[:name] } .join(" ,") %>
|
107
|
+
<% mdef[:constants][#{index}].each do |constant| %>
|
108
|
+
<%= constant %>
|
109
|
+
<% end %>
|
110
|
+
def initialize()
|
111
|
+
<% mdef[:fields][#{index}].each do |field| %>
|
112
|
+
@<%= field[:name] %> = <%= field[:field_init_value] %>
|
113
|
+
<% end %>
|
114
|
+
end
|
115
|
+
class FFIType < FFI::Struct
|
116
|
+
layout <%= mdef[:fields][#{index}].map { |f| ":" + f[:name] + ", " + f[:field_def_name] + ".ffi_type" }.join(" ,") %>
|
117
|
+
end
|
118
|
+
|
119
|
+
def #{klass_name}.get_ffi_struct(data)
|
120
|
+
if data.kind_of? FFIType
|
121
|
+
return data
|
122
|
+
else
|
123
|
+
return FFIType.new data
|
124
|
+
end
|
125
|
+
end
|
126
|
+
def #{klass_name}.parse_ros_message(data)
|
127
|
+
msg = #{klass_name}.new
|
128
|
+
ffi_struct = #{klass_name}.get_ffi_struct(data)
|
129
|
+
<% mdef[:fields][#{index}].each do |field| %>
|
130
|
+
msg.<%= field[:name] %> = <%= field[:field_def_name] %>.parse_ros_message ffi_struct[:<%= field[:name] %>]
|
131
|
+
<% end %>
|
132
|
+
return msg
|
133
|
+
end
|
134
|
+
def #{klass_name}.parse_ros_message_array(data, length)
|
135
|
+
return Fields.__generic_parse_array(data, length, #{klass_name}.ffi_size) { |ptr| #{klass_name}.parse_ros_message(ptr) }
|
136
|
+
end
|
137
|
+
def #{klass_name}.destroy_ros_message(data)
|
138
|
+
ffi_struct = #{klass_name}.get_ffi_struct(data)
|
139
|
+
<% mdef[:fields][#{index}].each do |field| %>
|
140
|
+
<%= field[:field_def_name] %>.destroy_ros_message(ffi_struct[:<%= field[:name] %>])
|
141
|
+
<% end %>
|
142
|
+
end
|
143
|
+
def #{klass_name}.get_ros_message(msg)
|
144
|
+
ffi_struct = FFIType.new
|
145
|
+
#{klass_name}.__fill_ros_message(ffi_struct, msg)
|
146
|
+
return ffi_struct
|
147
|
+
end
|
148
|
+
def #{klass_name}.fill_ros_message(ffi_struct, member, msg)
|
149
|
+
#{klass_name}.__fill_ros_message(ffi_struct[member], msg)
|
150
|
+
end
|
151
|
+
def #{klass_name}.__fill_ros_message(ffi_struct, msg)
|
152
|
+
<% mdef[:fields][#{index}].each do |field| %>
|
153
|
+
<%= field[:field_def_name] %>.fill_ros_message(ffi_struct, :<%= field[:name] %>, msg.<%= field[:name] %>)
|
154
|
+
<% end %>
|
155
|
+
end
|
156
|
+
def #{klass_name}.fill_ros_message_array(array_pointer, value)
|
157
|
+
Fields.__generic_fill_ros_message_array(array_pointer, value, #{klass_name}.ffi_size) { |ptr, value| #{klass_name}.__fill_ros_message(ffi_type.new(ptr), value) }
|
158
|
+
end
|
159
|
+
def #{klass_name}.ffi_type
|
160
|
+
return FFIType
|
161
|
+
end
|
162
|
+
def #{klass_name}.ffi_size
|
163
|
+
return FFIType.size
|
164
|
+
end
|
165
|
+
def #{klass_name}.is_atomic
|
166
|
+
return false
|
167
|
+
end
|
168
|
+
EOF
|
169
|
+
end
|
170
|
+
def Interfaces.load_definitions package_name
|
171
|
+
is_msg_package = package_name.end_with? "/msg"
|
172
|
+
is_srv_package = package_name.end_with? "/srv"
|
173
|
+
if is_msg_package or is_srv_package
|
174
|
+
ros_package_name = package_name[0, package_name.size - 4]
|
175
|
+
else
|
176
|
+
return false
|
177
|
+
end
|
178
|
+
module_name = Rclrb.camelize ros_package_name
|
179
|
+
p = "#{`ros2 pkg prefix #{ros_package_name}`.chop}/share/#{ros_package_name}"
|
180
|
+
return false unless File.directory?(p)
|
181
|
+
if is_msg_package
|
182
|
+
msg_defs_unsorted = Dir.glob("*.msg", base: p + "/msg").map { |name| Interfaces.__parse_msg_srv_act_file(ros_package_name, p, name, "msg", "Msg") }
|
183
|
+
# msg_defs.sort! { |a| a[:depends].length }
|
184
|
+
msg_defs = []
|
185
|
+
current_index = 0
|
186
|
+
while msg_defs_unsorted.length > 0
|
187
|
+
if current_index >= msg_defs_unsorted.length
|
188
|
+
current_index = 0
|
189
|
+
end
|
190
|
+
add_to_msg_defs = true
|
191
|
+
msg_defs_unsorted[current_index][:depends].each() { |x|
|
192
|
+
if x.start_with? module_name
|
193
|
+
found = false
|
194
|
+
msg_defs.each() { |y|
|
195
|
+
if y[:class_name] == x
|
196
|
+
found = true
|
197
|
+
break
|
198
|
+
end
|
199
|
+
}
|
200
|
+
unless found
|
201
|
+
add_to_msg_defs = false
|
202
|
+
break
|
203
|
+
end
|
204
|
+
end
|
205
|
+
}
|
206
|
+
if add_to_msg_defs
|
207
|
+
msg_defs.push msg_defs_unsorted[current_index]
|
208
|
+
msg_defs_unsorted.delete_at current_index
|
209
|
+
else
|
210
|
+
current_index += 1
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
msg_defs.each() do |mdef|
|
215
|
+
mdef[:ext_depends].each() { |d| require d }
|
216
|
+
end
|
217
|
+
CApi.create_type_support_library(module_name, ros_package_name, "message", "msg", "RosidlMessageTypeSupportT", msg_defs.map {|mdef| mdef[:name]})
|
218
|
+
template = ERB.new <<-EOF
|
219
|
+
module <%= module_name %>
|
220
|
+
module Msg
|
221
|
+
<% msg_defs.each do |mdef| %>
|
222
|
+
class <%= mdef[:name] %>
|
223
|
+
def <%= mdef[:name] %>.type_support()
|
224
|
+
TypeSupportCApi::TypeSupport_msg_<%= mdef[:name] %>
|
225
|
+
end
|
226
|
+
#{Interfaces.__fields_body(0, "<%= mdef[:name] %>")}
|
227
|
+
def <%= mdef[:name] %>.array_field()
|
228
|
+
@@array_field = Fields::ArrayField.new(<%= mdef[:name] %>) unless defined?(@@array_field)
|
229
|
+
return @@array_field
|
230
|
+
end
|
231
|
+
end
|
232
|
+
<% end %>
|
233
|
+
end
|
234
|
+
end
|
235
|
+
EOF
|
236
|
+
# puts template.result(binding)
|
237
|
+
Object.class_eval template.result(binding)
|
238
|
+
return true
|
239
|
+
elsif is_srv_package
|
240
|
+
srv_defs = Dir.glob("*.srv", base: p + "/srv").map { |name| Interfaces.__parse_msg_srv_act_file(ros_package_name, p, name, "srv", "Srv") }
|
241
|
+
srv_defs.each() do |mdef|
|
242
|
+
mdef[:ext_depends].each() { |d| require d }
|
243
|
+
end
|
244
|
+
CApi.create_type_support_library module_name, ros_package_name, "service", "srv", "RosidlServiceTypeSupportT", srv_defs.map {|mdef| mdef[:name]}
|
245
|
+
template = ERB.new <<-EOF
|
246
|
+
module <%= module_name %>
|
247
|
+
module Srv
|
248
|
+
<% srv_defs.each do |mdef| %>
|
249
|
+
module <%= mdef[:name] %>
|
250
|
+
def <%= mdef[:name] %>.type_support()
|
251
|
+
TypeSupportCApi::TypeSupport_srv_<%= mdef[:name] %>
|
252
|
+
end
|
253
|
+
class Request
|
254
|
+
#{Interfaces.__fields_body(0, "Request")}
|
255
|
+
end
|
256
|
+
class Response
|
257
|
+
#{Interfaces.__fields_body(1, "Response")}
|
258
|
+
end
|
259
|
+
end
|
260
|
+
<% end %>
|
261
|
+
end
|
262
|
+
end
|
263
|
+
EOF
|
264
|
+
Object.class_eval template.result(binding)
|
265
|
+
return true
|
266
|
+
end
|
267
|
+
return false
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Rclrb
|
2
|
+
##
|
3
|
+
# Uncamelize a string, for instance transform "ThisIsAnExample" to "this_is_an_example"
|
4
|
+
def Rclrb.uncamelize str
|
5
|
+
return str.gsub(/::/, '/')
|
6
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
7
|
+
.gsub(/([a-z\d])([A-Z])/,'\1_\2')
|
8
|
+
.tr("-", "_")
|
9
|
+
.downcase
|
10
|
+
end
|
11
|
+
##
|
12
|
+
# Camelize a string, for instance transform "this_is_an_example" to "ThisIsAnExample"
|
13
|
+
def Rclrb.camelize str
|
14
|
+
return str.split("_").map(&:capitalize).join
|
15
|
+
end
|
16
|
+
end
|