I have a single planning entity (task), and a single genuine planning variable (timeslot).
The planning variable is a resource that is being assigned to a task, with a course grained time range. When a timeslot is assigned, I need to compute the “actual” time range. This is derived from the timeslot, and additional task fields which tell me how to compute the “actual” time range.
We had what we thought was a working VariableListener, but after turning on TRACKED_FULL_ASSERT I see this error when solving begins:
java.lang.IllegalStateException: VariableListener corruption after completedAction (Initial score calculated):
The entity (Task() assigned to TimeSlot(2024-07-07T08:59:49.956Z to 2024-07-07T09:10:36.337Z: duration PT10M46.381S))'s shadow variable (Task.actualStartTime)'s corrupted value (2024-07-07T18:09:49.927Z) changed to uncorrupted value (2024-07-07T09:00:22.500Z) after all variable listeners were triggered without changes to the genuine variables.
The “corrupted” value is very different from the original time range, so i’m wondering where it could be coming from.
My Task PlanningEntity:
@PlanningVariable(valueRangeProviderRefs = "timeslotRange", allowsUnassigned = true)
private TimeSlot timeslot = null;
private Instant actualStartTime = null;
@ShadowVariable(
sourceVariableName = "timeslot",
variableListenerClass = TimeSlotStartTimeListener.class)
public Instant getActualStartTime() {
return this.actualStartTime;
}
private Instant actualEndTime = null;
@PiggybackShadowVariable(shadowVariableName = "actualStartTime")
public Instant getActualEndTime() {
return this.actualEndTime;
}
My variable listener, TimeSlotStartTimeListener
@Override
public boolean requiresUniqueEntityEvents() { // No idea if this is needed.
return true;
}
private void updateStartAndEndTimes(ScoreDirector<Solution> scoreDirector, Task task) {
scoreDirector.beforeVariableChanged(task, "actualStartTime");
task.setActualStartTime(someStartTimeMath(task)); // Cant show the actual logic here, but it's a pure function.
scoreDirector.afterVariableChanged(task, "actualStartTime");
scoreDirector.beforeVariableChanged(task, "actualEndTime");
task.setActualEndTime(someEndTimeMath(task));
scoreDirector.afterVariableChanged(task, "actualEndTime");
}
@Override
public void beforeVariableChanged(ScoreDirector<Solution> scoreDirector, Task task) {
this.updateStartAndEndTimes(scoreDirector, task);
}
@Override
public void afterVariableChanged(ScoreDirector<Solution> scoreDirector, Task task) {
this.updateStartAndEndTimes(scoreDirector, task);
}
I have no clue if this is set up correctly. The docs for this are kind of all over the place. They jump between examples, and don’t link to any full implementations.