# External Process Handler

## &#x20;How to setup your tasks with External Process Handler

This guide shows you how to integrate your own programs as a plugin into Seirios using `external_process_handler`package.

### 1. Install movel\_seirios\_message&#x20;

**Check your Ubuntu + ROS version.**&#x20;

* **For Ubuntu 20.04, ROS Noetic users:**

Go here to download the .deb package: <https://drive.google.com/drive/folders/1hpi5NaSkFyr6QVXs83taxeHjWLulw8SH>

Move the .deb into your home folder. Open a terminal window, run the command `sudo apt install ./ros-noetic-movel-seirios-msgs_0.0.0-0focalXXX64.deb` to instal&#x6C;*.*

* **Else:**

Git clone this repo here: <https://gitlab.com/movelai_public/movel_seirios_msgs/-/tree/master/srv> into catkin\_ws/src. Then go back to /catkin\_ws to run `catkin_make.`

### 2. Integration with Seirios

You need to mount your own packages/nodes as a plugin to task\_supervisor for Seirios to run it.

`File to modify: ../config/task_supervisor/config/`**`task_supervisor.yaml`**

Look for the plugins section in the config to add your own plugin. You can give any name and type. For type, give a number not used by previous plugins.

```yaml
plugins:
  - {name: detecting, type: 10, class: 'external_process_handler::ExternalProcessHandler'}
```

Then at the end of the file, create a new section with the same name you give for your plugin e.g. detecting. Then paste in the parameters.&#x20;

```yaml
detecting:
  watchdog_rate: 2.0
  watchdog_timeout: 0
  service_req: true
  launch_req: false
  service_start: "/external_process/trigger_detecting" #required if service_req is true
  service_start_msg: "" #optional  if there is any particular msg needs to sent or else empty string 
  service_stop: "/external_process/stop_detecting" #required if service_req is true
  service_stop_msg: "" #optional  if there is any particular msg needs to sent or else empty string 
  #launch_package: "yocs_velocity_smoother" #required if launch_req is true
  #launch_file: "standalone.launch"  #required if launch_req is true
  #launch_nodes: "/nodelet_manager /velocity_smoother" #required if launch_req is true
  topic_cancel_req: true
  topic_process_cancel: "external_process/cancel_publish"

```

### 3. Launching your nodes with Seirios

There are different ways to launch your own codes. Choose one in the below subsection that best suits you.

#### 3.1. Launch with your own code

***Start & stop***&#x20;

You need to write a ROS service service\_start with start\_handler as the callback function to start your code.&#x20;

And you need another ROS service service\_stop with stop\_handler to stop your code. Names must be service\_start and service\_stop.&#x20;

You must use the movel\_seirios\_msgs package for msg and srv.

(For ROS C++ only) Add movel\_seirios\_msgs to package.xml and CMakeLists.txt.

Then in task\_supervisor.yaml file. Change `service_req: true` and `service_start` and `service_stop` params to your own topics.

***Stop with topic (optional)***&#x20;

IF you have your own script to stop the launch.

Write a subscriber topic\_process\_cancel to subscribe to the UI topic. Inside the handle\_publish\_cancel callback, include your own codes to stop.

Then change the params `topic_cancel_req: true` and `topic_process_cancel` to your own topic.

***Getting status (optional)***&#x20;

IF you are launching your program from a launch file. Write a publisher client\_status that publishes 2 if the program runs successfully or 3 if it fails.

Then change `client_status: true`.

#### 3.2. Launch from the UI

Go to Custom Task UI, and choose Seirios Engine as your Execute Engine. Similarly, you need to give a task type number that is not used by other task supervisor plugins.\
\
Then, you will need a custom `service_start_msg` and/or `service_stop_msg` to run or stop your external process.

Input a Payload in the JSON format e.g. `{“service_start_msg”: “/eg/payload/start”, “service_stop_msg”:”/eg/payload/stop”}` to customize the  parameters.

Payload has to be parsable in JSON format so remember the “ ”s.

### 4. Sample Codes

For reference only.

{% tabs %}
{% tab title="Python" %}

