diff --git a/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py b/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py index e32f7829dd9e0e99f933503520974d9e5e64f8f5..6c67c10ecd52576ff36d427346ae7f542cf245c0 100644 --- a/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py +++ b/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py @@ -227,14 +227,7 @@ class Scheduler: filtered_scheduling_units = filter_scheduling_units_using_constraints(scheduling_units, lower_bound_start_time, upper_bound_stop_time, self._raise_if_triggered) if filtered_scheduling_units: - triggered_scheduling_units = [scheduling_unit for scheduling_unit in filtered_scheduling_units if scheduling_unit.interrupts_telescope] - if triggered_scheduling_units: - highest_priority_triggered_scheduling_unit = max(triggered_scheduling_units, key=lambda su: su.project.trigger_priority) - best_scored_scheduling_unit = ScoredSchedulingUnit(scheduling_unit=highest_priority_triggered_scheduling_unit, - start_time=get_earliest_possible_start_time(highest_priority_triggered_scheduling_unit, lower_bound_start_time), - scores={}, weighted_score=None) # we don't care about scores in case of a trigger - else: - best_scored_scheduling_unit = get_best_scored_scheduling_unit_scored_by_constraints(filtered_scheduling_units, lower_bound_start_time, upper_bound_stop_time) + best_scored_scheduling_unit = get_best_scored_scheduling_unit_scored_by_constraints(filtered_scheduling_units, lower_bound_start_time, upper_bound_stop_time) return best_scored_scheduling_unit # no filtered scheduling units found... @@ -246,16 +239,18 @@ class Scheduler: Overlapping existing scheduled units are unscheduled if their score is lower. :return: the scheduled scheduling unit.''' - # loop over the different priority queue's. 'A'-type projects/scheduling units always go first, then 'B'-type etc. - # if a scheduling unit can be scheduled for this priority_queue, then we exit early. - for priority_queue in models.PriorityQueueType.objects.order_by('value').all(): - schedulable_units = get_dynamically_schedulable_scheduling_units(priority_queue=priority_queue) + # prepare queries for subsets of schedulable_units (django uses lazy evaluation, so don't worry about wasted queries) + schedulable_units_triggered = get_triggered_schedulable_scheduling_units() + schedulable_units_queue_A = get_dynamically_schedulable_scheduling_units(priority_queue=models.PriorityQueueType.objects.get(value=models.PriorityQueueType.Choices.A.value)) + schedulable_units_queue_B = get_dynamically_schedulable_scheduling_units(priority_queue=models.PriorityQueueType.objects.get(value=models.PriorityQueueType.Choices.B.value)) + # We schedule in an absolute order: first triggered, then dynamic queue A, then queue B + # if a scheduling unit can be scheduled, then we exit early. + for schedulable_units in (schedulable_units_triggered, schedulable_units_queue_A, schedulable_units_queue_B): if not schedulable_units.exists(): - logger.info("No scheduling units found for priority_queue '%s'...", priority_queue.value) - continue # continue with the remaining priority_queue types + continue # continue with the next in order schedulable_units - # convert queryset to list and fetch them from db + # convert queryset to list (which fetches them from db) schedulable_units = list(schedulable_units) # --- core routine --- @@ -317,8 +312,8 @@ class Scheduler: # nothing was found, or an error occurred. # search again... (loop) with the remaining schedulable_units and new lower_bound_start_time - # it may be that in the mean time some scheduling_units are not (dynamically) schedulable anymore, fetch list again. - schedulable_units = get_dynamically_schedulable_scheduling_units(priority_queue=priority_queue) + # it may be that in the mean time some scheduling_units are not (dynamically) schedulable anymore, filter those out. + schedulable_units = [su for su in schedulable_units if su.status.value==models.SchedulingUnitStatus.Choices.SCHEDULABLE.value] lower_bound_start_time = get_min_earliest_possible_start_time(schedulable_units, lower_bound_start_time+timedelta(hours=1), self._raise_if_triggered) # TODO: update upper_bound_stop_time as well, stop when upper_bound_stop_time > cycle end. @@ -633,6 +628,13 @@ def get_scheduled_scheduling_units(lower_bound: datetime=None, upper_bound: date return scheduled_units +def get_triggered_schedulable_scheduling_units() -> QuerySet: + '''get a list of all trigger dynamically and fixed_time schedulable scheduling_units''' + scheduling_units = models.SchedulingUnitBlueprint.objects.filter(status__value=models.SchedulingUnitStatus.Choices.SCHEDULABLE.value) + scheduling_units = scheduling_units.filter(interrupts_telescope=True) + return scheduling_units + + def get_running_observation_subtasks(stopping_after:datetime=None) -> [models.Subtask]: '''get a list of all starting/started subtasks, optionally filter for those finishing after the provided time''' running_obs_subtasks = models.Subtask.objects.filter(state__value__in=[models.SubtaskState.Choices.STARTING.value, models.SubtaskState.Choices.STARTED.value],