我正在OptaPlanner中实现一个类似于NurseRoster的算法。我需要在drowls中实现一个规则,检查Employee
的工作天数是否不能超过其contract
的工作天数。由于我不知道如何在drowls中实现这一点,我决定将其作为一个类中的方法编写,然后在droolls中使用它来检查约束是否已被破坏。由于我需要Employee
类中ShiftAssignments
的List
,所以我需要使用@InverseRelationShadowVariable
来自动更新该列表——Employee
被分配给Shift
。由于我的Employee
现在必须是PlanningEntity
,因此出现了错误The entity was never added to this ScoreDirector
。我相信错误是由我的ShiftAssignment
实体引起的,它有一个employees
的@ValueRangeProvider
,可以在该Shift
中工作。我认为这是由于ScoreDirector.beforeEntityAdded
和ScoreDirector.afterEntityAdded
从未被调用,因此出现了错误。出于某种原因,当我从ShiftAssignment
中删除该范围提供程序并将其放在NurseRoster
(即@PlanningSolution
)上时,它起了作用。
这是代码:
员工:
@InverseRelationShadowVariable(sourceVariableName = "employee")
public List<ShiftAssignment> getEmployeeAssignedToShiftAssignments() {
return employeeAssignedToShiftAssignments;
}
轮班分配:
@PlanningVariable(valueRangeProviderRefs = {
"employeeRange" }, strengthComparatorClass = EmployeeStrengthComparator.class,nullable = true)
public Employee getEmployee() {
return employee;
}
// the value range for this planning entity
@ValueRangeProvider(id = "employeeRange")
public List<Employee> getPossibleEmployees() {
return getShift().getEmployeesThatCanWorkThisShift();
}
托儿所名册:
@ValueRangeProvider(id = "employeeRange")
@PlanningEntityCollectionProperty
public List<Employee> getEmployeeList() {
return employeeList;
}
这就是我用来更新listOfEmployeesThatCanWorkThisShift
:的方法
public static void checkIfAnEmployeeCanBelongInGivenShiftAssignmentValueRange(NurseRoster nurseRoster) {
List<Shift> shiftList = nurseRoster.getShiftList();
List<Employee> employeeList = nurseRoster.getEmployeeList();
for (Shift shift : shiftList) {
List<Employee> employeesThatCanWorkThisShift = new ArrayList<>();
String shiftDate = shift.getShiftDate().getDateString();
ShiftTypeDefinition shiftTypeDefinitionForShift = shift.getShiftType().getShiftTypeDefinition();
for (Employee employee : employeeList) {
AgentDailySettings agentDailySetting = SearchThroughSolution.findAgentDailySetting(employee, shiftDate);
List<ShiftTypeDefinition> shiftTypeDefinitions = agentDailySetting.getShiftTypeDefinitions();
if (shiftTypeDefinitions.contains(shiftTypeDefinitionForShift)) {
employeesThatCanWorkThisShift.add(employee);
}
}
shift.setEmployeesThatCanWorkThisShift(employeesThatCanWorkThisShift);
}
}
我使用的规则是:
rule "maxDaysInPeriod"
when
$shiftAssignment : ShiftAssignment(employee != null)
then
int differentDaysInPeriod = MethodsUsedInScoreCalculation.employeeMaxDaysPerPeriod($shiftAssignment.getEmployee());
int maxDaysInPeriod = $shiftAssignment.getEmployee().getAgentPeriodSettings().getMaxDaysInPeriod();
if(differentDaysInPeriod > maxDaysInPeriod)
{
scoreHolder.addHardConstraintMatch(kcontext, differentDaysInPeriod - maxDaysInPeriod);
}
end
如何修复此错误?
这肯定与创建"新的最佳解决方案"时进行的解决方案克隆有关。
我在实现自定义解决方案克隆时遇到了同样的错误。在我的项目中,我有多个规划实体类,它们都有相互引用(要么是单个值,要么是列表)。因此,当进行解决方案克隆时,需要更新引用,以便它们可以指向克隆的值。这是默认的克隆过程可以毫无问题地完成的,从而使解决方案处于一致状态。它甚至会正确更新父计划实体中的计划实体实例列表(由OptaPlanner核心的类"FieldAccessingSolutionCloner"中的方法"cloneCollectionsElementIfNeeded"涵盖)。
这只是我对规划实体类的一个演示:
@PlanningEntity
public class ParentPlanningEntityClass{
List<ChildPlanningEntityClass> childPlanningEntityClassList;
}
@PlanningEntity
public class ChildPlanningEntityClass{
ParentPlanningEntityClass parentPlanningEntityClass;
}
起初,我没有更新任何引用,甚至"ChildPlanningEntityClass"也出现了错误。然后我已经编写了更新引用的代码。当涉及到来自类"ChildPlanningEntityClass"的规划实体实例时,此时一切都正常,因为它们指向克隆的对象。在"ParentPlanningEntityClass"案例中,我的错误之处在于,我没有使用"new ArrayList();"从头开始创建"childPlanningEntityClassList"列表,而是只是更新了列表中的元素(使用"set"方法),以指向"ChildPlanningEntityClass"类的克隆实例。创建"new ArrayList();"时,填充元素以指向克隆的对象,并设置"childPlanningEntityClassList"列表,所有内容都是一致的(使用FULL_ASSERT测试)。
因此,将它与我的问题联系起来,也许列表"employeeAssignedToShiftAssignments"并不是用"new ArrayList();"从头开始创建的,而是从列表中添加或删除元素。因此,这里可能发生的情况(如果列表不是从头开始创建的)是,工作解决方案和新的最佳解决方案(克隆)都将指向同一列表,当工作解决方案继续更改此列表时,它将损坏最佳解决方案。