From 5fb5ac9718e37ca26a0cc66c547a2135f99d0001 Mon Sep 17 00:00:00 2001 From: Jorrit Schaap <schaap@astron.nl> Date: Tue, 10 Nov 2020 16:51:08 +0100 Subject: [PATCH] TMSS-190: first step towards stategy pattern --- .../scheduling/lib/dynamic_scheduling.py | 89 ++++++++++++------- SAS/TMSS/src/tmss/exceptions.py | 4 + 2 files changed, 62 insertions(+), 31 deletions(-) diff --git a/SAS/TMSS/services/scheduling/lib/dynamic_scheduling.py b/SAS/TMSS/services/scheduling/lib/dynamic_scheduling.py index f24e5b37ae5..926ba825f02 100644 --- a/SAS/TMSS/services/scheduling/lib/dynamic_scheduling.py +++ b/SAS/TMSS/services/scheduling/lib/dynamic_scheduling.py @@ -43,7 +43,9 @@ from lofar.sas.tmss.tmss.tmssapp.conversions import LOFAR_CENTER_OBSERVER, Time, def get_schedulable_scheduling_units() -> [models.SchedulingUnitBlueprint]: '''get a list of all schedulable scheduling_units''' defined_independend_subtasks = models.Subtask.independent_subtasks().filter(state__value='defined') - scheduling_units = models.SchedulingUnitBlueprint.objects.filter(id__in=defined_independend_subtasks.values('task_blueprint__scheduling_unit_blueprint_id').distinct()).all() + defined_independend_subtask_ids = defined_independend_subtasks.values('task_blueprint__scheduling_unit_blueprint_id').distinct().all() + # TODO: prefetch related models, like draft, spec, templates, etc + scheduling_units = models.SchedulingUnitBlueprint.objects.filter(id__in=defined_independend_subtask_ids).all() return [su for su in scheduling_units if su.status == 'schedulable'] @@ -56,37 +58,62 @@ def get_scheduled_scheduling_units(lower:datetime=None, upper:datetime=None) -> scheduled_subtasks = scheduled_subtasks.filter(start_time__lte=upper) return list(models.SchedulingUnitBlueprint.objects.filter(id__in=scheduled_subtasks.values('task_blueprint__scheduling_unit_blueprint_id').distinct()).all()) +def can_run_within_timewindow(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime, upper_bound: datetime) -> bool: + constraints_template = scheduling_unit.draft.scheduling_constraints_template + if constraints_template.name == 'constraints': + if constraints_template.version == 1: + return can_run_within_timewindow_template_constraints_1(scheduling_unit, lower_bound, upper_bound) + + raise UnknownTemplateException("Cannot check if scheduling_unit id=%s can run between '%s' and '%s', because we have no constraint solver for scheduling constraints template '%s' version=%s" % ( + scheduling_unit.id, lower_bound, upper_bound, constraints_template.name, constraints_template.version)) + +def can_run_within_timewindow_template_constraints_1(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime, upper_bound: datetime) -> bool: + constraints = scheduling_unit.draft.scheduling_constraints_doc + return can_run_within_timewindow_template_constraints_1_daily(scheduling_unit, lower_bound, upper_bound) + +def can_run_within_timewindow_template_constraints_1_daily(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime, upper_bound: datetime) -> bool: + constraints = scheduling_unit.draft.scheduling_constraints_doc + if not (constraints['daily']['require_day'] and constraints['daily']['require_night']): + # no day/night restrictions, can run any time + return True + + if constraints['daily']['require_day'] or constraints['daily']['require_night']: + # TODO: TMSS-254 and TMSS-255 + # TODO: take avoid_twilight into account + # Please note that this first crude proof of concept treats sunset/sunrise as 'events', + # whereas in our definition they are transition periods. See: TMSS-435 + + # Ugly code. Should be improved. Works for demo. + # create a series of timestamps in the window of opportunity, and evaluate of there are all during day or night + possible_start_time = get_first_possible_start_time(scheduling_unit, lower_bound) + possible_stop_time = possible_start_time + scheduling_unit.duration + timestamps = [possible_start_time] + while timestamps[-1] < possible_stop_time - timedelta(hours=8): + timestamps.append(timestamps[-1] + timedelta(hours=8)) + timestamps.append(possible_stop_time) + + if constraints['daily']['require_night'] and all(LOFAR_CENTER_OBSERVER.is_night(timestamp) for timestamp in timestamps): + return True + + if constraints['daily']['require_day'] and all(not LOFAR_CENTER_OBSERVER.is_night(timestamp) for timestamp in timestamps): + return True + + return False + + def filter_scheduling_units_using_constraints(scheduling_units:[models.SchedulingUnitBlueprint], lower_bound_start_time: datetime, upper_bound_stop_time: datetime) -> [models.SchedulingUnitBlueprint]: - filtered_scheduling_units = [] - for scheduling_unit in scheduling_units: - constraints = scheduling_unit.draft.scheduling_constraints_doc - - if not constraints: - # accept any scheduling_unit with no constraints - filtered_scheduling_units.append(scheduling_unit) - continue - - # TODO: use factory to get filter function based on scheduling_constraints_template and/or constraint name - # for now, assume there is only one template, allowing straightforward filtering. - if not (constraints['daily']['require_day'] and constraints['daily']['require_night']): - filtered_scheduling_units.append(scheduling_unit) - elif constraints['daily']['require_day'] or constraints['daily']['require_night']: - # TODO: take avoid_twilight into account - # Ugly code. Should be improved. Works for demo. - # create a series of timestamps in the window of opportunity, and evaluate of there are all during day or night - possible_start_time = get_first_possible_start_time(scheduling_unit, lower_bound_start_time) - possible_stop_time = possible_start_time + scheduling_unit.duration - timestamps = [possible_start_time] - while timestamps[-1] < possible_stop_time-timedelta(hours=8): - timestamps.append(timestamps[-1] + timedelta(hours=8)) - timestamps.append(possible_stop_time) - - if constraints['daily']['require_night'] and all(LOFAR_CENTER_OBSERVER.is_night(timestamp) for timestamp in timestamps): - filtered_scheduling_units.append(scheduling_unit) - elif constraints['daily']['require_day'] and all(not LOFAR_CENTER_OBSERVER.is_night(timestamp) for timestamp in timestamps): - filtered_scheduling_units.append(scheduling_unit) - - return filtered_scheduling_units + '''return the schedulable scheduling_units which for which the constraints are 'go' within the given timewindow''' + + def can_run_within_timewindow_no_exception(scheduling_unit, lower_bound, upper_bound): + try: + return can_run_within_timewindow(scheduling_unit, lower_bound, upper_bound) + except UnknownTemplateException as e: + logger.warning(e) + return False + + return [sub for sub in scheduling_units + if can_run_within_timewindow_no_exception(sub, lower_bound_start_time, upper_bound_stop_time)] + def find_best_next_schedulable_unit(lower_bound_start_time: datetime=None, upper_bound_stop_time: datetime=None, scheduling_units:[models.SchedulingUnitBlueprint]=None) -> (models.SchedulingUnitBlueprint, datetime): """ diff --git a/SAS/TMSS/src/tmss/exceptions.py b/SAS/TMSS/src/tmss/exceptions.py index a320dbd527a..358a5ede249 100644 --- a/SAS/TMSS/src/tmss/exceptions.py +++ b/SAS/TMSS/src/tmss/exceptions.py @@ -22,3 +22,7 @@ class SubtaskSchedulingException(SchedulingException): class TaskSchedulingException(SchedulingException): pass + +class UnknownTemplateException(TMSSException): + '''raised when TMSS trying to base its processing routines on the chosen template, but this specific template is unknown.''' + pass -- GitLab