Skip to content

Commit

Permalink
add app_scheduler
Browse files Browse the repository at this point in the history
  • Loading branch information
knorth55 committed Jul 10, 2021
1 parent dbd22f4 commit 83bc4f7
Show file tree
Hide file tree
Showing 8 changed files with 211 additions and 0 deletions.
12 changes: 12 additions & 0 deletions app_scheduler/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 2.8.3)
project(app_scheduler)

find_package(catkin REQUIRED)

catkin_python_setup()

catkin_package()

install(DIRECTORY launch scripts
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
USE_SOURCE_PERMISSIONS)
33 changes: 33 additions & 0 deletions app_scheduler/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# app_scheduler

Scheduler for `app_manager` with `schedule` python package

## Launch

```bash
roslaunch app_scheduler app_scheduler.launch
```

## Sample schedule yaml

You can set schedule with `schedule` python package syntax.

```yaml
- name: sample0
app_name: app_scheduler/sample0
app_schedule:
start: every(2).minutes.at(":00")
- name: sample1
app_name: app_scheduler/sample1
app_schedule:
start: every(60).seconds
- name: sample2
app_name: app_scheduler/sample2
app_schedule:
start: every(1).hour.at(":00")
- name: sample3
app_name: app_scheduler/sample3
app_schedule:
start: every(1).day.at("10:00:00")
stop: every(1).day.at("10:00:03")
```
14 changes: 14 additions & 0 deletions app_scheduler/launch/app_scheduler.launch
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<launch>
<arg name="yaml_path" />

<arg name="duration" default="1"/>
<arg name="update_duration" default="10" />

<node name="app_scheduler" pkg="app_scheduler" type="app_scheduler" output="screen">
<rosparam subst_value="true">
duration: $(arg duration)
update_duration: $(arg update_duration)
yaml_path: $(arg yaml_path)
</rosparam>
</node>
</launch>
18 changes: 18 additions & 0 deletions app_scheduler/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0"?>
<package format="2">
<name>app_scheduler</name>
<version>0.0.0</version>
<description>The scheduler for app_manager package</description>
<maintainer email="[email protected]">Shingo Kitagawa</maintainer>
<author email="[email protected]">Shingo Kitagawa</author>
<license>BSD</license>

<buildtool_depend>catkin</buildtool_depend>
<exec_depend>app_manager</exec_depend>
<exec_depend>python-schedule</exec_depend>
<exec_depend>std_msgs</exec_depend>

<export>
<app_manager app_dir="${prefix}/apps"/>
</export>
</package>
15 changes: 15 additions & 0 deletions app_scheduler/scripts/app_scheduler
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env python

import rospy

from app_scheduler import AppScheduler


if __name__ == '__main__':
rospy.init_node('app_scheduler')
robot_name = rospy.get_param('/robot/name', 'robot')
yaml_path = rospy.get_param('~yaml_path')
duration = rospy.get_param('~duration', 1)
update_duration = rospy.get_param('~update_duration', 60)
scheduler = AppScheduler(robot_name, yaml_path, duration, update_duration)
rospy.spin()
13 changes: 13 additions & 0 deletions app_scheduler/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env python

from catkin_pkg.python_setup import generate_distutils_setup
from distutils.core import setup
from setuptools import find_packages


d = generate_distutils_setup(
packages=find_packages('src'),
package_dir={'': 'src'},
)

setup(**d)
1 change: 1 addition & 0 deletions app_scheduler/src/app_scheduler/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .app_scheduler import AppScheduler # NOQA
105 changes: 105 additions & 0 deletions app_scheduler/src/app_scheduler/app_scheduler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import yaml

import rospy

import schedule

from app_manager.msg import AppList
from app_manager.msg import AppStatus
from app_manager.srv import StartApp
from app_manager.srv import StopApp


class AppScheduler(object):

def __init__(self, robot_name, yaml_path, duration, update_duration):
self.robot_name = robot_name
self.yaml_path = yaml_path
self.running_app_names = []
self.running_jobs = {}
self.app_list_topic_name = '/{}/app_list'.format(self.robot_name)
self.start_app = rospy.ServiceProxy(
'/{}/start_app'.format(self.robot_name), StartApp)
self.stop_app = rospy.ServiceProxy(
'/{}/stop_app'.format(self.robot_name), StopApp)
self.job_timer = rospy.Timer(rospy.Duration(duration), self._timer_cb)
self.update_timer = rospy.Timer(
rospy.Duration(update_duration), self._update_timer_cb)
self.sub = rospy.Subscriber(
'/{}/application/app_status'.format(self.robot_name),
AppStatus, self._sub_cb)
self._load_yaml()
self._register_apps()

def _load_yaml(self):
with open(self.yaml_path, 'r') as yaml_f:
self.apps = yaml.load(yaml_f)

def _register_apps(self):
for app in self.apps:
rospy.loginfo(
'register app schedule => name: {0}, app_name: {1}'.format(
app['name'], app['app_name']))
self._register_app(app)

def _register_app(self, app):
app_schedule = app['app_schedule']
start_job = self._create_start_job(app['name'], app['app_name']) # NOQA
eval('schedule.{}.do(start_job)'.format(app_schedule['start']))
if 'stop' in app_schedule:
stop_job = self._create_stop_job(app['name'], app['app_name']) # NOQA
eval('schedule.{}.do(stop_job)'.format(app_schedule['stop']))

def _create_start_job(self, name, app_name):
def start_job():
start_res = self.start_app(app_name)
self.running_jobs[name] = {
'app_name': app_name,
'running': start_res.started
}
return start_job

def _create_stop_job(self, name, app_name):
def stop_job():
if app_name in self.running_app_names:
stop_res = self.stop_app(app_name)
self.running_jobs[name] = {
'app_name': app_name,
'running': not stop_res.stopped
}
return stop_job

def _update_running_app_names(self):
try:
msg = rospy.wait_for_message(
self.app_list_topic_name, AppList, timeout=1)
except Exception as e:
rospy.logwarn(
'Failed to subscribe {}: {}'.format(
self.app_list_topic_name, e))
return
self.running_app_names = [x.name for x in msg.running_apps]

def _update_running_jobs(self):
for name, job_data in self.running_jobs.items():
if (job_data['running']
and job_data['app_name'] not in self.running_app_names):
self.running_jobs[name]['running'] = False

def _timer_cb(self, event):
schedule.run_pending()

def _update_timer_cb(self, event):
self._update_running_app_names()
self._update_running_jobs()

def _sub_cb(self, msg):
if msg.type == AppStatus.INFO:
# INFO
rospy.loginfo('app_scheduler: {}'.format(msg.status))
elif msg.type == AppStatus.WARN:
# WARN
rospy.logwarn('app_scheduler: {}'.format(msg.status))
else:
# ERROR
rospy.logerr('app_scheduler: {}'.format(msg.status))

0 comments on commit 83bc4f7

Please sign in to comment.