ruby-player 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.travis.yml +5 -0
- data/Guardfile +5 -0
- data/README.md +10 -17
- data/TODO.md +1 -12
- data/examples/simple_example.rb +27 -0
- data/{spec → examples}/world/test.cfg +0 -0
- data/{spec → examples}/world/test.world +5 -5
- data/lib/ruby-player.rb +12 -5
- data/lib/ruby-player/client.rb +136 -42
- data/lib/ruby-player/common.rb +44 -7
- data/lib/ruby-player/constants.rb +584 -0
- data/lib/ruby-player/dev_addr.rb +59 -0
- data/lib/ruby-player/device.rb +60 -0
- data/lib/ruby-player/header.rb +81 -0
- data/lib/ruby-player/position2d.rb +161 -91
- data/lib/ruby-player/ranger.rb +118 -88
- data/lib/ruby-player/version.rb +1 -1
- data/ruby-player.gemspec +3 -1
- data/spec/client_spec.rb +145 -11
- data/spec/position2d_spec.rb +115 -46
- data/spec/ranger_spec.rb +132 -41
- metadata +36 -28
- data/lib/ruby-player/c_type.rb +0 -36
- data/lib/ruby-player/c_type/bbox3d_t.rb +0 -24
- data/lib/ruby-player/c_type/client_t.rb +0 -36
- data/lib/ruby-player/c_type/devaddr.rb +0 -24
- data/lib/ruby-player/c_type/device_t.rb +0 -35
- data/lib/ruby-player/c_type/diagnostic.rb +0 -22
- data/lib/ruby-player/c_type/pose3d_t.rb +0 -27
- data/lib/ruby-player/c_type/position2d_t.rb +0 -32
- data/lib/ruby-player/c_type/ranger_t.rb +0 -42
- data/lib/ruby-player/c_type/sockaddr_in_t.rb +0 -24
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Guardfile
ADDED
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
Ruby Player - Ruby client library for Player (tools for robots)
|
1
|
+
Ruby Player - Ruby client library for Player (tools for robots) [![Build Status](https://secure.travis-ci.org/flipback/ruby-player.png)](http://travis-ci.org/flipback/ruby-player)
|
2
2
|
|
3
3
|
Summary
|
4
4
|
-------------------------------------
|
5
|
-
Ruby Player provide high level client library to access to Player server
|
6
|
-
|
5
|
+
Ruby Player provide high level client library to access to Player server in pure Ruby
|
6
|
+
Currently (2012-01-07) the Ruby Player are developing and testing with Player 3.1.0 latest svn version
|
7
7
|
|
8
8
|
API coverage
|
9
9
|
-------------------------------------
|
@@ -14,27 +14,21 @@ The list of support objects and devices of Player.
|
|
14
14
|
|
15
15
|
Install
|
16
16
|
-------------------------------------
|
17
|
-
Currently (2012-01-07) the Ruby Player are developing and testing with Player 3.1.0 latest svn version
|
18
|
-
and Stage v4.1.0. See them user guides for installation.
|
19
|
-
|
20
|
-
After installation Player/Stage run:
|
21
17
|
|
22
18
|
`gem install ruby-player`
|
23
19
|
|
24
|
-
For testing library in root dir library:
|
25
|
-
|
26
|
-
player spec/world/test.cfg
|
27
|
-
bundle exec rake spec
|
28
|
-
|
29
20
|
Example
|
30
21
|
-------------------------------------
|
31
22
|
|
32
23
|
require 'ruby-player'
|
33
24
|
Player::Client.connect("localhost") do |robot|
|
34
|
-
pos2d = robot
|
35
|
-
|
25
|
+
pos2d = robot.subscribe("position2d", index: 0)
|
26
|
+
ranger = robot.subscribe(:ranger)
|
27
|
+
pos2d.set_speed(vx: 1, vy: 0, va: 0.2)
|
28
|
+
#main loop
|
36
29
|
robot.loop do
|
37
|
-
puts "Position: x
|
30
|
+
puts "Position: x=%{px}, y=%{py}, a=%{pa}" % pos2d.position
|
31
|
+
puts "Rangers: #{ranger.rangers}"
|
38
32
|
end
|
39
33
|
end
|
40
34
|
|
@@ -45,10 +39,9 @@ References
|
|
45
39
|
|
46
40
|
[Player project](http://playerstage.sourceforge.net/)
|
47
41
|
|
48
|
-
[Stage project](https://github.com/rtv/Stage)
|
49
|
-
|
50
42
|
[C API Player](http://playerstage.sourceforge.net/doc/Player-3.0.2/player/group__player__clientlib__libplayerc.html)
|
51
43
|
|
52
44
|
Authors
|
53
45
|
-------------------------------------
|
46
|
+
|
54
47
|
Aleksey Timin <atimin@gmail.com>
|
data/TODO.md
CHANGED
@@ -3,12 +3,7 @@ For supported devices
|
|
3
3
|
-------------------------------------
|
4
4
|
**ranger** - The ranger proxy provides an interface to ranger sensor devices.
|
5
5
|
|
6
|
-
|
7
|
-
2. Get 3d scan points (x,y,z)
|
8
|
-
|
9
|
-
**position2d** - The position2d proxy provides an interface to a mobile robot base, such as the ActiveMedia Pioneer series.
|
10
|
-
1. Set speed and heading - wrap C `func playerc_position2d_set_cmd_vel_head`
|
11
|
-
2. Move to point - wrap C funcs `playerc_position2d_set_cmd_pose_with_vel` and `playerc_position2d_set_cmd_pose`
|
6
|
+
Implement PLAYER_RANGER_DATA_RANGESTAMPED and PLAYER_RANGER_DATA_INTNSTAMPED
|
12
7
|
|
13
8
|
Candidates for support
|
14
9
|
--------------------------------------
|
@@ -44,12 +39,8 @@ Device proxies are not started to develop
|
|
44
39
|
|
45
40
|
**health** - The health proxy provides an interface to the HEALTH Module.
|
46
41
|
|
47
|
-
**ir** - The ir proxy provides an interface to the ir sensors built into robots such as the RWI B21R.
|
48
|
-
|
49
42
|
**joystick** - The joystick proxy can be used to get images from a joystick.
|
50
43
|
|
51
|
-
**laser** - The laser proxy provides an interface to scanning laser range finders such as the sicklms200.
|
52
|
-
|
53
44
|
**limb** - The limb proxy provides an interface to limbs using forward/inverse kinematics, such as the ActivMedia Pioneer Arm.
|
54
45
|
|
55
46
|
**localize** - The localize proxy provides an interface to localization drivers.
|
@@ -72,8 +63,6 @@ Device proxies are not started to develop
|
|
72
63
|
|
73
64
|
**ptz** - The ptz proxy provides an interface to pan-tilt units such as the Sony PTZ camera.
|
74
65
|
|
75
|
-
**sonar** - The sonar proxy provides an interface to the sonar range sensors built into robots such as the ActiveMedia Pioneer series.
|
76
|
-
|
77
66
|
**wifi** - The wifi proxy is used to query the state of a wireless network.
|
78
67
|
|
79
68
|
**speech** - The speech proxy provides an interface to a speech synthesis system.
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'ruby-player'
|
2
|
+
Player::Client.connect("localhost") do |robot|
|
3
|
+
pos2d = robot.subscribe(:position2d)
|
4
|
+
ranger = robot.subscribe(:ranger)
|
5
|
+
#main loop
|
6
|
+
robot.loop(0.05) do
|
7
|
+
puts "Position: x=%{px}, y=%{py}, a=%{pa}" % pos2d.position
|
8
|
+
r = ranger.rangers
|
9
|
+
puts "Rangers: #{r}"
|
10
|
+
|
11
|
+
if r[0] < 2.5
|
12
|
+
pos2d.set_car vx: 0.2*r[0], a: -1 / r[0]
|
13
|
+
puts "Turn right"
|
14
|
+
next
|
15
|
+
end
|
16
|
+
|
17
|
+
if r[1] < 2.5
|
18
|
+
pos2d.set_car vx: 0.2*r[1], a: 1 / r[1]
|
19
|
+
puts "Turn left"
|
20
|
+
next
|
21
|
+
end
|
22
|
+
|
23
|
+
if r[0] > 2.5 && r[1] > 2.5
|
24
|
+
pos2d.set_car vx: 0.4, a: 0
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
File without changes
|
data/lib/ruby-player.rb
CHANGED
@@ -12,12 +12,19 @@
|
|
12
12
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
13
|
# GNU General Public License for more details.
|
14
14
|
|
15
|
-
require "ffi"
|
16
|
-
|
17
|
-
require "ruby-player/c_type"
|
18
|
-
|
19
15
|
require "ruby-player/version"
|
16
|
+
require "ruby-player/constants"
|
20
17
|
require "ruby-player/common"
|
18
|
+
|
19
|
+
#basic classes
|
20
|
+
require "ruby-player/dev_addr"
|
21
|
+
require "ruby-player/header"
|
22
|
+
require "ruby-player/device"
|
23
|
+
require "ruby-player/client"
|
24
|
+
|
25
|
+
#interfaces
|
21
26
|
require "ruby-player/position2d"
|
22
27
|
require "ruby-player/ranger"
|
23
|
-
|
28
|
+
|
29
|
+
|
30
|
+
|
data/lib/ruby-player/client.rb
CHANGED
@@ -17,39 +17,38 @@ module Player
|
|
17
17
|
#
|
18
18
|
# @example
|
19
19
|
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# pos2d.
|
23
|
-
#
|
24
|
-
#
|
20
|
+
# require 'ruby-player'
|
21
|
+
# Player::Client.connect("localhost") do |robot|
|
22
|
+
# pos2d = robot.subscribe("position2d", index: 0)
|
23
|
+
# pos2d.set_speed(vx: 1, vy: 0, va: 0.2)
|
24
|
+
# #main loop
|
25
|
+
# robot.loop do
|
26
|
+
# puts "Position: x=%{px}, y=%{py}, a=%{pa}" % pos2d.position
|
25
27
|
# end
|
26
28
|
# end
|
27
|
-
class Client
|
28
|
-
include CType
|
29
|
+
class Client < Device
|
29
30
|
include Common
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
32
|
+
# Initialize client
|
33
|
+
# @param [String] host host of Player server
|
34
|
+
# @param [Hash] opts client options
|
35
|
+
# @option opts :port port of connection
|
36
|
+
# @option opts :log_level level of log messages [:debug,:notice, :warn, :error] default :notice
|
37
|
+
def initialize(host, opts = {})
|
38
|
+
port = opts[:port] || 6665
|
39
|
+
@log_level = (opts[:log_level] || :notice).to_sym
|
39
40
|
|
41
|
+
@socket = TCPSocket.new(host, port)
|
42
|
+
@addr = DevAddr.new(host: 0, robot: 0, interface: PLAYER_PLAYER_CODE, index: 0)
|
43
|
+
@client = self
|
44
|
+
@devices = []
|
40
45
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
# Initialize client
|
45
|
-
# @param [String] host host address
|
46
|
-
# @param port number of port default 6665
|
47
|
-
def initialize(host, port=6665)
|
48
|
-
@client = ClientStruct.new(C.playerc_client_create(nil, host, port.to_i))
|
46
|
+
banner = @socket.read(PLAYER_IDENT_STRLEN)
|
47
|
+
notice "Connect with #{banner} in #{host}:#{port}"
|
48
|
+
send_message PLAYER_MSGTYPE_REQ, PLAYER_PLAYER_REQ_DATAMODE, [PLAYER_DATAMODE_PULL].pack("N")
|
49
49
|
|
50
|
-
|
50
|
+
debug "Set delivery mode in PULL"
|
51
51
|
|
52
|
-
ObjectSpace.define_finalizer(self, Client.finilazer(@client))
|
53
52
|
if block_given?
|
54
53
|
yield self
|
55
54
|
close
|
@@ -63,31 +62,44 @@ module Player
|
|
63
62
|
end
|
64
63
|
|
65
64
|
# Read data from server and update all subscribed proxy objects
|
66
|
-
def read
|
67
|
-
|
65
|
+
def read!
|
66
|
+
send_message PLAYER_MSGTYPE_REQ, PLAYER_PLAYER_REQ_DATA, ""
|
67
|
+
while read[0].type != PLAYER_MSGTYPE_SYNCH
|
68
|
+
end
|
69
|
+
nil
|
68
70
|
end
|
69
71
|
|
70
72
|
# Get proxy object
|
71
73
|
#
|
72
74
|
# @example
|
73
|
-
# pos2d = client
|
74
|
-
#
|
75
|
-
# @param type type name
|
76
|
-
# @param
|
77
|
-
# @
|
78
|
-
|
79
|
-
|
80
|
-
(
|
75
|
+
# pos2d = client.subscribe(:position2d, index: 0)
|
76
|
+
#
|
77
|
+
# @param [String,Symbol] type interface type name
|
78
|
+
# @param [Hash] opts of subscribing
|
79
|
+
# @option opts :index index of device (default 0)
|
80
|
+
# @option opts :access access level (default PLAYER_OPEN_MODE)
|
81
|
+
def subscribe(type, opts = {})
|
82
|
+
code = instance_eval("PLAYER_#{type.to_s.upcase}_CODE")
|
83
|
+
index = opts[:index] || 0
|
84
|
+
access = opts[:access] || PLAYER_OPEN_MODE
|
85
|
+
|
86
|
+
notice "Subscribing to #{type}:#{index}"
|
87
|
+
data = DevAddr.new(interface: code, index: index).to_a + [access, 0, 0]
|
88
|
+
send_message PLAYER_MSGTYPE_REQ, PLAYER_PLAYER_REQ_DEV, data.pack("N*")
|
89
|
+
|
90
|
+
read!
|
91
|
+
|
92
|
+
@devices.select { |d| d.addr.interface == code && d.addr.index == index}.first
|
81
93
|
end
|
82
94
|
|
83
95
|
# Check connection
|
84
96
|
def closed?
|
85
|
-
@
|
97
|
+
@socket.closed?
|
86
98
|
end
|
87
99
|
|
88
100
|
# Close connection
|
89
101
|
def close
|
90
|
-
|
102
|
+
@socket.close
|
91
103
|
end
|
92
104
|
|
93
105
|
# Loop for control code
|
@@ -100,17 +112,99 @@ module Player
|
|
100
112
|
# @param period period cicles in seconds
|
101
113
|
def loop(period=1.0)
|
102
114
|
while(true) do
|
103
|
-
read
|
115
|
+
read!
|
104
116
|
yield
|
105
117
|
sleep(period.to_f)
|
106
118
|
end
|
107
119
|
end
|
108
120
|
|
109
|
-
def
|
110
|
-
|
111
|
-
|
112
|
-
|
121
|
+
def write(hdr, msg)
|
122
|
+
send_header hdr
|
123
|
+
@socket.write msg
|
124
|
+
@socket.flush
|
125
|
+
end
|
126
|
+
|
127
|
+
def handle_response(hdr, msg)
|
128
|
+
case hdr.subtype
|
129
|
+
when 0
|
130
|
+
nil
|
131
|
+
when PLAYER_PLAYER_REQ_DEV
|
132
|
+
# read device identifier
|
133
|
+
dev = DevAddr.decode(msg[0,PLAYERXDR_DEVADDR_SIZE])
|
134
|
+
# read the granted access and driver name
|
135
|
+
data = msg[PLAYERXDR_DEVADDR_SIZE,8].unpack("N*")
|
136
|
+
access = data[0]
|
137
|
+
# read driver name
|
138
|
+
drv_name = msg[-data[1]-2..-1]
|
139
|
+
|
140
|
+
if access == PLAYER_ERROR_MODE
|
141
|
+
raise_error "Error subscribing to " + dev.interface_name + ":" + dev.index
|
142
|
+
end
|
143
|
+
|
144
|
+
debug "Got response: #{dev.interface_name}:#{dev.index} (driver name - #{drv_name})"
|
145
|
+
|
146
|
+
@devices << make_device(dev)
|
147
|
+
when PLAYER_PLAYER_REQ_DATAMODE, PLAYER_PLAYER_REQ_DATA
|
148
|
+
nil
|
149
|
+
else
|
150
|
+
warn "Don't implement ACK for subtype = #{hdr.subtype}"
|
113
151
|
end
|
114
152
|
end
|
153
|
+
|
154
|
+
private
|
155
|
+
def make_device(dev)
|
156
|
+
instance_eval(dev.interface_name.capitalize).send(:new, dev, self, @log_level)
|
157
|
+
end
|
158
|
+
|
159
|
+
def read
|
160
|
+
hdr = read_header
|
161
|
+
msg = @socket.read(hdr.size)
|
162
|
+
|
163
|
+
case hdr.type
|
164
|
+
# Data message
|
165
|
+
when PLAYER_MSGTYPE_DATA
|
166
|
+
debug "Data for #{hdr.dev_addr.interface_name}:#{hdr.dev_addr.index}"
|
167
|
+
fill_device hdr, msg
|
168
|
+
|
169
|
+
# Acknowledgement response message
|
170
|
+
when PLAYER_MSGTYPE_RESP_ACK
|
171
|
+
if hdr.dev_addr.interface != PLAYER_PLAYER_CODE
|
172
|
+
handle_response_device(hdr, msg)
|
173
|
+
else
|
174
|
+
handle_response(hdr, msg)
|
175
|
+
end
|
176
|
+
when PLAYER_MSGTYPE_RESP_NACK
|
177
|
+
warn "NACK for subtype = #{hdr.subtype} from #{hdr}"
|
178
|
+
when PLAYER_MSGTYPE_SYNCH
|
179
|
+
nil
|
180
|
+
else
|
181
|
+
warn "Unknow message type = #{hdr.type} received in read()"
|
182
|
+
end
|
183
|
+
[hdr, msg]
|
184
|
+
end
|
185
|
+
|
186
|
+
def handle_response_device(hdr, msg)
|
187
|
+
@devices.each do |dev|
|
188
|
+
dev.handle_response(hdr, msg) if dev.addr == hdr.dev_addr
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def fill_device(hdr, msg)
|
193
|
+
@devices.each do |dev|
|
194
|
+
dev.fill(hdr, msg) if dev.addr == hdr.dev_addr
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def send_header(hdr)
|
199
|
+
@socket.write hdr.encode
|
200
|
+
@socket.flush
|
201
|
+
debug "Send #{hdr}"
|
202
|
+
end
|
203
|
+
|
204
|
+
def read_header
|
205
|
+
hdr = Header.decode(@socket.read(PLAYERXDR_MSGHDR_SIZE))
|
206
|
+
debug "Read #{hdr}"
|
207
|
+
hdr
|
208
|
+
end
|
115
209
|
end
|
116
210
|
end
|
data/lib/ruby-player/common.rb
CHANGED
@@ -12,18 +12,55 @@
|
|
12
12
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
13
|
# GNU General Public License for more details.
|
14
14
|
|
15
|
+
require "isna"
|
16
|
+
|
15
17
|
module Player
|
16
18
|
module Common
|
17
|
-
|
18
|
-
|
19
|
-
|
19
|
+
private
|
20
|
+
def debug(msg)
|
21
|
+
if @log_level == :debug
|
22
|
+
puts log_msg(:debug, msg)
|
23
|
+
end
|
24
|
+
end
|
20
25
|
|
21
|
-
|
26
|
+
def notice(msg)
|
27
|
+
if [:debug, :notice].include?(@log_level)
|
28
|
+
puts log_msg(:notice, msg).to_ansi.normal
|
29
|
+
end
|
22
30
|
end
|
23
31
|
|
24
|
-
|
25
|
-
|
26
|
-
|
32
|
+
def warn(msg)
|
33
|
+
if [:debug, :log, :warn].include?(@log_level)
|
34
|
+
puts log_msg(:warn, msg).to_ansi.blue
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def error(msg)
|
39
|
+
if [:debug, :log, :warn, :debug].include?(@log_level)
|
40
|
+
puts log_msg(:error, msg).to_ansi.red
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def log_msg(level, msg)
|
45
|
+
"[ruby-player][#{level}]\t #{@addr.interface_name}:#{@addr.index}\t#{msg}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def raise_error(err_msg)
|
49
|
+
error err_msg
|
50
|
+
raise StandardError.new(err_msg)
|
51
|
+
end
|
52
|
+
|
53
|
+
def search_const_name(value, tmpl)
|
54
|
+
tmpl = Regexp.new(tmpl.to_s)
|
55
|
+
consts = Player.constants.select { |c| c =~ tmpl}
|
56
|
+
consts.each do |c|
|
57
|
+
return c.to_s if Player.module_eval(c.to_s) == value
|
58
|
+
end
|
59
|
+
""
|
60
|
+
end
|
61
|
+
|
62
|
+
def unexpected_message(hdr)
|
63
|
+
warn "Get unexpection message type #{hdr.type_name}::#{hdr.subtype_name} for #@addr"
|
27
64
|
end
|
28
65
|
end
|
29
66
|
end
|