890 lines
34 KiB
C
890 lines
34 KiB
C
|
|
/*
|
||
|
|
Arduino Library for Kangaroo
|
||
|
|
Copyright (c) 2013-2015 Dimension Engineering Inc.
|
||
|
|
http://www.dimensionengineering.com/kangaroo
|
||
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
||
|
|
purpose with or without fee is hereby granted, provided that the above
|
||
|
|
copyright notice and this permission notice appear in all copies.
|
||
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||
|
|
SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
||
|
|
RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||
|
|
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
||
|
|
USE OR PERFORMANCE OF THIS SOFTWARE.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#ifndef Kangaroo_h
|
||
|
|
#define Kangaroo_h
|
||
|
|
|
||
|
|
/*!
|
||
|
|
\file Kangaroo.h
|
||
|
|
Include this file to use the Kangaroo Arduino library.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#if defined(ARDUINO) && ARDUINO < 100
|
||
|
|
#error "This library requires Arduino 1.0 or newer."
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#include <Arduino.h>
|
||
|
|
#define KANGAROO_BIT_PACKED_MAX_VALUE 536870911
|
||
|
|
#define KANGAROO_COMMAND_MAX_DATA_LENGTH 27
|
||
|
|
#define KANGAROO_COMMAND_MAX_BUFFER_LENGTH 32
|
||
|
|
#define KANGAROO_CRC_GOOD_VALUE 0x307e
|
||
|
|
#define KANGAROO_DEFAULT_ADDRESS 128 //!< The default Packet Serial address for Kangaroo is 128.
|
||
|
|
#define KANGAROO_DEFAULT_COMMAND_RETRY_INTERVAL 100 //!< By default, commands retry after 100 milliseconds if no response is received.
|
||
|
|
#define KANGAROO_DEFAULT_COMMAND_TIMEOUT KANGAROO_INFINITE_TIMEOUT //!< By default, commands are retried forever.
|
||
|
|
#define KANGAROO_INFINITE_TIMEOUT -1 //!< This value, passed as a timeout, disables the timeout.
|
||
|
|
#define KANGAROO_UNSPECIFIED_LIMIT -1 //!< This value, passed as a limit, leaves the limit unspecified.
|
||
|
|
|
||
|
|
class Kangaroo;
|
||
|
|
class KangarooChannel;
|
||
|
|
class KangarooMonitor;
|
||
|
|
|
||
|
|
/*!
|
||
|
|
\enum KangarooError
|
||
|
|
Errors that can occur in response to status queries.
|
||
|
|
*/
|
||
|
|
enum KangarooError
|
||
|
|
{
|
||
|
|
// Negative values are responses from the library, though they may *correspond* to remote states.
|
||
|
|
// Positive values are responses from the Kangaroo.
|
||
|
|
KANGAROO_NO_ERROR = 0x00, //!< No error occurred.
|
||
|
|
KANGAROO_NOT_STARTED = 0x01, //!< The channel is not started. Call KangarooChannel::start() on the channel.
|
||
|
|
KANGAROO_NOT_HOMED = 0x02, //!< The channel needs to be homed. Call KangarooChannel::home() on the channel.
|
||
|
|
KANGAROO_CONTROL_ERROR = 0x03, //!< A control error has occurred. Call KangarooChannel::start() on the channel to clear the control error.
|
||
|
|
KANGAROO_WRONG_MODE = 0x04, //!< The controller is in the wrong mode. For example, the DIP switches may be in Mixed Mode while the tune was done in Independent Mode.
|
||
|
|
KANGAROO_UNRECOGNIZED_CODE = 0x05,
|
||
|
|
KANGAROO_SERIAL_TIMEOUT = 0x06, //!< A serial timeout occurred, or the TX line was disconnected. Call KangarooChannel::start() on the channel to clear the serial timeout.
|
||
|
|
KANGAROO_INVALID_STATUS = -0x01,
|
||
|
|
KANGAROO_TIMED_OUT = -0x02, //!< The requested timeout expired.
|
||
|
|
KANGAROO_PORT_NOT_OPEN = -0x03 // The serial port is not open. (Not returned by the Arduino library at present.)
|
||
|
|
};
|
||
|
|
|
||
|
|
/*!
|
||
|
|
\enum KangarooGetType
|
||
|
|
The possible 'get' request types.
|
||
|
|
*/
|
||
|
|
enum KangarooGetType
|
||
|
|
{
|
||
|
|
KANGAROO_GETP = 0x01, //!< Absolute position.
|
||
|
|
KANGAROO_GETPI = 0x41, //!< Incremental position (relative to the position when the last command was issued).
|
||
|
|
KANGAROO_GETS = 0x02, //!< Absolute speed (positive or negative depending on direction).
|
||
|
|
KANGAROO_GETSI = 0x42, //!< Incremental speed (relative to the speed when the last command was issued).
|
||
|
|
KANGAROO_GETMIN = 0x08, //!< The minimum position. This corresponds to DEScribe's Nominal Travel minimum.
|
||
|
|
KANGAROO_GETMAX = 0x09, //!< The maximum position. This corresponds to DEScribe's Nominal Travel maximum.
|
||
|
|
KANGAROO_GETPS = 0x05, //!< Setpoint position. 2014-11-13 or newer firmware is required for this request.
|
||
|
|
KANGAROO_GETSS = 0x06, //!< Setpoint speed. 2014-11-13 or newer firmware is required for this request.
|
||
|
|
};
|
||
|
|
|
||
|
|
/*!
|
||
|
|
\enum KangarooGetFlags
|
||
|
|
Flags that modify the behavior of a 'get' request.
|
||
|
|
*/
|
||
|
|
enum KangarooGetFlags
|
||
|
|
{
|
||
|
|
KANGAROO_GET_DEFAULT = 0x00, //!< Normal behavior.
|
||
|
|
KANGAROO_GET_ECHO_CODE = 0x10,
|
||
|
|
KANGAROO_GET_RAW_UNITS = 0x20, //!< Use raw units. For analog, raw units are millivolts. For quadrature, 4 raw units equal 1 line.
|
||
|
|
KANGAROO_GET_SEQUENCE_CODE = 0x40
|
||
|
|
};
|
||
|
|
|
||
|
|
/*!
|
||
|
|
\enum KangarooMoveFlags
|
||
|
|
Flags that modify the behavior of a motion request.
|
||
|
|
*/
|
||
|
|
enum KangarooMoveFlags
|
||
|
|
{
|
||
|
|
KANGAROO_MOVE_DEFAULT = 0x00, //!< Normal behavior.
|
||
|
|
KANGAROO_MOVE_ONLY_IF_NECESSARY = 0x04,
|
||
|
|
KANGAROO_MOVE_NO_DEFAULT_LIMITS = 0x08, //!< Do not apply the speed limit and acceleration limit source settings. By default, the speed limit comes from Kangaroo's potentiometers.
|
||
|
|
KANGAROO_MOVE_RAW_UNITS = 0x20, //!< Use raw units. For analog, raw units are millivolts. For quadrature, 4 raw units equal 1 line.
|
||
|
|
KANGAROO_MOVE_SEQUENCE_CODE = 0x40
|
||
|
|
};
|
||
|
|
|
||
|
|
/*!
|
||
|
|
\enum KangarooStatusFlags
|
||
|
|
Flags that provide details about a query response.
|
||
|
|
*/
|
||
|
|
enum KangarooStatusFlags
|
||
|
|
{
|
||
|
|
KANGAROO_STATUS_NONE = 0x00, //!< The response is normal.
|
||
|
|
KANGAROO_STATUS_ERROR = 0x01, //!< The response is an error.
|
||
|
|
KANGAROO_STATUS_BUSY = 0x02, //!< For a motion, this means the motion is not finished. For an error, this means the error (such as KANGAROO_NOT_HOMED) should self-clear. This corresponds to a lowercase letter in Simplified Serial.
|
||
|
|
KANGAROO_STATUS_ECHO_CODE = 0x10,
|
||
|
|
KANGAROO_STATUS_RAW_UNITS = 0x20, //!< The value in the response is in raw units. For analog, raw units are millivolts. For quadrature, 4 raw units equal 1 line.
|
||
|
|
KANGAROO_STATUS_SEQUENCE_CODE = 0x40
|
||
|
|
};
|
||
|
|
|
||
|
|
enum KangarooCommand
|
||
|
|
{
|
||
|
|
KANGAROO_CMD_START = 0x20,
|
||
|
|
KANGAROO_CMD_UNITS = 0x21,
|
||
|
|
KANGAROO_CMD_HOME = 0x22,
|
||
|
|
KANGAROO_CMD_STATUS = 0x23,
|
||
|
|
KANGAROO_CMD_MOVE = 0x24,
|
||
|
|
KANGAROO_CMD_SYSTEM = 0x25
|
||
|
|
};
|
||
|
|
|
||
|
|
/*!
|
||
|
|
\enum KangarooSystemCommand
|
||
|
|
Advanced commands that have special effects on the Kangaroo motion controller.
|
||
|
|
*/
|
||
|
|
enum KangarooSystemCommand
|
||
|
|
{
|
||
|
|
KANGAROO_SYS_POWER_DOWN = 0x00, //!< Powers down the channel.
|
||
|
|
KANGAROO_SYS_POWER_DOWN_ALL = 0x01, //!< Powers down all channels of the controller that receives the command.
|
||
|
|
KANGAROO_SYS_TUNE_ENTER_MODE = 0x03, /*!< Enters tune mode 1 (Teach), 2 (Limit Switches), or 3 (Mechanical Stops).
|
||
|
|
This corresponds to pressing the tune button to get to the mode you want.
|
||
|
|
|
||
|
|
Do not expect a reply from this command. */
|
||
|
|
KANGAROO_SYS_TUNE_SET_DISABLED_CHANNELS = 0x08, /*!< Initially all channels are disabled for safety reasons after entering a tune mode.
|
||
|
|
You must send this bitmask before beginning the tune. 0 enables all channels.
|
||
|
|
Do not expect a reply from this command. */
|
||
|
|
KANGAROO_SYS_TUNE_CONTROL_OPEN_LOOP = 0x06, /*!< Sets the open loop power. This can be used to position for a Teach tune.
|
||
|
|
The range is -(2^28-1) to 2^28-1.
|
||
|
|
|
||
|
|
Do not expect a reply from this command. */
|
||
|
|
KANGAROO_SYS_TUNE_GO = 0x04, /*!< Begins the tune.
|
||
|
|
This corresponds to pressing the tune button after you are in the desired mode.
|
||
|
|
Tuning has an automatic serial timeout for safety reasons.
|
||
|
|
You must continually send packets or it will abort. KangarooChannel::getP() in a loop does the job.
|
||
|
|
Do not expect a reply from this command. */
|
||
|
|
KANGAROO_SYS_TUNE_ABORT = 0x05, /*!< Aborts the tune.
|
||
|
|
|
||
|
|
Do not expect a reply from this command. */
|
||
|
|
KANGAROO_SYS_SET_BAUD_RATE = 0x20, /*!< Sets the baud rate.
|
||
|
|
|
||
|
|
Do not expect a reply from this command. */
|
||
|
|
KANGAROO_SYS_SET_SERIAL_TIMEOUT = 0x21 //!< Sets the serial timeout.
|
||
|
|
};
|
||
|
|
|
||
|
|
enum KangarooReplyCode
|
||
|
|
{
|
||
|
|
KANGAROO_RC_STATUS = 0x43
|
||
|
|
};
|
||
|
|
|
||
|
|
class KangarooCRC
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
void begin();
|
||
|
|
void write(byte data);
|
||
|
|
void write(const byte* data, size_t lengthOfData);
|
||
|
|
void end ();
|
||
|
|
|
||
|
|
public:
|
||
|
|
inline uint16_t value() const { return _crc; }
|
||
|
|
void value(uint16_t crc) { _crc = crc; }
|
||
|
|
|
||
|
|
static uint16_t value(const byte* data, size_t lengthOfData);
|
||
|
|
|
||
|
|
private:
|
||
|
|
uint16_t _crc;
|
||
|
|
};
|
||
|
|
|
||
|
|
class KangarooCommandWriter
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
KangarooCommandWriter();
|
||
|
|
|
||
|
|
public:
|
||
|
|
inline const byte* data () const { return _data; }
|
||
|
|
inline size_t length () const { return _length; }
|
||
|
|
|
||
|
|
public:
|
||
|
|
void write ( byte data );
|
||
|
|
void write (const byte* data, size_t length);
|
||
|
|
void writeBitPackedNumber( int32_t number );
|
||
|
|
|
||
|
|
public:
|
||
|
|
size_t writeToBuffer(byte* buffer, byte address, KangarooCommand command) const;
|
||
|
|
static size_t writeToBuffer(byte* buffer, byte address, KangarooCommand command, const byte* data, size_t lengthOfData);
|
||
|
|
void writeToStream(Stream& port, byte address, KangarooCommand command) const;
|
||
|
|
static void writeToStream(Stream& port, byte address, KangarooCommand command, const byte* data, size_t lengthOfData);
|
||
|
|
|
||
|
|
private:
|
||
|
|
byte _address, _command; size_t _length;
|
||
|
|
byte _data[KANGAROO_COMMAND_MAX_DATA_LENGTH];
|
||
|
|
};
|
||
|
|
|
||
|
|
class KangarooReplyReceiver
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
KangarooReplyReceiver();
|
||
|
|
|
||
|
|
public:
|
||
|
|
inline byte address() const { return _data[0]; }
|
||
|
|
inline KangarooReplyCode command() const { return (KangarooReplyCode)_data[1]; }
|
||
|
|
inline const byte* data () const { return &_data[3]; }
|
||
|
|
inline size_t length () const { return _length - 5; }
|
||
|
|
|
||
|
|
public:
|
||
|
|
inline boolean ready() const { return _ready; }
|
||
|
|
void read (byte data);
|
||
|
|
void reset();
|
||
|
|
|
||
|
|
private:
|
||
|
|
size_t _length; boolean _ready;
|
||
|
|
byte _data[KANGAROO_COMMAND_MAX_BUFFER_LENGTH];
|
||
|
|
};
|
||
|
|
|
||
|
|
class KangarooReplyReader
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
KangarooReplyReader(const byte* data, size_t length);
|
||
|
|
|
||
|
|
public:
|
||
|
|
boolean canRead () const;
|
||
|
|
boolean tryRead (byte* value);
|
||
|
|
byte read ();
|
||
|
|
int32_t readBitPackedNumber();
|
||
|
|
|
||
|
|
private:
|
||
|
|
const byte* _data;
|
||
|
|
const byte* _dataEnd;
|
||
|
|
};
|
||
|
|
|
||
|
|
/*!
|
||
|
|
\class KangarooStatus
|
||
|
|
\brief Stores the response to a status request.
|
||
|
|
Returned by KangarooChannel::getP(), KangarooMonitor::status(), and others.
|
||
|
|
*/
|
||
|
|
class KangarooStatus
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
KangarooStatus();
|
||
|
|
KangarooStatus(const byte* data, size_t length);
|
||
|
|
|
||
|
|
public:
|
||
|
|
static KangarooStatus createInvalidStatus();
|
||
|
|
static KangarooStatus createTimedOut ();
|
||
|
|
|
||
|
|
public:
|
||
|
|
/*!
|
||
|
|
Gets the channel name.
|
||
|
|
\return The channel name.
|
||
|
|
*/
|
||
|
|
inline char channel() const { return (char)_channel; }
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Gets the status flags associated with this response.
|
||
|
|
\return The status flags.
|
||
|
|
*/
|
||
|
|
inline KangarooStatusFlags flags() const { return (KangarooStatusFlags)_flags; }
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Gets the type of the request.
|
||
|
|
\return The type of the request.
|
||
|
|
*/
|
||
|
|
inline KangarooGetType type() const { return (KangarooGetType)_type; }
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Gets the value associated with this response.
|
||
|
|
\return The value.
|
||
|
|
*/
|
||
|
|
inline int32_t value() const { return _value; }
|
||
|
|
|
||
|
|
inline byte echoCode () const { return _echoCode; }
|
||
|
|
inline byte sequenceCode() const { return _sequenceCode; }
|
||
|
|
inline boolean valid () const { return _valid; }
|
||
|
|
|
||
|
|
public:
|
||
|
|
/*!
|
||
|
|
Gets whether a recently-requested command is still executing.
|
||
|
|
For example, KangarooChannel::p() is busy while the channel is getting into position, and KangarooChannel::home() is busy while the channel is homing.
|
||
|
|
\return True if the channel is busy.
|
||
|
|
*/
|
||
|
|
boolean busy() const { return 0 != (flags() & KANGAROO_STATUS_BUSY ); }
|
||
|
|
|
||
|
|
/*!
|
||
|
|
If the response is an error, gets the error.
|
||
|
|
\return The error, or KANGAROO_NO_ERROR if the response is not an error.
|
||
|
|
*/
|
||
|
|
KangarooError error() const { return (KangarooError)(0 != (flags() & KANGAROO_STATUS_ERROR) ? value() : 0); }
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Gets whether a recently-requested command has completed execution.
|
||
|
|
For example, KangarooChannel::p() is done when the channel is in position.
|
||
|
|
\return True if the command is done.
|
||
|
|
*/
|
||
|
|
boolean done() const { return !busy (); }
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Gets whether the response is not an error and KangarooStatus::value() is the requested value.
|
||
|
|
\return True if the response is not an error.
|
||
|
|
*/
|
||
|
|
boolean ok() const { return !error(); }
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Gets whether a timeout has occurred.
|
||
|
|
\return True if the KangarooStatus::error() equals KANGARO_TIMED_OUT.
|
||
|
|
*/
|
||
|
|
boolean timedOut() const { return error() == KANGAROO_TIMED_OUT; }
|
||
|
|
|
||
|
|
private:
|
||
|
|
static KangarooStatus createFromError(KangarooError error);
|
||
|
|
|
||
|
|
private:
|
||
|
|
void init ();
|
||
|
|
boolean parse(const byte* data, size_t length);
|
||
|
|
|
||
|
|
private:
|
||
|
|
boolean _valid;
|
||
|
|
byte _channel, _flags, _echoCode, _sequenceCode, _type;
|
||
|
|
int32_t _value;
|
||
|
|
};
|
||
|
|
|
||
|
|
/*!
|
||
|
|
\class KangarooSerial
|
||
|
|
\brief Create a KangarooSerial for the serial port you are using, and then attach a KangarooChannel for each channel you want to communicate with.
|
||
|
|
*/
|
||
|
|
class KangarooSerial
|
||
|
|
{
|
||
|
|
friend class KangarooChannel;
|
||
|
|
|
||
|
|
public:
|
||
|
|
/*!
|
||
|
|
Constructs a KangarooSerial object.
|
||
|
|
\param port The serial port the motion controller is on.
|
||
|
|
*/
|
||
|
|
KangarooSerial(Stream& port);
|
||
|
|
|
||
|
|
public:
|
||
|
|
/*!
|
||
|
|
Gets the serial port being used.
|
||
|
|
\return The serial port.
|
||
|
|
*/
|
||
|
|
inline Stream& port() { return _port; }
|
||
|
|
|
||
|
|
private:
|
||
|
|
boolean tryReceivePacket();
|
||
|
|
|
||
|
|
private:
|
||
|
|
KangarooSerial (KangarooSerial& serial); // no copy
|
||
|
|
void operator = (KangarooSerial& serial);
|
||
|
|
|
||
|
|
private:
|
||
|
|
KangarooReplyReceiver _receiver;
|
||
|
|
Stream& _port;
|
||
|
|
};
|
||
|
|
|
||
|
|
/*!
|
||
|
|
\class KangarooTimeout
|
||
|
|
\brief Encapsulates a starting time and duration.
|
||
|
|
The same KangarooTimeout can be reused between different calls.
|
||
|
|
*/
|
||
|
|
class KangarooTimeout
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
/*!
|
||
|
|
Constructs a KangarooTimeout object. The starting time is captured.
|
||
|
|
\param timeoutMS The timeout duration, in milliseconds.
|
||
|
|
*/
|
||
|
|
KangarooTimeout(int32_t timeoutMS);
|
||
|
|
|
||
|
|
public:
|
||
|
|
/*!
|
||
|
|
Gets whether the timeout can expire.
|
||
|
|
If the KangarooTimeout was created with a timeout of KANGAROO_INFINITE_TIMEOUT, it cannot expire.
|
||
|
|
\return True if the timeout can expire.
|
||
|
|
*/
|
||
|
|
boolean canExpire() const;
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Gets whether the timeout has expired.
|
||
|
|
\return True if the timeout has expired.
|
||
|
|
*/
|
||
|
|
boolean expired() const;
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Causes the timeout to expire immediately.
|
||
|
|
*/
|
||
|
|
void expire();
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Captures the current time and uses it as the new starting time for the timeout.
|
||
|
|
*/
|
||
|
|
void reset();
|
||
|
|
|
||
|
|
private:
|
||
|
|
uint32_t _start;
|
||
|
|
int32_t _timeoutMS;
|
||
|
|
};
|
||
|
|
|
||
|
|
/*!
|
||
|
|
\class KangarooChannel
|
||
|
|
\brief Lets you send commands to and get position, speed, etc. for a specific channel of the Kangaroo.
|
||
|
|
Any number of KangarooChannel objects can be associated with a single KangarooSerial object.
|
||
|
|
*/
|
||
|
|
class KangarooChannel
|
||
|
|
{
|
||
|
|
friend class Kangaroo;
|
||
|
|
friend class KangarooMonitor;
|
||
|
|
|
||
|
|
public:
|
||
|
|
/*!
|
||
|
|
Constructs a KangarooChannel object.
|
||
|
|
\param serial The KangarooSerial whose serial port the controller is on.
|
||
|
|
\param name The name of the channel (a single character).
|
||
|
|
By default, mixed mode channels are 'D' (Drive) and 'T' (Turn). Independent mode channels are '1' and '2'.
|
||
|
|
You can change these in DEScribe. Mainly this is useful if you have multiple Kangaroos daisy-chained and have
|
||
|
|
'Enable multi-Kangaroo mode (shared signal lines).' checked, or if your particular application would be clearer
|
||
|
|
if for instance the channels were named 'X', 'Y', and 'Z'.
|
||
|
|
\param address The Packet Serial address of the controller.
|
||
|
|
Normally this can be left at its default. If you daisy-chain the TX/S1 line between your Kangaroo and a
|
||
|
|
Sabertooth or SyRen motor driver set in Packet Serial, however, make sure they aren't both using the same address.
|
||
|
|
*/
|
||
|
|
KangarooChannel(KangarooSerial& serial, char name, byte address = KANGAROO_DEFAULT_ADDRESS);
|
||
|
|
~KangarooChannel();
|
||
|
|
|
||
|
|
public:
|
||
|
|
/*!
|
||
|
|
Gets the channel name.
|
||
|
|
\return The channel name.
|
||
|
|
*/
|
||
|
|
inline char name() const { return _name; }
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Gets the Packet Serial address of the controller.
|
||
|
|
\return The packet serial address of the controller.
|
||
|
|
*/
|
||
|
|
inline byte address() const { return _address; }
|
||
|
|
|
||
|
|
public:
|
||
|
|
/*!
|
||
|
|
Starts the channel. Also, the Kangaroo LED will shine brightly for a third of a second.
|
||
|
|
\param onlyIfNecessary Whether the channel should only be started if necessary. If true, and it is already started, it will not be restarted.
|
||
|
|
This option requires 2014-11-13 or newer firmware.
|
||
|
|
\return A KangarooError.
|
||
|
|
Most commonly, this will be KANGAROO_NO_ERROR if the channel does not require homing, or KANGAROO_NOT_HOMED if it does.
|
||
|
|
*/
|
||
|
|
KangarooError start(bool onlyIfNecessary = false);
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Sets custom units for the channel.
|
||
|
|
This command may be called after you start the channel but before you home it.
|
||
|
|
If you do not set custom units, the units you have set up in DEScribe will be used.
|
||
|
|
If you haven't set any, machine units will be used.
|
||
|
|
\param desiredUnits The amount in your units that correspond to the specified amount of machine units.
|
||
|
|
\param machineUnits The amount of machine units (millivolts or lines) corresponding to the specified amount in your units.
|
||
|
|
\return A KangarooError.
|
||
|
|
Most commonly, this will be KANGAROO_NO_ERROR if the channel does not require homing, or KANGAROO_NOT_HOMED if it does.
|
||
|
|
*/
|
||
|
|
KangarooError units(int32_t desiredUnits, int32_t machineUnits);
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Homes the channel.
|
||
|
|
\param onlyIfNecessary Whether or not the channel should only be homed if necessary. If true, and it has already been homed, it will not be rehomed.
|
||
|
|
This option requires 2014-11-13 or newer firmware.
|
||
|
|
\return A KangarooMonitor for tracking the homing request.
|
||
|
|
The easiest way to use the KangarooMonitor here is to call KangarooMonitor::wait().
|
||
|
|
*/
|
||
|
|
KangarooMonitor home(bool onlyIfNecessary = false);
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Moves to the specified absolute position.
|
||
|
|
This command is most useful for absolutely-positioned systems.
|
||
|
|
\param position The position to move to.
|
||
|
|
\param speedLimit The speed limit for the move.
|
||
|
|
\param flags Modifiers for the move command.
|
||
|
|
\return A KangarooMonitor for tracking the move request.
|
||
|
|
*/
|
||
|
|
KangarooMonitor p(int32_t position,
|
||
|
|
int32_t speedLimit = KANGAROO_UNSPECIFIED_LIMIT,
|
||
|
|
KangarooMoveFlags flags = KANGAROO_MOVE_DEFAULT);
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Makes an incremental move, relative to where you are right now.
|
||
|
|
Rovers are a case where this is almost always what you want.
|
||
|
|
\param positionIncrement The amount to increment the current position by.
|
||
|
|
\param speedLimit The speed limit for the move.
|
||
|
|
\param flags Modifiers for the move command.
|
||
|
|
\return A KangarooMonitor for tracking the move request.
|
||
|
|
*/
|
||
|
|
KangarooMonitor pi(int32_t positionIncrement,
|
||
|
|
int32_t speedLimit = KANGAROO_UNSPECIFIED_LIMIT,
|
||
|
|
KangarooMoveFlags flags = KANGAROO_MOVE_DEFAULT);
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Makes an incremental move, relative to the current position setpoint.
|
||
|
|
This is useful for conveyors.
|
||
|
|
2014-11-13 or newer firmware is required for this command.
|
||
|
|
\param positionIncrement The amount to increment the current position by.
|
||
|
|
\param speedLimit The speed limit for the move.
|
||
|
|
\param flags Modifiers for the move command.
|
||
|
|
\return A KangarooMonitor for tracking the move request.
|
||
|
|
*/
|
||
|
|
KangarooMonitor psi(int32_t positionIncrement,
|
||
|
|
int32_t speedLimit = KANGAROO_UNSPECIFIED_LIMIT,
|
||
|
|
KangarooMoveFlags flags = KANGAROO_MOVE_DEFAULT);
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Moves at a particular speed.
|
||
|
|
\param speed The speed to move at.
|
||
|
|
\param speedRampingLimit The speed ramping limit for the move.
|
||
|
|
\param flags Modifiers for the move command.
|
||
|
|
\return A KangarooMonitor for tracking the move request.
|
||
|
|
*/
|
||
|
|
KangarooMonitor s(int32_t speed,
|
||
|
|
int32_t speedRampingLimit = KANGAROO_UNSPECIFIED_LIMIT,
|
||
|
|
KangarooMoveFlags flags = KANGAROO_MOVE_DEFAULT);
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Moves at a particular speed, incremental from the current speed.
|
||
|
|
\param speedIncrement The amount to increment the current speed by.
|
||
|
|
\param speedRampingLimit The speed ramping limit for the move.
|
||
|
|
\param flags Modifiers for the move command.
|
||
|
|
\return A KangarooMonitor for tracking the move request.
|
||
|
|
*/
|
||
|
|
KangarooMonitor si(int32_t speedIncrement,
|
||
|
|
int32_t speedRampingLimit = KANGAROO_UNSPECIFIED_LIMIT,
|
||
|
|
KangarooMoveFlags flags = KANGAROO_MOVE_DEFAULT);
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Moves at a particular speed, incremental from the current speed setpoint.
|
||
|
|
This is useful for cruise control: a negative increment will slow down,
|
||
|
|
a positive increment will speed up,
|
||
|
|
and a zero increment will hold the current speed.
|
||
|
|
2014-11-13 or newer firmware is required for this command.
|
||
|
|
\param speedIncrement The amount to increment the current speed by.
|
||
|
|
\param speedRampingLimit The speed ramping limit for the move.
|
||
|
|
\param flags Modifiers for the move command.
|
||
|
|
\return A KangarooMonitor for tracking the move request.
|
||
|
|
*/
|
||
|
|
KangarooMonitor ssi(int32_t speedIncrement,
|
||
|
|
int32_t speedRampingLimit = KANGAROO_UNSPECIFIED_LIMIT,
|
||
|
|
KangarooMoveFlags flags = KANGAROO_MOVE_DEFAULT);
|
||
|
|
|
||
|
|
public:
|
||
|
|
/*!
|
||
|
|
Issues a 'get' request.
|
||
|
|
\param type The type of the 'get' request.
|
||
|
|
\param flags Flags modifying the 'get' request.
|
||
|
|
\return A KangarooStatus object describing the response.
|
||
|
|
*/
|
||
|
|
KangarooStatus get(KangarooGetType type, KangarooGetFlags flags = KANGAROO_GET_DEFAULT);
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Gets the absolute position.
|
||
|
|
\param flags Flags modifying the 'get' request.
|
||
|
|
\return A KangarooStatus object describing the position.
|
||
|
|
*/
|
||
|
|
inline KangarooStatus getP(KangarooGetFlags flags = KANGAROO_GET_DEFAULT)
|
||
|
|
{
|
||
|
|
return get(KANGAROO_GETP, flags);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Gets the incremental position (relative to the position when the last command was issued).
|
||
|
|
\param flags Flags modifying the 'get' request.
|
||
|
|
\return A KangarooStatus object describing the position.
|
||
|
|
*/
|
||
|
|
inline KangarooStatus getPI(KangarooGetFlags flags = KANGAROO_GET_DEFAULT)
|
||
|
|
{
|
||
|
|
return get(KANGAROO_GETPI, flags);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Gets the setpoint position.
|
||
|
|
2014-11-13 or newer firmware is required for this request.
|
||
|
|
\param flags Flags modifying the 'get' request.
|
||
|
|
\return A KangarooStatus object describing the position.
|
||
|
|
*/
|
||
|
|
inline KangarooStatus getPS(KangarooGetFlags flags = KANGAROO_GET_DEFAULT)
|
||
|
|
{
|
||
|
|
return get(KANGAROO_GETPS, flags);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Gets the absolute speed (positive or negative depending on direction).
|
||
|
|
\param flags Flags modifying the 'get' request.
|
||
|
|
\return A KangarooStatus object describing the speed.
|
||
|
|
*/
|
||
|
|
inline KangarooStatus getS(KangarooGetFlags flags = KANGAROO_GET_DEFAULT)
|
||
|
|
{
|
||
|
|
return get(KANGAROO_GETS, flags);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Gets the incremental speed (relative to the speed when the last command was issued).
|
||
|
|
\param flags Flags modifying the 'get' request.
|
||
|
|
\return A KangarooStatus object describing the speed.
|
||
|
|
*/
|
||
|
|
inline KangarooStatus getSI(KangarooGetFlags flags = KANGAROO_GET_DEFAULT)
|
||
|
|
{
|
||
|
|
return get(KANGAROO_GETSI, flags);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Gets the setpoint speed.
|
||
|
|
2014-11-13 or newer firmware is required for this request.
|
||
|
|
\param flags Flags modifying the 'get' request.
|
||
|
|
\return A KangarooStatus object describing the speed.
|
||
|
|
*/
|
||
|
|
inline KangarooStatus getSS(KangarooGetFlags flags = KANGAROO_GET_DEFAULT)
|
||
|
|
{
|
||
|
|
return get(KANGAROO_GETSS, flags);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Gets the minimum position. This corresponds to DEScribe's Nominal Travel minimum.
|
||
|
|
\param flags Flags modifying the 'get' request.
|
||
|
|
\return A KangarooStatus object describing the limit.
|
||
|
|
*/
|
||
|
|
inline KangarooStatus getMin(KangarooGetFlags flags = KANGAROO_GET_DEFAULT)
|
||
|
|
{
|
||
|
|
return get(KANGAROO_GETMIN, flags);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Gets the maximum position. This corresponds to DEScribe's Nominal Travel maximum.
|
||
|
|
\param flags Flags modifying the 'get' request.
|
||
|
|
\return A KangarooStatus object describing the limit.
|
||
|
|
*/
|
||
|
|
inline KangarooStatus getMax(KangarooGetFlags flags = KANGAROO_GET_DEFAULT)
|
||
|
|
{
|
||
|
|
return get(KANGAROO_GETMAX, flags);
|
||
|
|
}
|
||
|
|
|
||
|
|
public:
|
||
|
|
// While the following calls violate the standard Arduino casing, they sure are nicer to type.
|
||
|
|
// The contrast between 'p' and 'getP' may confuse as well, so we include these to make life easier for everyone.
|
||
|
|
inline KangarooStatus getp (KangarooGetFlags flags = KANGAROO_GET_DEFAULT) { return getP (flags); }
|
||
|
|
inline KangarooStatus getpi (KangarooGetFlags flags = KANGAROO_GET_DEFAULT) { return getPI (flags); }
|
||
|
|
inline KangarooStatus getps (KangarooGetFlags flags = KANGAROO_GET_DEFAULT) { return getPS (flags); }
|
||
|
|
inline KangarooStatus gets (KangarooGetFlags flags = KANGAROO_GET_DEFAULT) { return getS (flags); }
|
||
|
|
inline KangarooStatus getsi (KangarooGetFlags flags = KANGAROO_GET_DEFAULT) { return getSI (flags); }
|
||
|
|
inline KangarooStatus getss (KangarooGetFlags flags = KANGAROO_GET_DEFAULT) { return getSS (flags); }
|
||
|
|
inline KangarooStatus getmin(KangarooGetFlags flags = KANGAROO_GET_DEFAULT) { return getMin(flags); }
|
||
|
|
inline KangarooStatus getmax(KangarooGetFlags flags = KANGAROO_GET_DEFAULT) { return getMax(flags); }
|
||
|
|
|
||
|
|
public:
|
||
|
|
/*! Gets the command retry interval.
|
||
|
|
\return The command retry interval, in milliseconds.
|
||
|
|
*/
|
||
|
|
inline int32_t commandRetryInterval() const { return _commandRetryInterval; }
|
||
|
|
|
||
|
|
/*! Sets the command retry interval.
|
||
|
|
\param intervalMS The command retry interval, in milliseconds.
|
||
|
|
*/
|
||
|
|
inline void commandRetryInterval(int32_t intervalMS) { _commandRetryInterval = intervalMS; }
|
||
|
|
|
||
|
|
/*! Gets the command timeout.
|
||
|
|
\return The command timeout, in milliseconds.
|
||
|
|
*/
|
||
|
|
inline int32_t commandTimeout() const { return _commandTimeout; }
|
||
|
|
|
||
|
|
/*! Sets the command timeout.
|
||
|
|
\param timeoutMS The command timeout, in milliseconds.
|
||
|
|
*/
|
||
|
|
inline void commandTimeout(int32_t timeoutMS) { _commandTimeout = timeoutMS; }
|
||
|
|
|
||
|
|
/*! Gets whether streaming is enabled.
|
||
|
|
\return True if streaming is enabled.
|
||
|
|
*/
|
||
|
|
inline boolean streaming() const { return _streaming; }
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Enables or disables streaming.
|
||
|
|
When streaming is disabled (the default), commands are delivered reliably:
|
||
|
|
a request is sent to the Kangaroo after every command to make sure the command was received.
|
||
|
|
When streaming is enabled, commands are delivered unreliably:
|
||
|
|
no effort is made to verify that they are received.
|
||
|
|
If you are constantly sending position or speed commands, you may not care if an intermediate one is lost.
|
||
|
|
Enabling streaming can more than double your command rate, at the cost of reduced reliability.
|
||
|
|
If you only have a transmit line and no way to receive data back from the Kangaroo, you should enable streaming.
|
||
|
|
KangarooMonitor::wait() cannot be meaningfully called for a command that was streamed.
|
||
|
|
*/
|
||
|
|
inline void streaming(boolean enabled) { _streaming = enabled; }
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Sets the baud rate.
|
||
|
|
This affects all channels of the controller that receives the command.
|
||
|
|
\param baudRate The baud rate to use. Supported rates are 9600, 19200, 38400, and 115200.
|
||
|
|
*/
|
||
|
|
void baudRate(int32_t baudRate);
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Powers down the channel.
|
||
|
|
\return A KangarooError, usually KANGAROO_NO_ERROR.
|
||
|
|
*/
|
||
|
|
KangarooError powerDown();
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Powers down all channels of the controller that receives the command.
|
||
|
|
\return A KangarooError, usually KANGAROO_NO_ERROR.
|
||
|
|
*/
|
||
|
|
KangarooError powerDownAll();
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Sets the serial timeout.
|
||
|
|
This affects all channels of the controller that receives the command.
|
||
|
|
\param milliseconds The timeout, in milliseconds. A value of zero uses the DEScribe setting. KANGAROO_INFINITE_TIMEOUT disables the timeout.
|
||
|
|
\return A KangarooError, usually KANGAROO_NO_ERROR.
|
||
|
|
*/
|
||
|
|
KangarooError serialTimeout(int32_t milliseconds);
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Sends a system command. These are mostly for advanced use cases.
|
||
|
|
\param systemCommand The command to set.
|
||
|
|
\param expectReply Whether or not to expect a reply.
|
||
|
|
If a command has an unusual effect on the state of the Kangaroo, expecting a reply may
|
||
|
|
cause the command to retry until it times out.
|
||
|
|
\param values The parameters of the command.
|
||
|
|
\param valueCount The number of parameters.
|
||
|
|
\return A KangarooError.
|
||
|
|
*/
|
||
|
|
KangarooError systemCommand(KangarooSystemCommand systemCommand, boolean expectReply, int32_t values[], size_t valueCount);
|
||
|
|
|
||
|
|
private:
|
||
|
|
KangarooStatus getSpecial(KangarooGetType type, KangarooGetFlags flags, const KangarooTimeout& timeout);
|
||
|
|
KangarooMonitor motion (byte motionType, int32_t motionValue,
|
||
|
|
byte limit1Type, int32_t limit1Value,
|
||
|
|
byte limit2Type, int32_t limit2Value,
|
||
|
|
KangarooMoveFlags flags);
|
||
|
|
KangarooMonitor set (KangarooCommand command,
|
||
|
|
KangarooCommandWriter& contents,
|
||
|
|
KangarooMoveFlags moveFlags = KANGAROO_MOVE_DEFAULT,
|
||
|
|
KangarooGetType getType = KANGAROO_GETP);
|
||
|
|
void setNoReply(KangarooCommand command, KangarooCommandWriter& contents,
|
||
|
|
KangarooMoveFlags moveFlags = KANGAROO_MOVE_DEFAULT);
|
||
|
|
|
||
|
|
boolean getInitialSequenceCodeIfNecessary(const KangarooTimeout& timeout, KangarooStatus& status);
|
||
|
|
boolean updateMonitoredResult(const KangarooTimeout& timeout, boolean acceptRepliesWithStartupSequenceCode);
|
||
|
|
|
||
|
|
private:
|
||
|
|
KangarooChannel (KangarooChannel& channel); // no copy
|
||
|
|
void operator = (KangarooChannel& channel);
|
||
|
|
|
||
|
|
private:
|
||
|
|
KangarooSerial& _serial;
|
||
|
|
char _name;
|
||
|
|
byte _address;
|
||
|
|
int32_t _commandRetryInterval;
|
||
|
|
int32_t _commandTimeout;
|
||
|
|
byte _echoCode;
|
||
|
|
uint32_t _monitorCode;
|
||
|
|
KangarooGetType _monitoredGetType;
|
||
|
|
KangarooGetFlags _monitoredGetFlags;
|
||
|
|
KangarooStatus _monitoredGetResult;
|
||
|
|
byte _monitoredSequenceCode;
|
||
|
|
boolean _monitoredSequenceCodeIsReady;
|
||
|
|
boolean _streaming;
|
||
|
|
};
|
||
|
|
|
||
|
|
/*!
|
||
|
|
\class KangarooMonitor
|
||
|
|
\brief Makes it easy to track the status of position commands, speed commands, and any other commands that take time to complete.
|
||
|
|
*/
|
||
|
|
class KangarooMonitor
|
||
|
|
{
|
||
|
|
friend class KangarooChannel;
|
||
|
|
|
||
|
|
public:
|
||
|
|
/*!
|
||
|
|
Constructs a KangarooMonitor object.
|
||
|
|
This must be assigned before it will be valid.
|
||
|
|
*/
|
||
|
|
KangarooMonitor();
|
||
|
|
|
||
|
|
public:
|
||
|
|
/*!
|
||
|
|
Gets the most recently received status.
|
||
|
|
\return A KangarooStatus.
|
||
|
|
*/
|
||
|
|
KangarooStatus status() const;
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Gets whether the status is valid.
|
||
|
|
If you keep an old KangarooMonitor object around and issue a new command on the same channel, the old KangarooMonitor will become invalid.
|
||
|
|
\return True if the status is valid.
|
||
|
|
*/
|
||
|
|
boolean valid() const;
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Updates the status. The channel's command timeout is used.
|
||
|
|
\return The same KangarooMonitor.
|
||
|
|
*/
|
||
|
|
KangarooMonitor update();
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Updates the status.
|
||
|
|
\param timeoutMS The timeout to use, in milliseconds.
|
||
|
|
\return The same KangarooMonitor.
|
||
|
|
*/
|
||
|
|
KangarooMonitor update(int32_t timeoutMS);
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Updates the status.
|
||
|
|
\param timeout The timeout object to use.
|
||
|
|
\return The same KangarooMonitor.
|
||
|
|
*/
|
||
|
|
KangarooMonitor update(const KangarooTimeout& timeout);
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Waits until KangarooStatus::done() returns true or the timeout expires.
|
||
|
|
\param timeoutMS The timeout to use, in milliseconds.
|
||
|
|
\return The same KangarooMonitor.
|
||
|
|
*/
|
||
|
|
KangarooMonitor wait(int32_t timeoutMS = KANGAROO_INFINITE_TIMEOUT);
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Waits until KangarooStatus::done() returns true or the timeout object expires.
|
||
|
|
\param timeout The timeout object to use.
|
||
|
|
\return The same KangarooMonitor.
|
||
|
|
*/
|
||
|
|
KangarooMonitor wait(const KangarooTimeout& timeout);
|
||
|
|
|
||
|
|
private:
|
||
|
|
KangarooMonitor(KangarooChannel* channel, uint32_t monitorCode);
|
||
|
|
|
||
|
|
private:
|
||
|
|
struct
|
||
|
|
{
|
||
|
|
KangarooChannel* channel;
|
||
|
|
uint32_t monitorCode;
|
||
|
|
} _state;
|
||
|
|
};
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Waits until all of the monitors are done or the timeout expires.
|
||
|
|
\param count The number of monitors.
|
||
|
|
\param monitors The monitors to wait for.
|
||
|
|
\param timeoutMS The timeout to use, in milliseconds.
|
||
|
|
\return True if all of the monitors are done, or false if the timeout expired.
|
||
|
|
\remark If a monitor is null, it is ignored.
|
||
|
|
*/
|
||
|
|
boolean waitAll(size_t count, KangarooMonitor* monitors[], int32_t timeoutMS = KANGAROO_INFINITE_TIMEOUT);
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Waits until all of the monitors are done or the timeout object expires.
|
||
|
|
\param count The number of monitors.
|
||
|
|
\param monitors The monitors to wait for.
|
||
|
|
\param timeout The timeout object to use.
|
||
|
|
\return True if all of the monitors are done, or false if the timeout expired.
|
||
|
|
\remark If a monitor is null, it is ignored.
|
||
|
|
*/
|
||
|
|
boolean waitAll(size_t count, KangarooMonitor* monitors[], const KangarooTimeout& timeout);
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Waits until at least one of the monitors is done or the timeout expires.
|
||
|
|
\param count The number of monitors.
|
||
|
|
\param monitors The monitors to wait for.
|
||
|
|
\param timeoutMS The timeout to use, in milliseconds.
|
||
|
|
\return The array index of the monitor that is done, or -1 if the timeout expired.
|
||
|
|
\remark If a monitor is null, it is ignored.
|
||
|
|
*/
|
||
|
|
int waitAny(size_t count, KangarooMonitor* monitors[], int32_t timeoutMS = KANGAROO_INFINITE_TIMEOUT);
|
||
|
|
|
||
|
|
/*!
|
||
|
|
Waits until at least one of the monitors is done or the timeout expires.
|
||
|
|
\param count The number of monitors.
|
||
|
|
\param monitors The monitors to wait for.
|
||
|
|
\param timeout The timeout object to use.
|
||
|
|
\return The array index of the monitor that is done, or -1 if the timeout expired.
|
||
|
|
\remark If a monitor is null, it is ignored.
|
||
|
|
*/
|
||
|
|
int waitAny(size_t count, KangarooMonitor* monitors[], const KangarooTimeout& timeout);
|
||
|
|
|
||
|
|
#endif
|