SDK Error Handling

It is common for things to go wrong when executing code. Detecting and handling errors in your code can make the difference between quickly diagnosing and solving a problem and spending long nights debugging.

Many programming languages use exceptions as their primary error handling tool. The SDK does not, rather it has its own system. In this tutorial we will learn about the system that the SDK uses to report errors that occur.

The errors discussed in this tutorial are not to be confused with errors that are detected and reported by the motor. For information on those errors, see Motor Error Handling.


We’ll start with the following code as our baseline:

#include <iostream>
#include "actuator.h"

using namespace orcaSDK ;

int main() {
	Actuator motor;

	int serial_port;
	std::cout << "Please input the serial port number of your connected motor. ";
	std::cin >> serial_port;

	motor.open_serial_port(serial_port);

	return 0;
}
from pyorcasdk import Actuator

motor = Actuator()

serial_port = int(input("Please input the serial port number of your connected motor. "))

motor.open_serial_port(serial_port)

The OrcaError Object

Some functions in the SDK can fail. These functions will return one of two objects. If a function wouldn’t normally return anything, but it has a chance of failure, it will instead return an OrcaError object. This OrcaError object will evaluate to true when converted to a boolean if an error has occurred. In this case, the object will contain an error message which can be accessed through the OrcaError.what() method. The Actuator object’s open_serial_port() is a function that might fail, either in the case that the port doesn’t exist, or if it is already in use by another program. Let’s add error handling to our starting code.

...
OrcaError serial_port_error = motor.open_serial_port(serial_port);

if (serial_port_error)
{
        std::cout << "Error Detected! Message: " << serial_port_error.what() << "\n";
        return 1;
}
else
{
        std::cout << "Serial port opened successfully!\n";
}
...
import sys
...
serial_port_error = motor.open_serial_port(serial_port)

if serial_port_error:
        print("Error Detected! Message: " + serial_port_error.what())
        sys.exit(1)
else:
        print("Serial Port Opened Successfully")
...

We also exit the program because we know we know that any subsequent messages will fail if we could not open a valid port.

Try this code out! Try running your program, and instead of passing in your actual ORCA’s rs422 port number, pass in a random number instead.

The OrcaResult Object

If a function can fail, but also must return a value, the function will instead return an OrcaResult object. These objects are simple structs containing an OrcaError and an object of whatever type the function would return for its ‘happy path’. OrcaResult is a generic type, so a full definition of any instance will also include it’s ‘happy path’ type. Here is an example instance type:

OrcaResult<uint16_t>
OrcaResultUInt16

In this case, the OrcaResult contains an unsigned 16 bit integer in addition to its OrcaError.

To access the OrcaError object within the OrcaResult, check the OrcaResult.error member variable, and when you want to access the ‘happy path’ object, instead check the OrcaResult.value member variable. Let’s add an example showing use of an OrcaResult object.

...
OrcaResult<int32_t> position_result = motor.get_position_um();

if (position_result.error)
{
        std::cout << "Error Getting Position! Message: " << position_result.error.what() << "\n";
}
else
{
        std::cout << "Motor Position: " << position_result.value << "\n";
}
...
...
position_result = motor.get_position_um()

if position_result.error:
        print("Error Getting Position! Message: " + position_result.error.what())
else:
        print("Current Position: " + str(position_result.value))
...

Why Handle Errors?

In general, when handling errors for an OrcaResult, it is wise to first check the error, and only access the value after confirming that an error hasn’t occurred. We make no promise as to what will be contained in the OrcaResult.value member in case of an error, so using it blindly in a situation where error is possible may lead to unpredictable behaviour.

In these tutorials, we often simply access the value from returned OrcaResult objects. This is for brevity in most cases, focusing on the content of the specific tutorial. For the purpose of tutorials, we will generally only handle errors in the case where not handling it may lead to more confusion.

When writing your own code, we highly recommend handling all errors. Errors can arise due to a variety of situations, including misconfiguration, undiscovered bugs, faulty hardware, or instability of the underlying platform. For example, a Windows update may change your registry keys, updating the comport latency of your connected rs422 cable. This will be invisible to you, but will lead to communication instability. We have experienced errors like this. In general, assume that errors will occur, and handle them wherever using an incorrect or unpredictable value would lead to problems.


Complete Example

#include <iostream>
#include "actuator.h"

using namespace orcaSDK;

int main() {
	Actuator motor;

	int serial_port;
	std::cout << "Please input the serial port number of your connected motor. ";
	std::cin >> serial_port;

	OrcaError serial_port_error = motor.open_serial_port(serial_port);

	if (serial_port_error)
	{
		std::cout << "Error Detected! Message: " << serial_port_error.what() << "\n";
		return 1;
	}
	else
	{
		std::cout << "Serial port opened successfully!\n";
	}

	OrcaResult<int32_t> position_result = motor.get_position_um();

	if (position_result.error)
	{
		std::cout << "Error Getting Position! Message: " << position_result.error.what() << "\n";
	}
	else
	{
		std::cout << "Motor Position: " << position_result.value << "\n";
	}

	return 0;
}
from pyorcasdk import Actuator
import sys

motor = Actuator()

serial_port = int(input("Please input the serial port number of your connected motor. "))

serial_port_error = motor.open_serial_port(serial_port)

if serial_port_error:
	print("Error Detected! Message: " + serial_port_error.what())
	sys.exit(1)
else:
	print("Serial Port Opened Successfully")

position_result = motor.get_position_um()

if position_result.error:
	print("Error Getting Position! Message: " + position_result.error.what())
else:
	print("Current Position: " + str(position_result.value))