Package ckbot :: Module dynamixel :: Class Protocol
[hide private]
[frames] | no frames]

Class Protocol

source code


( concrete )
DESCRIPTION: 
  -- The Protocol class provides the functionality needed to send messages using the
     Dynamixel Protocol as per section 3 of the EX-106 document
RESPONSIBILITIES: 
  -- detect modules on the bus and generate NodeAdaptors for each one
  -- ping modules periodically to ensure that they are still on the Bus??
  -- store a queue of pending messages and handle them for during each update 
     for a given timeslice
  -- act as a abstraction layer for basic Bus functionality?
OWNERSHIP:
  -- owned by the Cluster
THEORY: 
  -- The protocol owns a dictionary of the NodeAdaptor of each module 
     It initially checks for modules by performing a scan for all nodes
     that the user states should be on the Bus and for each module
     that exists it updates the heartbeat dictionary entry for that node
     
     A heartbeat, is a simple indicator of node existing, holding only a 
     timestamp and the error received.

     Message transmission and reception is based upon the following ideas:
     - If asked, Dynamixel servos always send a response
     - Dynamixel servos have no Bus arbitration, therefore the response
       to a request always follows the request
     
     - There are three types of requests 
       - Writes that require an acknowledgement         
       - Reads that require an acknowledgement
       - Writes that do not require an acknowledgement

     The aformentioned ideas led to the following decisions: 
     - Messages should be placed in a queue and handled in order of requests
     - Message that do not require a response should be sent using the 
       "SYNC_WRITE" command as per section 3-5-7 pp. 39 in the EX-106 document
       and no response should be expected.
     - Messages that require a response should be sent and listened for. If a 
       response is not found, a number of retries should occur to accomodate 
       for errors

     A basic representation of the update() algorithm follows:

       get the allowed timeslice
       while len queue > 0: 
         pop message from queue
         if message has timed out:
           declare message promise as Err
         if allowed timeslice is used up: 
           append message to front of queue and exit
         if the message is a broadcast message:
           if ping:
             warn that broadcast pings are not allowed outside of scan             
           else:
             send message through Bus, and continue to next message
         else:
           for number of retries remaining in message:
             if allowed timeslice is used up:
               append message to front of queue and exit 
             send message through Bus
             while have not reached timeout or timeslice is used up:
               read in packet
               if packet exists:
                 fulfill message promise, and continue to next message
               sleep so that we don't overwork the OS
           if no more retries remaining: 
             declare message promise as Err

CONSTRAINTS:
  -- a single write when handled by update must have only a single response
  -- it is expected that a Dynamixel Bus has been instantiated as shown in the
     Dynamixel Module Examples   

Instance Methods [hide private]
 
__init__(self, bus=None, nodes=None, *args, **kw)
Initialize a Dynamixel Protocol
source code
 
reset(self, nodes=None, ping_rate=1.0)
Reset the Protocol object
source code
 
scan(self, timeout=1.0, retries=1, get_model=True)
Build a broadcast ping message and then read for responses, and if the get_model flag is set, then get model numbers for all existing nodes
source code
 
hintNodes(self, nodes)
Generate NodeAdaptors for nodes that are not already in pnas
source code
 
generatePNA(self, nid)
Generate NodeAdaptors for given Node ID
source code
 
off(self)
Turn all servos off ...
source code
 
mem_write(self, nid, addr, pars)
Send a memory write command and don't wait for a response
source code
 
mem_write_sync(self, nid, addr, pars)
Send a memory write command and wait for response, returning it
source code
 
mem_read_sync(self, nid, addr, length, retries=4)
A Protocol level syncronous memory read wrapper around bus.send_cmd_sync
source code
 
request(self, nid, cmd, pars='', lifetime=0.05, **kw)
Send a response request by creating an incomplete messsage and appending it to the queue for handling by update
source code
 
_get_heartbeats(self, now)
Use pollRing to poll all nodes that haven't been seen for self.ping_rate seconds with ping packets.
source code
 
update(self, timeout=0.01)
The update method handles current incomplete message requests and ensures that nodes are pinged to check for their existance
source code
 
_doRequest(self, now, inc)
Process a single incomplete request If it requires a response -- complete it or time-out; if not, complete with a None reply
source code

Inherited from object: __delattr__, __format__, __getattribute__, __hash__, __new__, __reduce__, __reduce_ex__, __repr__, __setattr__, __sizeof__, __str__, __subclasshook__

Properties [hide private]

Inherited from object: __class__

Method Details [hide private]

__init__(self, bus=None, nodes=None, *args, **kw)
(Constructor)

source code 
Initialize a Dynamixel Protocol

INPUTS:
  bus -- object -- dynamixel bus object used for basic communication with dynamixel servos
  nodes -- list -- list of expected dynamixel servo IDs 

ATTRIBUTES:
  pnas -- dictionary -- dict of NodeAdaptor objects, used for interfacing with Dynamixel Module
  heartbeats -- dictionary -- dict of node heartbeats. nid : (timestamp, err)
  requests -- deque object -- queue of pending requests <<< maybe should just be a list?  
  ping_period -- float -- expected ping period 

