diff --git a/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py b/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py index 87846f357287571f5be0724e55cbf946cf9f2590..250ca852f9453bfc0c0e1c4a9c9e1dcac455ffbd 100644 --- a/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py +++ b/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py @@ -262,7 +262,11 @@ class Scheduler: self.log_schedule(log_level=logging.DEBUG) except Exception as e: - logger.exception("Could not schedule fixed_time-scheduled scheduling unit id=%d: %s", schedulable_unit.id, e) + if isinstance(e, SubtaskSchedulingException): + logger.warning("Could not schedule fixed_time-scheduled scheduling unit id=%d: %s", schedulable_unit.id, e) + else: + logger.exception("Could not schedule fixed_time-scheduled scheduling unit id=%d: %s", schedulable_unit.id, e) + unschedulable_unit = determine_unschedulable_reason_and_mark_unschedulable_if_needed(schedulable_unit, at_timestamp, at_timestamp + schedulable_unit.specified_observation_duration, @@ -1017,12 +1021,16 @@ class TMSSDynamicSchedulingMessageHandler(TMSSEventMessageHandler): scheduling_unit = models.SchedulingUnitBlueprint.objects.get(id=id) # trigger scheduler if needed - if not self.scheduler.is_scheduling: + if scheduling_unit.is_fixed_time_scheduled and self.scheduler.fixed_time_scheduling_enabled: + if status == models.SchedulingUnitStatus.Choices.SCHEDULABLE.value: + logger.info("triggering scheduler for fixed_time unit id=%s status=%s", id, status) + self.scheduler.trigger() + elif scheduling_unit.is_dynamically_scheduled and self.scheduler.dynamic_scheduling_enabled and not self.scheduler.is_scheduling: if (status in [models.SchedulingUnitStatus.Choices.SCHEDULABLE.value] and not scheduling_unit.placed) or \ status in [models.SchedulingUnitStatus.Choices.OBSERVING.value, models.SchedulingUnitStatus.Choices.CANCELLED.value, models.SchedulingUnitStatus.Choices.OBSERVED.value]: - logger.info("onSchedulingUnitBlueprintStatusChanged(id=%s, status=%s, placed=%s): triggering update of dynamic & fixed_time schedule...", id, status, scheduling_unit.placed) + logger.info("triggering scheduler for dynamic unit id=%s status=%s placed=%s", id, status, scheduling_unit.placed) self.scheduler.trigger() diff --git a/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py b/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py index 3e6ad969cf1c4d046453f261fef9206b0d4ed86d..2d82c672d2e704e9d546f379c23adb3866d87053 100755 --- a/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py +++ b/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py @@ -1235,6 +1235,10 @@ class TestFixedTimeScheduling(BaseDynamicSchedulingTestCase): self.assertEqual(models.SchedulingUnitStatus.Choices.SCHEDULED.value, scheduling_unit_blueprint.status.value) self.assertEqual(at, scheduling_unit_blueprint.scheduled_start_time) + # keep track of state changes for synchronization (waiting in this test, see below) + obs_subtask = scheduling_unit_blueprint.subtasks.filter(specifications_template__type__value=models.SubtaskType.Choices.OBSERVATION.value, state__value=models.SubtaskState.Choices.SCHEDULED.value).first() + scheduled_state_change = obs_subtask.subtaskstatelog_set.filter(new_state__value=models.SubtaskState.Choices.SCHEDULED.value).order_by('-updated_at').first() + # create an new reservation overlapping with only one station reservation_template = models.ReservationTemplate.objects.first() # there is only one reservation_doc = reservation_template.get_default_json_document_for_schema() @@ -1246,9 +1250,14 @@ class TestFixedTimeScheduling(BaseDynamicSchedulingTestCase): specifications_template=reservation_template, description='') + # now wait and poll until subtask/unit is unscheduled, and scheduled again, or timeout from time import sleep - sleep(2) #idealy we should not sleep in tests. - # now wait and poll until unit is unscheduled, and scheduled again, or timeout + while True: + latest_scheduled_state_change = obs_subtask.subtaskstatelog_set.filter(new_state__value=models.SubtaskState.Choices.SCHEDULED.value).order_by('-updated_at').first() + if latest_scheduled_state_change.updated_at > scheduled_state_change.updated_at: + break + sleep(0.1) + # wait/check if the unit is also scheduled again scheduling_unit_blueprint = wait_for_scheduling_unit_blueprint_status(scheduling_unit_blueprint.id, models.SchedulingUnitStatus.Choices.SCHEDULED.value) # is it scheduled at the right time? and is reserved station CS002 taken out? @@ -1257,11 +1266,19 @@ class TestFixedTimeScheduling(BaseDynamicSchedulingTestCase): self.assertEqual(['CS003'], scheduling_unit_blueprint.main_observation_used_stations) self.assertEqual(['CS002', 'CS003'], scheduling_unit_blueprint.main_observation_specified_stations) + # keep track of state changes for synchronization (waiting in this test, see below) + scheduled_state_change = obs_subtask.subtaskstatelog_set.filter(new_state__value=models.SubtaskState.Choices.SCHEDULED.value).order_by('-updated_at').first() + # remove the reservation as a whole reservation.delete() # should trigger the scheduler event handler.... - # now wait and poll until unit is unscheduled, and scheduled again, or timeout - sleep(2) #idealy we should not sleep in tests. + # now wait and poll until subtask/unit is unscheduled, and scheduled again, or timeout + while True: + latest_scheduled_state_change = obs_subtask.subtaskstatelog_set.filter(new_state__value=models.SubtaskState.Choices.SCHEDULED.value).order_by('-updated_at').first() + if latest_scheduled_state_change.updated_at > scheduled_state_change.updated_at: + break + sleep(0.1) + # wait/check if the unit is also scheduled again scheduling_unit_blueprint = wait_for_scheduling_unit_blueprint_status(scheduling_unit_blueprint.id, models.SchedulingUnitStatus.Choices.SCHEDULED.value) # is it scheduled at the right time? and is free station CS002 taken in again? diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py b/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py index c7c9df38c4972610f9197804a39fd5305d0cf8ab..e2c52c74b96c87ac0d540ba5edd6ac01171ee22f 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py @@ -690,11 +690,8 @@ def unschedule_subtasks_in_scheduling_unit_blueprint(scheduling_unit_blueprint: def reschedule_subtasks_in_scheduling_unit_blueprint(scheduling_unit_blueprint: SchedulingUnitBlueprint, misc_unavailable_stations: Iterable[str]=None) -> models.SchedulingUnitBlueprint: '''Convenience method: Unschedule all scheduled subtasks and schedule them again in one transaction''' with transaction.atomic(): - unschedule_subtasks_in_scheduling_unit_blueprint(scheduling_unit_blueprint) - schedule_independent_subtasks_in_scheduling_unit_blueprint(scheduling_unit_blueprint, misc_unavailable_stations=misc_unavailable_stations) - - scheduling_unit_blueprint.refresh_from_db() - return scheduling_unit_blueprint + unscheduled_unit = unschedule_subtasks_in_scheduling_unit_blueprint(scheduling_unit_blueprint) + return schedule_independent_subtasks_in_scheduling_unit_blueprint(unscheduled_unit, misc_unavailable_stations=misc_unavailable_stations) def set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint: SchedulingUnitBlueprint, first_start_time: datetime, placed: bool=None) -> models.SchedulingUnitBlueprint: