在一个简单的排班问题中,我希望OptaPlanner在我的日程安排中返回“空缺”,以便在没有任何员工具备所需技能的情况下,发现某些班次。
假设3个基本类,并且我只想用HardMediumSoftScore.ONE_SOFT
惩罚未发现的班次。
如何写这样的约束?
Employee.java
public class Employee {
private long id = 0L;
private List<Skill> skills;
Shift.java
@PlanningEntity
public class Shift {
private RequiredSkills
@PlanningVariable(valueRangeProviderRefs = "employeeRange")
private Employee employee;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
private OffsetDateTime start;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
private OffsetDateTime end;
Schedule.java
@PlanningSolution
public class Schedule {
@PlanningEntityCollectionProperty
private List<Shift> shifts;
@ProblemFactCollectionProperty
@ValueRangeProvider(id = "employeeRange")
private List<Employee> employees;
进一步假设一个简单的ConstraintProvider
public class ScheduleConstraints implements ConstraintProvider {
@Override
public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
return new Constraint[]{
requiredSkill(constraintFactory),
};
}
private Constraint requiredSkill(ConstraintFactory constraintFactory) {
return constraintFactory.from(Shift.class)
.filter(shift -> {
return !shift.getEmployee().hasSkills(shift.getSkills());
}).penalize("Required skill", HardMediumSoftScore.ONE_HARD);
}
这是我的尝试。好吗?
private Constraint allShiftsMustBeCovered(ConstraintFactory constraintFactory) {
return constraintFactory.fromUnfiltered(Shift.class)
.filter(shift-> {
return shift.getEmployee().getId() == 0L;
}).penalize("All shifts must be covered", HardMediumSoftScore.ONE_SOFT);
}
除非我没有记错,否则您的代码将抛出NullPointerException
。如果未涵盖班次,则Shift.employee
为null
。您的约束条件假设Employee
永远不会是null
-您必须解决此问题,也许是这样的:
// I added a null check as from() only excludes uninitialized entities.
// If employee is nullable, from() will still return Shifts with null employees.
private Constraint requiredSkill(ConstraintFactory constraintFactory) {
return constraintFactory.from(Shift.class)
.filter(shift -> shift.getEmployee() != null)
.filter(shift -> {
return !shift.getEmployee().hasSkills(shift.getSkills());
}).penalize("Required skill", HardMediumSoftScore.ONE_HARD);
}
private Constraint allShiftsMustBeCovered(ConstraintFactory constraintFactory) {
return constraintFactory.fromUnfiltered(Shift.class)
.filter(shift-> shift.getEmployee() == null ||
shift.getEmployee().getId() == 0L
).penalize("All shifts must be covered", HardMediumSoftScore.ONE_SOFT);
}
由于null
在此处是有效值,因此请确保Shift.employee
variable is nullable。这种方法在文档中称为过度约束的计划。