Search in sources :

Example 26 with TaskAssigningSolution

use of org.kie.kogito.taskassigning.core.model.TaskAssigningSolution in project kogito-apps by kiegroup.

the class TaskAssigningService method onPlanningExecuted.

/**
 * Invoked when the PlanningExecutor finalized the execution of a plan.
 * Three main scenarios might happen:
 * a) There are successful invocations and thus tasks were assigned, the corresponding "pinning changes" must be produced.
 * Create and add them to the Solver.
 *
 * b) No "pinning changes" to execute there and no available events, retry with the planning items that failed.
 *
 * c) No "pinning changes" but there are available events, execute them.
 *
 * @param result a PlanningExecutionResult with results of the planning execution.
 */
synchronized void onPlanningExecuted(PlanningExecutionResult result) {
    if (isNotOperative()) {
        LOGGER.warn(SERVICE_INOPERATIVE_MESSAGE, context.getStatus());
        return;
    }
    try {
        LOGGER.debug("Planning was executed");
        applyingPlanningExecutionResult.set(false);
        TaskAssigningSolution solution = currentSolution.get();
        Map<String, User> usersById = solution.getUserList().stream().collect(Collectors.toMap(User::getId, Function.identity()));
        List<ProblemFactChange<TaskAssigningSolution>> pinningChanges = new ArrayList<>();
        Task task;
        User user;
        boolean published;
        for (PlanningExecutionResultItem resultItem : result.getItems()) {
            task = resultItem.getItem().getTask();
            published = !resultItem.hasError();
            if (published) {
                user = usersById.get(resultItem.getItem().getTargetUser());
                pinningChanges.add(new AssignTaskProblemFactChange(new TaskAssignment(task), user));
            }
            context.setTaskPublished(task.getId(), published);
        }
        if (!pinningChanges.isEmpty()) {
            LOGGER.debug("Pinning changes must be executed for the successful invocations: {}", pinningChanges.size());
            pinningChanges.add(0, scoreDirector -> context.setCurrentChangeSetId(context.nextChangeSetId()));
            applyingPlanningExecutionResult.set(true);
            cancelScheduledImproveSolutionOnBackgroundTimer();
            solverExecutor.addProblemFactChanges(pinningChanges);
        } else if (!hasQueuedEvents()) {
            List<PlanningItem> failingItems = result.getItems().stream().filter(PlanningExecutionResultItem::hasError).map(PlanningExecutionResultItem::getItem).collect(Collectors.toList());
            LOGGER.debug("No new events to process, but some items failed: {}, we must retry", failingItems.size());
            cancelScheduledImproveSolutionOnBackgroundTimer();
            planningExecutor.start(failingItems, this::onPlanningExecuted);
        } else {
            LOGGER.debug("Some items failed but there are events to process, try to adjust the solution accordingly.");
            resumeEvents();
        }
    } catch (Exception e) {
        failFast(e);
    }
}
Also used : Task(org.kie.kogito.taskassigning.core.model.Task) User(org.kie.kogito.taskassigning.core.model.User) TaskAssigningSolution(org.kie.kogito.taskassigning.core.model.TaskAssigningSolution) ArrayList(java.util.ArrayList) AssignTaskProblemFactChange(org.kie.kogito.taskassigning.core.model.solver.realtime.AssignTaskProblemFactChange) ProblemFactChange(org.optaplanner.core.api.solver.ProblemFactChange) AssignTaskProblemFactChange(org.kie.kogito.taskassigning.core.model.solver.realtime.AssignTaskProblemFactChange) List(java.util.List) ArrayList(java.util.ArrayList) TaskAssignment(org.kie.kogito.taskassigning.core.model.TaskAssignment)

Example 27 with TaskAssigningSolution

use of org.kie.kogito.taskassigning.core.model.TaskAssigningSolution in project kogito-apps by kiegroup.

the class TaskAssigningService method processDataEvents.

/**
 * Invoked when a set of events are received for processing.
 * Three main scenarios might happen:
 * a) A solution already exists and thus the proper problem fact changes are calculated and passed to the solver for
 * execution. If there are no changes to apply, wait for more events.
 *
 * b) No solution exists. Instruct the solution data loader to read the users information and the solver will be
 * started when this information is returned plus the information collected from the events.
 *
 * c) A solution improved on background event arrives and must be processed accordingly.
 *
 * @param events a list of events to process.
 */
