Sophie

Sophie

distrib > Fedora > 14 > x86_64 > media > updates > by-pkgid > 727fa15453fcace956b835e2377d4269 > files > 1758

player-doc-3.0.2-5.fc14.noarch.rpm

/** @ingroup tutorials
@defgroup tutorial_migrating_drivers Migrating from Player 1.6 to Player 2.0
@brief Including a guide to updating your drivers.

Much changed in Player between 1.6 and 2.0, from the fundamental message
model to the nuts and bolts of message formats.  This page aims to ease the
transition by explaining what changed (and sometimes why it changed).  At
least initially, the focus here will be on providing driver maintainers
with the information necessary to update their code.

- @ref fundamentals
  - @ref framework
  - @ref message-model
- @ref details
  - @ref message-formats
  - @ref arrays
  - @ref subtypes
  - @ref dynamic-libs
- @ref low-details

@section fundamentals Fundamentals

Two core aspects of Player have changed:
- @ref framework
- @ref message-model

@subsection framework TCP server vs. robot programming framework

Player 1.6.x was many things:

- A state-based message model.
- A list of interfaces, specifying the messages used to interact with a
  device.
- A format for transmitting these messages over a network.
- A TCP/IP client/server protocol for developing robot control programs.
- A C++ API for developing device drivers.
- A collection of device drivers that control common hardware and implement
  useful algorithms.
- A server that loads, configures, and provides client access to device
  drivers.
- A collection of libraries in various languages that facilitate the
  development of client programs.

Though we tried to keep these aspects of Player separate, they were really
inextricably intertwined.  While Player was useful to a lot of people, it
couldn't easily be extended or reused.

Player 2.0 aims to clarify and compartmentalize its components in such a
way that they can be extended, resused, and replaced, as the situation
demands.  At its core, Player 2.0 comprises 5 components:

- @ref interfaces "interface": The world of robotic devices is carved up
  into a set of interfaces.  Each interface defines the syntax and
  semantics of the messages that a conforming device can consume and
  produce.  This specification is written as a C header file, <player.h>,
  in which the messages are defined as C structs.

- @ref libplayercore : A C++ library that defines a device driver API,
  facilities for instantiating drivers, and the message queues used to move
  messages around between drivers.  This library also provides methods to
  parse configuration files and load plugin drivers from shared objects.

- @ref libplayerdrivers : A C++ library that contains the device drivers
  that are included with the Player distribution (the drivers that were
  formerly "built-in" to the player server).  The exact contents of this
  library vary from system to system, depending on which drivers'
  prerequisites are satisifed, as well as the user-supplied options to the
  configure script.  Each driver is also built as a standalone C++ library.

- @ref libplayerinterface : A C library that provides functions to translate
  Player messages between the native C struct format and the XDR-encoded
  format that can be safely sent over a network.  This code is
  autogenerated by a program that parses <player.h>.

- @ref libplayertcp : A C++ library that provides the facilities for
  servicing clients over TCP.  This library moves messages between TCP
  sockets and message queues (as defined in libplayercore).  All messages
  are XDR-encoded (with libplayerinterface) before network transmission.

Because it's just so darn useful, Player 2.0 still contains a TCP @ref
util_player.  As you can imagine, this server is a short C++
program that uses the above libraries to provide the ability to parse a
configuration file, load/instantiate device drivers, and allow clients
access to these devices via XDR-encoded messages over a TCP socket.

Use the player server if it's what you need, but don't hesitate to use the
various components however you like.  For example, currently in development
are SWIG-generated Java bindings to libplayercore.  Using these bindings,
you can write a native Java program that instantiates and control device
drivers.  You might then plug that Java program into a Jini network and
exchange Player messages as serialized Java objects, instead of XDR-encoded
structs.

@subsection message-model State-based vs. message-passing

From its inception through version 1.6.x, Player was a <i>state-based</i>
system.  In this model, each device is presumed to have some time-varying
state, and the goal of Player is to provide the ability to read and
(sometimes) write this state.  The content of a device's state and whether
it is mutable is defined by the interface(s) that the device supports.  For
example, a mobile robot that supports the @ref interface_position2d
interface maintains as its state the robot's position and velocity.  These
data are reported in the <i>data</i> messages produced by the robot and can
be changed by <i>command</i> messages sent to the robot.  This model is
conceptually simple but is inflexible and not always appropriate.

Player 2.0 employs a more general <i>message-passing</i> model.  In this
approach, each device can produce a certain set of messages, and it can
consume a certain set of messages.  These messages are still defined by the
interface(s) that the device supports, but they no longer need to report
the entire state of the device.  For example, in addition to periodic
updates on its current position and velocity, a mobile robot might send out
a different message when it has reached a goal or when a motor current
limit was exceeded.

@section details Details

A variety of details have changed, including:
  - @ref message-formats
  - @ref arrays
  - @ref subtypes
  - @ref dynamic-libs

@subsection message-formats Message formats

Because of the techniques used to encode messages for network transmission,
Player 1.6.x had frustrating limits on the data types that were allowed.
Specifically, floating point values were not supported, so real numbers
were represented as integers in fixed point.

Player 2.0 is far more flexible in this regard.  Virtually all C data types
are supported, as are nested message structures and fixed- and
variable-length one-dimensional arrays.  Multi-dimensional arrays are not
supported.  

Unless otherwise noted, all message fields are represented in MKS units.

@subsection arrays Arrays

Some arrays in message structures are fixed-length and some are
variable-length.   An array 'foo' is variable-length if the message
structure containing 'foo' also contains a uint32_t called 'foo_count'
('foo_count' must appear before 'foo' in the structure).  Otherwise, it is
fixed-length.  Basically, small arrays (2 or 3 elements) are fixed-length
and bigger ones are variable-length.

The name 'foo_count' is special.  It tells you how many items are actually
in the array 'foo'.   This information is used by the XDR functions during
(de)marshaling.  As a result, you must *always* fill in this field when
sending a message containing a variable-length array (and you should always
consult this field when receiving such a message).

For example, when sending a camera image, put the data in 'image' and fill
in 'image_count' with the actual size of the frame.  Then only those bytes
will be transferred.  Another example: when asking for the list of
available devices, you send a player_device_devlist, with the field
'devices_count' set to 0.  The server responds with it filled in.

@subsection subtypes Message type namespace

Player 2.0 defines a 2-level namespace of messages.  At the first level,
there are 7 <tt>type</tt>s:

- data (PLAYER_MSGTYPE_DATA) : A message emitted by a device, usually
  communicating something about the state of the device.  Data messages are
  not acknowledged.
- command (PLAYER_MSGTYPE_CMD) : A message sent to a device, usually
  changing something about the state of the device.  Command messages are
  not acknowledged.
- request (PLAYER_MSGTYPE_REQ) : A message sent to a device, usually
  requesting a configuration change or querying some information.  Each
  request message is acknowledged by a response message.
- positive response (PLAYER_MSGTYPE_RESP_ACK) : Sent in response to a
  request message, a positive response message indicates that the request
  was successfully processed by the device.  Any requested information is
  contained in the body of the message.
- negative response (PLAYER_MSGTYPE_RESP_NACK) : Sent in response to a
  request message, a negative response indicates that the request was
  received by the device but could not be processed (e.g., the request was
  incorrectly formatted, the device doesn't support that requst, or the
  underlying hardware refused to make the requested configuration change).
  The body of this response will be empty.
- error response (PLAYER_MSGTYPE_RESP_ERR) : Sent in response to a request,
  an error response indicates that the request was never sent to  the
  device (e.g., the device's address is invalid, or the device's message
  queue is full).  The body of this response will be empty.
- synch (PLAYER_MSGTYPE_SYNCH) : This message will likely be deprecated.

Each message also has an interface-specific <tt>subtype</tt>.  For example,
the @ref interface_laser interface defines 2 data messages: one
contains a scan, while the other contains both a scan and a pose.  The
<tt>subtype</tt> field of the message header allows the recipient to
disambiguate the two.  The <tt>subtype</tt> of a response message will
be identical to the corresponding request message.

@subsection dynamic-libs Dynamic libs

All libraries are now built using libtool in both static and dynamic
versions (assuming your system supports building and loading shared
libraries).

@section low-details Low-level details (how to update your driver)

The amount of work required to update a Player 1.6.x driver to the Player
2.0 API varies greatly from driver to driver.  Simple, single-interface
drivers tend to be pretty easy to update, whereas complex, multi-interface
drivers can be quite tedious.  There is no tool for automatically updating
driver code; writing such a tool would be very difficult indeed.

Because each driver can be structured differently and use different parts
of the Driver API, I don't have step-by-step instructions for updating a
driver.  Instead, below is a list of tips, hints, and things to look out
for.  I find that a combination of systematically following the items on
this list and compilation by attrition (i.e., fix each problem as the
compiler finds and complains about it) does the job.

- <tt>libplayerinterface/functiontable.c</tt> must be updated manually to handle
  each message.  The array <tt>init_ftable</tt> defined in that file
  contains a (interface, type, subtype, XDR packing function) tuple for
  each message.  Any message without an entry in that table will not be
  sent or received.

- The player_device_id_t structure has been replaced with the
  @ref player_devaddr_t structure.  The <tt>code</tt> field is now
  called <tt>interf</tt>, the <tt>port</tt> field is <tt>robot</tt>, and a
  <tt>host</tt> field has been added.

- The @ref player_msghdr_t structure has changed:
  -  The addressing information formerly stored in <tt>code</tt> and
     <tt>index</tt> is now contained in <tt>addr</tt>, which is of type
     @ref player_devaddr_t.
  - As explained above (@ref subtypes), the message header has 
    <tt>type</tt> and a <tt>subtype</tt>.
  - The timestamp is a double, containing the number of seconds since the
    epoch.

- ConfigFile::ReadDeviceId is now ConfigFile::ReadDeviceAddr.

- The <tt>position</tt> interface is now the @ref
  interface_position2d interface, and its code is
  PLAYER_POSITION2D_CODE.

- The driver no longer specifies an allowable access mode, in either the
  Driver constructor or in Driver::AddInterface.  The READ, WRITE, and ALL
  access modes have been collapsed into OPEN.  A subscriber can simply OPEN
  or CLOSE a subscription to a device.

- The data/command buffers and request/reply queues have been replaced
  by general-purpose message queues, of type MessageQueue.  Each driver has
  an incoming queue, Driver::InQueue.  All messages to the driver, whether
  commands or requests, arrive on this queue.  The queue has a maximum
  length, set in the Driver constructor or in Driver::AddInterface.

- All external access to a driver is done via the appropriate Device, NOT
  the Driver itself.  For example, to subscribe to another device, call
  Device::Subscribe on the Device* (which you can retrieve from the
  deviceTable with DeviceTable::GetDevice).

- It is no longer to possible to block on another device to wait for
  data from that device.  Instead, a driver can, via Driver::Wait, block on
  its own queue (Driver::InQueue).  If any message are pending on the
  queue, Driver::Wait returns immediately; otherwise, it blocks until a new
  message is pushed onto the queue (or until the specified timeout).

- The Get/Put Data/Command/Config/Reply methods have been replaced with
  methods for pushing and popping messages on queues.  To process incoming
  messages, override the default implementation of Driver::ProcessMessage;
  this method should handle a single message.  The method
  Driver::ProcessMessages will pop all pending messages from
  Driver::InQueue, calling Driver::ProcessMessage once for each message.
  Driver::ProcessMessages facilitates sending replies and should be used in
  place of calling MessageQueue::Pop directly on Driver::InQueue (although
  you can certainly do this if you want).  To send a message to a device
  (i.e., to push a message onto a device's queue), call Device::PutMsg.

- Non-threaded drivers MUST override Driver::ProcessMessage, and they MUST
  respond to each request message in place in this method.
  Driver::ProcessMessages will be invoked periodically on each non-threaded
  driver.  Forwarding requests to an underlying driver from a non-threaded
  driver is tricky; look at the @ref driver_lasercspace driver for
  an example.

- Device::Request provides an easy way to send a request to another device
  and wait for the reply.  Do NOT call this method inside
  Driver::ProcessMessage in a non-threaded driver, as it would block the
  caller, which could be a very bad thing.

- No more byte-swapping or unit-conversions in driver code.  Drivers are
  transport-independent modules, and as such send and receive all messages
  in their native C struct formats (host byte-order, MKS units), as defined
  in <player.h>.  Nobody should include <netinet/in.h>.

- Only one file should be included from Player:
  @code
    #include <libplayerinterface/player.h>
  @endcode
  Use pkg-config (i.e., <tt>pkg-config --cflags libplayercore</tt>) to get
  the appropriate compiler flags.

- For "built-in" drivers (i.e., those included in the Player distribution,
  NOT plugin drivers), the Makefile.am layout has changed:
    - All drivers are now built as libtool libraries, with the extension
      <tt>.la</tt>.
    - An automake conditional tells you whether your driver should be
      built.

  - As an example, if your driver is called <tt>foo</tt>, and is built from
  <tt>foo.cc</tt>, then your Makefile.am might look like this:
  @code
    noinst_LTLIBRARIES = 
    if INCLUDE_FOO
    noinst_LTLIBRARIES += libfoo.la
    endif

    AM_CPPFLAGS = -Wall -I$(top_srcdir)

    libfoo_la_SOURCES = foo.cc
  @endcode

*/