There are three mechanisms available to manage errors triggered by the C++ Kortex API:
- standard
Exception
- accessing the
Error
object returned by the callback function, and - catching the exception thrown by a
std::future
.
When you use the Kortex API, the mechanism to be used depends on the type of called method:
- With the blocking method, use standard
Exception
. - With the callback version of a method, use the
Error
object provided in the callback header. - With the async version of a method, use the
Exception
thrown by the future.
Note that there is a special case explained at the end of this section.
Exceptions are only used if a blocking or async function is used. The code is surrounded with a try/catch statement pair and to catch any exceptions. The Kortex API offers its own exception object: Kinova::Api::KDetailedException
.
try
{
// Your code...
}
catch(k_api::KDetailedException& ex)
{
// You can print the error informations and error codes
auto error_info = ex.getErrorInfo().getError();
std::cout << "KDetailedoption detected what: " << ex.what() << std::endl;
std::cout << "KError error_code: " << error_info.error_code() << std::endl;
std::cout << "KError sub_code: " << error_info.error_sub_code() << std::endl;
std::cout << "KError sub_string: " << error_info.error_sub_string() << std::endl;
// Error codes by themselves are not very verbose if you don't see their corresponding enum value
// You can use google::protobuf helpers to get the string enum element for every error code and sub-code
std::cout << "Error code string equivalent: " << k_api::ErrorCodes_Name(k_api::ErrorCodes(error_info.error_code())) << std::endl;
std::cout << "Error sub-code string equivalent: " << k_api::SubErrorCodes_Name(k_api::SubErrorCodes (error_info.error_sub_code())) << std::endl;
}
Here are the details of the object Kinova::Api::KDetailedException
thrown by the Kortex API.
class KDetailedException : public KBasicException
{
public:
KDetailedException(const KError& error);
KDetailedException(const KDetailedException &other);
virtual const char* what() const throw() override;
virtual std::string toString() override;
KError& getErrorInfo() { return m_error; }
private:
void init(const HeaderInfo& header, const Error& error);
KError m_error;
std::string m_errorStr;
};
Here are the details of the object KError
nested in the exception.
class KError
{
public:
KError(Kinova::Api::ErrorCodes errorCode, Kinova::Api::SubErrorCodes errorSubCode, std::string errorDescription);
KError(const HeaderInfo& header, Kinova::Api::ErrorCodes errorCode, Kinova::Api::SubErrorCodes errorSubCode, std::string errorDescription);
KError(const Error& error);
KError(const HeaderInfo& header, const Error& error);
static Error fillError(Kinova::Api::ErrorCodes errorCode, Kinova::Api::SubErrorCodes errorSubCode, std::string errorDescription);
std::string toString() const;
bool isThereHeaderInfo();
HeaderInfo getHeader();
Error getError();
KError& operator =(const KError& other) = default;
private:
bool m_isThereHeaderInfo;
HeaderInfo m_header;
Error m_error;
};
The KError
object holds an error code and a sub error code to identify the fault.
Here is a link to documentation explaining all of the error and sub error codes:
If the callback version is used, a std::function
is given as a parameter to act as a callback. The header of this std::function
includes an Error object containing the error returned by the execution. Since it is a std::function
, you can either use a lambda expression or a standard C callback.
// callback function used in Refresh_callback
auto lambda_fct_callback = [](const Kinova::Api::Error &err, const k_api::BaseCyclic::Feedback data)
{
// We are printing the data for example purposes
// avoid this for a real-time loop
std::string serialized_data;
google::protobuf::util::MessageToJsonString(data, &serialized_data);
std::cout << serialized_data << std::endl;
};
base_cyclic->Refresh_callback(BaseCommand, lambda_fct_callback, 0);
// callback function used in Refresh_callback
void fct_callback(const k_api::Error &err, const k_api::BaseCyclic::Feedback data)
{
std::cout << "Callback function results: " << std::endl;
//react to the fault...
}
void example_function_call()
{
base_cyclic->Refresh_callback(BaseCommand, fct_callback, 0);
}
If an async function is used, exceptions must be used to catch any error triggered by the Kortex API. The exception is thrown during the execution of the get()
function.
// The function returns a future object, and not a workable object.
std::future<k_api::Base::JointsLimitationsList> limitations_future_async = base->GetAllJointsSpeedHardLimitation_async();
// Waiting for the promise to be completed by the API.
auto timeout_ms = std::chrono::milliseconds(10000);
std::future_status status = limitations_future_async.wait_for(timeout_ms);
if(status != std::future_status::ready)
{
throw std::runtime_error("Timeout detected while waiting for function\n");
}
// Retrieve the workable object from the future object.
try
{
auto limitations_async = limitations_future_async.get();
}
catch(k_api::KDetailedException& ex)
{
// Respond to the fault
}
This section describes a case that doesn't follow the standard error management rules documented earlier in this document.
When a RouterClient object is instantiated a callback can be specified for execution when an error occurs.
RouterClient* router = new RouterClient(pTransport, [](KError err){ cout << "callback error" << err.toString(); });