rosruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/bin/rubyroscore +5 -0
  2. data/lib/ros.rb +25 -0
  3. data/lib/ros/duration.rb +63 -0
  4. data/lib/ros/graph_manager.rb +408 -0
  5. data/lib/ros/log.rb +72 -0
  6. data/lib/ros/master.rb +408 -0
  7. data/lib/ros/master_proxy.rb +256 -0
  8. data/lib/ros/message.rb +65 -0
  9. data/lib/ros/name.rb +88 -0
  10. data/lib/ros/node.rb +442 -0
  11. data/lib/ros/package.rb +144 -0
  12. data/lib/ros/parameter_manager.rb +127 -0
  13. data/lib/ros/parameter_subscriber.rb +47 -0
  14. data/lib/ros/publisher.rb +96 -0
  15. data/lib/ros/rate.rb +41 -0
  16. data/lib/ros/ros.rb +10 -0
  17. data/lib/ros/roscore.rb +29 -0
  18. data/lib/ros/service.rb +37 -0
  19. data/lib/ros/service_client.rb +83 -0
  20. data/lib/ros/service_server.rb +92 -0
  21. data/lib/ros/slave_proxy.rb +153 -0
  22. data/lib/ros/subscriber.rb +119 -0
  23. data/lib/ros/tcpros/client.rb +108 -0
  24. data/lib/ros/tcpros/header.rb +89 -0
  25. data/lib/ros/tcpros/message.rb +74 -0
  26. data/lib/ros/tcpros/server.rb +137 -0
  27. data/lib/ros/tcpros/service_client.rb +104 -0
  28. data/lib/ros/tcpros/service_server.rb +132 -0
  29. data/lib/ros/time.rb +109 -0
  30. data/lib/ros/topic.rb +47 -0
  31. data/lib/ros/xmlrpcserver.rb +40 -0
  32. data/samples/add_two_ints_client.rb +25 -0
  33. data/samples/add_two_ints_server.rb +20 -0
  34. data/samples/gui.rb +126 -0
  35. data/samples/sample_log.rb +16 -0
  36. data/samples/sample_param.rb +20 -0
  37. data/samples/sample_publisher.rb +20 -0
  38. data/samples/sample_subscriber.rb +19 -0
  39. data/scripts/genmsg_ruby.py +1135 -0
  40. data/scripts/genmsg_ruby.pyc +0 -0
  41. data/scripts/gensrv_ruby.py +105 -0
  42. data/scripts/gensrv_ruby.pyc +0 -0
  43. data/scripts/rosruby_genmsg.py +67 -0
  44. data/scripts/run-test.rb +21 -0
  45. data/test/test_header.rb +36 -0
  46. data/test/test_log.rb +45 -0
  47. data/test/test_master_proxy.rb +73 -0
  48. data/test/test_message.rb +13 -0
  49. data/test/test_node.rb +166 -0
  50. data/test/test_package.rb +10 -0
  51. data/test/test_param.rb +27 -0
  52. data/test/test_pubsub.rb +154 -0
  53. data/test/test_rate.rb +16 -0
  54. data/test/test_service.rb +34 -0
  55. data/test/test_slave_proxy.rb +49 -0
  56. data/test/test_time.rb +39 -0
  57. metadata +170 -0
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ros'
4
+
5
+ def main
6
+ node = ROS::Node.new('/hoge')
7
+ sleep(1)
8
+ node.loginfo('some information')
9
+ node.logdebug('some debug information')
10
+ node.logerr('some error information')
11
+ node.logerror('some error information')
12
+ node.logfatal('some fatal information')
13
+ sleep(1)
14
+ end
15
+
16
+ main
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ros'
4
+ require 'std_msgs/String'
5
+
6
+ def main
7
+ node = ROS::Node.new('/rosruby/sample_publisher')
8
+ publisher = node.advertise('/chatter', Std_msgs::String)
9
+ sleep(1)
10
+ msg = Std_msgs::String.new
11
+
12
+ while node.ok?
13
+ msg.data = "local param = #{node.get_param('~message')}, global = #{node.get_param('/message')}"
14
+ publisher.publish(msg)
15
+ node.loginfo(msg.data)
16
+ sleep (1.0)
17
+ end
18
+ end
19
+
20
+ main
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ros'
4
+ require 'std_msgs/String'
5
+
6
+ def main
7
+ node = ROS::Node.new('/rosruby/sample_publisher')
8
+ publisher = node.advertise('/chatter', Std_msgs::String)
9
+ sleep(1)
10
+ msg = Std_msgs::String.new
11
+ i = 0
12
+ while node.ok?
13
+ msg.data = "Hello, rosruby!: #{i}"
14
+ publisher.publish(msg)
15
+ sleep(1.0)
16
+ i += 1
17
+ end
18
+ end
19
+
20
+ main
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ros'
4
+ require 'std_msgs/String'
5
+
6
+ def main
7
+ node = ROS::Node.new('/rosruby/sample_subscriber')
8
+ node.subscribe('/chatter', Std_msgs::String) do |msg|
9
+ puts "message come! = \'#{msg.data}\'"
10
+ end
11
+
12
+ while node.ok?
13
+ node.spin_once
14
+ sleep(1)
15
+ end
16
+
17
+ end
18
+
19
+ main
@@ -0,0 +1,1135 @@
1
+ #! /usr/bin/env python
2
+ # Software License Agreement (BSD License)
3
+ #
4
+ # Copyright (c) 2012, Takashi Ogura
5
+ #
6
+ # based on
7
+ #
8
+ # Copyright (c) 2008, Willow Garage, Inc.
9
+ # All rights reserved.
10
+ #
11
+ # Redistribution and use in source and binary forms, with or without
12
+ # modification, are permitted provided that the following conditions
13
+ # are met:
14
+ #
15
+ # * Redistributions of source code must retain the above copyright
16
+ # notice, this list of conditions and the following disclaimer.
17
+ # * Redistributions in binary form must reproduce the above
18
+ # copyright notice, this list of conditions and the following
19
+ # disclaimer in the documentation and/or other materials provided
20
+ # with the distribution.
21
+ # * Neither the name of Willow Garage, Inc. nor the names of its
22
+ # contributors may be used to endorse or promote products derived
23
+ # from this software without specific prior written permission.
24
+ #
25
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28
+ # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29
+ # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31
+ # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34
+ # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35
+ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36
+ # POSSIBILITY OF SUCH DAMAGE.
37
+ #
38
+ # Revision $Id:$
39
+
40
+ """
41
+ Library for Ruby message generation.
42
+
43
+ The structure of the serialization descends several levels of serializers:
44
+ - msg_generator: generator for an individual msg file
45
+ - serialize_fn_generator: generator for msg.serialize()
46
+ - serializer_generator
47
+ - field-type-specific serializers
48
+ - deserialize_fn_generator: generator for msg.deserialize()
49
+ - serializer_generator
50
+ - field-type-specific serializers
51
+ """
52
+
53
+ # NOTE: genpy must be in the roslib package as placing it in rospy
54
+ # creates circular deps
55
+
56
+ import os
57
+ import shutil
58
+ import atexit
59
+ import itertools
60
+ import sys
61
+ import tempfile
62
+ import traceback
63
+ import struct
64
+
65
+ try:
66
+ from cStringIO import StringIO # Python 2.x
67
+ except ImportError:
68
+ from io import StringIO # Python 3.x
69
+
70
+ import roslib.exceptions
71
+ import roslib.gentools
72
+ import roslib.msgs
73
+ import roslib.packages #for get_pkg_dir
74
+
75
+ # indent width
76
+ INDENT = ' '
77
+
78
+ class MsgGenerationException(roslib.exceptions.ROSLibException):
79
+ """
80
+ Exception type for errors in roslib.genpy
81
+ """
82
+ pass
83
+
84
+ def get_registered_ex(type_):
85
+ """
86
+ wrapper for roslib.msgs.get_registered that wraps unknown types with a MsgGenerationException
87
+ @param type_: ROS message type
88
+ @type type_: str
89
+ """
90
+ try:
91
+ return roslib.msgs.get_registered(type_)
92
+ except KeyError:
93
+ raise MsgGenerationException("Unknown type [%s]. Please check that the manifest.xml correctly declares dependencies."%type_)
94
+
95
+ ################################################################################
96
+ # Primitive type handling for ROS builtin types
97
+
98
+ PYTHON_RUBY_DICT = { #see python module struct
99
+ 'b': 'c',
100
+ 'B': 'C',
101
+ 'h' : 's',
102
+ 'H' : 'S',
103
+ 'i' : 'l',
104
+ 'I' : 'L',
105
+ 'q' : 'q',
106
+ 'Q' : 'Q',
107
+ 'f': 'f',
108
+ 'd': 'd'
109
+ }
110
+
111
+ SIMPLE_TYPES_DICT = { #see python module struct
112
+ 'int8': 'b',
113
+ 'uint8': 'B',
114
+ # Python 2.6 adds in '?' for C99 _Bool, which appears equivalent to an uint8,
115
+ # thus, we use uint8
116
+ 'bool': 'B',
117
+ 'int16' : 'h',
118
+ 'uint16' : 'H',
119
+ 'int32' : 'i',
120
+ 'uint32' : 'I',
121
+ 'int64' : 'q',
122
+ 'uint64' : 'Q',
123
+ 'float32': 'f',
124
+ 'float64': 'd',
125
+ # deprecated
126
+ 'char' : 'B', #unsigned
127
+ 'byte' : 'b', #signed
128
+ }
129
+
130
+ ## Simple types are primitives with fixed-serialization length
131
+ SIMPLE_TYPES = list(SIMPLE_TYPES_DICT.keys()) #py3k
132
+
133
+ def is_simple(type_):
134
+ """
135
+ @return bool: True if type is a 'simple' type, i.e. is of
136
+ fixed/known serialization length. This is effectively all primitive
137
+ types except for string
138
+ @rtype: bool
139
+ """
140
+ return type_ in SIMPLE_TYPES
141
+
142
+ def is_special(type_):
143
+ """
144
+ @return True if type_ is a special type (i.e. builtin represented as a class instead of a primitive)
145
+ @rtype: bool
146
+ """
147
+ return type_ in _SPECIAL_TYPES
148
+
149
+ def get_special(type_):
150
+ """
151
+ @return: special type handler for type_ or None
152
+ @rtype: L{Special}
153
+ """
154
+ return _SPECIAL_TYPES.get(type_, None)
155
+
156
+ ################################################################################
157
+ # Special type handling for ROS builtin types that are not primitives
158
+
159
+ class Special:
160
+
161
+ def __init__(self, constructor, post_deserialize, import_str):
162
+ """
163
+ @param constructor: expression to instantiate new type instance for deserialization
164
+ @type constructor: str
165
+ @param post_Deserialize: format string for expression to evaluate on type instance after deserialization is complete.
166
+ @type post_Deserialize: str
167
+ variable name will be passed in as the single argument to format string.
168
+ @param import_str: import to include if type is present
169
+ @type import_str: str
170
+ """
171
+ self.constructor = constructor
172
+ self.post_deserialize = post_deserialize
173
+ self.import_str = import_str
174
+
175
+ def get_post_deserialize(self, varname):
176
+ """
177
+ @return: Post-deserialization code to executed (unindented) or
178
+ None if no post-deserialization is required
179
+ @rtype: str
180
+ """
181
+ if self.post_deserialize:
182
+ return self.post_deserialize%varname
183
+ else:
184
+ return None
185
+
186
+ _SPECIAL_TYPES = {
187
+ roslib.msgs.HEADER: Special('Std_msgs::Header.new', None, 'require "std_msgs/Header"'),
188
+ roslib.msgs.TIME: Special('ROS::Time.new', None, 'require "ros/time"'),
189
+ roslib.msgs.DURATION: Special('ROS::Duration.new', None, 'require "ros/duration"'),
190
+ }
191
+
192
+ ################################################################################
193
+ # utilities
194
+
195
+ # #671
196
+ def default_value(field_type, default_package):
197
+ """
198
+ Compute default value for field_type
199
+ @param default_package: default package
200
+ @type default_package: str
201
+ @param field_type str: ROS .msg field type
202
+ @type field_type: ROS .msg field type
203
+ @return: default value encoded in Python string representation
204
+ @rtype: str
205
+ """
206
+ if field_type in ['byte', 'int8', 'int16', 'int32', 'int64',\
207
+ 'char', 'uint8', 'uint16', 'uint32', 'uint64']:
208
+ return '0'
209
+ elif field_type in ['float32', 'float64']:
210
+ return '0.0'
211
+ elif field_type == 'string':
212
+ # strings, char[], and uint8s are all optimized to be strings
213
+ return "''"
214
+ elif field_type == 'bool':
215
+ return 'false'
216
+ elif field_type.endswith(']'): # array type
217
+ base_type, is_array, array_len = roslib.msgs.parse_type(field_type)
218
+ if base_type in ['char', 'uint8']:
219
+ # strings, char[], and uint8s are all optimized to be strings
220
+ if array_len is not None:
221
+ return "[0].pack('c')*%s"%array_len
222
+ else:
223
+ return "''"
224
+ elif array_len is None: #var-length
225
+ return '[]'
226
+ else: # fixed-length, fill values
227
+ def_val = default_value(base_type, default_package)
228
+ return '[' + ','.join(itertools.repeat(def_val, array_len)) + ']'
229
+ else:
230
+ return compute_constructor(default_package, field_type)
231
+
232
+ def flatten(msg):
233
+ """
234
+ Flattens the msg spec so that embedded message fields become
235
+ direct references. The resulting MsgSpec isn't a true/legal
236
+ L{MsgSpec} and should only be used for serializer generation
237
+ @param msg: msg to flatten
238
+ @type msg: L{MsgSpec}
239
+ @return: flatten message
240
+ @rtype: L{MsgSpec}
241
+ """
242
+ new_types = []
243
+ new_names = []
244
+ for t, n in zip(msg.types, msg.names):
245
+ #flatten embedded types - note: bug #59
246
+ if roslib.msgs.is_registered(t):
247
+ msg_spec = flatten(roslib.msgs.get_registered(t))
248
+ new_types.extend(msg_spec.types)
249
+ for n2 in msg_spec.names:
250
+ new_names.append(n+'.'+n2)
251
+ else:
252
+ #I'm not sure if it's a performance win to flatten fixed-length arrays
253
+ #as you get n __getitems__ method calls vs. a single *array call
254
+ new_types.append(t)
255
+ new_names.append(n)
256
+ return roslib.msgs.MsgSpec(new_types, new_names, msg.constants, msg.text)
257
+
258
+ def make_ruby_safe(spec):
259
+ """
260
+ Remap field/constant names in spec to avoid collision with Python reserved words.
261
+ @param spec: msg spec to map to new, python-safe field names
262
+ @type spec: L{MsgSpec}
263
+ @return: python-safe message specification
264
+ @rtype: L{MsgSpec}
265
+ """
266
+ new_c = [roslib.msgs.Constant(c.type, _remap_reserved(c.name), c.val, c.val_text) for c in spec.constants]
267
+ return roslib.msgs.MsgSpec(spec.types, [_remap_reserved(n) for n in spec.names], new_c, spec.text)
268
+
269
+ def _remap_reserved(field_name):
270
+ """
271
+ Map field_name to a python-safe representation, if necessary
272
+ @param field_name: msg field name
273
+ @type field_name: str
274
+ @return: remapped name
275
+ @rtype: str
276
+ """
277
+ # include 'self' as well because we are within a class instance
278
+ if field_name in ['BEGIN', 'END', 'alias', 'and', 'begin', 'break', 'case', 'class', 'def', 'defined?', 'do', 'else', 'elsif', 'end', 'ensure', 'false', 'for', 'if', 'in', 'module', 'next', 'nil', 'not', 'or', 'redo', 'rescue', 'retry', 'return', 'self', 'super', 'then', 'true', 'undef', 'unless', 'until', 'when', 'while', 'yield']:
279
+ return field_name + "_"
280
+ return field_name
281
+
282
+ ################################################################################
283
+ # (de)serialization routines
284
+
285
+ def compute_struct_pattern(types):
286
+ """
287
+ @param types: type names
288
+ @type types: [str]
289
+ @return: format string for struct if types are all simple. Otherwise, return None
290
+ @rtype: str
291
+ """
292
+ if not types: #important to filter None and empty first
293
+ return None
294
+ try:
295
+ return ''.join([SIMPLE_TYPES_DICT[t] for t in types])
296
+ except:
297
+ return None
298
+
299
+ def compute_post_deserialize(type_, varname):
300
+ """
301
+ Compute post-deserialization code for type_, if necessary
302
+ @return: code to execute post-deserialization (unindented), or None if not necessary.
303
+ @rtype: str
304
+ """
305
+ s = get_special(type_)
306
+ if s is not None:
307
+ return s.get_post_deserialize(varname)
308
+
309
+ def compute_constructor(package, type_):
310
+ """
311
+ Compute python constructor expression for specified message type implementation
312
+ @param package str: package that type is being imported into. Used
313
+ to resolve type_ if package is not specified.
314
+ @type package: str
315
+ @param type_: message type
316
+ @type type_: str
317
+ """
318
+ if is_special(type_):
319
+ return get_special(type_).constructor
320
+ else:
321
+ base_pkg, base_type_ = compute_pkg_type(package, type_)
322
+ if not roslib.msgs.is_registered("%s/%s"%(base_pkg,base_type_)):
323
+ return None
324
+ else:
325
+ return '%s::%s.new'%(base_pkg.capitalize(), base_type_)
326
+
327
+ def compute_pkg_type(package, type_):
328
+ """
329
+ @param package: package that type is being imported into
330
+ @type package: str
331
+ @param type: message type (package resource name)
332
+ @type type: str
333
+ @return (str, str): python package and type name
334
+ """
335
+ splits = type_.split(roslib.msgs.SEP)
336
+ if len(splits) == 1:
337
+ return package, splits[0]
338
+ elif len(splits) == 2:
339
+ return tuple(splits)
340
+ else:
341
+ raise MsgGenerationException("illegal message type: %s"%type_)
342
+
343
+ def compute_import(package, type_):
344
+ """
345
+ Compute python import statement for specified message type implementation
346
+ @param package: package that type is being imported into
347
+ @type package: str
348
+ @param type_: message type (package resource name)
349
+ @type type_: str
350
+ @return: list of import statements (no newline) required to use type_ from package
351
+ @rtype: [str]
352
+ """
353
+ # orig_base_type is the unresolved type
354
+ orig_base_type = roslib.msgs.base_msg_type(type_) # strip array-suffix
355
+ # resolve orig_base_type based on the current package context.
356
+ # base_type is the resolved type stripped of any package name.
357
+ # pkg is the actual package of type_.
358
+ pkg, base_type = compute_pkg_type(package, orig_base_type)
359
+ type_str = "%s/%s"%(pkg, base_type) # compute fully-qualified type
360
+ # important: have to do is_builtin check first. We do this check
361
+ # against the unresolved type builtins/specials are never
362
+ # relative. This requires some special handling for Header, which has
363
+ # two names (Header and std_msgs/Header).
364
+ if roslib.msgs.is_builtin(orig_base_type) or \
365
+ roslib.msgs.is_header_type(orig_base_type):
366
+ # of the builtin types, only special types require import
367
+ # handling. we switch to base_type as special types do not
368
+ # include package names.
369
+ if is_special(base_type):
370
+ retval = [get_special(base_type).import_str]
371
+ else:
372
+ retval = []
373
+ elif not roslib.msgs.is_registered(type_str):
374
+ retval = []
375
+ else:
376
+ retval = ['require "%s/%s"'%(pkg, base_type)]
377
+ for t in get_registered_ex(type_str).types:
378
+ sub = compute_import(package, t)
379
+ retval.extend([x for x in sub if not x in retval])
380
+ return retval
381
+
382
+ def compute_full_text_escaped(gen_deps_dict):
383
+ """
384
+ Same as roslib.gentools.compute_full_text, except that the
385
+ resulting text is escaped to be safe for Python's triple-quote string
386
+ quoting
387
+
388
+ @param get_deps_dict: dictionary returned by get_dependencies call
389
+ @type get_deps_dict: dict
390
+ @return: concatenated text for msg/srv file and embedded msg/srv types. Text will be escaped for triple-quote
391
+ @rtype: str
392
+ """
393
+ msg_definition = roslib.gentools.compute_full_text(gen_deps_dict)
394
+ msg_definition = msg_definition.replace('"', '\\"')
395
+ return msg_definition
396
+
397
+ def reduce_pattern(pattern):
398
+ """
399
+ Optimize the struct format pattern.
400
+ @param pattern: struct pattern
401
+ @type pattern: str
402
+ @return: optimized struct pattern
403
+ @rtype: str
404
+ """
405
+ if not pattern or len(pattern) == 1 or '%' in pattern:
406
+ return pattern
407
+ prev = pattern[0]
408
+ count = 1
409
+ new_pattern = ''
410
+ nums = [str(i) for i in range(0, 9)]
411
+ for c in pattern[1:]:
412
+ if c == prev and not c in nums:
413
+ count += 1
414
+ else:
415
+ if count > 1:
416
+ new_pattern = new_pattern + str(count) + prev
417
+ else:
418
+ new_pattern = new_pattern + prev
419
+ prev = c
420
+ count = 1
421
+ if count > 1:
422
+ new_pattern = new_pattern + str(count) + c
423
+ else:
424
+ new_pattern = new_pattern + prev
425
+ return new_pattern
426
+
427
+ ## @param expr str: string python expression that is evaluated for serialization
428
+ ## @return str: python call to write value returned by expr to serialization buffer
429
+ def serialize(expr):
430
+ return "buff.write(%s)"%expr
431
+
432
+ # int32 is very common due to length serialization, so it is special cased
433
+ def int32_pack(var):
434
+ """
435
+ @param var: variable name
436
+ @type var: str
437
+ @return: struct packing code for an int32
438
+ """
439
+ return serialize('@@struct_L.pack(%s)'%var)
440
+
441
+ # int32 is very common due to length serialization, so it is special cased
442
+ def int32_unpack(var, buff):
443
+ """
444
+ @param var: variable name
445
+ @type var: str
446
+ @return: struct unpacking code for an int32
447
+ """
448
+ return '(%s,) = @@struct_L.unpack(%s)'%(var, buff)
449
+
450
+ #NOTE: '<' = little endian
451
+ def pack(pattern, vars):
452
+ """
453
+ create struct.pack call for when pattern is a string pattern
454
+ @param pattern: pattern for pack
455
+ @type pattern: str
456
+ @param vars: name of variables to pack
457
+ @type vars: str
458
+ """
459
+ # - store pattern in context
460
+ pattern = reduce_pattern(pattern)
461
+ add_pattern(pattern)
462
+ return serialize("@@struct_%s.pack(%s)"%(convert_to_ruby_pattern(pattern),
463
+ vars))
464
+ def pack2(pattern, vars):
465
+ """
466
+ create struct.pack call for when pattern is the name of a variable
467
+ @param pattern: name of variable storing string pattern
468
+ @type pattern: struct
469
+ @param vars: name of variables to pack
470
+ @type vars: str
471
+ """
472
+ return serialize("%s.pack(%s)"%(vars, pattern))
473
+ def unpack(var, pattern, buff):
474
+ """
475
+ create struct.unpack call for when pattern is a string pattern
476
+ @param var: name of variable to unpack
477
+ @type var: str
478
+ @param pattern: pattern for pack
479
+ @type pattern: str
480
+ @param buff: buffer to unpack from
481
+ @type buff: str
482
+ """
483
+ # - store pattern in context
484
+ pattern = reduce_pattern(pattern)
485
+ add_pattern(pattern)
486
+ return var + " = @@struct_%s.unpack(%s)"%(convert_to_ruby_pattern(pattern),
487
+ buff)
488
+ def unpack2(var, pattern, buff):
489
+ """
490
+ Create struct.unpack call for when pattern refers to variable
491
+ @param var: variable the stores the result of unpack call
492
+ @type var: str
493
+ @param pattern: name of variable that unpack will read from
494
+ @type pattern: str
495
+ @param buff: buffer that the unpack reads from
496
+ @type buff: StringIO
497
+ """
498
+ return "%s = %s.unpack(%s)"%(var, buff, pattern)
499
+
500
+ ################################################################################
501
+ # numpy support
502
+
503
+ # this could obviously be directly generated, but it's nice to abstract
504
+
505
+ ## maps ros msg types to numpy types
506
+ _NUMPY_DTYPE = {
507
+ 'float32': 'numpy.float32',
508
+ 'float64': 'numpy.float64',
509
+ 'bool': 'numpy.bool',
510
+ 'int8': 'numpy.int8',
511
+ 'int16': 'numpy.int16',
512
+ 'int32': 'numpy.int32',
513
+ 'int64': 'numpy.int64',
514
+ 'uint8': 'numpy.uint8',
515
+ 'uint16': 'numpy.uint16',
516
+ 'uint32': 'numpy.uint32',
517
+ 'uint64': 'numpy.uint64',
518
+ # deprecated type
519
+ 'char' : 'numpy.uint8',
520
+ 'byte' : 'numpy.int8',
521
+ }
522
+
523
+ ################################################################################
524
+ # (De)serialization generators
525
+
526
+ _serial_context = ''
527
+ _context_stack = []
528
+
529
+ _counter = 0
530
+ def next_var():
531
+ # we could optimize this by reusing vars once the context is popped
532
+ global _counter
533
+ _counter += 1
534
+ return '_v%s'%_counter
535
+
536
+ def push_context(context):
537
+ """
538
+ Push new variable context onto context stack. The context stack
539
+ manages field-reference context for serialization, e.g. 'self.foo'
540
+ vs. 'self.bar.foo' vs. 'var.foo'
541
+ """
542
+ global _serial_context, _context_stack
543
+ _context_stack.append(_serial_context)
544
+ _serial_context = context
545
+
546
+ def pop_context():
547
+ """
548
+ Pop variable context from context stack. The context stack manages
549
+ field-reference context for serialization, e.g. 'self.foo'
550
+ vs. 'self.bar.foo' vs. 'var.foo'
551
+ """
552
+ global _serial_context
553
+ _serial_context = _context_stack.pop()
554
+
555
+ _context_patterns = []
556
+ def add_pattern(p):
557
+ """
558
+ Record struct pattern that's been used for (de)serialization
559
+ """
560
+ _context_patterns.append(p)
561
+ def clear_patterns():
562
+ """
563
+ Clear record of struct pattern that have been used for (de)serialization
564
+ """
565
+ del _context_patterns[:]
566
+ def get_patterns():
567
+ """
568
+ @return: record of struct pattern that have been used for (de)serialization
569
+ """
570
+ return _context_patterns[:]
571
+
572
+ # These are the workhorses of the message generation. The generators
573
+ # are implemented as iterators, where each iteration value is a line
574
+ # of Python code. The generators will invoke underlying generators,
575
+ # using the context stack to manage any changes in variable-naming, so
576
+ # that code can be reused as much as possible.
577
+
578
+ def len_serializer_generator(var, is_string, serialize):
579
+ """
580
+ Generator for array-length serialization (32-bit, little-endian unsigned integer)
581
+ @param var: variable name
582
+ @type var: str
583
+ @param is_string: if True, variable is a string type
584
+ @type is_string: bool
585
+ @param serialize bool: if True, generate code for
586
+ serialization. Other, generate code for deserialization
587
+ @type serialize: bool
588
+ """
589
+ if serialize:
590
+ yield "length = %s.length"%var
591
+ # NOTE: it's more difficult to save a call to struct.pack with
592
+ # the array length as we are already using *array_val to pass
593
+ # into struct.pack as *args. Although it's possible that
594
+ # Python is not optimizing it, it is potentially worse for
595
+ # performance to attempt to combine
596
+ if not is_string:
597
+ yield int32_pack("length")
598
+ else:
599
+ yield "start = end_point"
600
+ yield "end_point += 4"
601
+ yield int32_unpack('length', 'str[start..(end_point-1)]') #4 = struct.calcsize('<i')
602
+
603
+ def string_serializer_generator(package, type_, name, serialize):
604
+ """
605
+ Generator for string types. similar to arrays, but with more
606
+ efficient call to struct.pack.
607
+ @param name: spec field name
608
+ @type name: str
609
+ @param serialize: if True, generate code for
610
+ serialization. Other, generate code for deserialization
611
+ @type serialize: bool
612
+ """
613
+ # don't optimize in deserialization case as assignment doesn't
614
+ # work
615
+ if _serial_context and serialize:
616
+ # optimize as string serialization accesses field twice
617
+ yield "_x = %s%s"%(_serial_context, name)
618
+ var = "_x"
619
+ else:
620
+ var = _serial_context+name
621
+
622
+ # the length generator is a noop if serialize is True as we
623
+ # optimize the serialization call.
624
+ base_type, is_array, array_len = roslib.msgs.parse_type(type_)
625
+ # - don't serialize length for fixed-length arrays of bytes
626
+ if base_type not in ['uint8', 'char'] or array_len is None:
627
+ for y in len_serializer_generator(var, True, serialize):
628
+ yield y #serialize string length
629
+
630
+ if serialize:
631
+ #serialize length and string together
632
+
633
+ #check to see if its a uint8/char type, in which case we need to convert to string before serializing
634
+ base_type, is_array, array_len = roslib.msgs.parse_type(type_)
635
+ if base_type in ['uint8', 'char']:
636
+ yield "# - if encoded as a list instead, serialize as bytes instead of string"
637
+ if array_len is None:
638
+ yield "if type(%s) in [list, tuple]"%var
639
+ yield INDENT+pack2('"La#{length}"', "[length, *%s]"%var)
640
+ yield "else"
641
+ yield INDENT+pack2('"La#{length}"', "[length, %s]"%var)
642
+ yield "end"
643
+ else:
644
+ yield "if type(%s) in [list, tuple]"%var
645
+ yield INDENT+pack('C%s'%array_len, "*%s"%var)
646
+ yield "else"
647
+ yield INDENT+pack('C%s'%array_len, var)
648
+ yield "end"
649
+ else:
650
+ # py3k: struct.pack() now only allows bytes for the s string pack code.
651
+ # FIXME: for py3k, this needs to be w/ encode, but this interferes with actual byte data
652
+ #yield pack2("'<I%ss'%length", "length, %s.encode()"%var) #Py3k bugfix (see http://docs.python.org/dev/whatsnew/3.2.html#porting-to-python-3-2)
653
+ yield pack2('"La#{length}"', "[length, %s]"%var)
654
+ else:
655
+ yield "start = end_point"
656
+ if array_len is not None:
657
+ yield "end_point += %s" % array_len
658
+ else:
659
+ yield "end_point += length"
660
+ yield "%s = str[start..(end_point-1)]" % var
661
+
662
+ def array_serializer_generator(package, type_, name, serialize, is_numpy):
663
+ """
664
+ Generator for array types
665
+ @raise MsgGenerationException: if array spec is invalid
666
+ """
667
+ base_type, is_array, array_len = roslib.msgs.parse_type(type_)
668
+ if not is_array:
669
+ raise MsgGenerationException("Invalid array spec: %s"%type_)
670
+ var_length = array_len is None
671
+
672
+ # handle fixed-size byte arrays could be slightly more efficient
673
+ # as we recalculated the length in the generated code.
674
+ if base_type in ['char', 'uint8']: #treat unsigned int8 arrays as string type
675
+ for y in string_serializer_generator(package, type_, name, serialize):
676
+ yield y
677
+ return
678
+
679
+ var = _serial_context+name
680
+ try:
681
+ # yield length serialization, if necessary
682
+ if var_length:
683
+ for y in len_serializer_generator(var, False, serialize):
684
+ yield y #serialize array length
685
+ length = None
686
+ else:
687
+ length = array_len
688
+
689
+ #optimization for simple arrays
690
+ if is_simple(base_type):
691
+ if var_length:
692
+ pattern = compute_struct_pattern([base_type])
693
+ yield 'pattern = "%s#{length}"'%convert_to_ruby_pattern(pattern)
694
+ if serialize:
695
+ if is_numpy:
696
+ yield pack_numpy(var)
697
+ else:
698
+ yield pack2('pattern', "*"+var)
699
+ else:
700
+ yield "start = end_point"
701
+ yield 'end_point += ROS::Struct::calc_size("#{pattern}")'
702
+ if is_numpy:
703
+ dtype = _NUMPY_DTYPE[base_type]
704
+ yield unpack_numpy(var, 'length', dtype, 'str[start..(end_point-1)]')
705
+ else:
706
+ yield unpack2(var, 'pattern', 'str[start..(end_point-1)]')
707
+ else:
708
+ pattern = "%s%s"%(length, compute_struct_pattern([base_type]))
709
+ if serialize:
710
+ if is_numpy:
711
+ yield pack_numpy(var)
712
+ else:
713
+ yield pack(pattern, "*"+var)
714
+ else:
715
+ yield "start = end_point"
716
+ yield "end_point += %s"%struct.calcsize('%s'%convert_to_ruby_pattern(pattern))
717
+ if is_numpy:
718
+ dtype = _NUMPY_DTYPE[base_type]
719
+ yield unpack_numpy(var, length, dtype, 'str[start..(end_point-1)]')
720
+ else:
721
+ yield unpack(var, pattern, 'str[start..(end_point-1)]')
722
+ if not serialize and base_type == 'bool':
723
+ # convert uint8 to bool
724
+ if base_type == 'bool':
725
+ yield "%s = map(bool, %s)"%(var, var)
726
+
727
+ else:
728
+ #generic recursive serializer
729
+ #NOTE: this is functionally equivalent to the is_registered branch of complex_serializer_generator
730
+
731
+ # choose a unique temporary variable for iterating
732
+ loop_var = 'val%s'%len(_context_stack)
733
+
734
+ # compute the variable context and factory to use
735
+ if base_type == 'string':
736
+ push_context('')
737
+ factory = string_serializer_generator(package, base_type, loop_var, serialize)
738
+ else:
739
+ push_context('%s.'%loop_var)
740
+ factory = serializer_generator(package, get_registered_ex(base_type), serialize, is_numpy)
741
+
742
+ if serialize:
743
+ yield 'for %s in %s'%(loop_var, var)
744
+ else:
745
+ yield '%s = []'%var
746
+ if var_length:
747
+ yield 'length.times do'
748
+ else:
749
+ yield '%s.times do'%length
750
+ if base_type != 'string':
751
+ yield INDENT + '%s = %s'%(loop_var, compute_constructor(package, base_type))
752
+ for y in factory:
753
+ yield INDENT + y
754
+ if not serialize:
755
+ yield INDENT + '%s.push(%s)'%(var, loop_var)
756
+ pop_context()
757
+ yield 'end'
758
+
759
+ except MsgGenerationException:
760
+ raise #re-raise
761
+ except Exception as e:
762
+ raise MsgGenerationException(e) #wrap
763
+
764
+ def complex_serializer_generator(package, type_, name, serialize, is_numpy):
765
+ """
766
+ Generator for serializing complex type
767
+ @param serialize: if True, generate serialization
768
+ code. Otherwise, deserialization code.
769
+ @type serialize: bool
770
+ @param is_numpy: if True, generate serializer code for numpy
771
+ datatypes instead of Python lists
772
+ @type is_numpy: bool
773
+ @raise MsgGenerationException: if type is not a valid
774
+ """
775
+ # ordering of these statements is important as we mutate the type
776
+ # string we are checking throughout. parse_type strips array
777
+ # brackets, then we check for the 'complex' builtin types (string,
778
+ # time, duration, Header), then we canonicalize it to an embedded
779
+ # message type.
780
+ _, is_array, _ = roslib.msgs.parse_type(type_)
781
+
782
+ #Array
783
+ if is_array:
784
+ for y in array_serializer_generator(package, type_, name, serialize, is_numpy):
785
+ yield y
786
+ #Embedded Message
787
+ elif type_ == 'string':
788
+ for y in string_serializer_generator(package, type_, name, serialize):
789
+ yield y
790
+ else:
791
+ if not is_special(type_):
792
+ # canonicalize type
793
+ pkg, base_type = compute_pkg_type(package, type_)
794
+ type_ = "%s/%s"%(pkg, base_type)
795
+ if roslib.msgs.is_registered(type_):
796
+ # descend data structure ####################
797
+ ctx_var = next_var()
798
+ yield "%s = %s"%(ctx_var, _serial_context+name)
799
+ push_context(ctx_var+'.')
800
+ # unoptimized code
801
+ #push_context(_serial_context+name+'.')
802
+ for y in serializer_generator(package, get_registered_ex(type_), serialize, is_numpy):
803
+ yield y #recurs on subtype
804
+ pop_context()
805
+ else:
806
+ #Invalid
807
+ raise MsgGenerationException("Unknown type: %s. Package context is %s"%(type_, package))
808
+
809
+ def simple_serializer_generator(spec, start, end_point, serialize): #primitives that can be handled with struct
810
+ """
811
+ Generator (de)serialization code for multiple fields from spec
812
+ @param spec: MsgSpec
813
+ @type spec: MsgSpec
814
+ @param start: first field to serialize
815
+ @type start: int
816
+ @param end: last field to serialize
817
+ @type end: int
818
+ """
819
+ # optimize member var access
820
+ if end_point - start > 1 and _serial_context.endswith('.'):
821
+ yield '_x = '+_serial_context[:-1]
822
+ vars_ = '_x.' + (', _x.').join(spec.names[start:end_point])
823
+ else:
824
+ vars_ = _serial_context + (', '+_serial_context).join(spec.names[start:end_point])
825
+
826
+ pattern = compute_struct_pattern(spec.types[start:end_point])
827
+ if serialize:
828
+ yield pack(pattern, vars_)
829
+ else:
830
+ yield "start = end_point"
831
+ yield "end_point += ROS::Struct::calc_size('%s')"%convert_to_ruby_pattern(reduce_pattern(pattern))
832
+ yield unpack('(%s,)'%vars_, pattern, 'str[start..(end_point-1)]')
833
+
834
+ # convert uint8 to bool. this doesn't add much value as Python
835
+ # equality test on a field will return that True == 1, but I
836
+ # want to be consistent with bool
837
+ bool_vars = [(f, t) for f, t in zip(spec.names[start:end_point], spec.types[start:end_point]) if t == 'bool']
838
+ for f, t in bool_vars:
839
+ #TODO: could optimize this as well
840
+ var = _serial_context+f
841
+ yield "%s = bool(%s)"%(var, var)
842
+
843
+ def serializer_generator(package, spec, serialize, is_numpy):
844
+ """
845
+ Python generator that yields un-indented python code for
846
+ (de)serializing MsgSpec. The code this yields is meant to be
847
+ included in a class method and cannot be used
848
+ standalone. serialize_fn_generator and deserialize_fn_generator
849
+ wrap method to provide appropriate class field initializations.
850
+ @param package: name of package the spec is being used in
851
+ @type package: str
852
+ @param serialize: if True, yield serialization
853
+ code. Otherwise, yield deserialization code.
854
+ @type serialize: bool
855
+ @param is_numpy: if True, generate serializer code for numpy datatypes instead of Python lists
856
+ @type is_numpy: bool
857
+ """
858
+ # Break spec into chunks of simple (primitives) vs. complex (arrays, etc...)
859
+ # Simple types are batch serialized using the python struct module.
860
+ # Complex types are individually serialized
861
+ if spec is None:
862
+ raise MsgGenerationException("spec is none")
863
+ names, types = spec.names, spec.types
864
+ if serialize and not len(names): #Empty
865
+ yield "pass"
866
+ return
867
+
868
+ # iterate through types. whenever we encounter a non-simple type,
869
+ # yield serializer for any simple types we've encountered until
870
+ # then, then yield the complex type serializer
871
+ curr = 0
872
+ for (i, type_) in enumerate(types):
873
+ if not is_simple(type_):
874
+ if i != curr: #yield chunk of simples
875
+ for y in simple_serializer_generator(spec, curr, i, serialize):
876
+ yield y
877
+ curr = i+1
878
+ for y in complex_serializer_generator(package, type_, names[i], serialize, is_numpy):
879
+ yield y
880
+ if curr < len(types): #yield rest of simples
881
+ for y in simple_serializer_generator(spec, curr, len(types), serialize):
882
+ yield y
883
+
884
+ def serialize_fn_generator(package, spec, is_numpy=False):
885
+ """
886
+ generator for body of serialize() function
887
+ @param is_numpy: if True, generate serializer code for numpy
888
+ datatypes instead of Python lists
889
+ @type is_numpy: bool
890
+ """
891
+ # method-var context #########
892
+ yield "begin"
893
+ push_context('@')
894
+ #NOTE: we flatten the spec for optimal serialization
895
+ for y in serializer_generator(package, flatten(spec), True, is_numpy):
896
+ yield " "+y
897
+ pop_context()
898
+ yield """rescue => exception
899
+ raise "some erro in serialize: #{exception}"
900
+ """
901
+ yield "end"
902
+ # done w/ method-var context #
903
+
904
+ def deserialize_fn_generator(package, spec, is_numpy=False):
905
+ """
906
+ generator for body of deserialize() function
907
+ @param is_numpy: if True, generate serializer code for numpy
908
+ datatypes instead of Python lists
909
+ @type is_numpy: bool
910
+ """
911
+ yield "begin"
912
+
913
+ #Instantiate embedded type classes
914
+ for type_, name in spec.fields():
915
+ if roslib.msgs.is_registered(type_):
916
+ yield " if @%s == nil"%name
917
+ yield " @%s = %s"%(name, compute_constructor(package, type_))
918
+ yield " end"
919
+ yield " end_point = 0" #initialize var
920
+
921
+ # method-var context #########
922
+ push_context('@')
923
+ #NOTE: we flatten the spec for optimal serialization
924
+ for y in serializer_generator(package, flatten(spec), False, is_numpy):
925
+ yield " "+y
926
+ pop_context()
927
+ # done w/ method-var context #
928
+
929
+ # generate post-deserialization code
930
+ for type_, name in spec.fields():
931
+ code = compute_post_deserialize(type_, "@%s"%name)
932
+ if code:
933
+ yield " %s"%code
934
+
935
+ yield " return self"
936
+ yield "rescue => exception"
937
+ yield """ raise \"message DeserializationError: #{exception}\" #most likely buffer underfill
938
+ end"""
939
+
940
+ def msg_generator_internal(package, name, spec):
941
+ """
942
+ Python code generator for .msg files. Takes in a package name,
943
+ message name, and message specification and generates a Python
944
+ message class.
945
+
946
+ @param package: name of package for message
947
+ @type package: str
948
+ @param name: base type name of message, e.g. 'Empty', 'String'
949
+ @type name: str
950
+ @param spec: parsed .msg specification
951
+ @type spec: L{MsgSpec}
952
+ """
953
+
954
+ # #2990: have to compute md5sum before any calls to make_python_safe
955
+
956
+ # generate dependencies dictionary. omit files calculation as we
957
+ # rely on in-memory MsgSpecs instead so that we can generate code
958
+ # for older versions of msg files
959
+ try:
960
+ gendeps_dict = roslib.gentools.get_dependencies(spec, package, compute_files=False)
961
+ except roslib.msgs.MsgSpecException as e:
962
+ raise MsgGenerationException("Cannot generate .msg for %s/%s: %s"%(package, name, str(e)))
963
+ md5sum = roslib.gentools.compute_md5(gendeps_dict)
964
+
965
+ # remap spec names to be Python-safe
966
+ spec = make_ruby_safe(spec)
967
+ spec_names = spec.names
968
+
969
+ # for not capital class like tfMessages
970
+ capitalized_name = name[0].upper() + name[1:]
971
+
972
+ # #1807 : this will be much cleaner when msggenerator library is
973
+ # rewritten to not use globals
974
+ clear_patterns()
975
+
976
+ yield '# autogenerated by genmsg_ruby from %s.msg. Do not edit.'%name
977
+ yield "require 'ros/message'\n"
978
+ import_strs = []
979
+ for t in spec.types:
980
+ import_strs.extend(compute_import(package, t))
981
+ import_strs = set(import_strs)
982
+ for i in import_strs:
983
+ if i:
984
+ yield i
985
+
986
+ yield ''
987
+
988
+ yield "module %s\n"%package.capitalize()
989
+
990
+ fulltype = '%s%s%s'%(package, roslib.msgs.SEP, name)
991
+
992
+ #Yield data class first, e.g. Point2D
993
+ yield 'class %s <::ROS::Message'%capitalized_name
994
+
995
+ yield """ def self.md5sum
996
+ \"%s\"
997
+ end
998
+ """%(md5sum)
999
+ yield """ def self.type
1000
+ \"%s\"
1001
+ end
1002
+ """%(fulltype)
1003
+ if spec.has_header():
1004
+ bool_val = 'true'
1005
+ else:
1006
+ bool_val = 'false'
1007
+ yield """ def has_header?
1008
+ %s
1009
+ end
1010
+ """%bool_val
1011
+ # note: we introduce an extra newline to protect the escaping from quotes in the message
1012
+ yield """ def message_definition
1013
+ \"%s\n\"
1014
+ end"""%compute_full_text_escaped(gendeps_dict)
1015
+
1016
+ if spec.constants:
1017
+ yield ' # Pseudo-constants'
1018
+ for c in spec.constants:
1019
+ if c.type == 'string':
1020
+ val = c.val
1021
+ if '"' in val and "'" in val:
1022
+ # crude escaping of \ and "
1023
+ escaped = c.val.replace('\\', '\\\\')
1024
+ escaped = escaped.replace('\"', '\\"')
1025
+ yield ' %s = "%s"'%(c.name, escaped)
1026
+ elif '"' in val: #use raw encoding for prettiness
1027
+ yield " %s = r'%s'"%(c.name, val)
1028
+ elif "'" in val: #use raw encoding for prettiness
1029
+ yield ' %s = r"%s"'%(c.name, val)
1030
+ else:
1031
+ yield " %s = '%s'"%(c.name, val)
1032
+ else:
1033
+ yield ' %s = %s'%(c.name, c.val)
1034
+ yield ''
1035
+ yield " attr_accessor "+", ".join([":"+x for x in spec_names])+"\n"
1036
+
1037
+ yield '_REPLACE_FOR_STRUCT_'
1038
+ if len(spec_names):
1039
+ yield " @@struct_L = ::ROS::Struct.new(\"L\")"
1040
+ yield " @@slot_types = ['"+"','".join(spec.types)+"']"
1041
+ else:
1042
+ yield " @@struct_L = ::ROS::Struct.new(\"L\")"
1043
+ yield " @@slot_types = []"
1044
+
1045
+ yield """
1046
+ def initialize
1047
+ # Constructor. Any message fields that are implicitly/explicitly
1048
+ # set to None will be assigned a default value. The recommend
1049
+ # use is keyword arguments as this is more robust to future message
1050
+ # changes. You cannot mix in-order arguments and keyword arguments.
1051
+ #
1052
+ # The available fields are:
1053
+ # %s
1054
+ #
1055
+ # @param args: complete set of field values, in .msg order
1056
+ # @param kwds: use keyword arguments corresponding to message field names
1057
+ # to set specific fields.
1058
+ #
1059
+ """%','.join(spec_names)
1060
+ if len(spec_names):
1061
+ yield " # message fields cannot be None, assign default values for those that are"
1062
+ if len(spec_names) > 0:
1063
+ for (t, s) in zip(spec.types, spec_names):
1064
+ yield " @%s = %s"%(s, default_value(t, package))
1065
+ yield " end"
1066
+ yield """
1067
+ def _get_types
1068
+ # internal API method
1069
+ return @slot_types
1070
+ end
1071
+
1072
+ def serialize(buff)
1073
+ # serialize message into buffer
1074
+ # @param buff: buffer
1075
+ # @type buff: StringIO"""
1076
+ for y in serialize_fn_generator(package, spec):
1077
+ yield " "+ y
1078
+ yield " end"
1079
+ yield """
1080
+ def deserialize(str)
1081
+ # unpack serialized message in str into this message instance
1082
+ # @param str: byte array of serialized message
1083
+ # @type str: str
1084
+ """
1085
+ for y in deserialize_fn_generator(package, spec):
1086
+ yield " " + y
1087
+ yield " end"
1088
+ yield "end # end of class"
1089
+ yield "end # end of module"
1090
+
1091
+ import copy
1092
+ import re
1093
+
1094
+ def convert_to_ruby_pattern(python_pattern):
1095
+ ruby_pattern = copy.copy(python_pattern)
1096
+ # get python key
1097
+ for (py, ru) in PYTHON_RUBY_DICT.iteritems():
1098
+ ruby_pattern = re.sub(py, ru, ruby_pattern)
1099
+ # swap number and char
1100
+ return re.sub(r"([0-9]+)([A-Za-z])", r'\2\1', ruby_pattern)
1101
+
1102
+ def msg_generator(package, base_name, spec):
1103
+ generated = ''
1104
+ for l in msg_generator_internal(package, base_name, spec):
1105
+ generated += l + "\n"
1106
+
1107
+ patterns = get_patterns()
1108
+ structs = ''
1109
+ for p in set(patterns):
1110
+ if p == 'I':
1111
+ continue
1112
+ ruby_p = convert_to_ruby_pattern(p)
1113
+ var_name = ' @@struct_%s'%ruby_p
1114
+ structs += '%s = ::ROS::Struct.new("%s")\n'%(var_name, ruby_p)
1115
+ clear_patterns()
1116
+ import re
1117
+ return re.sub('_REPLACE_FOR_STRUCT_', structs, generated, 1)
1118
+
1119
+ def gen_msg(path, output_dir_prefix=None):
1120
+ f = os.path.abspath(path)
1121
+ (package_dir, package) = roslib.packages.get_dir_pkg(f)
1122
+ (name, spec) = roslib.msgs.load_from_file(f, package)
1123
+ base_name = roslib.names.resource_name_base(name)
1124
+ if not output_dir_prefix:
1125
+ output_dir_prefix = '%s/msg_gen/ruby'%package_dir
1126
+ output_dir = '%s/%s'%(output_dir_prefix, package)
1127
+ if not os.path.exists(output_dir):
1128
+ os.makedirs(output_dir)
1129
+ out = open('%s/%s.rb'%(output_dir, base_name), 'w')
1130
+ out.write(msg_generator(package, base_name, spec))
1131
+ out.close()
1132
+
1133
+ if __name__ == '__main__':
1134
+ for arg in sys.argv[1:]:
1135
+ gen_msg(arg)