I’d like to get some hints to improve the rules of my simple planning system.

 

My simple planning system is derived from the HelloWorld Drools Project. The job of my planning system is to calculate startTime and endTime as function of duration and predecessors for each task. Each task has the fields

long duration;

Set<Task> predesessors;

long startTime;

long endTime;

For each task the rule (endTime = startTime + duration) should hold. If a task has no predecessors, then its startTime should be 0. If a task has predecessors, then is should start at the latest endTime of all predecessors.

 

These are my DRL-rules:

 

            rule "Calc EndTime"

                        no-loop true

                        when

                                   $t : Task()

                        then

                                   $t.setEndTime( $t.getStartTime() + $t.getDuration() );

                                   System.out.println( "Drools: Set " + $t.getName() + ".EndTime to " + $t.getEndTime() );

                                   update( $t );

            end

           

            rule "Calc StartTime without predecessors"

                        lock-on-active true

                when

                        $t : Task(predecessorCount == 0)

                        then

                                   $t.setStartTime( 0 );

                                   System.out.println( "Drools: " + $t.toString() + " can start immediately" );

                                   update( $t );

            end

           

            rule "Calc StartTime with predecessors"

                when

                    $t : Task(predecessorCount > 0, $predecessors : predecessors )

           

                    # Checking EndTime > 0 for all predecessors means that all EndTimes are

                    # initialy calculated. This is not what we realy want. Actually we'd like

                    # to recalculate the successors EndTime, when ever it is needed.

                   

                    forall (

                                               $succTask : Task( this == $t )

                                                $predTask : Task( this memberOf $predecessors, endTime > 0 )

                                    )

                    $endTime : Number()

                                    from accumulate( $p : Task() from $predecessors,

                                                                                                            max($p.getEndTime()) )

                        then

                                   $t.setStartTime( $endTime.longValue() );

                                   System.out.println( "Drools: " + $t.toString() + " has new StartTime" );

                                   # !!!! If we did an update($t); here, we would get an infinite recursion.

            end

 

My TestClass creates three tasks: task0, task1 and task2. Each task has an initial duration and (task0, task1) are predecessors of task2. It is important that this example has no circular dependencies; otherwise the rules, mentioned above, couldn’t be applied.

 

After fireAllRuels() the endTime of task2 is calculated wrong, so obviously the rule "Calc EndTime" should be activated. Is the rule-attribute (no-loop = true) not the right solution to avoid circularity here?

 

Then the duration of task0 is updated outside of the rule engine. After a second fireAllRuels() the endTime of task0 is recalculated, but task2 is not recalculated at all. How can I tell the rule engine, that a task depends on its predecessors?

 

I'd appreciate any input that could help me making this example work and if it works very efficient on a big number of tasks it’s brilliant. If anybody is interested in my TestClass, to run this example with some logging, please tell me.

 

Ralph Ammon