ruby-player 0.0.1 → 0.1.0
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.
- 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) [](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
|