Program Listing for File SoarRunner.hpp

Return to documentation for file (include/soar_ros/SoarRunner.hpp)

// Copyright 2024 Moritz Schmidt
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef SOAR_ROS__SOARRUNNER_HPP_
#define SOAR_ROS__SOARRUNNER_HPP_

#include <cstdlib>
#include <iostream>
#include <map>
#include <memory>
#include <queue>
#include <sml_Client.h>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>

#include <rclcpp/rclcpp.hpp>
#include <std_msgs/msg/string.hpp>
#include <std_srvs/srv/trigger.hpp>

#include "Client.hpp"
#include "Interface.hpp"
#include "Publisher.hpp"
#include "Service.hpp"
#include "Subscriber.hpp"
#include "ActionClient.hpp"

namespace soar_ros
{
void SoarPrintEventHandler([[maybe_unused]] sml::smlPrintEventId id, void * pSoarRunner_,
  sml::Agent * pAgent,
  char const * pMessagee);

void updateEventHandler([[maybe_unused]] sml::smlUpdateEventId id, void * pSoarRunner_,
  [[maybe_unused]] sml::Kernel * kernel_ptr, [[maybe_unused]] sml::smlRunFlags run_flags);

class SoarRunner : public rclcpp::Node
{
private:
  std::vector<sml::Agent *> agents;

  sml::Kernel * pKernel;

  std::thread runThread;

  rclcpp::Service<std_srvs::srv::Trigger>::SharedPtr m_getSoarKernelStatus;

  rclcpp::Service<std_srvs::srv::Trigger>::SharedPtr m_kernelRun;

  rclcpp::Service<std_srvs::srv::Trigger>::SharedPtr m_kernelStop;

  rclcpp::Service<std_srvs::srv::Trigger>::SharedPtr m_debuggerLaunch;

  std::map<sml::Agent *,
    std::map<std::string, std::shared_ptr<soar_ros::OutputBase>>> outputs_by_agent;

  std::map<sml::Agent *, std::vector<std::shared_ptr<soar_ros::InputBase>>> inputs_by_agent;

  bool m_debug;

  std::map<sml::Agent *, sml::Identifier *> output_links;

  void processOutputLinkChanges(sml::Agent * agent);

  void processInput(sml::Agent * agent);

  void run()
  {
    while (isRunning.load()) {
      pKernel->RunAllAgents(1);
    }
  }

  std::string getSoarLogFilePath();

  std::atomic<bool> isRunning;

public:
  SoarRunner();

  void debuggerLaunch([[maybe_unused]] const std::shared_ptr<rmw_request_id_t> request_header,
    [[maybe_unused]] std::shared_ptr<std_srvs::srv::Trigger::Request> request,
    std::shared_ptr<std_srvs::srv::Trigger::Response> response);

  void getSoarKernelStatus([[maybe_unused]] const std::shared_ptr<rmw_request_id_t> request_header,
    [[maybe_unused]] std::shared_ptr<std_srvs::srv::Trigger::Request> request,
    std::shared_ptr<std_srvs::srv::Trigger::Response> response);

  void runSoarKernel([[maybe_unused]] const std::shared_ptr<rmw_request_id_t> request_header,
    [[maybe_unused]] std::shared_ptr<std_srvs::srv::Trigger::Request> request,
    std::shared_ptr<std_srvs::srv::Trigger::Response> response);

  void stopSoarKernel([[maybe_unused]] const std::shared_ptr<rmw_request_id_t> request_header,
    [[maybe_unused]] std::shared_ptr<std_srvs::srv::Trigger::Request> request,
    std::shared_ptr<std_srvs::srv::Trigger::Response> response);

  sml::Agent * addAgent(const std::string & agent_name, const std::string & path_productions);

  ~SoarRunner();

  const std::vector<sml::Agent *> & getAgents() const {return agents;}

  template<typename T>
  bool addPublisher(std::shared_ptr<Publisher<T>> output)
  {
    auto agent = output.get()->getAgent();
    outputs_by_agent[agent][output.get()->getTopic()] = output;
    return true;
  }

