.. _command-stream: Command Stream ============== In this tutorial we introduce an important concept for communicating with ORCA motors with the SDK, Command Streaming. Command streaming allows for sending commands to the motor while simultaneously reading commonly used data. It is also the only method of `asynchronous communication `_ with an ORCA available within the SDK. For more details see the ORCA Series Modbus RTU User Guide pdf available through our `Downloads `_ page. ---- We'll start with the following code as our baseline: .. tab-set:: :sync-group: language .. tab-item:: C++ :sync: cpp .. literalinclude:: shared/boilerplate-with-loop.cpp :language: cpp .. tab-item:: Python :sync: python .. literalinclude:: shared/boilerplate-with-loop.py :language: python Enabling Command Streaming -------------------------- Command streaming can be enabled by a call to the ``enable_stream()`` method of your ``Actuator`` object. .. tab-set:: :sync-group: language .. tab-item:: C++ :sync: cpp .. code-block:: cpp ... motor.enable_stream(); ... .. tab-item:: Python :sync: python .. code-block:: python ... motor.enable_stream() ... When this method is called, the SDK will now attempt to send and receive asynchronous messages to the motor. Note that enabling streaming doesn't dispatch a new thread of execution. In order for the SDK to perform any streaming work, it must be explicitly requested to do so. To send and receive command stream messages, we must add regular calls to our ``Actuator`` object's ``run()`` method. We'll introduce it within the ``while (true)`` loop. .. tab-set:: :sync-group: language .. tab-item:: C++ :sync: cpp .. code-block:: cpp ... while (true) { motor.run(); std::cout << "Current Position: " << motor.get_position_um().value << " \r"; } ... .. tab-item:: Python :sync: python .. code-block:: python ... while True: motor.run() print("Current Position: " + str(motor.get_position_um().value), end=" \r") ... .. note:: The ``run()`` method acts as a simple self-contained `state machine `_. It checks to see if there's room to insert a new command stream message, and sends one if there is room. It then monitors for a response by the ORCA, and automatically reads, validates, and stores the response data. It isn't necessary to worry about what the content of the ``run()`` method is doing at any given time, it is good enough and preferred to simply place it in a place where it will get called regularly. Reading Returned Data --------------------- ``run()`` does not return any data. Instead, it deposits the returned data from the motor into a local cache. Let's replace the call to ``get_position_um()`` with an equivalent read from the cache. .. note:: ``run()`` doesn't return any data because it performs many jobs, and it isn't easy to predict what it is doing at any given time. This is why it deposits into a cache. At any given time, this cache will always contain the data from the most recent response. .. tab-set:: :sync-group: language .. tab-item:: C++ :sync: cpp .. code-block:: cpp ... while (true) { motor.run(); std::cout << "Current Position: " << motor.stream_cache.position << " \r"; } ... .. tab-item:: Python :sync: python .. code-block:: python ... while True: motor.run() print("Current Position: " + str(motor.get_stream_data().position), end=" \r") ... Now instead of injecting additional read messages in order to get the motor's position, we're using the position data automatically returned while streaming. ---- Complete Example ---------------- .. tab-set:: :sync-group: language .. tab-item:: C++ :sync: cpp .. literalinclude:: Command-Stream/main.cpp :language: cpp .. tab-item:: Python :sync: python .. literalinclude:: Command-Stream/main.py :language: python