rosruby 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/rubyroscore +5 -0
- data/lib/ros.rb +25 -0
- data/lib/ros/duration.rb +63 -0
- data/lib/ros/graph_manager.rb +408 -0
- data/lib/ros/log.rb +72 -0
- data/lib/ros/master.rb +408 -0
- data/lib/ros/master_proxy.rb +256 -0
- data/lib/ros/message.rb +65 -0
- data/lib/ros/name.rb +88 -0
- data/lib/ros/node.rb +442 -0
- data/lib/ros/package.rb +144 -0
- data/lib/ros/parameter_manager.rb +127 -0
- data/lib/ros/parameter_subscriber.rb +47 -0
- data/lib/ros/publisher.rb +96 -0
- data/lib/ros/rate.rb +41 -0
- data/lib/ros/ros.rb +10 -0
- data/lib/ros/roscore.rb +29 -0
- data/lib/ros/service.rb +37 -0
- data/lib/ros/service_client.rb +83 -0
- data/lib/ros/service_server.rb +92 -0
- data/lib/ros/slave_proxy.rb +153 -0
- data/lib/ros/subscriber.rb +119 -0
- data/lib/ros/tcpros/client.rb +108 -0
- data/lib/ros/tcpros/header.rb +89 -0
- data/lib/ros/tcpros/message.rb +74 -0
- data/lib/ros/tcpros/server.rb +137 -0
- data/lib/ros/tcpros/service_client.rb +104 -0
- data/lib/ros/tcpros/service_server.rb +132 -0
- data/lib/ros/time.rb +109 -0
- data/lib/ros/topic.rb +47 -0
- data/lib/ros/xmlrpcserver.rb +40 -0
- data/samples/add_two_ints_client.rb +25 -0
- data/samples/add_two_ints_server.rb +20 -0
- data/samples/gui.rb +126 -0
- data/samples/sample_log.rb +16 -0
- data/samples/sample_param.rb +20 -0
- data/samples/sample_publisher.rb +20 -0
- data/samples/sample_subscriber.rb +19 -0
- data/scripts/genmsg_ruby.py +1135 -0
- data/scripts/genmsg_ruby.pyc +0 -0
- data/scripts/gensrv_ruby.py +105 -0
- data/scripts/gensrv_ruby.pyc +0 -0
- data/scripts/rosruby_genmsg.py +67 -0
- data/scripts/run-test.rb +21 -0
- data/test/test_header.rb +36 -0
- data/test/test_log.rb +45 -0
- data/test/test_master_proxy.rb +73 -0
- data/test/test_message.rb +13 -0
- data/test/test_node.rb +166 -0
- data/test/test_package.rb +10 -0
- data/test/test_param.rb +27 -0
- data/test/test_pubsub.rb +154 -0
- data/test/test_rate.rb +16 -0
- data/test/test_service.rb +34 -0
- data/test/test_slave_proxy.rb +49 -0
- data/test/test_time.rb +39 -0
- metadata +170 -0
@@ -0,0 +1,132 @@
|
|
1
|
+
# ros/tcpros/service_server.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
|
+
# TCPROS protocol Service Server
|
14
|
+
#
|
15
|
+
class ServiceServer < ::GServer
|
16
|
+
|
17
|
+
##
|
18
|
+
# max number of connection with clients
|
19
|
+
#
|
20
|
+
MAX_CONNECTION = 100
|
21
|
+
|
22
|
+
include ::ROS::TCPROS::Message
|
23
|
+
|
24
|
+
##
|
25
|
+
# @param [String] caller_id caller_id of this node
|
26
|
+
# @param [String] service_name name of this service
|
27
|
+
# @param [Class] service_type class of this service message
|
28
|
+
# @param [Proc] callback of this service
|
29
|
+
# @param [Integer] port port number
|
30
|
+
# @param [host] host host name
|
31
|
+
def initialize(caller_id, service_name, service_type, callback,
|
32
|
+
port=0, host=::GServer::DEFAULT_HOST)
|
33
|
+
super(port, host, MAX_CONNECTION)
|
34
|
+
@caller_id = caller_id
|
35
|
+
@service_name = service_name
|
36
|
+
@service_type = service_type
|
37
|
+
@callback = callback
|
38
|
+
@byte_received = 0
|
39
|
+
@byte_sent = 0
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# message must send 1 byte for service call result (success)
|
44
|
+
# @param [IO] socket
|
45
|
+
def send_ok_byte(socket)
|
46
|
+
socket.write([1].pack('c'))
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# message must send 1 byte for service call result (fail)
|
51
|
+
# @param [IO] socket
|
52
|
+
def send_ng_byte(socket)
|
53
|
+
socket.write([0].pack('c'))
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# main loop of this connection.
|
58
|
+
# read data and do callback.
|
59
|
+
# @param [IO] socket
|
60
|
+
# @return [Boolean] result of callback
|
61
|
+
def read_and_callback(socket)
|
62
|
+
request = @service_type.request_class.new
|
63
|
+
response = @service_type.response_class.new
|
64
|
+
data = read_all(socket)
|
65
|
+
@byte_received += data.length
|
66
|
+
request.deserialize(data)
|
67
|
+
result = @callback.call(request, response)
|
68
|
+
if result
|
69
|
+
send_ok_byte(socket)
|
70
|
+
data = write_msg(socket, response)
|
71
|
+
@byte_sent += data.length
|
72
|
+
else
|
73
|
+
send_ng_byte(socket)
|
74
|
+
write_header(socket, build_header)
|
75
|
+
# write some message
|
76
|
+
end
|
77
|
+
result
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# this is called by socket accept
|
82
|
+
# @param [IO] socket given socket
|
83
|
+
def serve(socket)
|
84
|
+
header = read_header(socket)
|
85
|
+
# not documented protocol?
|
86
|
+
if header['probe'] == '1'
|
87
|
+
write_header(socket, build_header)
|
88
|
+
elsif check_header(header)
|
89
|
+
write_header(socket, build_header)
|
90
|
+
read_and_callback(socket)
|
91
|
+
if header['persistent'] == '1'
|
92
|
+
loop do
|
93
|
+
read_and_callback(socket)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
else
|
97
|
+
socket.close
|
98
|
+
raise 'header check error'
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# check header.
|
104
|
+
# check md5sum only.
|
105
|
+
# @param [Header] header header for checking
|
106
|
+
# @return [Boolean] check result (true means ok)
|
107
|
+
def check_header(header)
|
108
|
+
header.valid?('md5sum', @service_type.md5sum)
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# build header message for service server.
|
113
|
+
# It contains callerid, type, md5sum.
|
114
|
+
# @return [Header] built header
|
115
|
+
def build_header
|
116
|
+
header = Header.new
|
117
|
+
header["callerid"] = @caller_id
|
118
|
+
header['type'] = @service_type.type
|
119
|
+
header['md5sum'] = @service_type.md5sum
|
120
|
+
header
|
121
|
+
end
|
122
|
+
|
123
|
+
# received data amout for slave api
|
124
|
+
# @return [Integer] byte received
|
125
|
+
attr_reader :byte_received
|
126
|
+
|
127
|
+
# sent data amout for slave api
|
128
|
+
# @return [Integer] byte sent
|
129
|
+
attr_reader :byte_sent
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
data/lib/ros/time.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# ros/time.rb
|
2
|
+
#
|
3
|
+
# License: BSD
|
4
|
+
#
|
5
|
+
# Copyright (C) 2012 Takashi Ogura <t.ogura@gmail.com>
|
6
|
+
#
|
7
|
+
# Time object for ROS.
|
8
|
+
#
|
9
|
+
module ROS
|
10
|
+
|
11
|
+
##
|
12
|
+
# super class of times
|
13
|
+
class TimeValue
|
14
|
+
|
15
|
+
include Comparable
|
16
|
+
|
17
|
+
# @return [Integer] seconds
|
18
|
+
attr_accessor :secs
|
19
|
+
|
20
|
+
# @return [Integer] nano seconds
|
21
|
+
attr_accessor :nsecs
|
22
|
+
|
23
|
+
# canonicalize secs and nsecs
|
24
|
+
# @return [TimeValue] self
|
25
|
+
def canonicalize
|
26
|
+
while @nsecs >= 1e9.to_i
|
27
|
+
@secs += 1
|
28
|
+
@nsecs -= 1e9.to_i
|
29
|
+
end
|
30
|
+
while @nsecs < 0
|
31
|
+
@secs -= 1
|
32
|
+
@nsecs += 1e9.to_i
|
33
|
+
end
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
# convert to seconds
|
38
|
+
# @return [Float] seconds
|
39
|
+
def to_sec
|
40
|
+
@secs + (@nsecs / 1e9)
|
41
|
+
end
|
42
|
+
|
43
|
+
# convert to nano seconds
|
44
|
+
# @return [Float] nano seconds
|
45
|
+
def to_nsec
|
46
|
+
@nsecs + (@secs * 1e9)
|
47
|
+
end
|
48
|
+
|
49
|
+
# compare time value
|
50
|
+
# @param [TimeValue] other compare target
|
51
|
+
# @return [Integer] result
|
52
|
+
def <=>(other)
|
53
|
+
diff = self.to_nsec - other.to_nsec
|
54
|
+
if diff > 0
|
55
|
+
1
|
56
|
+
elsif diff < 0
|
57
|
+
-1
|
58
|
+
else
|
59
|
+
0
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# ROS Time object. This is used as msg object for time
|
67
|
+
#
|
68
|
+
class Time < TimeValue
|
69
|
+
|
70
|
+
# initialize with current time
|
71
|
+
def self.now
|
72
|
+
self.new(::Time::now)
|
73
|
+
end
|
74
|
+
|
75
|
+
# @overload initialize(time)
|
76
|
+
# @param [::Time] initialize with this time
|
77
|
+
# @overload initialize
|
78
|
+
def initialize(time=nil)
|
79
|
+
if time
|
80
|
+
@secs = time.to_i
|
81
|
+
@nsecs = ((time.to_f - @secs) * 1e9.to_i).to_i
|
82
|
+
else
|
83
|
+
@secs = 0
|
84
|
+
@nsecs = 0
|
85
|
+
end
|
86
|
+
canonicalize
|
87
|
+
end
|
88
|
+
|
89
|
+
# add time value
|
90
|
+
# @param [Duration] duration duration for adding
|
91
|
+
# @return [Time] new time
|
92
|
+
def +(duration)
|
93
|
+
tm = ::ROS::Time.new
|
94
|
+
tm.secs = @secs + duration.secs
|
95
|
+
tm.nsecs = @nsecs + duration.nsecs
|
96
|
+
tm.canonicalize
|
97
|
+
end
|
98
|
+
|
99
|
+
# subtract time value
|
100
|
+
# @param [Time] other
|
101
|
+
# @return [Duration] duration
|
102
|
+
def -(other)
|
103
|
+
d = ::ROS::Duration.new
|
104
|
+
d.secs = @secs - other.secs
|
105
|
+
d.nsecs = @nsecs - other.nsecs
|
106
|
+
d.canonicalize
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/lib/ros/topic.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# ros/topic.rb
|
2
|
+
#
|
3
|
+
# License: BSD
|
4
|
+
#
|
5
|
+
# Copyright (C) 2012 Takashi Ogura <t.ogura@gmail.com>
|
6
|
+
#
|
7
|
+
# Super class of Publisher/Subscriber
|
8
|
+
#
|
9
|
+
module ROS
|
10
|
+
|
11
|
+
##
|
12
|
+
# Base class of {Publisher} and {Subscriber}
|
13
|
+
class Topic
|
14
|
+
|
15
|
+
# initialize member variables
|
16
|
+
# @param [String] caller_id caller id of this node
|
17
|
+
# @param [String] topic_name name of this topic
|
18
|
+
# @param [Class] topic_type class of msg
|
19
|
+
def initialize(caller_id, topic_name, topic_type)
|
20
|
+
@caller_id = caller_id
|
21
|
+
@topic_name = topic_name
|
22
|
+
@topic_type = topic_type
|
23
|
+
@connections = []
|
24
|
+
@connection_id_number = 0
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [String] caller id
|
28
|
+
attr_reader :caller_id
|
29
|
+
|
30
|
+
# @return [String] name of this topic
|
31
|
+
attr_reader :topic_name
|
32
|
+
|
33
|
+
# @return [Class] class of msg
|
34
|
+
attr_reader :topic_type
|
35
|
+
|
36
|
+
# shutdown all connections
|
37
|
+
def close #:nodoc:
|
38
|
+
@connections.each {|connection| connection.shutdown}
|
39
|
+
end
|
40
|
+
|
41
|
+
# set manager for shutdown
|
42
|
+
# @param [GraphManager] manager
|
43
|
+
def set_manager(manager) #:nodoc:
|
44
|
+
@manager = manager
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# ros/xmlrpcserver.rb
|
2
|
+
#
|
3
|
+
# License: BSD
|
4
|
+
#
|
5
|
+
# Copyright (C) 2012 Takashi Ogura <t.ogura@gmail.com>
|
6
|
+
#
|
7
|
+
#
|
8
|
+
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'xmlrpc/server'
|
12
|
+
|
13
|
+
module ROS
|
14
|
+
|
15
|
+
# original XMLRPC Server (remove access log)
|
16
|
+
class XMLRPCServer < XMLRPC::WEBrickServlet
|
17
|
+
def initialize(port=8080,
|
18
|
+
host="127.0.0.1",
|
19
|
+
maxConnections=100,
|
20
|
+
stdlog="#{ENV['HOME']}/.ros/log/rosruby.log")
|
21
|
+
super({})
|
22
|
+
require 'webrick'
|
23
|
+
@server = WEBrick::HTTPServer.new(:Port => port,
|
24
|
+
:BindAddress => host,
|
25
|
+
:MaxClients => maxConnections,
|
26
|
+
:Logger => WEBrick::Log.new(stdlog),
|
27
|
+
:AccessLog => [])
|
28
|
+
@server.mount("/", self)
|
29
|
+
end
|
30
|
+
|
31
|
+
def serve
|
32
|
+
@server.start
|
33
|
+
end
|
34
|
+
|
35
|
+
def shutdown
|
36
|
+
@server.shutdown
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'ros'
|
4
|
+
|
5
|
+
require 'roscpp_tutorials/TwoInts'
|
6
|
+
|
7
|
+
def main
|
8
|
+
if ARGV.length < 2
|
9
|
+
puts "usage: #{$0} X Y"
|
10
|
+
return
|
11
|
+
end
|
12
|
+
node = ROS::Node.new('/rosruby/sample_service_client')
|
13
|
+
if node.wait_for_service('/add_two_ints', 1)
|
14
|
+
service = node.service('/add_two_ints', Roscpp_tutorials::TwoInts)
|
15
|
+
req = Roscpp_tutorials::TwoInts.request_class.new
|
16
|
+
res = Roscpp_tutorials::TwoInts.response_class.new
|
17
|
+
req.a = ARGV[0].to_i
|
18
|
+
req.b = ARGV[1].to_i
|
19
|
+
if service.call(req, res)
|
20
|
+
p res.sum
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
main
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'ros'
|
4
|
+
require 'roscpp_tutorials/TwoInts'
|
5
|
+
|
6
|
+
def main
|
7
|
+
node = ROS::Node.new('/rosruby/sample_service_server')
|
8
|
+
server = node.advertise_service('/add_two_ints', Roscpp_tutorials::TwoInts) do |req, res|
|
9
|
+
res.sum = req.a + req.b
|
10
|
+
node.loginfo("a=#{req.a}, b=#{req.b}")
|
11
|
+
node.loginfo(" sum = #{res.sum}")
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
while node.ok?
|
16
|
+
sleep (1.0)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
main
|
data/samples/gui.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
#
|
4
|
+
# = sample of gui (Ruby/Tk)
|
5
|
+
# this sample needs geometry_msgs/Twist.
|
6
|
+
# The GUI publishes velocity as Twist.
|
7
|
+
#
|
8
|
+
# if you don't have the message files, try
|
9
|
+
#
|
10
|
+
# $ rosrun rosruby rosruby_genmsg.py geometry_msgs
|
11
|
+
#
|
12
|
+
|
13
|
+
require 'tk'
|
14
|
+
require 'ros'
|
15
|
+
require 'geometry_msgs/Twist'
|
16
|
+
|
17
|
+
topic_name = '/cmd_vel'
|
18
|
+
node = ROS::Node.new('/gui_test')
|
19
|
+
pub = node.advertise(topic_name, Geometry_msgs::Twist)
|
20
|
+
|
21
|
+
linear_vel = 0.5
|
22
|
+
angular_vel = 1.0
|
23
|
+
|
24
|
+
TkButton.new {
|
25
|
+
text "FORWARD"
|
26
|
+
command do
|
27
|
+
msg = Geometry_msgs::Twist.new
|
28
|
+
msg.linear.x = linear_vel
|
29
|
+
pub.publish(msg)
|
30
|
+
end
|
31
|
+
width 10
|
32
|
+
grid("row"=>0, "column"=>1)
|
33
|
+
}
|
34
|
+
|
35
|
+
TkButton.new {
|
36
|
+
text "LEFT"
|
37
|
+
command do
|
38
|
+
msg = Geometry_msgs::Twist.new
|
39
|
+
msg.angular.z = angular_vel
|
40
|
+
pub.publish(msg)
|
41
|
+
end
|
42
|
+
width 10
|
43
|
+
grid("row"=>1, "column"=>0)
|
44
|
+
}
|
45
|
+
|
46
|
+
TkButton.new {
|
47
|
+
text "STOP"
|
48
|
+
command do
|
49
|
+
msg = Geometry_msgs::Twist.new
|
50
|
+
pub.publish(msg)
|
51
|
+
end
|
52
|
+
width 10
|
53
|
+
grid("row"=>1, "column"=>1)
|
54
|
+
}
|
55
|
+
|
56
|
+
TkButton.new {
|
57
|
+
text "RIGHT"
|
58
|
+
command do
|
59
|
+
msg = Geometry_msgs::Twist.new
|
60
|
+
msg.angular.z = -angular_vel
|
61
|
+
pub.publish(msg)
|
62
|
+
end
|
63
|
+
width 10
|
64
|
+
grid("row"=>1, "column"=>2)
|
65
|
+
}
|
66
|
+
|
67
|
+
|
68
|
+
TkButton.new {
|
69
|
+
text "BACKWARD"
|
70
|
+
command do
|
71
|
+
msg = Geometry_msgs::Twist.new
|
72
|
+
msg.linear.x = -linear_vel
|
73
|
+
pub.publish(msg)
|
74
|
+
end
|
75
|
+
width 10
|
76
|
+
grid("row"=>2, "column"=>1)
|
77
|
+
}
|
78
|
+
|
79
|
+
TkScale.new {
|
80
|
+
label 'linear vel(%)'
|
81
|
+
from 0
|
82
|
+
to 100
|
83
|
+
orient 'horizontal'
|
84
|
+
command do |val|
|
85
|
+
linear_vel = val.to_f * 0.005
|
86
|
+
end
|
87
|
+
grid("row"=>3, "column"=>0)
|
88
|
+
}.set(50)
|
89
|
+
|
90
|
+
TkScale.new {
|
91
|
+
label 'angular vel(%)'
|
92
|
+
from 0
|
93
|
+
to 100
|
94
|
+
orient 'horizontal'
|
95
|
+
command do |val|
|
96
|
+
angular_vel = val.to_f * 0.01
|
97
|
+
end
|
98
|
+
grid("row"=>3, "column"=>1)
|
99
|
+
}.set(50)
|
100
|
+
|
101
|
+
TkButton.new {
|
102
|
+
text "exit"
|
103
|
+
command do
|
104
|
+
node.shutdown
|
105
|
+
exit
|
106
|
+
end
|
107
|
+
width 5
|
108
|
+
grid("row"=>3, "column"=>2)
|
109
|
+
}
|
110
|
+
|
111
|
+
TkLabel.new {
|
112
|
+
text 'topic'
|
113
|
+
grid("row"=>4, "column"=>0)
|
114
|
+
}
|
115
|
+
|
116
|
+
TkEntry.new {
|
117
|
+
self.value = topic_name
|
118
|
+
bind 'Return', proc {
|
119
|
+
topic_name = self.value
|
120
|
+
pub.shutdown
|
121
|
+
pub = node.advertise(topic_name, Geometry_msgs::Twist)
|
122
|
+
}
|
123
|
+
grid("row"=>4, "column"=>1, "columnspan"=>2, "sticky"=>"news")
|
124
|
+
}
|
125
|
+
|
126
|
+
Tk.mainloop
|