Bota Control Toolkit C++ ======================== Definitions ----------------- BotaControlBlock ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``BotaControlBlock`` is defined as a parent class from which all control blocks inherit. .. cpp:class:: BotaControlBlock Abstract base class providing the interface for all control processing blocks. **Constructor and Destructor** .. cpp:function:: BotaControlBlock(const std::string &config_path) = default Default constructor with path to JSON configuration file. Prefered method for instantiating blocks. .. cpp:function:: BotaControlBlock(const nlohmann::json &config_json) = default Default constructor with a JSON object containing the configuration data. .. cpp:function:: BotaControlBlock(const BotaControlConfig &config_object) = default Default constructor with a configuration object populated somewhere else in the code. .. cpp:function:: virtual ~BotaControlBlock() = default Virtual destructor for proper inheritance handling. **Runtime Update Methods** .. cpp:function:: virtual BotaControlReturnCode update(std::span input_signal, std::span output_signal) = 0 Reads input data and performs processing. :param input_signal: Reference to a span containing the input signal data. :param output_signal: Reference to a span where the processed output signal should be stored. :return: A return code indicating status of the update operation. .. cpp:function:: virtual BotaControlReturnCode updateInplace(std::span signal) = 0 Reads data and performs processing in place. :param signal: Reference to a span containing the signal data to be processed in place. :return: A return code indicating status of the update operation. .. cpp:function:: virtual BotaControlReturnCode updateObserver(std::span measurement, std::span state_estimate) = 0 Reads input data and performs processing. :param measurement: Reference to a span containing the measurement data. :param state_estimate: Reference to a span where the state estimate should be stored. :return: A return code indicating status of the update operation. .. cpp:function:: virtual BotaControlReturnCode updateController(std::span reference, std::span state_estimate, std::span control_action) = 0 Reads input data and performs processing. :param reference: Reference to a span containing the reference data. :param state_estimate: Reference to a span containing the state estimate data. :param control_action: Reference to a span where the control action should be stored. :return: A return code indicating status of the update operation. **In-line configuration methods** The specific implementations of the control blocks may include additional in-line configuration methods that allow users to modify block parameters at configuration or runtime without needing to re-instantiate the block. These methods are specific to each block and are not part of the base class interface. BotaControlReturnCode ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. cpp:enum-struct:: BotaControlReturnCode .. cpp:enumerator:: OK .. cpp:enumerator:: Stale .. cpp:enumerator:: Degraded .. cpp:enumerator:: DeadlineMissed .. cpp:enumerator:: NonFatalError .. cpp:enumerator:: FatalError ----------------- Pipeline Wiring ----------------- Here's how to connect multiple blocks to create a processing pipeline: .. code-block:: cpp //////////////////////////////////////////////////////////////////////////////////// // EXAMPLE OF A BUNDLED CONTROL PIPELINE USING THE BOTA CONTROL BLOCKS // /////////////////////////////////////////////////////////////////////////////////// #include #include // Define signals - they can be std::array or std::vector as long as they are std::span compatible std::array signal_1; std::array signal_2; std::vector signal_3; // Path to the JSON configuration file for the blocks std::string config_1 = "path/to/config_1.json"; std::string config_2 = "path/to/config_2.json"; std::string config_3 = "path/to/config_3.json"; // Instantiation & Configuration at initialization time std::unique_ptr block_1 = std::make_unique(config_1); std::unique_ptr block_2 = std::make_unique(config_2); std::unique_ptr block_3 = std::make_unique(config_3); // Block in-line static configuration block_1->setParameter(value_1); block_2->setParameter(value_2); block_3->setParameter(value_3); // Set the desired processing rate const double rate_hz = 500.0; // 500 Hz const auto dt = std::chrono::duration(1.0 / rate_hz); bool running = true; // loop reactor lamda function to handle return codes and control flow auto loop_reactor = [&](BotaControlReturnCode code) { switch (code) { case BotaControlReturnCode::OK: // Normal operation, continue processing break; {...other cases...} case BotaControlReturnCode::FatalError: // Handle fatal errors (e.g., log the error, shut down the system safely, etc.) running = false; // Example: stop the loop on fatal error break; } while (running) { auto start_time = std::chrono::steady_clock::now(); // Update signals with new data here (e.g., from sensors, reference generators, etc.) signal_1 = getNewDataForSignal1(); // Process data through the pipeline loop_reactor(block_1->update(signal_1, signal_2)); //=> signal_2 freshly updated by block_1 algorithm can be used HERE loop_reactor(block_2->updateInplace(signal_2)); //=> signal_2 freshly updated by block_2 algorithm can be used HERE loop_reactor(block_3->update(signal_2, signal_3)); //=> signal_3 freshly updated by block_3 algorithm can be used HERE //////////////////////// // Control logic here // //////////////////////// // Rate control - sleep to maintain desired frequency auto elapsed_time = std::chrono::steady_clock::now() - start_time; auto sleep_time = dt - elapsed_time; if (sleep_time > std::chrono::duration::zero()) { std::this_thread::sleep_for(sleep_time); } } ----------------- Distribution ----------------- The **Bota Control Toolkit** definitions are distributed as a collection of header files, available `here `_. .. important:: They must be included in every C++ project using any of the Bota Control Toolkit blocks.