S12 - turned schedule status into enum and some other review comments
All checks were successful
Build, test and publish / verify (push) Successful in 7m6s
All checks were successful
Build, test and publish / verify (push) Successful in 7m6s
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
package nl.agilecc.agiledancers.model.scheduling;
|
||||
|
||||
public enum SchedulingPhase {
|
||||
PREPARING,
|
||||
ATTENDANCE,
|
||||
SPREAD,
|
||||
VARIETY
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package nl.agilecc.agiledancers.model.scheduling;
|
||||
|
||||
/** Progress is observational; intermediate assignments are never published or applied. */
|
||||
public record SchedulingProgress(String phase, boolean feasible) {}
|
||||
public record SchedulingProgress(SchedulingPhase phase, boolean feasible) {}
|
||||
|
||||
@@ -14,6 +14,7 @@ import nl.agilecc.agiledancers.model.scheduling.SchedulingProgress;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingResult;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingSummary;
|
||||
import nl.agilecc.agiledancers.service.model.SchedulingRun;
|
||||
import nl.agilecc.agiledancers.service.model.SchedulingState;
|
||||
import nl.agilecc.agiledancers.service.port.SchedulingEngine;
|
||||
|
||||
/** Owns background tasks and publishes current state independently of any HTTP connection. */
|
||||
@@ -44,7 +45,8 @@ public class SchedulingRuns {
|
||||
executor.submit(() -> calculate(run, input));
|
||||
} catch (final RuntimeException failure) {
|
||||
schools.endScheduling(showId);
|
||||
publish(run.completed("SOLVER_FAILED", "Berekening kon niet starten. Bestaande indeling behouden.", null));
|
||||
publish(run.completed(
|
||||
SchedulingState.SOLVER_FAILED, "Berekening kon niet starten. Bestaande indeling behouden.", null));
|
||||
throw failure;
|
||||
}
|
||||
return run;
|
||||
@@ -86,7 +88,7 @@ public class SchedulingRuns {
|
||||
schools.finishScheduling(run.schoolId(), input, result);
|
||||
completed = latest(run)
|
||||
.completed(
|
||||
result.optimal() ? "OPTIMAL" : "VALID",
|
||||
result.optimal() ? SchedulingState.OPTIMAL : SchedulingState.VALID,
|
||||
result.optimal()
|
||||
? "Optimaal conceptrooster."
|
||||
: "Geldig conceptrooster; optimaliteit niet bewezen.",
|
||||
@@ -94,16 +96,22 @@ public class SchedulingRuns {
|
||||
} catch (final ManagementException failure) {
|
||||
completed = failed(run, failure);
|
||||
} catch (final RuntimeException | LinkageError failure) {
|
||||
completed =
|
||||
latest(run).completed("SOLVER_FAILED", "Berekening mislukt. Bestaande indeling behouden.", null);
|
||||
completed = latest(run)
|
||||
.completed(SchedulingState.SOLVER_FAILED, "Berekening mislukt. Bestaande indeling behouden.", null);
|
||||
}
|
||||
finish(completed);
|
||||
}
|
||||
|
||||
private SchedulingRun failed(final SchedulingRun run, final ManagementException failure) {
|
||||
final String outcome = failure instanceof final SchedulingFailure scheduling
|
||||
? scheduling.getOutcome().name()
|
||||
: (failure.getKind() == ManagementExceptionKind.CONFLICT ? "STALE" : "INVALID_INPUT");
|
||||
final SchedulingState outcome = failure instanceof final SchedulingFailure scheduling
|
||||
? switch (scheduling.getOutcome()) {
|
||||
case INFEASIBLE -> SchedulingState.INFEASIBLE;
|
||||
case NO_SOLUTION -> SchedulingState.NO_SOLUTION;
|
||||
case SOLVER_FAILED -> SchedulingState.SOLVER_FAILED;
|
||||
}
|
||||
: (failure.getKind() == ManagementExceptionKind.CONFLICT
|
||||
? SchedulingState.STALE
|
||||
: SchedulingState.INVALID_INPUT);
|
||||
return latest(run).completed(outcome, failure.getMessage() + " Bestaande indeling behouden.", null);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package nl.agilecc.agiledancers.service.model;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingPhase;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingProgress;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingSummary;
|
||||
|
||||
@@ -13,7 +14,7 @@ public record SchedulingRun(
|
||||
String showName,
|
||||
Instant startedAt,
|
||||
Instant finishedAt,
|
||||
String state,
|
||||
SchedulingState state,
|
||||
SchedulingProgress progress,
|
||||
String message,
|
||||
SchedulingSummary summary) {
|
||||
@@ -25,8 +26,8 @@ public record SchedulingRun(
|
||||
name,
|
||||
Instant.now(),
|
||||
null,
|
||||
"RUNNING",
|
||||
new SchedulingProgress("PREPARING", false),
|
||||
SchedulingState.RUNNING,
|
||||
new SchedulingProgress(SchedulingPhase.PREPARING, false),
|
||||
"Berekening gestart.",
|
||||
null);
|
||||
}
|
||||
@@ -45,7 +46,8 @@ public record SchedulingRun(
|
||||
null);
|
||||
}
|
||||
|
||||
public SchedulingRun completed(final String outcome, final String explanation, final SchedulingSummary scores) {
|
||||
public SchedulingRun completed(
|
||||
final SchedulingState outcome, final String explanation, final SchedulingSummary scores) {
|
||||
return new SchedulingRun(
|
||||
runId,
|
||||
schoolId,
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package nl.agilecc.agiledancers.service.model;
|
||||
|
||||
public enum SchedulingState {
|
||||
RUNNING,
|
||||
OPTIMAL,
|
||||
VALID,
|
||||
STALE,
|
||||
INVALID_INPUT,
|
||||
INFEASIBLE,
|
||||
NO_SOLUTION,
|
||||
SOLVER_FAILED
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingFailureKind;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingInput;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingPhase;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingProgress;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingResult;
|
||||
import nl.agilecc.agiledancers.service.SchedulingFailure;
|
||||
@@ -46,10 +47,11 @@ public class OrToolsSchedulingEngine implements SchedulingEngine {
|
||||
solver.getParameters().setNumSearchWorkers(1).setRandomSeed(0);
|
||||
SchedulingResult best = null;
|
||||
final List<LinearExpr> objectives = List.of(attendance, LinearExpr.term(vars.spread(), 1), variety);
|
||||
final List<String> phases = List.of("ATTENDANCE", "SPREAD", "VARIETY");
|
||||
final List<SchedulingPhase> phases =
|
||||
List.of(SchedulingPhase.ATTENDANCE, SchedulingPhase.SPREAD, SchedulingPhase.VARIETY);
|
||||
for (int stage = 0; stage < objectives.size(); stage++) {
|
||||
final LinearExpr objective = objectives.get(stage);
|
||||
final String phase = phases.get(stage);
|
||||
final SchedulingPhase phase = phases.get(stage);
|
||||
observer.accept(new SchedulingProgress(phase, best != null));
|
||||
final double remaining = (deadline - System.nanoTime()) / 1_000_000_000.0;
|
||||
if (remaining <= 0) {
|
||||
|
||||
@@ -2,15 +2,16 @@ package nl.agilecc.agiledancers.solver;
|
||||
|
||||
import com.google.ortools.sat.CpSolverSolutionCallback;
|
||||
import java.util.function.Consumer;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingPhase;
|
||||
import nl.agilecc.agiledancers.model.scheduling.SchedulingProgress;
|
||||
|
||||
/** Publish only the first feasible solution in a phase, avoiding an event per solver improvement. */
|
||||
public class SchedulingSolutionObserver extends CpSolverSolutionCallback {
|
||||
private final Consumer<SchedulingProgress> observer;
|
||||
private final String phase;
|
||||
private final SchedulingPhase phase;
|
||||
private boolean reported;
|
||||
|
||||
public SchedulingSolutionObserver(final Consumer<SchedulingProgress> observer, final String phase) {
|
||||
public SchedulingSolutionObserver(final Consumer<SchedulingProgress> observer, final SchedulingPhase phase) {
|
||||
this.observer = observer;
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user