SolverController.java 4.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. package fr.jaquin.bdlg.planner;
  2. import java.util.UUID;
  3. import java.util.concurrent.ExecutionException;
  4. import java.util.stream.Collector;
  5. import java.util.stream.Collectors;
  6. import fr.jaquin.bdlg.planner.domain.Assignement;
  7. import fr.jaquin.bdlg.planner.domain.MealAssignement;
  8. import fr.jaquin.bdlg.planner.domain.MealSlot;
  9. import fr.jaquin.bdlg.planner.domain.Planning;
  10. import fr.jaquin.bdlg.planner.domain.PlanningInput;
  11. import fr.jaquin.bdlg.planner.domain.PlanningSolution;
  12. import org.optaplanner.core.api.score.ScoreExplanation;
  13. import org.optaplanner.core.api.score.ScoreManager;
  14. import org.optaplanner.core.api.score.buildin.hardmediumsoft.HardMediumSoftScore;
  15. import org.optaplanner.core.api.solver.SolverJob;
  16. import org.optaplanner.core.api.solver.SolverManager;
  17. import org.springframework.beans.factory.annotation.Autowired;
  18. import org.springframework.web.bind.annotation.CrossOrigin;
  19. import org.springframework.web.bind.annotation.PostMapping;
  20. import org.springframework.web.bind.annotation.RequestBody;
  21. import org.springframework.web.bind.annotation.RequestMapping;
  22. import org.springframework.web.bind.annotation.RestController;
  23. @RestController
  24. @RequestMapping("/planning")
  25. public class SolverController {
  26. @Autowired
  27. private SolverManager<Planning, UUID> solverManager;
  28. @Autowired
  29. private ScoreManager<Planning, HardMediumSoftScore> scoreManager;
  30. private Collector<CharSequence, ?, String> listCollector = Collectors.joining(",", "[", "]");
  31. @CrossOrigin(origins = "http://localhost:8081")
  32. @PostMapping("/solve")
  33. public PlanningSolution solve(@RequestBody PlanningInput inputs) {
  34. Planning problem = inputs.generatePlanningProblem();
  35. UUID problemId = UUID.randomUUID();
  36. // Submit the problem to start solving
  37. SolverJob<Planning, UUID> solverJob = solverManager.solve(problemId, problem);
  38. Planning solution;
  39. try {
  40. // Wait until the solving ends
  41. solution = solverJob.getFinalBestSolution();
  42. } catch (InterruptedException | ExecutionException e) {
  43. throw new IllegalStateException("Solving failed.", e);
  44. }
  45. ScoreExplanation<Planning, HardMediumSoftScore> explanation =
  46. scoreManager.explainScore(solution);
  47. PlanningSolution output = PlanningSolution.from(solution);
  48. output.setExplanation(stringifyExplanation(explanation));
  49. System.out.println(explanation.getSummary());
  50. return output;
  51. }
  52. private String stringifyExplanation(ScoreExplanation<Planning, HardMediumSoftScore> explanation) {
  53. return explanation.getConstraintMatchTotalMap().values().stream()
  54. // Return only Hard Constraint
  55. .filter(predicate -> predicate.getScore().getHardScore() < 0)
  56. // Create a dictionnary with the name of the constraint as key
  57. .map(constraint -> "\"" + constraint.getConstraintName() + "\":"
  58. // Populate the value with an array of justification objects
  59. + constraint.getConstraintMatchSet().stream()
  60. // Filter pair that have a negative impact on the score
  61. .filter(matchElt -> matchElt.getScore().getHardScore() < 0)
  62. .map(match -> match.getJustificationList().stream()
  63. .map(elt -> stringifyConstraint(elt)).collect(listCollector))
  64. .collect(listCollector))
  65. .collect(Collectors.joining(",", "{", "}"));
  66. }
  67. private String stringifyConstraint(Object val) {
  68. if (val instanceof Assignement) {
  69. Assignement casted = (Assignement) val;
  70. return "{\"type\":\"Assignement\",\"slotId\":\"" + casted.getSlot().getId()
  71. + "\",\"volonteerId\":" + casted.getVolonteer().getId().toString() + "}";
  72. }
  73. if (val instanceof MealAssignement) {
  74. MealAssignement casted = (MealAssignement) val;
  75. return "{\"type\":\"MealAssignement\",\"slotId\":\"" + casted.getMealSlot().getId()
  76. + "\",\"volonteerId\":" + casted.getVolonteer().getId().toString() + "}";
  77. }
  78. if (val instanceof MealSlot) {
  79. MealSlot casted = (MealSlot) val;
  80. return "{\"type\":\"MealSlot\",\"slotId\":\"" + casted.getId() + "\"}";
  81. }
  82. return "\"" + val.getClass().getName() + "\"";
  83. }
  84. }