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 CHANGED
@@ -7,3 +7,4 @@ html/
7
7
  doc/
8
8
  Gemfile.lock
9
9
  .rvmrc
10
+ tags
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+
5
+ script: "bundle exec rake spec"
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard 'rspec' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { "spec" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
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
- It are using the FFI for binding with playerc C client library.
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[:position2d, 0]
35
- pos2d.set_vel(1, 0, 0.2)
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=#{pos2d.px}, y=#{pos2d.py}, a=#{pos2d.pa}
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
- 1. Get geometry of device and sensors
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
@@ -75,11 +75,11 @@ define bender2dx position
75
75
  size [1 1 1]
76
76
  )
77
77
 
78
- #carton
79
- #(
80
- # name "c0"
81
- # pose [ 1 0 0 0.0]
82
- #)
78
+ carton
79
+ (
80
+ name "c0"
81
+ pose [ 3 0 0 0.0]
82
+ )
83
83
 
84
84
  bender2dx
85
85
  (
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
- require "ruby-player/client"
28
+
29
+
30
+
@@ -17,39 +17,38 @@ module Player
17
17
  #
18
18
  # @example
19
19
  #
20
- # Player::Client.connect("localhost") do |cl|
21
- # pos2d = cl[:position2d, 0]
22
- # pos2d.set_vel(1, 0, 0.2)
23
- # cl.loop do
24
- # puts "Position: x=#{pos2d.px}, y=#{pos2d.py}, a=#{pos2d.pa}
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
- module C
32
- extend FFI::Library
33
- ffi_lib "playerc"
34
-
35
- attach_function :playerc_client_create, [:pointer, :string, :int], :pointer
36
- attach_function :playerc_client_destroy, [:pointer], :void
37
- attach_function :playerc_client_connect, [:pointer], :int
38
- attach_function :playerc_client_disconnect, [:pointer], :int
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
- attach_function :playerc_client_read, [:pointer], :void
42
- end
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
- try_with_error C.playerc_client_connect(@client)
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
- C.playerc_client_read(@client)
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[:position2d, 0]
74
- #
75
- # @param type type name of proxy object
76
- # @param index index of proxy object
77
- # @return proxy object
78
- def [](type, index)
79
- type_name = type.to_s.capitalize
80
- (eval type_name).send(:new, @client, index)
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
- @client[:connected] == 0
97
+ @socket.closed?
86
98
  end
87
99
 
88
100
  # Close connection
89
101
  def close
90
- try_with_error C.playerc_client_disconnect(@client)
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 Client.finilazer(client)
110
- lambda do
111
- C.playerc_client_disconnect(client) if client[:connected] > 0
112
- C.playerc_client_destroy(client)
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
@@ -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
- module C
18
- extend FFI::Library
19
- ffi_lib "playerc"
19
+ private
20
+ def debug(msg)
21
+ if @log_level == :debug
22
+ puts log_msg(:debug, msg)
23
+ end
24
+ end
20
25
 
21
- attach_function :playerc_error_str, [], :string
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
- private
25
- def try_with_error(result)
26
- raise StandardError.new(C.playerc_error_str) if result != 0
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