Reputation: 13906
This is a shift scheduling application. I'm already dynamically control some of the constraints on/off by changing weight, which is great. And some specific constraints can already be dynamically controlled with the planning object properties (Employee's contract maximum). I still haven't found a way to make the ConstraintProvider parametrized based on configuration. My question is:
Can a ConstraintProvider be programmatically created, instead of defining in the solverConfig.xml? So I can inject my own parameters
I would want this constraint to be dyniamically adjusted with not just 10 hours fixed duration (that 10 * 60 hardcoded value):
Constraint breakBetweenNonConsecutiveShiftsIsAtLeastTenHours(ConstraintFactory constraintFactory) {
return getAssignedShiftConstraintStream(constraintFactory)
.join(Shift.class,
equal(Shift::getEmployee),
lessThan(Shift::getEndDateTime, Shift::getStartDateTime))
.filter((s1, s2) -> !Objects.equals(s1, s2))
.filter((s1, s2) -> s1.getEndDateTime().until(s2.getStartDateTime(), ChronoUnit.HOURS) < 10)
.penalizeConfigurableLong(CONSTRAINT_BREAK_BETWEEN_NON_CONSECUTIVE_SHIFTS, (s1, s2) -> {
long breakLength = s1.getEndDateTime().until(s2.getStartDateTime(), ChronoUnit.MINUTES);
return (10 * 60) - breakLength;
});
}
(I could add a field on Shift to return the same values for all shifts, but that's messy).
Upvotes: 1
Views: 668
Reputation: 11192
What is also possible is to expose parameters as functions in the constraint provider implementation and later inherit the base constraint provider impl to override the respective parameters
open class MyConstraintsProvider : ConstraintProvider{
open fun doAvoidOverlap() = true
}
// parameterized version
class CustomProvider : MyConstraintsProvider() {
override fun doAvoidOverlap() = false
}
// instantiate the solver
SolverConfig()
.withConstraintProviderClass(CustomProvider::class.java)
However to make this truly configurable (e.g. from a config file), we need to resort to the jsr223 scripting compiler because optaplanner does only support zero argument constructors. See https://kotlinlang.slack.com/archives/C7L3JB43G/p1661783750781029 for an example (which was motivated from optaplanner but abstracted for kotlin-slack).
The above mentioned approach could be adjusted to plain java as well.
Upvotes: 0
Reputation: 13906
Currently, the best way is to create a custom class to store the duration (ex: MinimumBreakBetweenNonConsecutiveShifts) add it as a field to Roster, and annotate the field with @ProblemFact
. Then you can join the stream with the problem fact with constraintFactory.from(...).join(MinimumBreakBetweenNonConsecutiveShifts.class)
We are currently discussing alternative methods for access to problem facts here: https://kie.zulipchat.com/#narrow/stream/232679-optaplanner/topic/Constraint.20context
(https://github.com/kiegroup/optaweb-employee-rostering/issues/502#issuecomment-701479372)
Upvotes: 1