Currently I'm using adjustedEndTime to keep track of when end time was updated in @PlanningEntity class
@ShadowVariable(variableListenerClass = TaskChangeListener.class, sourceVariableName = "previousTask")
@ShadowVariable(variableListenerClass = TaskChangeListener.class, sourceVariableName = "adjustEndTime")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime startTime;
@PiggybackShadowVariable(shadowVariableName = "startTime")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime endTime;
@JsonIgnore
@ShadowVariable(variableListenerClass = TaskChangeListener.class, sourceVariableName = "machine")
@ShadowVariable(variableListenerClass = TaskChangeListener.class, sourceVariableName = "previousTask")
private LocalTime adjustEndTime;
@PiggybackShadowVariable(shadowVariableName = "startTime")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime endTimePlusChangeOver;
In variable listener class, I set it to update end time if it is bewteen break. I set adjustEndTime here for the next start time to update.
public Map<String, LocalTime> skipBreakTime(ScoreDirector score, Task task, LocalTime start, LocalTime end, LocalTime endWithChangeover, BreakTime breakTime) {
Map<String, LocalTime> planningTime = new HashMap<>();
Duration breakDuration = Duration.between(breakTime.getFromTime(), breakTime.getToTime());
// Case 1: Task starts before break time and ends during break time
if (start.isBefore(breakTime.getFromTime())
&& end.isAfter(breakTime.getFromTime())
&& (end.isBefore(breakTime.getToTime()) || end.equals(breakTime.getToTime()) ) ) {
end = end.plus(breakDuration);
endWithChangeover = endWithChangeover.plus(breakDuration);
score.beforeVariableChanged(task, "adjustEndTime");
task.setAdjustEndTime(end);
score.afterVariableChanged(task, "adjustEndTime");
// Case 2: Task starts during the break time
} else if (!start.isBefore(breakTime.getFromTime())
//&& start.isAfter(task.getPreviousTask().getEndTimePlusChangeOver().toLocalTime())
&& !start.isAfter(breakTime.getToTime())) {
start = breakTime.getToTime();
end = task.calculateEndTime(start);
// Case 3: Task starts before and ends after the break time
} else if (start.isBefore(breakTime.getFromTime()) && end.isAfter(breakTime.getToTime())) {
end = end.plus(breakDuration);
score.beforeVariableChanged(task, "adjustEndTime");
task.setAdjustEndTime(end);
score.afterVariableChanged(task, "adjustEndTime");
}
// log.info(task.getPreviousTask().getMachine().getMachine_name() + " " + task.getMachine().getMachine_name());
planningTime.put("startTime", start);
planningTime.put("endTime", end);
return planningTime;
}
After the solving ends, I got error. Before , when I mistakenly add at to cas 'Start time between break time, it runs no have no error.
2024-09-14 11:20:58 ERROR a.t.s.c.i.s.DefaultSolverManager - Solving failed for problemId (d81ec522-24cd-4b0e-bf6c-7679f11ba30b).
java.util.ConcurrentModificationException: null
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1096)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:1050)
at ai.timefold.solver.core.impl.util.ListBasedScalingOrderedSet$1.next(ListBasedScalingOrderedSet.java:64)
at ai.timefold.solver.core.impl.domain.variable.listener.support.AbstractNotifiable.triggerAllNotifications(AbstractNotifiable.java:86)
at ai.timefold.solver.core.impl.domain.variable.listener.support.VariableListenerSupport.triggerVariableListenersInNotificationQueues(VariableListenerSupport.java:224)
at ai.timefold.solver.core.impl.score.director.AbstractScoreDirector.triggerVariableListeners(AbstractScoreDirector.java:314)
at ai.timefold.solver.core.impl.heuristic.move.AbstractMove.doMoveOnly(AbstractMove.java:28)
at ai.timefold.solver.core.impl.score.director.AbstractScoreDirector.doAndProcessMove(AbstractScoreDirector.java:264)
at ai.timefold.solver.core.impl.localsearch.decider.LocalSearchDecider.doMove(LocalSearchDecider.java:117)
at ai.timefold.solver.core.impl.localsearch.decider.LocalSearchDecider.decideNextStep(LocalSearchDecider.java:98)
at ai.timefold.solver.core.impl.localsearch.DefaultLocalSearchPhase.solve(DefaultLocalSearchPhase.java:72)
at ai.timefold.solver.core.impl.solver.AbstractSolver.runPhases(AbstractSolver.java:82)
at ai.timefold.solver.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:200)
at ai.timefold.solver.core.impl.solver.DefaultSolverJob.call(DefaultSolverJob.java:119)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1570)
2024-09-14 11:20:58 INFO c.w.c.controller.TimetableController - /solve failed for plan d81ec522-24cd-4b0e-bf6c-7679f11ba30b
java.util.concurrent.ExecutionException: java.lang.IllegalStateException: Solving failed for problemId (d81ec522-24cd-4b0e-bf6c-7679f11ba30b).
at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
at ai.timefold.solver.core.impl.solver.DefaultSolverJob.getFinalBestSolution(DefaultSolverJob.java:209)
at com.win.cuttingsop.controller.TimetableController.solve(TimetableController.java:69)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:255)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:188)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:926)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:831)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:389)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:904)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1741)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
at java.base/java.lang.Thread.run(Thread.java:1570)
Caused by: java.lang.IllegalStateException: Solving failed for problemId (d81ec522-24cd-4b0e-bf6c-7679f11ba30b).
at ai.timefold.solver.core.impl.solver.DefaultSolverJob.call(DefaultSolverJob.java:125)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
... 1 common frames omitted
Caused by: java.util.ConcurrentModificationException: null
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1096)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:1050)
at ai.timefold.solver.core.impl.util.ListBasedScalingOrderedSet$1.next(ListBasedScalingOrderedSet.java:64)
at ai.timefold.solver.core.impl.domain.variable.listener.support.AbstractNotifiable.triggerAllNotifications(AbstractNotifiable.java:86)
Anyone know how can I notify start time to change after endtime changes?
When I just use shadow variable for start time, piggyback for end time, each time end time move 1 hour, the next task start at case 2 (next task start after break time is over), and overlap the previous task. It becomes:
break time: 11:30 - 12:30 (if start between break, move start to end of break. If end between break, add 60 more minutes)
before skip break time method: Task 2: start: 11:29 - end: 11:55
after skip break time method: Task 2: start : 11:29 - end: 12:55
finale result: Task 2: start : 11:29 - end: 12:55
next task by same employee: Task 5: start: 12:30 - end 12: 50 (overlap)
next task by same employee: Task 5: start: 12:50 - end 13: 20
Without looking deeply into your problem, I'm going to recommend you use the recently released cascading shadow variable to update your tail chains. This is a significantly simplified API (as compared to variable listeners) and is likely to remove whatever problem that arose in your variable listener.