synchronized void processDataEvents(List<DataEvent<?>> events) {
    if (isNotOperative()) {
        LOGGER.warn(SERVICE_INOPERATIVE_MESSAGE, context.getStatus());
        return;
    }
    try {
        List<TaskDataEvent> newTaskDataEvents = filterNewestTaskEventsInContext(context, events);
        if (currentSolution.get() == null) {
            List<TaskDataEvent> activeTaskEvents = newTaskDataEvents.stream().filter(IS_ACTIVE_TASK_EVENT).collect(Collectors.toList());
            if (!activeTaskEvents.isEmpty()) {
                // b) no solution exists, store the events and get the users from the external user service.
                startingEvents = activeTaskEvents;
                startingFromEvents.set(true);
                loadSolutionData(false, true, config.getDataLoaderPageSize());
            } else {
                resumeEvents();
            }
        } else {
            // a) a solution exists, calculate and apply the potential changes if any.
            UserDataEvent userDataEvent = filterNewestUserEvent(events);
            List<ProblemFactChange<TaskAssigningSolution>> changes = SolutionChangesBuilder.create().forSolution(currentSolution.get()).withContext(context).withUserServiceConnector(userServiceConnectorDelegate).withProcessors(processorRegistry).fromTasksData(fromTaskDataEvents(newTaskDataEvents)).fromUserDataEvent(userDataEvent).build();
            if (!changes.isEmpty()) {
                LOGGER.debug("processDataEvents - there are changes: {} to apply", changes.size());
                cancelScheduledImproveSolutionOnBackgroundTimer();
                solverExecutor.addProblemFactChanges(changes);
            } else {
                // c) check if an event for the improve solution on background period has arrived and a better
                // solution was produced
                SolutionUpdatedOnBackgroundDataEvent solutionImprovedOnBackgroundEvent = filterNewestSolutionUpdatedOnBackgroundEvent(events);
                TaskAssigningSolution currentLastBestSolution = lastBestSolution.get();
                if (solutionImprovedOnBackgroundEvent != null && hasToApplyImprovedOnBackgroundSolution(solutionImprovedOnBackgroundEvent, currentLastBestSolution)) {
                    // a better solution was produced during the improveSolutionOnBackgroundDuration period
                    LOGGER.debug("processDataEvents - apply the improved on background solution: {}", currentLastBestSolution);
                    executeSolutionChange(currentLastBestSolution);
                } else {
                    executePlanOrResumeEvents(currentSolution.get());
                }
            }
        }
    } catch (Exception e) {
        failFast(e);
    }
}
Also used : TaskDataEvent(org.kie.kogito.taskassigning.service.event.TaskDataEvent) TaskAssigningSolution(org.kie.kogito.taskassigning.core.model.TaskAssigningSolution) UserDataEvent(org.kie.kogito.taskassigning.service.event.UserDataEvent) AssignTaskProblemFactChange(org.kie.kogito.taskassigning.core.model.solver.realtime.AssignTaskProblemFactChange) ProblemFactChange(org.optaplanner.core.api.solver.ProblemFactChange) SolutionUpdatedOnBackgroundDataEvent(org.kie.kogito.taskassigning.service.event.SolutionUpdatedOnBackgroundDataEvent)

Example 28 with TaskAssigningSolution

use of org.kie.kogito.taskassigning.core.model.TaskAssigningSolution in project kogito-apps by kiegroup.

the class TaskAssigningService method onSolutionDataLoad.

/**
 * Invoked by the SolutionDataLoader when the data for initializing the solution has been loaded successfully.
 * Two main scenarios might happen:
 * a) The service is starting and thus the initial solution load is attempted. If there are available tasks, the
 * solver will be started, first solution will arrive, etc.
 *
 * b) No tasks where available at the time of service initialization and thus no solution to start the solver.
 * At a later point in time events arrived and the solution can be started with the information coming for them plus
 * the user information loaded by the solution data loader.
 *
 * @param result contains the requested data for creating the initial solution.
 */
synchronized void onSolutionDataLoad(SolutionDataLoader.Result result) {
    if (isNotOperative()) {
        LOGGER.warn(SERVICE_INOPERATIVE_MESSAGE, context.getStatus());
        return;
    }
    try {
        LOGGER.debug("Solution data loading has finished, startingFromEvents: {}, includeTasks: {}" + ", includeUsers: {}, tasks: {}, users: {}", startingFromEvents, !startingFromEvents.get(), true, result.getTasks().size(), result.getUsers().size());
        context.setStatus(ServiceStatus.READY);
        TaskAssigningSolution solution;
        List<TaskAssignment> taskAssignments;
        if (startingFromEvents.get()) {
            // data loader has responded with the users.
            if (hasQueuedEvents()) {
                // incorporate the events that could have been arrived in the middle while the users were being loaded.
                List<TaskDataEvent> newEvents = filterNewestTaskEventsInContext(context, pollEvents());
                startingEvents = combineAndFilerNewestActiveTaskEvents(startingEvents, newEvents);
            }
            solution = SolutionBuilder.newBuilder().withTasks(fromTaskDataEvents(startingEvents)).withUsers(result.getUsers()).withProcessors(processorRegistry).build();
            startingFromEvents.set(false);
            startingEvents = null;
        } else {
            // a) normal initialization procedure after getting the tasks and users from the solution data loader
            solution = SolutionBuilder.newBuilder().withTasks(result.getTasks()).withUsers(result.getUsers()).withProcessors(processorRegistry).build();
        }
        // if the solution has non dummy tasks the solver can be started.
        taskAssignments = filterNonDummyAssignments(solution.getTaskAssignmentList());
        if (!taskAssignments.isEmpty()) {
            taskAssignments.forEach(taskAssignment -> {
                context.setTaskPublished(taskAssignment.getId(), taskAssignment.isPinned());
                context.setTaskLastEventTime(taskAssignment.getId(), taskAssignment.getTask().getLastUpdate());
            });
            solverExecutor.start(solution);
            userServiceAdapter.start();
        } else {
            resumeEvents();
        }
    } catch (Exception e) {
        failFast(e);
    }
}
Also used : TaskDataEvent(org.kie.kogito.taskassigning.service.event.TaskDataEvent) TaskAssigningSolution(org.kie.kogito.taskassigning.core.model.TaskAssigningSolution) TaskAssignment(org.kie.kogito.taskassigning.core.model.TaskAssignment)

Example 29 with TaskAssigningSolution

use of org.kie.kogito.taskassigning.core.model.TaskAssigningSolution in project kogito-apps by kiegroup.

the class TaskAssigningServiceTest method prepareStartAndSetInitialSolution.

private void prepareStartAndSetInitialSolution(SolutionDataLoader.Result result) throws Exception {
    prepareStart();
    solutionDataLoaderInitialExecution.complete(result);
    verify(taskAssigningService).onSolutionDataLoad(result);
    verify(solverExecutor).start(solutionCaptor.capture());
    TaskAssigningSolution initialSolution = solutionCaptor.getValue();
    BestSolutionChangedEvent<TaskAssigningSolution> solutionChangedEvent = mockEvent(initialSolution, true, true);
    solverListenerCaptor.getValue().bestSolutionChanged(solutionChangedEvent);
    verify(managedExecutor).runAsync(managedExecutorCaptor.capture());
    managedExecutorCaptor.getValue().run();
    verify(serviceEventConsumer).resume();
}
Also used : TaskAssigningSolution(org.kie.kogito.taskassigning.core.model.TaskAssigningSolution)

Example 30 with TaskAssigningSolution

use of org.kie.kogito.taskassigning.core.model.TaskAssigningSolution in project kogito-apps by kiegroup.

the class TaskAssigningServiceTest method onSolutionChangeWithPlanningItems.

@Test
void onSolutionChangeWithPlanningItems() throws Exception {
    prepareStart();
    TaskAssigningSolution solution = buildSolution();
    context.setTaskPublished(TASK_1_ID, false);
    context.setTaskPublished(TASK_2_ID, true);
    context.setTaskPublished(TASK_3_ID, false);
    context.setTaskPublished(TASK_4_ID, true);
    taskAssigningService.onBestSolutionChange(mockEvent(solution, true, true));
    verify(managedExecutor).runAsync(managedExecutorCaptor.capture());
    managedExecutorCaptor.getValue().run();
    verify(planningExecutor).start(planningCaptor.capture(), any());
    List<PlanningItem> planningItems = planningCaptor.getValue();
    assertThat(planningItems).isNotNull().hasSize(2);
}
Also used : TaskAssigningSolution(org.kie.kogito.taskassigning.core.model.TaskAssigningSolution) Test(org.junit.jupiter.api.Test)

Aggregations

TaskAssigningSolution (org.kie.kogito.taskassigning.core.model.TaskAssigningSolution)65 Test (org.junit.jupiter.api.Test)30 User (org.kie.kogito.taskassigning.core.model.User)24 TaskAssignment (org.kie.kogito.taskassigning.core.model.TaskAssignment)23 ArrayList (java.util.ArrayList)15 AssignTaskProblemFactChange (org.kie.kogito.taskassigning.core.model.solver.realtime.AssignTaskProblemFactChange)12 TaskUtil.fromTaskData (org.kie.kogito.taskassigning.service.util.TaskUtil.fromTaskData)12 AddTaskProblemFactChange (org.kie.kogito.taskassigning.core.model.solver.realtime.AddTaskProblemFactChange)11 TestUtil.mockTaskData (org.kie.kogito.taskassigning.service.TestUtil.mockTaskData)11 ProblemFactChange (org.optaplanner.core.api.solver.ProblemFactChange)11 RemoveTaskProblemFactChange (org.kie.kogito.taskassigning.core.model.solver.realtime.RemoveTaskProblemFactChange)10 AddUserProblemFactChange (org.kie.kogito.taskassigning.core.model.solver.realtime.AddUserProblemFactChange)9 DisableUserProblemFactChange (org.kie.kogito.taskassigning.core.model.solver.realtime.DisableUserProblemFactChange)7 ReleaseTaskProblemFactChange (org.kie.kogito.taskassigning.core.model.solver.realtime.ReleaseTaskProblemFactChange)7 RemoveUserProblemFactChange (org.kie.kogito.taskassigning.core.model.solver.realtime.RemoveUserProblemFactChange)7 UserPropertyChangeProblemFactChange (org.kie.kogito.taskassigning.core.model.solver.realtime.UserPropertyChangeProblemFactChange)7 List (java.util.List)5 BeforeEach (org.junit.jupiter.api.BeforeEach)5 Task (org.kie.kogito.taskassigning.core.model.Task)5 UserDataEvent (org.kie.kogito.taskassigning.service.event.UserDataEvent)5