  template<typename T>
  bool addPublisher(std::shared_ptr<Publisher<T>> output, const std::string & commandName)
  {
    auto agent = output.get()->getAgent();
    outputs_by_agent[agent][commandName] = output;
    return true;
  }

  template<typename T>
  bool addSubscriber(std::shared_ptr<Subscriber<T>> input)
  {
    auto agent = input.get()->getAgent();
    inputs_by_agent[agent].push_back(input);
    return true;
  }

  template<typename T>
  bool addService(std::shared_ptr<Service<T>> service)
  {
    return addService(service, service.get()->getTopic());
  }

  template<typename T>
  bool addService(std::shared_ptr<Service<T>> service, const std::string & commandName)
  {
    auto agent = service.get()->getAgent();
    outputs_by_agent[agent][commandName] = std::static_pointer_cast<soar_ros::OutputBase>(service);
    inputs_by_agent[agent].push_back(std::static_pointer_cast<soar_ros::InputBase>(service));
    return true;
  }

  template<typename T>
  bool addClient(std::shared_ptr<Client<T>> client)
  {
    return addClient(client, client.get()->getTopic());
  }

  template<typename T>
  bool addClient(std::shared_ptr<Client<T>> client, const std::string & commandName)
  {
    auto agent = client.get()->getAgent();
    outputs_by_agent[agent][commandName] = std::static_pointer_cast<soar_ros::OutputBase>(client);
    inputs_by_agent[agent].push_back(std::static_pointer_cast<soar_ros::InputBase>(client));
    return true;
  }

  template<typename T>
  bool addActionClient(std::shared_ptr<ActionClient<T>> action_client)
  {
    return addActionClient(action_client, action_client.get()->getTopic());
  }

  template<typename T>
  bool addActionClient(
    std::shared_ptr<ActionClient<T>> action_client,
    const std::string & commandName)
  {
    auto agent = action_client.get()->getAgent();
    outputs_by_agent[agent][commandName] =
      std::static_pointer_cast<soar_ros::Output<typename T::Goal::SharedPtr>>(action_client);
    inputs_by_agent[agent].push_back(
        std::static_pointer_cast<soar_ros::Input<typename T::Feedback::SharedPtr>>(action_client));
    inputs_by_agent[agent].push_back(
        std::static_pointer_cast<soar_ros::Input<typename rclcpp_action::ClientGoalHandle<T>::
      WrappedResult>>(
            action_client));
    inputs_by_agent[agent].push_back(std::static_pointer_cast<soar_ros::Input<bool>>(
        action_client));
    return true;
  }

  void startThread()
  {
    // If thread is already running, skip
    if (isRunning.load() == true) {
      RCLCPP_WARN(this->get_logger(), "runThread already running");
      return;
    } else {
      isRunning.store(true);
    }

    RCLCPP_INFO(this->get_logger(), "Starting runThread");
    runThread = std::thread(&SoarRunner::run, this);
  }

  void stopThread()
  {
    if (isRunning.load() == false) {
      RCLCPP_INFO(this->get_logger(), "Run thread already stopped!");
      return;
    }

    isRunning.store(false);
    RCLCPP_WARN(this->get_logger(), "Stopping runThread");

    if (runThread.joinable()) {
      RCLCPP_INFO(this->get_logger(), "Waiting for run thread to join");
      runThread.join();
    }
  }

  void updateWorld()
  {
    for (auto * agent : agents) {
      if (agent == nullptr) {
        continue;
      }

      // Read Soar output
      processOutputLinkChanges(agent);

      if (output_links[agent] == nullptr) {
        output_links[agent] = agent->GetOutputLink();
        RCLCPP_DEBUG(this->get_logger(), "Output link reference invalid!");
      }

      // Write to Soar input-link
      processInput(agent);
    }
  }
};  // class SoarRunner

}  // namespace soar_ros

#endif  // SOAR_ROS__SOARRUNNER_HPP_