Source code for pyax12.status_packet

# -*- coding : utf-8 -*-

# PyAX-12

# The MIT License
#
# Copyright (c) 2010,2015 Jeremie DECOCK (http://www.jdhp.org)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

"""
This module contain the `StatusPacket` class which implements
"status packets" (the response packets from the Dynamixel units to the main
controller after receiving an instruction packet).
"""

__all__ = ['StatusPacket']

import pyax12.packet as pk

# EXCEPTION CLASSES ###########################################################

class StatusPacketError(Exception):
    """Base class for exceptions in the `status_packet` module."""
    pass

class InstructionError(StatusPacketError):
    """Exception raised if an undefined instruction is sent or an action
    instruction is sent without a Reg_Write instruction."""
    pass

class OverloadError(StatusPacketError):
    """Exception raised if the specified maximum torque can't control the
    applied load."""
    pass

class InstructionChecksumError(StatusPacketError):
    """Exception raised if the checksum of the instruction packet is
    incorrect."""
    pass

class StatusChecksumError(StatusPacketError):
    """Exception raised if the checksum of the status packet is
    incorrect."""
    pass

class RangeError(StatusPacketError):
    """Exception raised if the instruction sent is out of the defined range."""
    pass

class OverheatingError(StatusPacketError):
    """Exception raised if the internal temperature of the Dynamixel unit is
    above the operating temperature range as defined in the control table."""
    pass

class AngleLimitError(StatusPacketError):
    """Exception raised if the goal position is set outside of the range
    between "CW Angle Limit" and "CCW Angle Limit"."""
    pass

class InputVoltageError(StatusPacketError):
    """Exception raised if the voltage is out of the operating voltage range as
    defined in the control table."""
    pass

# STATUS PACKET CLASS #########################################################

[docs]class StatusPacket(pk.Packet): """The "status packet" is the response packet from the Dynamixel units to the main controller after receiving an "instruction packet". The structure of the status packet is as the following: +----+----+--+------+-----+----------+---+-----------+---------+ |0XFF|0XFF|ID|LENGTH|ERROR|PARAMETER1|...|PARAMETER N|CHECK SUM| +----+----+--+------+-----+----------+---+-----------+---------+ StatusPacket is not intended to be instancied by users (except maybe for testing and debugging prupose). Under normal conditions of use, `StatusPacket`'s instances are automatically created by the `Connection` class. :param bytes packet: a sequence of bytes containing the full status packet returned by Dynamixel units. It must be compatible with the "bytes" type. """ def __init__(self, packet): # Check the argument and convert it to "bytes" if necessary. # Assert "packet" items are in range (0, 0xff). # "TypeError" and "ValueError" are sent by the "bytes" constructor if # necessary. # The statement "tuple(packet)" implicitely rejects integers (and all # non-iterable objects) to compensate the fact that the bytes # constructor doesn't reject them: bytes(6) is valid and returns # b'\x00\x00\x00'. self._bytes = bytes(tuple(packet)) # Assert the argument is a sequence with at least 6 items. if len(self._bytes) < 6: raise ValueError("Incomplete packet.") # Check the header bytes. # Should be tested before the length and checksum because if the header # is wrong then the length and the checksum are wrong too (thus testing # the header first gives a more relevant information when its value is # wrong). if bytes(self.header) != b'\xff\xff': raise ValueError("Wrong header (should be b'\xff\xff').") # Check length (length = num_params + 2 = full_packet_length - 4). # Should be tested before the checksum because if the length is wrong # then the checksum is wrong too (thus testing the length first gives a # more relevant information when its value is wrong). if self.length != len(self._bytes) - 4: raise ValueError('Wrong length byte.') # Verify the checksum. computed_checksum = pk.compute_checksum(self._bytes[2:-1]) if computed_checksum != self.checksum: raise StatusChecksumError('Wrong checksum.') # Check error bit flags. if self.instruction_error: raise InstructionError() if self.overload_error: raise OverloadError() if self.checksum_error: raise InstructionChecksumError() if self.range_error: raise RangeError() if self.overheating_error: raise OverheatingError() if self.angle_limit_error: raise AngleLimitError() if self.input_voltage_error: raise InputVoltageError() # Check the ID byte if not(0x00 <= self.dynamixel_id <= 0xfd): msg = "Wrong dynamixel_id, a value in range (0, 0xFD) is required." raise ValueError(msg) # READ ONLY PROPERTIES @property def error(self): """The byte representing errors sent from the Dynamixel unit. This member is a read-only property. """ return self._bytes[4] @property def instruction_error(self): """A boolean which is set to True if an undefined instruction is sent or an action instruction is sent without a Reg_Write instruction. This member is a read-only property. """ return bool(self.error & (1 << 6)) @property def overload_error(self): """A boolean which is set to True if the specified maximum torque can't control the applied load. This member is a read-only property. """ return bool(self.error & (1 << 5)) @property def checksum_error(self): """A boolean which is set to True if the checksum of the instruction packet is incorrect. This member is a read-only property. """ return bool(self.error & (1 << 4)) @property def range_error(self): """A boolean which is set to True if the instruction sent is out of the defined range. This member is a read-only property. """ return bool(self.error & (1 << 3)) @property def overheating_error(self): """A boolean which is set to True if the internal temperature of the Dynamixel unit is above the operating temperature range as defined in the control table. This member is a read-only property. """ return bool(self.error & (1 << 2)) @property def angle_limit_error(self): """A boolean which is set to True if the goal position is set outside of the range between "CW Angle Limit" and "CCW Angle Limit". This member is a read-only property. """ return bool(self.error & (1 << 1)) @property def input_voltage_error(self): """A boolean which is set to True if the voltage is out of the operating voltage range as defined in the control table. This member is a read-only property. """ return bool(self.error & (1 << 0))