Optaplanner:Drools规则在解决期间不会被解雇,只有在之后

时间:2017-12-17 18:05:21

标签: java drools optaplanner

我正在研究类似于optaplanner的护士安排示例的求解器(员工被分配到班次,员工是计划变量,转移计划实体),除了班次被分成1小时的间隔,员工可以工作每天多班次。

其中一个难点是每位员工每月只能工作一定时间。我目前使用以下规则对此进行建模,它可以工作:

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和ShiftAssignmentSwapMove,以确保每次移动都调用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对象,这是实际触发的规则,并且确实显示为违规规则。

我做错了什么?

在解决过程中跟踪变化变量以在约束规则中使用它们的最佳方法是什么?

1 个答案:

答案 0 :(得分:0)

When Shift.employee gets modified by OptaPlanner, Drools get's told that Shift is modified (so it can do delta incremental score calculation). Drools doesn't get told that Employee is also modified...

Solution: make that Employee.stats a shadow variable.

Employee should be a shadow <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> and it's stats field should a @PlanningEntity with its source being the setEmployee @CustomShadowVariable and its listener doing the code in Shift.employee so that setEmployee can become a simple setter again.

Don't forget to register Employee as a planning entity in the solver configuration unless it's doing scanning.