Hi,
I can't seem to find an answer to this, or a similar solution anywhere so appreciate any help!
Problem:
We have a planning entity Task with a planning variable StartTime.
We need to generate a Forecast which is a continuous set of values dependent on the start times of tasks. Constraints will also be dependent on the values of the forecast.
Whenever a move is explored by the solver, one or more planning variables, and thereby entities, are modified, and the forecast needs to be regenerated. (We'll ignore the cost of this operation for now, as well as the possibility of calculating delta changes to reduce this).
Attempted Solution:
Essentially I need to trigger this regeneration after a move is complete. The options I've seen and tried are:
- Forecast is a planning entity on the solution, with a "forecast" CustomShadowVariable triggered by a VariableListener on StartTime. I suppose this is a "Shadow Entity", however, it isn't ever documented explicitly as far as I can see.
Unless I've misunderstood, the listener would trigger after each StartTime is changed; if a move or a custom move changes 5 tasks then this triggers regeneration 5 times when it only needs to be done once.
I also don't understand this structure. I spent a while changing the class into a planning entity so that it would clone by default as per the documentation, yet I still had to add a DeepPlanningClone annotiation to the objects within it to stop it being co-modified by separate threads. The shadow variable also seems like a hack because the CustomShadowVariable annotiation doesn't really do anything special here. Is it only to notify drools?
- Forecast is a deep cloned class on the solution and a normal "forecast" array which is triggered by a drools rule.
A separate rule is added to drools to trigger the regeneration. This should only fire when Forecast is dirty, so somehow add an "isDirty" variable to Forecast and set it per move via a VariableListener or otherwise.
This is just a different hacky way to trigger it on demand but I don't know how to correctly implement this "demand".
- Somehow add a MoveListener.
I've searched a lot for this and best I've found is PhaseLifecycleListener which only exposes stepEnded whereas we need one phase lower down; moveEnded. This, again, feels like if it isn't commonly available from the documentation, there must be a better way to achieve this.
I'm keen to stick to doing things a standardised way rather than create custom code. I suppose, in that case, I just need to know that its necessary before going down that route. Appreciate any help I can get on this!
Best,
Rob