rosruby 0.0.1

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.
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,108 @@
1
+ # ros/tcpros/client.rb
2
+ #
3
+ # License: BSD
4
+ #
5
+ # Copyright (C) 2012 Takashi Ogura <t.ogura@gmail.com>
6
+ #
7
+
8
+ require 'socket'
9
+ require 'ros/tcpros/message'
10
+ require 'ros/tcpros/header'
11
+
12
+ module ROS::TCPROS
13
+
14
+ ##
15
+ # rosrpc's client for subscriber
16
+ #
17
+ class Client
18
+
19
+ include ::ROS::TCPROS::Message
20
+
21
+ ##
22
+ # @param [String] host host name
23
+ # @param [Integer] port port number
24
+ # @param [String] caller_id caller id of this node
25
+ # @param [String] topic_name name of this topic
26
+ # @param [Class] topic_type type of topic
27
+ # @param [String] target_uri URI of connection target
28
+ # @param [Boolean] tcp_no_delay use tcp no delay option or not
29
+ def initialize(host, port,
30
+ caller_id, topic_name, topic_type, target_uri,
31
+ tcp_no_delay)
32
+ @caller_id = caller_id
33
+ @topic_name = topic_name
34
+ @topic_type = topic_type
35
+ @port = port
36
+ @host = host
37
+ @target_uri = target_uri
38
+ @msg_queue = Queue.new
39
+ @socket = TCPSocket.open(@host, @port)
40
+ @tcp_no_delay = tcp_no_delay
41
+ if tcp_no_delay
42
+ @socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
43
+ end
44
+ @byte_received = 0
45
+ @is_running = true
46
+ end
47
+
48
+ ##
49
+ # build header data for subscription.
50
+ # @return [TCPROS::Header] built header
51
+ def build_header
52
+ header = Header.new
53
+ header.push_data("callerid", @caller_id)
54
+ header.push_data("topic", @topic_name)
55
+ header.push_data("md5sum", @topic_type.md5sum)
56
+ header.push_data("type", @topic_type.type)
57
+ if @tcp_no_delay
58
+ header.push_data("tcp_nodelay", '1')
59
+ else
60
+ header.push_data("tcp_nodelay", '0')
61
+ end
62
+ header
63
+ end
64
+
65
+ ##
66
+ # start recieving data.
67
+ # The received messages are pushed into a message queue.
68
+ def start
69
+ write_header(@socket, build_header)
70
+ read_header(@socket)
71
+ @thread = Thread.start do
72
+ while @is_running
73
+ data = read_all(@socket)
74
+ msg = @topic_type.new
75
+ msg.deserialize(data)
76
+ @byte_received += data.length
77
+ @msg_queue.push(msg)
78
+ end
79
+ end
80
+ end
81
+
82
+ ##
83
+ # close the connection.
84
+ # kill the thread if it is not response.
85
+ def shutdown
86
+ @is_running = false
87
+ if not @thread.join(0.1)
88
+ Thread::kill(@thread)
89
+ end
90
+ if not @socket.closed?
91
+ @socket.close
92
+ end
93
+ end
94
+
95
+ # @return [Integer] port number of this client
96
+ attr_reader :port
97
+ # @return [String] host name
98
+ attr_reader :host
99
+ # @return [Queue] message queue
100
+ attr_reader :msg_queue
101
+ # @return [Integer] received byte
102
+ attr_reader :byte_received
103
+ # @return [String] URI of connection target
104
+ attr_reader :target_uri
105
+ # @return [String] id for slave API
106
+ attr_accessor :id
107
+ end
108
+ end
@@ -0,0 +1,89 @@
1
+ # ros/tcpros/message.rb
2
+ #
3
+ # License: BSD
4
+ #
5
+ # Copyright (C) 2012 Takashi Ogura <t.ogura@gmail.com>
6
+ #
7
+
8
+ require 'ros/ros'
9
+
10
+ module ROS::TCPROS
11
+
12
+ ##
13
+ # header of rorpc's protocol
14
+ #
15
+ class Header
16
+
17
+ # rosrpc uses this wild card for cancel md5sum check or any.
18
+ WILD_CARD = '*'
19
+
20
+ # initialize with hash
21
+ # @param [Hash] data
22
+ def initialize(data={})
23
+ @data = data
24
+ end
25
+
26
+ # add key-value data to this header.
27
+ # @param [String] key key for header
28
+ # @param [String] value value for key
29
+ # @return [Header] self
30
+ def push_data(key, value)
31
+ if (not key.kind_of?(String)) or (not value.kind_of?(String))
32
+ raise ArgumentError::new('header key and value must be string')
33
+ end
34
+ @data[key] = value
35
+ self
36
+ end
37
+
38
+ # @param [String] key
39
+ # @return [String] value of key
40
+ def get_data(key)
41
+ @data[key]
42
+ end
43
+
44
+ alias_method :[]=, :push_data
45
+ alias_method :[], :get_data
46
+
47
+ # deserialize the data to header.
48
+ # this does not contain total byte number.
49
+ # @param [String] data
50
+ # @return [Header] self
51
+ def deserialize(data)
52
+ while data.length > 0
53
+ len, data = data.unpack('Va*')
54
+ msg = data[0..(len-1)]
55
+ equal_position = msg.index('=')
56
+ key = msg[0..(equal_position-1)]
57
+ value = msg[(equal_position+1)..-1]
58
+ @data[key] = value
59
+ data = data[(len)..-1]
60
+ end
61
+ self
62
+ end
63
+
64
+ ##
65
+ # validate the key with the value.
66
+ # if it is WILD_CARD, it is ok!
67
+ # @param [String] key
68
+ # @param [String] value
69
+ # @return [Boolean] check result
70
+ def valid?(key, value)
71
+ (@data[key] == value) or value == WILD_CARD
72
+ end
73
+
74
+ # serialize the data into header.
75
+ # return the byte of the serialized data.
76
+ # @param [IO] buff where to write data
77
+ # @return [Integer] byte of serialized data
78
+ def serialize(buff)
79
+ serialized_data = ''
80
+ @data.each_pair do |key, value|
81
+ data_str = key + '=' + value
82
+ serialized_data = serialized_data + [data_str.length, data_str].pack('Va*')
83
+ end
84
+ total_byte = serialized_data.length
85
+ return buff.write([total_byte, serialized_data].pack('Va*'))
86
+ end
87
+
88
+ end
89
+ end
@@ -0,0 +1,74 @@
1
+ # ros/tcpros/message.rb
2
+ #
3
+ # License: BSD
4
+ #
5
+ # Copyright (C) 2012 Takashi Ogura <t.ogura@gmail.com>
6
+ #
7
+ # super class of TCPROS connections
8
+ # document is http://ros.org/wiki/ROS/TCPROS
9
+ #
10
+ require 'stringio'
11
+ require 'ros/tcpros/header'
12
+
13
+
14
+ # TCP connection between nodes.
15
+ # protocol document is http://ros.org/wiki/ROS/TCPROS
16
+ module ROS::TCPROS
17
+
18
+ # functions for TCPROS
19
+ module Message
20
+
21
+ ##
22
+ # write message to socket.
23
+ # @param [IO] socket socket for writing
24
+ # @param [Message] msg message for writing
25
+ # @return [String] wrote data
26
+ def write_msg(socket, msg)
27
+ sio = StringIO.new('', 'r+')
28
+ len = msg.serialize(sio)
29
+ sio.rewind
30
+ data = sio.read
31
+ len = data.length
32
+ data = [len, data].pack("La#{len}")
33
+ socket.write(data)
34
+ data
35
+ end
36
+
37
+ ##
38
+ # read the size of data and read it from socket.
39
+ # @param [IO] socket socket for reading
40
+ # @return [String] received data
41
+ def read_all(socket)
42
+ total_bytes = socket.recv(4).unpack("V")[0]
43
+ if total_bytes and total_bytes > 0
44
+ socket.recv(total_bytes)
45
+ else
46
+ ''
47
+ end
48
+ end
49
+
50
+ ##
51
+ # read a connection header from socket
52
+ # @param [String] socket socket for reading
53
+ # @return [Header] header
54
+ def read_header(socket)
55
+ header = ::ROS::TCPROS::Header.new
56
+ header.deserialize(read_all(socket))
57
+ header
58
+ end
59
+
60
+ ##
61
+ # write a connection header to socket.
62
+ # @param [IO] socket socket for writing.
63
+ # @param [Header] header header data
64
+ def write_header(socket, header)
65
+ header.serialize(socket)
66
+ socket.flush
67
+ end
68
+
69
+ # @return [String] prototol string 'TCPROS'
70
+ def protocol
71
+ 'TCPROS'
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,137 @@
1
+ # ros/subscriber.rb
2
+ #
3
+ # License: BSD
4
+ #
5
+ # Copyright (C) 2012 Takashi Ogura <t.ogura@gmail.com>
6
+ #
7
+ require 'ros/tcpros/message'
8
+ require 'gserver'
9
+
10
+ module ROS::TCPROS
11
+
12
+ ##
13
+ # This is TCPROS connection for {ROS::Publihser}
14
+ #
15
+ class Server < ::GServer
16
+
17
+ include ::ROS::TCPROS::Message
18
+
19
+ # max number of connections with {TCPROS::Client}
20
+ MAX_CONNECTION = 100
21
+
22
+ ##
23
+ # @param [String] caller_id caller id of this node
24
+ # @param [String] topic_name name of this topic
25
+ # @param [Class] topic_type type of topic
26
+ # @param [Hash] options :latched, :port, host, :last_published_msg
27
+ # @option [Boolean] options :latched (false)
28
+ # @option [Integer] options :port (0) tcp port number.
29
+ # @option [String] options :host (GServer::DEFAULT_HOST) host name
30
+ # @option [Message] options :last_published_msg
31
+ def initialize(caller_id, topic_name, topic_type, options={})
32
+ if options[:port]
33
+ port = options[:port]
34
+ else
35
+ port = 0
36
+ end
37
+ if options[:host]
38
+ host = options[:host]
39
+ else
40
+ host = GServer::DEFAULT_HOST
41
+ end
42
+
43
+ super(port, host, MAX_CONNECTION)
44
+ @caller_id = caller_id
45
+ @topic_name = topic_name
46
+ @topic_type = topic_type
47
+ @msg_queue = Queue.new
48
+ @is_latched = options[:latched]
49
+ @last_published_msg = options[:last_published_msg]
50
+ @byte_sent = 0
51
+ @num_sent = 0
52
+ end
53
+
54
+ ##
55
+ # Is this latching publisher?
56
+ # @return [Boolean] is latched or not
57
+ def latching?
58
+ @is_latched
59
+ end
60
+
61
+ ##
62
+ # send a message to reciever
63
+ # @param [IO] socket socket for writing
64
+ # @param [Class] msg msg class instance
65
+ def publish_msg(socket, msg)
66
+ data = write_msg(socket, msg)
67
+ @last_published_msg = msg
68
+ # for getBusStats
69
+ @byte_sent += data.length
70
+ @num_sent += 1
71
+ end
72
+
73
+ ##
74
+ # this is called if a socket accept a connection.
75
+ # This is GServer's function
76
+ # @param [IO] socket given socket
77
+ def serve(socket) #:nodoc:
78
+ header = read_header(socket)
79
+ if check_header(header)
80
+ if header['tcp_nodelay'] == '1'
81
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
82
+ end
83
+ begin
84
+ write_header(socket, build_header)
85
+ if latching?
86
+ if @last_published_msg
87
+ publish_msg(socket, @last_published_msg)
88
+ end
89
+ end
90
+ loop do
91
+ @last_published_msg = @msg_queue.pop
92
+ publish_msg(socket, @last_published_msg)
93
+ end
94
+ rescue
95
+ socket.shutdown
96
+ end
97
+ else
98
+ socket.shutdown
99
+ p "header check error: #{header}"
100
+ raise 'header check error'
101
+ end
102
+ end
103
+
104
+ attr_reader :caller_id, :msg_queue, :byte_sent, :num_sent
105
+
106
+ # id for slave API
107
+ # @return [String]
108
+ attr_accessor :id
109
+ attr_accessor :last_published_msg
110
+
111
+ ##
112
+ # validate header for this publisher
113
+ # @param [Header] header for checking
114
+ # @return [Boolean] ok(true) or not
115
+ def check_header(header)
116
+ header.valid?('type', @topic_type.type) and
117
+ header.valid?('md5sum', @topic_type.md5sum)
118
+ end
119
+
120
+ ##
121
+ # build {TCPROS::Header} message for this publisher.
122
+ # It contains callerid, topic, md5sum, type, latching.
123
+ # @return [Header] built header
124
+ def build_header
125
+ header = Header.new
126
+ header["callerid"] = @caller_id
127
+ header["topic"] = @topic_name
128
+ header["md5sum"] = @topic_type.md5sum
129
+ header["type"] = @topic_type.type
130
+ if latching?
131
+ header["latching"] = '1'
132
+ end
133
+ header
134
+ end
135
+
136
+ end
137
+ end
@@ -0,0 +1,104 @@
1
+ # ros/tcpros/service_client.rb
2
+ #
3
+ # License: BSD
4
+ #
5
+ # Copyright (C) 2012 Takashi Ogura <t.ogura@gmail.com>
6
+ #
7
+ require 'socket'
8
+ require 'ros/tcpros/header'
9
+ require 'ros/tcpros/message'
10
+
11
+ module ROS::TCPROS
12
+
13
+ ##
14
+ # TCPROS protocol service client connection
15
+ #
16
+ class ServiceClient
17
+
18
+ include ::ROS::TCPROS::Message
19
+
20
+ # @param [String] host host name
21
+ # @param [Integer] port port number
22
+ # @param [String] caller_id caller_id of this node
23
+ # @param [String] service_name name of service
24
+ # @param [Class] service_type class of this service
25
+ # @param [Boolean] persistent use persistent connection or not
26
+ def initialize(host, port, caller_id, service_name, service_type, persistent)
27
+ @caller_id = caller_id
28
+ @service_name = service_name
29
+ @service_type = service_type
30
+ @port = port
31
+ @host = host
32
+ @socket = TCPSocket.open(@host, @port)
33
+ @persistent = persistent
34
+ @socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
35
+ end
36
+
37
+ ##
38
+ # build henader message for service client.
39
+ # It contains callerid, service, md5sum, type, persistent.
40
+ # @return [Header] header
41
+ def build_header
42
+ header = Header.new
43
+ header.push_data("callerid", @caller_id)
44
+ header.push_data("service", @service_name)
45
+ header.push_data("md5sum", @service_type.md5sum)
46
+ header.push_data("type", @service_type.type)
47
+ if @persistent
48
+ header.push_data("persistent", '1')
49
+ end
50
+ header
51
+ end
52
+
53
+ ##
54
+ # call the service by sending srv request message,
55
+ # and receive response message.
56
+ # @param [Message] srv_request call with this request
57
+ # @param [Message] srv_response response is stored in this message
58
+ # @return [Boolean] result of call
59
+ def call(srv_request, srv_response)
60
+ write_header(@socket, build_header)
61
+ if check_header(read_header(@socket))
62
+ write_msg(@socket, srv_request)
63
+ @socket.flush
64
+ ok_byte = read_ok_byte
65
+ if ok_byte == 1
66
+ srv_response.deserialize(read_all(@socket))
67
+ return true
68
+ end
69
+ false
70
+ end
71
+ false
72
+ end
73
+
74
+ ##
75
+ # read ok byte for boolean service result.
76
+ # @return [Integer] 1 for OK, 0 for NG
77
+ def read_ok_byte
78
+ @socket.recv(1).unpack('c')[0]
79
+ end
80
+
81
+ ##
82
+ # check md5sum only.
83
+ # @param [Header] header received header
84
+ # @return [Boolean] true if it is ok.
85
+ def check_header(header)
86
+ header.valid?('md5sum', @service_type.md5sum)
87
+ end
88
+
89
+ ##
90
+ # close the socket
91
+ def shutdown
92
+ @socket.close
93
+ end
94
+
95
+ # port number of this socket
96
+ # @return [Integer] port number
97
+ attr_reader :port
98
+
99
+ # host of this connection
100
+ # @return [String] host name
101
+ attr_reader :host
102
+
103
+ end
104
+ end