Java Optaplanner:Drools规则在求解过程中不触发,仅在
我正在处理一个类似于optaplanner中护士排班示例的解算器。员工被分配到班次,员工是计划变量,班次是计划实体,但班次被划分为1小时间隔,员工每天可以在多个班次上工作 其中一个硬约束是每个员工每月只能工作一定的小时数。我目前使用以下规则对此进行建模,并且它是有效的:Java Optaplanner:Drools规则在求解过程中不触发,仅在,java,drools,optaplanner,Java,Drools,Optaplanner,我正在处理一个类似于optaplanner中护士排班示例的解算器。员工被分配到班次,员工是计划变量,班次是计划实体,但班次被划分为1小时间隔,员工每天可以在多个班次上工作 其中一个硬约束是每个员工每月只能工作一定的小时数。我目前使用以下规则对此进行建模,并且它是有效的: rule "At most 173h work per month per fulltime employee, assuming roster is only for 1 month" when $e: Employe
rule "At most 173h work per month per fulltime employee, assuming roster is only for 1 month"
when
$e: Employee(type == EmployeeType.FULLTIME)
$total : Number(intValue() > 10320)
from accumulate(
Shift(employee == $e,
$minutes : getTimeSlot().getMinutesInterval()),
sum($minutes))
then
scoreHolder.addHardConstraintMatch(kcontext, -1);
end
现在,由于预计有很多轮班需要计划1000+我想我可以通过跟踪每个员工的工作时间来加快处理速度
为此,我为每个员工提供了一个对象统计信息,该统计信息应该能够跟踪这些信息。对象在Shift对象的setEmployee方法期间更新,如下所示:
public void setEmployee(Employee employee) {
if(this.employee != null){
this.employee.getStats().subtractWorkedMinutes(this.timeSlot);
}
this.employee = employee;
if(this.employee != null){
this.employee.getStats().addWorkedMinutes(this.timeSlot);
}
}
我还实现了EmployeeChangeMove和ShiftasSignementSwapMove,如护士日程安排示例中所示,以确保每个移动都调用setEmployee方法,因此应该更改员工统计信息。现在我想在规则中使用这些递增计算的员工统计数据,如下所示:
rule "At most 173h work per month per fulltime employee, assuming roster is only for 1 month"
when
Employee(type == EmployeeType.FULLTIME,
getStats().getTotalMinutesWorked() > 10320)
then
scoreHolder.addHardConstraintMatch(kcontext, -1);
end
但是,我遇到的问题是,这些规则在求解过程中从未触发,求解者在计算分数时没有考虑这些规则。只有在解算器完成并且我从解决方案生成一个新的ScoreDirector以获取ConstraintMatchTotal对象之后,规则才会被实际触发,并显示为违反规则
我做错了什么
在求解过程中跟踪变量变化的最佳方法是什么,以便在约束规则中使用它们?当OptaPlanner修改Shift.employee时,Drools get被告知已修改Shift,以便可以进行增量分数计算。Drools不会被告知员工也被修改了 解决方案:将Employee.stats设为影子变量 Employee应该是shadow@PlanningEntity,其stats字段应该是@CustomShadowVariable,其源是setEmployee Shift.Employee,其侦听器在Shift.setEmployee中执行代码,以便setEmployee可以再次成为简单的setter
不要忘记在解算器配置中将员工注册为规划实体,除非它正在进行扫描。调用“setEmployee”时是否更新了员工事实?我将如何进行/检查?所有员工都在@PlanningSolution中的一个列表中,该列表用@ProblemFactCollectionProperty注释,setEmployee所接收的员工对象来自我所理解的列表,因此如上所述的setEmployee应该更新该列表中的对象引用,不是吗?@laune我想我刚刚找到了答案。非常感谢你为我指明了正确的方向。解决方案是调用scoreDirector.beforeProblemPropertyChanged和scoreDirector.afterProblemPropertyChanged,在这种情况下,每次调用setEmployee时都会调用班次的员工。现在来看看这是否比需要迭代所有轮班来总结工作时间的表现更差;除了调用setter通知规则引擎以便重新评估规则之外,update是一个单独的调用。我按照如下方式实现了VariableListener:@Override public void afterVariableChangedCoreDirector scoreDirector,Shift Shift{ifshift.getEmployee!=null{scoreDirector.beforeVariableChangedshift.getEmployee,统计信息;shift.getEmployee.getStats.AddWorkedMinuteshift.getTimeSlot;scoreDirector.beforeVariableChangedshift.getEmployee,统计信息;}侦听器的beforeVariableChanged方法是相同的,只是我调用了.subtractWorkedMinutes。我仍然遇到这样一个问题,即解决方案的分数与匹配的规则不符。您是否尝试打开environmentMode FULL_ASSERT?如果没有,则会给出更准确的错误消息。启用此选项后,我得到java.lang.IllegalStateException:分数损坏:解决方案的分数0hard/-260medium/-103900soft不是未损坏的core 0hard/-260medium/-104899soft.at org.optaplanner.core.impl.Score.director.AbstractScoreDirectorFactory.assertScoreFromScratchAbstractScoreDirectorFactory.java:100 at org.optaplanner.core.impl.solver.scope.DefaultSolverScope.assertScoreFromScratchDefaultSolverScope.java:136位于org.optaplanner.core.impl.solver.recaller.BestSolutionRecaller.processWorkingSolutionDuringStepBestSolutionRecaller.java:98