尽管所有其他要素(模式)(包括请求)都在更新,但我没有看到轮班分配提前正确更新。我正在为12名员工运行14天的轮班花名册。 11必须每两周工作9天1必须每两周工作5天。 (104班)。谁能看到我的代码和/或方法有任何明显的错误。仅供参考,我尝试了所有不同的加权方法。
我已经基于网球示例实现了负载平衡解决方案。这对于所有全职工作9个班次(108个班次)的员工来说非常有用。所有元素均正确更新。请求,不需要的模式等,并且进展良好。维护所有模式,请求和轮班要求
private void advancePlanningWindowStart() {
logger.info("Advancing planningWindowStart.");
if (solutionBusiness.isSolving()) {
JOptionPane.showMessageDialog(this.getTopLevelAncestor(),
"The GUI does not support this action yet during solving.\nOptaPlanner itself does support it.\n"
+ "\nTerminate solving first and try again.",
"Unsupported in GUI", JOptionPane.INFORMATION_MESSAGE);
return;
}
doProblemFactChange(scoreDirector -> {
NurseRoster nurseRoster = scoreDirector.getWorkingSolution();
NurseRosterParametrization nurseRosterParametrization = nurseRoster
.getNurseRosterParametrization();
List<ShiftDate> shiftDateList = nurseRoster.getShiftDateList();
Shift oldLastShift = nurseRoster.getShiftList()
.get(nurseRoster.getShiftList().size() - 1);
long shiftId = oldLastShift.getId() + 1L;
int shiftIndex = oldLastShift.getIndex() + 1;
ShiftDate oldLastShiftDate = shiftDateList
.get(shiftDateList.size() - 1);
long shiftDateId = (oldLastShiftDate.getId() + 1L);
int shiftDayIndex = (oldLastShiftDate.getDayIndex() + 1);
scoreDirector
.beforeProblemPropertyChanged(nurseRosterParametrization);
// Update to get the first day along with adding 14 days to the run
LocalDate startDate = (oldLastShiftDate.getDate().plusDays(1));
LocalDate endDate = (oldLastShiftDate.getDate().plusDays(14));
int maxDayIndex = Math.toIntExact(DAYS.between(startDate, endDate));
int shiftDateSize = maxDayIndex + 1;
List<ShiftDate> newshiftdateList = new ArrayList<>(shiftDateSize);
shiftDateMap = new HashMap<>(shiftDateSize);
LocalDate date = startDate;
for (int i = 0; i < shiftDateSize; i++) {
ShiftDate shiftDate = new ShiftDate();
shiftDate.setId(shiftDateId);
shiftDate.setDayIndex(shiftDayIndex);
shiftDate.setDate(date);
shiftDate.setShiftList(new ArrayList<>());
shiftDateMap.put(date, shiftDate);
shiftDateId++;
shiftDayIndex++;
date = date.plusDays(1);
nurseRoster.getShiftDateList().add(shiftDate);
newshiftdateList.add(shiftDate);
scoreDirector.afterProblemFactAdded(shiftDate);
}
List<Skill> skillList;
List<Skill> skillElementList = (List<Skill>) nurseRoster
.getSkillList();
skillList = new ArrayList<>(skillElementList.size());
skillMap = new HashMap<>(skillElementList.size());
for (Skill element : skillElementList) {
Skill skill = new Skill();
long skillid = element.getId();
skill.setId(skillid);
skill.setCode(element.getCode());
Skill skillnew = scoreDirector.lookUpWorkingObject(skill);
skillList.add(skillnew);
if (skillMap.containsKey(skillnew.getCode())) {
throw new IllegalArgumentException(
"There are 2 skills with the same code ("
+ skill.getCode() + ").");
}
skillMap.put(skillnew.getCode(), skillnew);
}
List<Contract> contractElementList = (List<Contract>) nurseRoster
.getContractList();
List<Contract> contractList = new ArrayList<>(
contractElementList.size());
contractMap = new HashMap<>(contractElementList.size());
for (Contract element : contractElementList) {
Contract contract = new Contract();
long Id = element.getId();
contract.setId(Id);
contract.setCode(element.getCode());
contract.setDescription(element.getDescription());
WeekendDefinition weekend = element.getWeekendDefinition();
contract.setWeekendDefinition(weekend);
contract.setContractLineList(new ArrayList<ContractLine>());
Contract contractnew = scoreDirector
.lookUpWorkingObject(contract);
contractMap.put(contractnew.getCode(), contractnew);
contractList.add(contractnew);
}
List<Employee> employeeElementList = (List<Employee>) nurseRoster
.getEmployeeList();
Employee oldLastEmployee = nurseRoster.getEmployeeList()
.get(nurseRoster.getEmployeeList().size() - 1);
long empId = oldLastEmployee.getId() + 1L;
List<Employee> employeeList = new ArrayList<>(
employeeElementList.size());
employeeMap = new HashMap<>(employeeElementList.size());
for (Employee element : employeeElementList) {
Employee employee = new Employee();
String name = element.getName();
String code = element.getCode();
employee.setId(empId);
empId++;
Contract c = scoreDirector
.lookUpWorkingObject(element.getContract());
employee.setCode(code);
employee.setContract(c);
employee.setName(name);
employeeList.add(employee);
employeeMap.put(employee.getName(), employee);
scoreDirector.afterProblemFactAdded(employee);
}
List<ShiftTypeSkillRequirement> coverRequirementElementList = (List<ShiftTypeSkillRequirement>) nurseRoster
.getShiftTypeSkillRequirementList();
List<ShiftType> shiftTypeElementList = (List<ShiftType>) nurseRoster
.getShiftTypeList();
List<ShiftType> shiftTypeList = new ArrayList<>(
shiftTypeElementList.size());
shiftTypeMap = new HashMap<>(shiftTypeElementList.size());
int index = 0;
long shiftTypeSkillRequirementId = 0L;
List<ShiftTypeSkillRequirement> shiftTypeSkillRequirementList = new ArrayList<>(
shiftTypeElementList.size() * 2);
for (ShiftType shiftelement : shiftTypeElementList) {
ShiftType shiftType = new ShiftType();
long Id = shiftelement.getId();
shiftType.setId(Id);
shiftType.setCode(shiftelement.getCode());
shiftType.setIndex(index);
String startTimeString = shiftelement.getStartTimeString();
shiftType.setStartTimeString(startTimeString);
String endTimeString = shiftelement.getEndTimeString();
shiftType.setEndTimeString(endTimeString);
shiftType
.setNight(startTimeString.compareTo(endTimeString) > 0);
shiftType.setDescription(shiftelement.getDescription());
ShiftType shiftTypenew = scoreDirector
.lookUpWorkingObject(shiftType);
for (ShiftTypeSkillRequirement skillElement : coverRequirementElementList) {
ShiftTypeSkillRequirement shiftTypeSkillRequirement = new ShiftTypeSkillRequirement();
shiftTypeSkillRequirement
.setId(shiftTypeSkillRequirementId);
ShiftType shiftTypen = scoreDirector
.lookUpWorkingObject(shiftType);
shiftTypeSkillRequirement.setShiftType(shiftTypen);
Skill skill = skillMap
.get(skillElement.getSkill().getCode());
Skill skillnew = scoreDirector.lookUpWorkingObject(skill);
if (skillnew == null) {
throw new IllegalArgumentException("The skill ("
+ skillElement.getSkill().getCode()
+ ") of shiftType (" + shiftType.getCode()
+ ") does not exist.");
}
shiftTypeSkillRequirement.setSkill(skillnew);
shiftTypeSkillRequirementList
.add(shiftTypeSkillRequirement);
shiftTypeSkillRequirementId++;
}
shiftTypeList.add(shiftTypenew);
if (shiftTypeMap.containsKey(shiftTypenew.getCode())) {
throw new IllegalArgumentException(
"There are 2 shiftTypes with the same code ("
+ shiftTypenew.getCode() + ").");
}
shiftTypeMap.put(shiftTypenew.getCode(), shiftTypenew);
index++;
}
nurseRoster.setShiftTypeList(shiftTypeList);
nurseRoster.setShiftTypeSkillRequirementList(
shiftTypeSkillRequirementList);
int shiftListSize = shiftDateMap.size() * shiftTypeList.size();
List<Shift> shiftList1 = new ArrayList<>(shiftListSize);
dateAndShiftTypeToShiftMap = new HashMap<>(shiftListSize);
dayOfWeekAndShiftTypeToShiftListMap = new HashMap<>(
7 * shiftTypeList.size());
for (ShiftDate shiftDate : newshiftdateList) {
for (ShiftType shiftType : shiftTypeList) {
Shift shift = new Shift();
shift.setId(shiftId);
shift.setShiftDate(shiftDate);
shiftDate.getShiftList().add(shift);
ShiftType type = scoreDirector
.lookUpWorkingObject(shiftType);
shift.setShiftType(type);
shift.setIndex(shiftIndex);
shift.setRequiredEmployeeSize(0); // Filled in later
shiftList1.add(shift);
dateAndShiftTypeToShiftMap.put(
Pair.of(shiftDate.getDate(), type.getCode()),
shift);
addShiftToDayOfWeekAndShiftTypeToShiftListMap(shiftDate,
type, shift);
shiftId++;
shiftIndex++;
nurseRoster.getShiftList().add(shift);
scoreDirector.afterProblemFactAdded(shift);
}
}
List<DayOffRequest> dayOffRequestList;
List<DayOffDate> dayOffElementList = rosterService.listDayOffDate();
dayOffRequestList = new ArrayList<>(dayOffElementList.size());
long id = 0L;
for (DayOffDate element : dayOffElementList) {
DayOffRequest dayOffRequest = new DayOffRequest();
dayOffRequest.setId(id);
id++;
int weight = element.getWeight();
LocalDate shiftDate = element.getDate();
ShiftDate dateoff = shiftDateMap.get(shiftDate);
Employee employee = element.getEmployee();
Employee workingEmployee = scoreDirector
.lookUpWorkingObject(employee);
dayOffRequest.setWeight(weight);
dayOffRequest.setEmployee(workingEmployee);
dayOffRequest.setShiftDate(dateoff);
dayOffRequestList.add(dayOffRequest);
workingEmployee.getDayOffRequestMap().put(dateoff,
dayOffRequest);
nurseRoster.getDayOffRequestList().add(dayOffRequest);
}
List<DayOnRequest> dayOnRequestList;
List<DayOnDate> dayOnElementList1 = rosterService.listDayOnDate();
long onid = 0L;
dayOnRequestList = new ArrayList<>(dayOnElementList1.size());
for (DayOnDate element : dayOnElementList1) {
DayOnRequest dayOnRequest = new DayOnRequest();
int weight = element.getWeight();
LocalDate localshiftDate = element.getDate();
ShiftDate dateon = shiftDateMap.get(localshiftDate);
Employee employee = element.getEmployee();
Employee workingEmployee = scoreDirector
.lookUpWorkingObject(employee);
dayOnRequest.setId(onid);
onid++;
dayOnRequest.setWeight(weight);
dayOnRequest.setEmployee(workingEmployee);
dayOnRequest.setShiftDate(dateon);
dayOnRequestList.add(dayOnRequest);
workingEmployee.getDayOnRequestMap().put(dateon, dayOnRequest);
nurseRoster.getDayOnRequestList().add(dayOnRequest);
}
List<ShiftOffRequest> shiftOffRequestList;
List<ShiftOffDate> shiftOffElementList = (List<ShiftOffDate>) rosterService
.listShiftOffDate();
shiftOffRequestList = new ArrayList<>(shiftOffElementList.size());
for (ShiftOffDate element : shiftOffElementList) {
ShiftOffRequest shiftOffRequest = new ShiftOffRequest();
long ShiftonId = element.getId();
int weight = element.getWeight();
Employee employee = element.getEmployee();
Employee workingEmployee = scoreDirector
.lookUpWorkingObject(employee);
LocalDate date1 = element.getDate();
String shiftcode = element.getShiftType().getCode();
Shift shift = dateAndShiftTypeToShiftMap
.get(Pair.of(date1, shiftcode));
shiftOffRequest.setId(ShiftonId);
shiftOffRequest.setEmployee(workingEmployee);
shiftOffRequest.setShift(shift);
shiftOffRequest.setWeight(weight);
shiftOffRequestList.add(shiftOffRequest);
workingEmployee.getShiftOffRequestMap().put(shift,
shiftOffRequest);
nurseRoster.setShiftOffRequestList(shiftOffRequestList);
}
List<ShiftOnRequest> shiftOnRequestList;
List<ShiftOnDate> shiftOnElementList = (List<ShiftOnDate>) rosterService
.listShiftOnDate();
shiftOnRequestList = new ArrayList<>(shiftOnElementList.size());
for (ShiftOnDate element : shiftOnElementList) {
ShiftOnRequest shiftOnRequest = new ShiftOnRequest();
long ShiftonId = element.getId();
int weight = element.getWeight();
Employee employee = element.getEmployee();
Employee workingEmployee = scoreDirector
.lookUpWorkingObject(employee);
LocalDate date1 = element.getDate();
String shiftcode = element.getShiftType().getCode();
Shift shift = dateAndShiftTypeToShiftMap
.get(Pair.of(date1, shiftcode));
shiftOnRequest.setId(ShiftonId);
shiftOnRequest.setEmployee(workingEmployee);
shiftOnRequest.setShift(shift);
shiftOnRequest.setWeight(weight);
shiftOnRequestList.add(shiftOnRequest);
workingEmployee.getShiftOnRequestMap().put(shift,
shiftOnRequest);
nurseRoster.setShiftOnRequestList(shiftOnRequestList);
}
List<CoverRequirements> coverRequirementElementList1 = (List<CoverRequirements>) rosterService
.listCoverRequirements();
for (CoverRequirements element : coverRequirementElementList1) {
String type = element.getShiftType().getCode();
ShiftType shiftTypen = shiftTypeMap.get(type);
DayOfWeek day = element.getDayOfWeek();
int req = element.getRequiredEmployeesize();
Pair<DayOfWeek, ShiftType> key = Pair.of(day, shiftTypen);
List<Shift> shiftList = dayOfWeekAndShiftTypeToShiftListMap
.get(key);
for (Shift shift : shiftList) {
shift.setRequiredEmployeeSize(
shift.getRequiredEmployeeSize() + req);
}
}
List<ShiftAssignment> shiftAssignmentList = new ArrayList<>(
shiftList1.size());
long shiftAssignmentId = nurseRoster.getShiftAssignmentList()
.get(nurseRoster.getShiftAssignmentList().size() - 1)
.getId() + 1L;
for (Shift shift : shiftList1) {
for (int i = 0; i < shift.getRequiredEmployeeSize(); i++) {
ShiftAssignment newShiftAssignment = new ShiftAssignment();
newShiftAssignment.setId(shiftAssignmentId);
shiftAssignmentId++;
newShiftAssignment.setShift(shift);
newShiftAssignment.setIndexInShift(i);
shiftAssignmentList.add(newShiftAssignment);
nurseRoster.getShiftAssignmentList()
.add(newShiftAssignment);
scoreDirector.afterEntityAdded(newShiftAssignment);
}
}
// This should move the planning window
nurseRosterParametrization
.setFirstShiftDate(newshiftdateList.get(0));
nurseRosterParametrization.setLastShiftDate(
newshiftdateList.get(newshiftdateList.size() - 1));
nurseRosterParametrization
.setPlanningWindowStart(newshiftdateList.get(0));
nurseRoster
.setNurseRosterParametrization(nurseRosterParametrization);
scoreDirector
.afterProblemPropertyChanged(nurseRosterParametrization);
// scoreDirector.triggerVariableListeners();
}, true);
}
private void addShiftToDayOfWeekAndShiftTypeToShiftListMap(ShiftDate shiftDate, ShiftType shiftType, Shift shift) {
Pair<DayOfWeek, ShiftType> key = Pair.of(shiftDate.getDayOfWeek(), shiftType);
List<Shift> dayOfWeekAndShiftTypeToShiftList = dayOfWeekAndShiftTypeToShiftListMap.computeIfAbsent(key,
k -> new ArrayList<>((shiftDateMap.size() + 6) / 7));
dayOfWeekAndShiftTypeToShiftList.add(shift);
}
下面是一个带有完全断言的小调试摘要,没有显示任何错误 步骤(122),花费的时间(102920),得分(0hard / 1342soft),新的最高得分(0hard / 1342soft),接受/选择的移动计数(800/884),选择的移动(2019-07-20 / E {Reece班福德} <-> 2019-07-20 / D {David Hemmings})。 17:53:37.746 [l-3-thread-2]调试LS步骤(123),花费的时间(103652),得分(0hard / 1342soft),最佳得分(0hard / 1342soft),接受/选择的移动计数(800 / 874),选择了动作([[2019-07-23 / D,2019-07-24 / E,2019-07-25 / E] {?-> Alana Gill},[2019-07-23 / E,2019 -07-24 / E,2019-07-25 / E,2019-07-26 / E,2019-07-27 / D,2019-07-28 / E] {?-> David Hemmings}]。 17:53:38.513 [l-3-thread-2]调试LS步骤(124),花费的时间(104419),得分(0hard / 1342soft),最佳得分(0hard / 1342soft),接受/选择的移动计数(800 / 887),选择了举动([[2019-07-15 / N] {?-> Sophie Dreves},[2019-07-15 / N] {?-> Reece Bamford}])。 17:53:39.337 [l-3-thread-2]调试LS步骤(125),花费的时间(105243),得分(0hard / 1342soft),最佳得分(0hard / 1342soft),接受/选择的移动计数(800 / 925),然后选择移动(2019-07-22 / E {Anthony Phan} <-> 2019-07-22 / D {Michael C})。 17:53:40.121 [l-3-thread-2]调试LS步骤(126),花费的时间(106027),得分(0hard / 1342soft),最佳得分(0hard / 1342soft),接受/选择的移动计数(800 / 943),采取了行动(2019-07-15 / L {David Hemmings-> Clayton Fletcher})。 17:53:40.972 [l-3-thread-2]调试LS步骤(127),花费的时间(106878),得分(0hard / 1342soft),最佳得分(0hard / 1342soft),接受/选择的移动计数(800 / 932),选择了举动(2019-07-22 / E {Sophie Dreves} <-> 2019-07-22 / L {Karen Ahrens})。 17:53:41.815 [l-3-thread-2]调试LS步骤(128),花费的时间(107721),得分(0hard / 1342soft),最佳得分(0hard / 1342soft),接受/选择的移动计数(800 / 932),已选择移动([[2019-07-21 / E,2019-07-22 / E,2019-07-23 / D,2019-07-24 / L,2019-07-25 / D,2019- 07-26 / E] {?-> Michael C},[2019-07-21 / D,2019-07-22 / E,2019-07-23 / E,2019-07-24 / L,2019-07 -25 / L,2019-07-26 / N] {?-> Karen Ahrens}])。 17:53:42.635 [l-3-thread-2]调试LS步骤(129),花费的时间(108541),得分(0hard / 1342soft),最佳得分(0hard / 1342soft),接受/选择的移动计数(800 / 950),选择了动作([[2019-07-19 / D,2019-07-20 / E,2019-07-21 / L] {?-> Gemma Nevin},[2019-07-19 / D,2019 -07-20 / L] {?-> David Hemmings}])。 17:53:43.481 [l-3-thread-2]调试LS步骤(130),花费的时间(109387),得分(0hard / 1343soft),新的最佳得分(0hard / 1343soft),接受/选择的移动计数(800 / 950),已选择的举动([[2019-07-15 / D,2019-07-16 / E,2019-07-17 / E,2019-07-18 / E,2019-07-19 / E] { ?-> David Hemmings},[2019-07-16 / N] {?-> Isabella},[2019-07-19 / D,2019-07-20 / L] {?-> Isabella}])。 17:53:44.290 [l-3-thread-2]调试LS步骤(131),花费的时间(110196),得分(0hard / 1343soft),最佳得分(0hard / 1343soft),接受/选择的移动计数(800 / 944),选择了举动(2019-07-28 / E {David Hemmings} <-> 2019-07-28 / L {Anthony Phan})。 17:53:45.053 [l-3-thread-2]调试LS步骤(132),花费的时间(110959),得分(0hard / 1343soft),最佳得分(0hard / 1343soft),接受/选择的移动计数(800 / 919),选择了动作([[2019-07-15 / L,2019-07-16 / L,2019-07-17 / N] {?-> Lisa Coull},[2019-07-15 / L,2019 -07-16 / L,2019-07-17 / N] {?-> Clayton Fletcher}])。 17:53:45.857 [l-3-thread-2]调试LS步骤(133),花费的时间(111763),得分(0hard / 1343soft),最佳得分(0hard / 1343soft),接受/选择的移动计数(800 / 936),选择了移动(2019-07-19 / N {Anthony Phan} <-> 2019-07-19 / N {Sophie Dreves})。 17:53:46.638 [l-3-thread-2]调试LS步骤(134),花费的时间(112544),得分(0hard / 1343soft),最佳得分(0hard / 1343soft),接受/选择的移动计数(800 / 952),选择了移动(2019-07-23 / E {David Hemmings} <-> 2019-07-23 / D {Alana Gill})。 17:53:47.437 [l-3-thread-2]调试LS步骤(135),花费的时间(113343),得分(0hard / 1343soft),最佳得分(0hard / 1343soft),接受/选择的移动计数(800 / 926),选择了招式([[2019-07-15 / N] {?-> Sophie Dreves},[2019-07-15 / N] {?-> Reece Bamford}])。 17:53:48.253 [l-3-thread-2]调试LS步骤(136),花费的时间(114159),得分(0hard / 1343soft),最佳得分(0hard / 1343soft),接受/选择的移动计数(800 / 963),选择了举动([[2019-07-27 / E,2019-07-28 / E] {?-> Isabella},[2019-07-27 / N,2019-07-28 / N] {? -> Sophie Dreves}]。 17:53:49.035 [l-3-thread-2]调试LS步骤(137),花费的时间(114941),得分(0hard / 1343soft),最佳得分(0hard / 1343soft),接受/选择的移动计数(800 / 964),选择了举动(2019-07-17 / N {Lisa Coull} <-> 2019-07-17 / N {Clayton Fletcher})。