-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
211 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .app_scheduler import AppScheduler # NOQA |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) |