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