.. _sdk-error-handling: 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 :ref:`motor-error-handling`. ---- We'll start with the following code as our baseline: .. tab-set:: :sync-group: language .. tab-item:: C++ :sync: cpp .. literalinclude:: shared/boilerplate.cpp :language: cpp .. tab-item:: Python :sync: python .. literalinclude:: shared/boilerplate.py :language: python 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. .. tab-set:: :sync-group: language .. tab-item:: C++ :sync: cpp .. code-block:: cpp ... 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"; } ... .. tab-item:: Python :sync: python .. code-block:: python 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: .. tab-set:: :sync-group: language .. tab-item:: C++ :sync: cpp .. code-block:: cpp OrcaResult .. tab-item:: Python :sync: python .. code-block:: python 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. .. tab-set:: :sync-group: language .. tab-item:: C++ :sync: cpp .. code-block:: cpp ... OrcaResult 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"; } ... .. tab-item:: Python :sync: python .. code-block:: python ... 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 ---------------- .. tab-set:: :sync-group: language .. tab-item:: C++ :sync: cpp .. literalinclude:: SDK-Error-Handling/main.cpp :language: cpp .. tab-item:: Python :sync: python .. literalinclude:: SDK-Error-Handling/main.py :language: python