问题
Optaplanner allows for a shadow variable to have more than one source (sources = {}) but only one variableListsnerClass. In my implementation i have a planning entity with shadow variables that should be able to change by two listsners, but this is not supported it seems or am i wrong? is there a way to have two listeners affect one shadow variable?
I have the following planning entities: PlannerActivity, PlannerTask and PlannerTaskResourceAllocation.
Any change on a PlannerActivity startIndex (genuine var) is listened to by the ActivityStartIndexVariableListener which moves the startindex (shadow var) and endIndex (shadow var) on all tasks belonging to that activity. this works fine
In addition to that, any change on a PlannerTaskResourceAllocation resource (geniune var), is listened to by TaskResourceVariableListener, and when the resource is a product, also updates the ohHandAmounts for that product, this also works fine.
The problem i have is that i need to add logic that when a resource is changed on a PlannerTaskResourceAllocation and that resource is an equipment, i need to possibly recalculate the task duration is the new equipment might be slower or faster than what was assigned before. so what i need here is that the PlannerActivity and PlannerTask startIndex and endIndex should be able to be changed by the TaskResourceVariableListener as well, but they are already listed to by the ActivityStartIndexVariableListener, and there's no way for me to specify two listeners for one shadow variable.
PlannerTask:
public class PlannerTask extends InventoryTransactionCause {
private static final long serialVersionUID = 1L;
@Getter
@Setter
private Activity activity;
@Getter
@Setter
private Integer indexInActivity;
// shadow variable
private Integer startIndex;
@Getter
@Setter
private double startOffset;
// shadow variable
private Integer length;
// shadow variable
private Integer endIndex;
@Getter
@Setter
private double endOffset;
@Getter
@Setter
private Integer originalStartIndex;
@Getter
@Setter
private Integer originalEndIndex;
@Getter
@Setter
private String state;
// getters and setters for shadow variables
// this is one of the shadow variables that i need affected by two
// listeners, one is the ActivityStartIndexVariableListener and the
// other is TaskResourceVariableListener
@CustomShadowVariable(variableListenerClass = ActivityStartIndexVariableListener.class,
sources = { @CustomShadowVariable.Source(entityClass = PlannerActivity.class, variableName = "endIndex"),
@CustomShadowVariable.Source(entityClass = PlannerTaskResourceAllocation.class,
variableName = "resource") })
public Integer getStartIndex() {
return this.startIndex;
}
public void setStartIndex(Integer startIndex) {
this.startIndex = startIndex;
}
@CustomShadowVariable(variableListenerClass = ActivityStartIndexVariableListener.class,
sources = { @CustomShadowVariable.Source(entityClass = PlannerActivity.class, variableName = "endIndex"),
@CustomShadowVariable.Source(entityClass = PlannerTaskResourceAllocation.class,
variableName = "resource") })
public Integer getEndIndex() {
return this.endIndex;
}
public void setEndIndex(Integer endIndex) {
this.endIndex = endIndex;
}
@CustomShadowVariable(variableListenerClass = TaskResourceVariableListener.class,
sources = { @CustomShadowVariable.Source(entityClass = PlannerTaskResourceAllocation.class,
variableName = "resource") })
public Integer getLength() {
return this.length;
}
public void setLength(Integer length) {
this.length = length;
}
}
回答1:
This is supported with the variableListenerRef
attribute: the first shadow variable has a normal shadow variable annotation and the second shadow variable points to the first shadow variable with @CustomShadowVariable(variableListenerRef = @PlanningVariableReference(variableName = "firstShadow"))
For example, 1 variable listener that changes 2 shadow variables that is based on 2 genuine variables:
@PlanningVariable(valueRangeProviderRefs = "valueRange")
public TestdataValue getPrimaryValue() {
return primaryValue;
}
public void setPrimaryValue(TestdataValue primaryValue) {
this.primaryValue = primaryValue;
}
@PlanningVariable(valueRangeProviderRefs = "valueRange")
public TestdataValue getSecondaryValue() {
return secondaryValue;
}
public void setSecondaryValue(TestdataValue secondaryValue) {
this.secondaryValue = secondaryValue;
}
@CustomShadowVariable(variableListenerClass = ComposedValuesUpdatingVariableListener.class,
sources = {@CustomShadowVariable.Source(variableName = "primaryValue"),
@CustomShadowVariable.Source(variableName = "secondaryValue")})
public String getComposedCode() {
return composedCode;
}
public void setComposedCode(String composedCode) {
this.composedCode = composedCode;
}
@CustomShadowVariable(variableListenerRef = @PlanningVariableReference(variableName = "composedCode"))
public String getReverseComposedCode() {
return reverseComposedCode;
}
public void setReverseComposedCode(String reverseComposedCode) {
this.reverseComposedCode = reverseComposedCode;
}
回答2:
You can make shadow variables that depend on shadow variables.
Create a custom shadow variable (with a VariableListener
impl) for startIndex
that depends on endIndex
and length
(which are both shadow vars).
来源:https://stackoverflow.com/questions/38141670/optaplanner-shadow-variable-with-more-than-one-source