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,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