Overrides: object.__init__

reset(self, nodes=None, ping_rate=1.0)

source code 
Reset the Protocol object 

INPUTS:
  ping_rate -- float -- rate at which to ping nodes 
OUTPUTS: 
  None
THEORY OF OPERATION:
  scan for nodes, add each node to the pollRing and generate a ProtocolNodeAdaptor

scan(self, timeout=1.0, retries=1, get_model=True)

source code 
Build a broadcast ping message and then read for responses, and if 
  the get_model flag is set, then get model numbers for all existing 
  nodes

INPUTS:
  timeout -- float -- amount of time allowed to scan
  retries -- int -- number of times allowed to retry scan  
  get_model -- bool -- flag determining if node model numbers 
                       should be read to indicate the servo
                       type                                
OUTPUTS: 
  found -- dict -- map from servo IDs that were discovered
    to their model numbers, or None if get_model is False 
 
THEORY OF OPERATION:
  Assemble packet as per EX-106 section 3-2 pp. 16, using 
  PING Command as per section 3-5-5 pp. 37 and broadcast ID
  Send this packet, then read responses and add to set  until 
  timeout and number of retries.

hintNodes(self, nodes)

source code 
Generate NodeAdaptors for nodes that are not already in pnas 

INPUTS:
  nodes -- list -- list of expected nodes 

THEORY OF OPERATION:
  Get all node IDs that are in nodes but not in pnas and 
  generate a NodeAdaptors for those nodes

Overrides: ckmodule.AbstractProtocol.hintNodes

generatePNA(self, nid)

source code 
Generate NodeAdaptors for given Node ID

INPUTS:
  nid -- int -- ID of node to generate ProtocolNodeAdaptor object for
OUTPUTS: 
  pna -- object -- ProtocolNodeAdaptor object for given nid 

THEORY OF OPERATION:
  Instantiate ProtocolNodeAdaptor add to pnas dictionary 

Overrides: ckmodule.AbstractProtocol.generatePNA

off(self)

source code 

Turn all servos off ... see Bus.off

mem_write(self, nid, addr, pars)

source code 
Send a memory write command and don't wait for a response

INPUTS:
  addr -- char -- address
  val -- int -- value
  pars -- string -- parameters
OUTPUTS: 
  msg -- string -- transmitted packet minus SYNC        

mem_write_sync(self, nid, addr, pars)

source code 
Send a memory write command and wait for response, returning it

INPUTS:
  addr -- char -- address
  val -- int -- value
  pars -- string -- parameters
OUTPUTS: 
  msg -- string -- transmitted packet minus SYNC        

mem_read_sync(self, nid, addr, length, retries=4)

source code 
A Protocol level syncronous memory read wrapper around bus.send_cmd_sync
    
INPUTS:
  nid -- int -- node ID
  addr -- string hex -- address location to read from
  length -- int -- number of bytes to read
  retries -- int -- number of times to retry if there exist protocol errors

request(self, nid, cmd, pars='', lifetime=0.05, **kw)

source code 
Send a response request by creating an incomplete messsage
and appending it to the queue for handling by update

INPUTS:
  nid -- int -- node ID of module
  cmd -- int -- command value. ie: CMD_PING, CMD_WRITE_DATA, etc ...
  pars -- string -- parameter string
OUTPUTS: 
  promise -- list -- promise of incomplete message, will be fulfilled upon 
                     successful read
PRECONDITIONS:
  None?
POSTCONDITIONS:
  None?

THEORY OF OPERATION:
  Create an incomplete message for a given request that holds the 
  packet to be written and has a number of other attributes ( see 
  Request ) Then append to Protocols queue for later handling
  and return a promise that will be fulfilled by a successful read 
  during Protocol.update()

_get_heartbeats(self, now)

source code 
Use pollRing to poll all nodes that haven't been seen for
self.ping_rate seconds with ping packets.

INPUTS:
  now -- float -- current time

update(self, timeout=0.01)

source code 
The update method handles current incomplete message requests and ensures that 
nodes are pinged to check for their existance

INPUTS:
  None
OUTPUTS: 
  num_requests -- int -- length of request queue 
PRECONDITIONS:
  instantiation of dynamixel.Bus, dynamixel.Protocol
POSTCONDITIONS:
  No response occurs 

THEORY OF OPERATION:
  get the allowed timeslice
  while timeslice not done:
    if queue empty --> return
    pop 1st message on queue
    if message has timed out: 
      fill promise with ProtocolError(timeout)
      continue
    if message ID is BROADCAST_ID:
      if message cmd not CMD_SYNC_WRITE:
        bus.send the message
        fill promise with None
      else:
        fill promise with ProtocolError(not CMD_SYNC_WRITE bcast not allowed) 
      continue
    else:
      bus.send_cmd_sync the message
      store reply in promise 

Overrides: ckmodule.AbstractProtocol.update

_doRequest(self, now, inc)

source code 

Process a single incomplete request If it requires a response -- complete it or time-out; if not, complete with a None reply

TODO finish comment