```python
#!/usr/bin/env python3
import rospy
from movel_seirios_msgs.srv import StringTrigger, StringTriggerResponse # like setbool
from std_msgs.msg import Bool, UInt8

class Pytest:

    def __init__(self):
        print('starting test node')
        self.node = rospy.init_node('python_test')

        self.service_start = rospy.Service('/external_process/trigger_detecting', StringTrigger, self.start_handler)
        self.service_stop = rospy.Service('/external_process/stop_detecting', StringTrigger, self.stop_handler)
        
        # IGNORE dummy_publisher - just to simulate UI cancel topic
        self.dummy_publisher = rospy.Publisher('/cancel_publish', Bool, queue_size=1)

        self.topic_process_cancel = rospy.Subscriber('/cancel_publish', Bool, self.handle_publish_cancel)
        self.client_status = rospy.Publisher('/external_process/client_status', UInt8, queue_size=1)

        self.flag = True

    def start_handler(self, req):
        print('inside start handler')

        start_msg = StringTriggerResponse() 
        start_msg.success = True
        start_msg.message = 'starting detect'

        self.flag = True
        self.publish_client_status()

        while(self.flag):
            print('executing something')
            rospy.sleep(1)
    

        return start_msg # return must be [success, message format]

    def stop_handler(self, req):
        print('inside stop handler')

        self.flag = False
        print('stop handler called')
        stop_msg = StringTriggerResponse()
        stop_msg.success = False
        stop_msg.message = 'stopping detect'

        return stop_msg
    
    # IGNORE if you are not using a topic to stop your code
    def handle_publish_cancel(self, req):
        print("inside publish cancel", req.data)

        cancel_trigger = req.data
        if(cancel_trigger):
            print('executing cancel stuff') # replace print statement
    
    
    # IGNORE if you are not using launch files
    def publish_client_status(self):
        print("publish client status called")
        
        cstatus = UInt8()
        
        #Dummy code
        #Decide how do you determine if your launch file is successful
        #And replace the if-else statement
        if (self.flag == True):
            cstatus.data = 2 # if start_handler successfully called, task is successful and return 2
        else:
            cstatus.data = 3 # else task not successful, return 3

        self.client_status.publish(cstatus)


if __name__ == '__main__':

    py_test = Pytest()
    #py_test.publish_cancel()

    rospy.spin()
```

{% endtab %}

{% tab title="C++" %}

```cpp
#include <ros/ros.h>
#include <std_msgs/Bool.h>
#include <std_msgs/UInt8.h>
#include <movel_seirios_msgs/StringTrigger.h>


class Cpptest {

    private:

        ros::ServiceServer service_start;
        ros::ServiceServer service_stop;

        ros::Publisher dummy_publisher;
        ros::Subscriber topic_process_cancel;

        ros::Publisher client_status;
        bool flag;

    public:
        Cpptest(ros::NodeHandle *nh) {
            service_start = nh->advertiseService("/external_package/triggering_detect_cpp", &Cpptest::start_handler, this);
            service_stop = nh->advertiseService("/external_package/stopping_detect_cpp", &Cpptest::stop_handler, this);

            // Ignore dummy_publisher - just to simulate ui cancel button
            dummy_publisher = nh->advertise<std_msgs::Bool>("/cancel_publish_cpp", 10);
            topic_process_cancel = nh->subscribe("/cancel_publish_cpp", 1, &Cpptest::handle_publish_cancel, this);

            client_status = nh->advertise<std_msgs::UInt8>("/external_process/client_status_cpp", 10);
        
        }

        bool start_handler(movel_seirios_msgs::StringTrigger::Request &req, movel_seirios_msgs::StringTrigger::Response &res) {
            std::cout << "inside start handler\n";
            
            res.success = true;
            res.message = "starting detect";
            //std::cout << res << "\n";

            flag = true;
            publish_client_status();
            std::cout << "start handler flag "<< flag << "\n";

            return true;
        }

        bool stop_handler(movel_seirios_msgs::StringTrigger::Request &req, movel_seirios_msgs::StringTrigger::Response &res) {
            std::cout << "inside stop handler\n";
            
            res.success = false;
            res.message = "stopping detect";
            //std::cout << res << "\n";

            flag = false;
            std::cout << "stop handler flag "<< flag << "\n";

            return true;
        }        

        // Ignore this method if not subscribing to stop topic
        void handle_publish_cancel(const std_msgs::Bool& msg) {
            std::cout << "inside handle publish cancel\n";

            bool cancel_trigger;
            cancel_trigger = msg.data;
            //std::cout << "published msg " << cancel_trigger << "\n";
            if(cancel_trigger){
                std::cout << "do canceling stuff. cancel_trigger: " << cancel_trigger << "\n";
            }
        }

        // Ignore this method if not using launch
        void publish_client_status() {
            std::cout << "publish client status called\n";

            std_msgs::UInt8 cstatus;

            if(flag) {
                cstatus.data = 2;
            }
            else {
                cstatus.data = 3;
            }

            std::cout << "client status " << cstatus << "2 for success, 3 for fail \n";
            client_status.publish(cstatus);

        }

};

int main(int argc, char** argv) {
    
    ros::init(argc, argv, "cpp_test");
    ros::NodeHandle nh;
    
    Cpptest cpp_test(&nh);
 
    ros::spin();

    return 0;
}
```

{% endtab %}
{% endtabs %}
