5 from strands_executive_msgs
import task_utils
6 from strands_executive_msgs.msg
import Task, ExecutionStatus
7 from strands_executive_msgs.srv
import AddTasks, SetExecutionStatus, DemandTask
8 from std_srvs.srv
import Empty
9 from mongodb_store.message_store
import MessageStoreProxy
11 from scitos_msgs.msg
import BatteryState
14 from datetime
import *
16 from task_executor
import task_routine
17 from task_executor.task_routine
import delta_between
18 from copy
import deepcopy
20 from dynamic_reconfigure.server
import Server
21 from routine_behaviours.cfg
import ChargingThresholdsConfig
25 """ This class manages the over-arching routine behaviour of the robot. It monitors battery charge task level and can force charging when necessary and also generate tasks when the robot is idle. """
27 def __init__(self, daily_start, daily_end, idle_duration, charging_point = 'ChargingPoint'):
30 daily_start (datetime.time): The time of day the routine should start.
31 daily_end (datetime.time): The time of day the routine should end.
32 idle_duration (rospy.Duration): How long to be idle before triggering on_idle.
33 charging_point: Where to head to when charge is low
41 rospy.loginfo(
'Fetching parameters from dynamic_reconfigure')
54 rospy.Subscriber(
'/battery_state', BatteryState, self.
_check_battery)
61 self.
routine = task_routine.DailyRoutine(daily_start, daily_end)
67 start_time_secs = task_routine.time_to_secs(daily_start)
68 end_time_secs = task_routine.time_to_secs(daily_end)
69 full_day_secs = task_routine.time_to_secs(time(23,59))
72 night_secs = (full_day_secs - end_time_secs) + start_time_secs
97 rospy.Subscriber(
'current_schedule', ExecutionStatus, self.
_check_idle)
102 Return true if a task can be send to the task_executor now.
105 This checks if battery level is fine and that the robot is not at the charging point. Subclasses can override this to provide additional checks.
111 rospy.loginfo(
"Config set to {force_charge_threshold}, {force_charge_addition}".format(**config))
119 Add a task to be executed after the routine ends. These tasks cannot involve movement and therefore must either have an empty start_node_id or be performed at the charging station.
121 if task.start_node_id == charging_point:
122 task.start_node_id =
''
124 if task.start_node_id !=
'':
125 rospy.logwarn(
'Rejecting task to do %s at %s as only location-free tasks are allowed at night')
128 self.night_tasks.append(task)
132 Check if the given time is before the start of the routine.
135 return task_routine.time_less_than(time, self.
daily_start)
137 return task_routine.time_less_than(time, self.
daily_start)
and task_routine.time_less_than(self.
daily_end, time)
141 Check if the given time is after the end of the routine.
144 return task_routine.time_less_than(self.
daily_end, time)
146 return task_routine.time_less_than(self.
daily_end, time)
and task_routine.time_less_than(time, self.
daily_start)
150 Check if the given time is during the routine.
158 rostime_now = rospy.get_rostime()
159 now = datetime.fromtimestamp(rostime_now.to_sec(), tzlocal()).time()
166 on_charger = self.
battery_state is not None and (self.battery_state.charging
or self.battery_state.powerSupplyPresent)
169 if not self.
is_during_day(now)
and not schedule.currently_executing
and not on_charger:
174 if schedule.currently_executing:
177 if len(schedule.execution_queue) == 0:
181 delay_until_next = schedule.execution_queue[0].execution_time - rostime_now
199 """ Reports false if battery is below force_charge_threshold or if it is above it but within force_charge_addition of the threshold and charging """
204 if self.battery_state.lifePercent < self.
threshold:
208 elif self.battery_state.charging
or self.battery_state.powerSupplyPresent:
210 return self.battery_state.lifePercent > threshold
215 rospy.logwarn(
'No battery received when checking')
226 rospy.logwarn(
'Battery below force charge threshold: %s ' % (self.battery_state.lifePercent))
233 rospy.logwarn(
'Still not charging, trying again')
239 rostime_now = rospy.get_rostime()
240 now = datetime.fromtimestamp(rostime_now.to_sec(), tzlocal()).time()
243 rospy.loginfo(
'Sending night tasks')
247 instantiated_night_tasks = []
249 now = rospy.get_rostime()
252 night_task = deepcopy(task)
253 night_task.start_after = now
255 night_task.end_before = now + rospy.Duration(60 * 60 * 12)
256 instantiated_night_tasks.append(night_task)
263 add_tasks_srv_name =
'/task_executor/add_tasks'
264 set_exe_stat_srv_name =
'/task_executor/set_execution_status'
265 demand_task_srv_name =
'/task_executor/demand_task'
266 clear_schedule_srv_name =
'/task_executor/clear_schedule'
267 rospy.loginfo(
"Waiting for task_executor service...")
268 rospy.wait_for_service(add_tasks_srv_name)
269 rospy.wait_for_service(set_exe_stat_srv_name)
270 rospy.loginfo(
"Done")
271 self.
add_tasks = rospy.ServiceProxy(add_tasks_srv_name, AddTasks)
273 self.
demand_task = rospy.ServiceProxy(demand_task_srv_name, DemandTask)
288 Create an on-demand task to charge the robot for the given duration.
291 charge_task = Task(action=
'wait_action', start_node_id=charging_point, end_node_id=charging_point, max_duration=charge_duration)
292 task_utils.add_time_argument(charge_task, rospy.Time())
293 task_utils.add_duration_argument(charge_task, charge_duration)
297 """ Clears the schedule of the robot before demanding a charge task. """
300 except rospy.ServiceException, e:
301 rospy.loginfo(
'Empty service complaint occurs here. Should be safe: %s'% e)
309 Triggered when the routine ends for the day.
311 This clears tasks and triggers an on-demand charge.
316 rospy.loginfo(
'ok, I\'m calling it a day until %s' % datetime.fromtimestamp((rospy.get_rostime() + self.
night_duration).to_sec()))
321 Triggered when the routine starts for the day.
323 rospy.loginfo(
'Good morning')
328 Starts the routine running. Must be called to generate behaviour.
330 self.runner.add_tasks(self.routine.get_routine_tasks())
334 Called when the routine is idle. Default is to trigger travel to the charging. As idleness is determined by the current schedule, if this call doesn't utlimately cause a task schedule to be generated this will be called repeatedly.
336 rospy.loginfo(
'Idle for too long, going to charging point')
342 Create routine behaviour from given tasks using provided parameter.
344 This largely passes arguments on to similar arguments in task_routine.repeat_every_delta, but constrains repeats to the bounds of the this routine.
346 If daily start or end not supplied use routine start or end
347 If delta not supplied, just do once during the start to end window
350 if daily_start
is None:
353 if daily_end
is None:
356 if repeat_delta
is None:
357 repeat_delta = delta_between(daily_start, daily_end)
360 self.routine.repeat_every_delta(tasks, delta=repeat_delta, times=1, start_time=daily_start, duration=delta_between(daily_start, daily_end))
def _create_services(self)
def is_after_day_end(self, time)
def clear_then_charge(self, charge_duration)
def task_allowed_now(self, task)
def add_night_task(self, task)
def _check_idle(self, schedule)
def demand_charge(self, charge_duration)
def is_before_day_start(self, time)
def is_during_day(self, time)
def dynamic_reconfigure_cb(self, config, level)
def _check_battery(self, battery_state)
def _send_night_tasks(self)