View Javadoc

1   /*
2   * The contents of this file are subject to the BT "ZEUS" Open Source 
3   * Licence (L77741), Version 1.0 (the "Licence"); you may not use this file 
4   * except in compliance with the Licence. You may obtain a copy of the Licence
5   * from $ZEUS_INSTALL/licence.html or alternatively from
6   * http://www.labs.bt.com/projects/agents/zeus/licence.htm
7   * 
8   * Except as stated in Clause 7 of the Licence, software distributed under the
9   * Licence is distributed WITHOUT WARRANTY OF ANY KIND, either express or 
10  * implied. See the Licence for the specific language governing rights and 
11  * limitations under the Licence.
12  * 
13  * The Original Code is within the package zeus.*.
14  * The Initial Developer of the Original Code is British Telecommunications
15  * public limited company, whose registered office is at 81 Newgate Street, 
16  * London, EC1A 7AJ, England. Portions created by British Telecommunications 
17  * public limited company are Copyright 1996-9. All Rights Reserved.
18  * 
19  * THIS NOTICE MUST BE INCLUDED ON ANY COPY OF THIS FILE
20  */
21  
22  
23  
24  /*
25   * @(#)Planner.java 1.00
26   */
27  
28  package zeus.actors;
29  
30  import java.util.*;
31  import zeus.util.*;
32  import zeus.concepts.*;
33  import zeus.actors.rtn.util.DelegationStruct;
34  import zeus.actors.rtn.Engine;
35  import zeus.actors.event.*;
36  
37  /***
38   * This component implements the agent's Planning and Scheduling mechanism. 
39   * Its role is to construct action sequences that will achieve desired input goals.
40   * Hence the Planner is under the control of the {@link Engine} component, which 
41   * initiates planning and manages the contracting of any subgoals that the agent 
42   * cannot achieve. <p>
43   *
44   * Planning operators (actions or tasks) are represented in the classical fashion 
45   * as primitive or summary operators.  Primitive operators are defined in terms 
46   * of their preconditions, effects, cost, duration and constraints and precondition 
47   * order, while summary operators are defined in terms of a graph of existing 
48   * primitive tasks. <p>
49   *
50   * The Planner utilises classical partial order means end planning in its plan 
51   * construction process. So when given a goal the Planner searches its {@link PlanDb}
52   * for an operator with a public effect that unifies (with unification bindings q) 
53   * with the desired_effect of the goal.  If multiple operators are found, they are 
54   * ranked by cost, and then by duration. Next, the Planner selects the first 
55   * ranked operator, constrains its preconditions and effects with q, and then 
56   * attempts to schedule the operator into its diary.  If the operator cannot be 
57   * scheduled, the Planner backtracks and re-peats the process with the next 
58   * applicable operator. <p>
59   *
60   * Details on how the Planner functions are provided in the Zeus Technical Manual.
61   */
62  
63  
64  public class Planner extends PlanDb 
65  {
66     private HSet[]  eventMonitor = new HSet[6];
67  
68     public static final int START   = 0;
69     public static final int FAIL    = 1;
70     public static final int SUCCEED = 2;
71  
72     public static final int CREATE       = 3;
73     public static final int DISPOSE      = 4;
74     public static final int STATE_CHANGE = 5;
75  
76     public static final boolean EXPAND = true;
77     public static final int     REPLAN = 0;
78     public static final int     PLAN   = 1;
79  
80  
81   
82  
83     /*** This object stores the Planner's diary as is a two-dimensional array, with
84         time on one dimension and processors on another. */
85     protected PlanRecord[][] table;
86  
87     protected int            plannerWidth;
88     protected int            plannerLength;
89     protected int            now;
90     protected Hashtable      BindTable;
91  
92     protected boolean user_responded = false;
93     protected long USER_TIME_OUT = 0;
94     protected AgentContext context = null;
95  
96  
97     public Planner () {
98      for(int i = 0; i < eventMonitor.length; i++ )
99           eventMonitor[i] = new HSet();
100    }
101 
102 
103    public Planner(AgentContext context, int plannerWidth,
104                   int plannerLength ) 
105    {
106       Assert.notNull(context);
107       this.context = context;
108       context.set(this);
109 
110       Core.ERROR(plannerLength > 0 && plannerWidth > 0, 1005, this);
111 
112       table = new PlanRecord[plannerWidth][plannerLength];
113       this.plannerLength = plannerLength;
114       this.plannerWidth = plannerWidth;
115       now = (int) now();
116       BindTable = new Hashtable();
117 
118       for(int i = 0; i < eventMonitor.length; i++ )
119          eventMonitor[i] = new HSet();
120 
121       USER_TIME_OUT = (long)(0.5 * context.getClockStep());
122    }
123 
124    public AgentContext getAgentContext() {
125       return context;
126    }
127 
128 
129    public int getPlannerWidth() {
130       return   plannerWidth;
131    }
132    public int getPlannerLength() {
133      return plannerLength;
134    }
135 
136     //synchronized
137   int  anySideEffect(Fact desc, PlanRecord rec,
138                                  int position, int required) {
139       return anySideEffect(desc,rec,new Integer(position),required);
140    }
141    
142    
143     // synchronized 
144   int  anySideEffect(Fact desc, PlanRecord rec,
145                                   Object precond, int required) 
146    {
147       if ( !context.getSharePlan() ) return required;
148       Core.DEBUG(2,"checking for serendipitous side-effects for " + desc);
149 
150       int no = desc.getNumber();
151       PlanRecord crec;
152       int st = rec.getStartTime();
153       if ( desc.isa(OntologyDb.ENTITY) ) desc.setNumber(desc.newVar());
154       Enumeration enum = this.elements();
155       while( required > 0 && enum.hasMoreElements() ) {
156          crec = (PlanRecord)enum.nextElement();
157          if ( crec != rec && crec.getEndTime() <= st )
158             required = crec.anySideEffect(desc,rec,precond,required);
159       }
160       desc.setNumber(no);
161       return required;
162    }
163    
164    
165 // synchronized 
166    public PlannerQueryStruct canAchieve(Vector goals, String key) 
167    {
168       Core.DEBUG(3,"\nCanAchieve:\n" + goals + "\n");
169 
170       Goal g;
171       PlannerQueryStruct struct = new PlannerQueryStruct(goals);
172 
173       for(int i = 0; i < goals.size(); i++ ) {
174          g = (Goal)goals.elementAt(i);
175          notifyMonitors(g,START,PlanningEvent.PLANNING);
176       }
177 
178       struct.timeout = context.getAcceptTimeout();
179 
180       // check for Looping i.e. A --> B --> C --> A and
181       // the goal.fact at the two A's are identical
182       if ( loopFound(goals) ) {
183          Core.DEBUG(0,"CanAchieve loop found = " + struct);
184          index(struct);
185          return struct;
186       }
187 
188       // check confirm & reply times
189       double ct = Double.MAX_VALUE;
190       double rt = Double.MAX_VALUE;
191       for(int i = 0; i < goals.size(); i++ ) {
192          g = (Goal)goals.elementAt(i);
193          ct = Math.min(ct,g.getConfirmTime().getTime());
194          if ( g.getReplyTime() != null )
195             rt = Math.min(rt,g.getReplyTime().getTime());
196       }
197       double now = now();
198       if ( now >= Math.min(ct,rt) ) {
199          Core.DEBUG(0,"CanAchieve rt/ct problem: " + now + "\n" + struct);
200          index(struct);
201          return struct;
202       }
203 
204       struct.internal = schedule(key,null,new Vector(),goals,struct,EXPAND);
205       if ( struct.internal.isEmpty() ) {
206          Core.DEBUG(0,"CanAchieve = " + struct);
207          index(struct);
208          return struct;
209       }
210 
211       double lct = (double) latestConfirmTime(goals);
212       if ( lct < ct ) {
213          Core.DEBUG(0,"CanAchieve lct < ct " + now + "\n" + struct);
214          reject(struct.goals,struct.internal);
215          struct.internal = new Vector();
216          index(struct);
217          return struct;
218       }
219 
220       if ( !struct.internal.isEmpty() && !struct.external.isEmpty() ) {
221          // timeout heuristic
222          double t;
223          t = Math.min(ct,rt);
224          t = t - now();
225          t = t/2*struct.external.size();
226          struct.timeout = t;
227       }
228       index(struct);
229       Core.DEBUG(0,"canAchieve = " + struct);
230       return struct;
231    }
232 
233    protected boolean loopFound(Vector goals) {
234       Enumeration enum = BindTable.elements();
235       PlannerQueryStruct struct;
236       Vector stored;
237       Goal g1, g2;
238       Fact f1, f2;
239       String r1, r2;
240       Bindings b = new Bindings(context.whoami());
241 
242       while( enum.hasMoreElements() ) {
243          struct = (PlannerQueryStruct) enum.nextElement();
244          stored = struct.goals;
245          for( int i = 0; i < goals.size(); i++ ) {
246             g1 = (Goal)goals.elementAt(i);
247             f1 = g1.getFact();
248             r1 = g1.getRootId();
249             for( int j = 0; j < stored.size(); j++, b.clear() ) {
250                g2 = (Goal)stored.elementAt(j);
251                if ( g2.getDesiredBy().equals(context.whoami()) ) {
252                   f2 = g2.getFact();
253                   r2 = g2.getRootId();
254                   if ( r1.equals(r2) && f1.unifiesWith(f2,b) ) {
255                      Core.DEBUG(0,"Loop found: " + g1 + "\n" + g2);
256                      return true;
257                   }
258                }
259             }
260          }
261       }
262       return false;
263    }
264 
265    public PlannerQueryStruct clear_bind(Vector goals) {
266       if ( goals.isEmpty() ) return null;
267       Core.DEBUG(3,"Entering Clear Bind goals\n" +  goals + "\n");
268 
269       String index = makeIndex(goals);
270       return (PlannerQueryStruct) BindTable.remove(index);
271    }
272 
273    public void reset_bind(Vector goals, PlannerQueryStruct data) {
274       Core.DEBUG(3,"Entering Reset Bind goals\n" +  goals + "\n");
275 
276       String index = makeIndex(goals);
277       Core.ERROR(BindTable.put(index,data) == null,1009,this);
278    }
279 
280    public Vector bind(Vector goals) {
281       Core.DEBUG(3,"Entering Final Bind goals\n" +  goals + "\n");
282 
283       String index = makeIndex(goals);
284       PlannerQueryStruct struct = (PlannerQueryStruct) BindTable.get(index);
285       PlanRecord rec;
286       Bindings b;
287       Core.DEBUG(3,"Final Bind checking record" );
288       for(int i = 0; i < struct.internal.size(); i++ ) {
289          rec = (PlanRecord)struct.internal.elementAt(i);
290          b = rec.getBindings();
291          Core.ERROR(struct.bindings.add(b),1010,this);
292       }
293       Goal g;
294       Vector data = new Vector();
295       Core.DEBUG(3,"Final Bind checking costs" );
296       for(int i = 0; i < goals.size(); i++ ) {
297          g = new Goal( (Goal)goals.elementAt(i) );
298          g.constrain(struct.bindings);
299          rec = lookUp(g);
300          g.setCost(rec.getCost());
301          data.addElement(g);
302       }
303       Core.DEBUG(3,"Exiting Final Bind goals\n" +  data + "\n" );
304       return data;
305    }
306 
307 
308 //synchronized
309    public synchronized void userResponded() {
310       this.user_responded = true;
311    }
312 
313    public BindResults bind(Vector goals, Vector input, int mode) {
314       Core.DEBUG(3,"Entering bind...");
315       String index = makeIndex(goals);
316       PlannerQueryStruct struct = (PlannerQueryStruct) BindTable.get(index);
317 
318       Goal g0, g1;
319       Fact f0, f1;
320       PlanRecord rec;
321       Vector[] reduced = new Vector[struct.external.size()];
322       Bindings bindings = new Bindings(context.whoami());
323       BindResults result = new BindResults();
324       boolean found = false;
325 
326       Vector absent = new Vector();
327       Vector present = new Vector();
328 
329       for(int i = 0; i < struct.external.size(); i++ ) {
330          g0 = (Goal)struct.external.elementAt(i);
331          reduced[i] = sortFeasible(g0.getId(),input);
332          if ( reduced[i].isEmpty() )
333             absent.addElement(g0);
334          else
335             present.addElement(g0);
336       }
337 
338       if ( !absent.isEmpty() ) {
339          Core.DEBUG(3,"From bind...");
340          Core.DEBUG(3,"PlannerQueryStruct = " + struct);
341          Core.DEBUG(3,"absent = " + absent);
342          for( int x = 0; x < struct.internal.size(); x++ ) {
343             rec = (PlanRecord)struct.internal.elementAt(x);
344             Core.DEBUG(3,rec.getConsumedDb());
345          }
346 
347          softFailParentOf(absent,struct,mode);
348          for(int j = 0; j < present.size(); j++ ) {
349             g1 = (Goal)present.elementAt(j);
350             found = false;
351             for(int i = 0; !found && i < struct.external.size(); i++ ) {
352                g0 = (Goal)struct.external.elementAt(i);
353                found = found || g0.getId().equals(g1.getId());
354                if ( found ) struct.external.removeElementAt(i--);
355             }
356             if ( !found ) present.removeElementAt(j--);
357          }
358       }
359 
360       Core.DEBUG(3,"Present = " + present);
361       Core.DEBUG(3,"Struct = " + struct);
362 
363       if ( present.isEmpty() ) {
364          result.rejection = input;
365          result.ok = !struct.internal.isEmpty() ||
366                      !struct.decompositions.isEmpty();
367          result.external = Misc.copyVector(struct.external);
368          return result;
369       }
370      
371 
372       if ( present.size() != reduced.length ) {
373          reduced = new Vector[present.size()];
374          for(int i = 0; i < present.size(); i++ ) {
375             g0 = (Goal)present.elementAt(i);
376             reduced[i] = sortFeasible(g0.getId(),input);
377          }
378       }
379 
380 /*
381       AskUser ask_user;
382       user_responded = false;
383       long user_timeout = System.currentTimeMillis() + USER_TIME_OUT;
384       synchronized(this) {
385          ask_user = new AskUser(reduced,this);
386          while( !user_responded && 
387                 System.currentTimeMillis() < user_timeout ) {
388             try {
389                wait(USER_TIME_OUT);
390             }
391             catch(InterruptedException e) {
392             }
393          }
394       }
395       if ( !user_responded && ask_user != null && ask_user.isShowing() )
396          ask_user.okBtnFn();
397 */
398 
399       Object[] data;
400       DelegationStruct[] ds = new DelegationStruct[present.size()];
401       Selector selector = new Selector(reduced);
402       result.ok = false;
403       while( !result.ok && selector.hasMoreElements() ) {
404          data = (Object[]) selector.nextElement();
405          bindings.clear();
406          result.ok = true;
407          for(int i = 0; i < data.length; i++ ) {
408             ds[i] = (DelegationStruct)data[i];
409             // assumes only one goal in request
410             g1 = (Goal)ds[i].goals.elementAt(0);
411             g0 = (Goal)present.elementAt(i);
412             f0 = g0.getFact();
413             f1 = g1.getFact();
414             result.ok = f1.unifiesWith(f0,bindings);
415             if ( !result.ok )
416                break;
417          }
418 
419          Core.DEBUG(3,"Current selection ... ");
420          Core.DEBUG(3,ds);
421 
422          result.ok = result.ok && bindings.add(struct.bindings);
423          if ( result.ok ) {
424             for(int j = 0; j < struct.internal.size(); j++ ) {
425                rec = (PlanRecord) struct.internal.elementAt(j);
426                Core.ERROR(rec.applyConstraints(bindings),1011,this);
427             }
428             struct.bindings = bindings;
429 
430             for( int i = 0; i < data.length; i++ ) {
431                ds[i] = (DelegationStruct)data[i];
432                result.selection.addElement(ds[i]);
433             }
434          }
435       }
436 
437       if ( result.ok ) {
438          result.rejection = Misc.difference(input,result.selection);
439          resume_planning(ds,struct);
440       }
441       else {
442          result.rejection = input;
443          softFailParentOf(struct.external,struct,mode);
444          result.ok = !struct.internal.isEmpty() ||
445                      !struct.decompositions.isEmpty();
446       }
447 
448       result.external = Misc.copyVector(struct.external);
449 
450       Core.DEBUG(3," Bind struct\n" + struct);
451       Core.DEBUG(3," Bind goals\n" + goals);
452       Core.DEBUG(3," Bind bindings\n" + struct.bindings);
453       Core.DEBUG(3," Bind result\n" +  result + "\n" );
454 
455       return result;
456    }
457 
458    protected Vector sortFeasible(String gid, Vector input) {
459       Core.DEBUG(3,"sortFeasible input " + gid + "\n" + input);
460       Goal g0, g1;
461       Object obj;
462       DelegationStruct ds;
463       Vector reduced = new Vector();
464       for(int i = 0; i < input.size(); i++ ) {
465          ds = (DelegationStruct)input.elementAt(i);
466          // assumes only one goal in goals field
467          g0 = (Goal)ds.goals.elementAt(0);
468          if ( gid.equals(g0.getId()) )
469             reduced.addElement(ds);
470       }
471       Core.DEBUG(3,"sortFeasible reduced " + gid + "\n" + reduced);
472       boolean changed = true;
473       while( changed ) {
474          changed = false;
475          for( int i = 0; i < reduced.size()-1; i++ ) {
476             // assumes only one goal in goals field
477             ds = (DelegationStruct)reduced.elementAt(i);
478             g0 = (Goal)ds.goals.elementAt(0);
479             ds = (DelegationStruct)reduced.elementAt(i+1);
480             g1 = (Goal)ds.goals.elementAt(0);
481             if ( g0.getCost() > g1.getCost() ) {
482                obj = reduced.elementAt(i);
483                reduced.setElementAt(reduced.elementAt(i+1),i);
484                reduced.setElementAt(obj,i+1);
485                changed = true;
486             }
487          }
488       }
489       Core.DEBUG(3,"sortFeasible results " + gid + "\n" + reduced);
490       return reduced;
491    }
492 
493    // Assess feasilibility of enactment of a prearranged SLA
494    public PlannerEnactStruct enact(Goal goal, Goal sla) {
495       PlannerEnactStruct es = new PlannerEnactStruct();
496       PlanRecord rec, image;
497 
498       notifyMonitors(goal,START,PlanningEvent.ENACTMENT);
499 
500       rec = lookUp(sla);
501       if ( rec == null ) {
502          es.ok = false;
503          notifyMonitors(goal,FAIL,PlanningEvent.ENACTMENT);
504          Core.DEBUG(3,"PlannerEnactStruct no sla found for\n" +  sla);
505          return es;
506       }
507 
508       rec.enact(es,goal,null,goal.getId(),new Hashtable());
509 
510       if ( es.ok  ) {
511          for( int i = 0; i < es.images.size(); i++ ) {
512             image = (PlanRecord)es.images.elementAt(i);
513             image.setState(PlanRecord.FIRM);
514          }
515          if ( !rec.hasMoreEnactments() )
516             rec.dispose();
517          notifyMonitors(goal,SUCCEED,PlanningEvent.ENACTMENT);
518       }
519       else {
520          notifyMonitors(goal,FAIL,PlanningEvent.ENACTMENT);
521       }
522       
523       Core.DEBUG(3,"Final PlannerEnactStruct\n" +  es);
524       return es;
525    }
526 
527    int latestConfirmTime(Vector goals) {
528       Core.ERROR(goals,1012,this);
529       Core.ERROR(!goals.isEmpty(),1013,this);
530       int  lct = now + plannerLength;
531       for( int i = 0; i < goals.size(); i++ )
532          lct = Math.min(lct,latestConfirmTime((Goal)goals.elementAt(i)));
533       return lct;
534    }
535    int latestConfirmTime(Goal goal) {
536       PlanRecord rec;
537       rec = lookUp(goal);
538       return rec.latestConfirmTime();
539    }
540 
541    protected String makeIndex(Vector goals) {
542       Core.ERROR(goals,1014,this);
543       if ( goals.isEmpty() ) return null;
544 
545       String[] Items = new String[goals.size()];
546       for( int i = 0; i < goals.size(); i++ )
547          Items[i] = ((Goal)goals.elementAt(i)).getId();
548 
549       Misc.sort(Items);
550 
551       String index = new String();
552       for(int i = 0; i < Items.length-1; i++ )
553          index += Items[i] + "/";
554       index += Items[Items.length-1];
555 
556       return index;
557    }
558 
559    protected void index(PlannerQueryStruct Results) {
560       String index = makeIndex(Results.goals);
561       Core.ERROR(BindTable.put(index,Results) == null,1015,this);
562    }
563 
564    protected void removeFromIndexTable(Vector goals) {
565      Core.DEBUG(3,"Planner removeFromIndexTable");
566       String index = makeIndex(goals);
567       BindTable.remove(index);
568    }
569 
570    public void book(int type, Vector goals, Vector records) {
571       Vector rootSet = new Vector();
572       PlanRecord root;
573 
574       for(int i = 0; i < records.size(); i++ ) {
575          root = ((PlanRecord)records.elementAt(i)).getRoot();
576          if ( !rootSet.contains(root) ) rootSet.addElement(root);
577       }
578       Goal g;
579       for(int i = 0; i < goals.size(); i++ ) {
580          g = (Goal)goals.elementAt(i);
581          book(type,g,rootSet);
582          if ( type == PlanRecord.FIRM )
583             notifyMonitors(g,SUCCEED,PlanningEvent.PLANNING);
584       }
585       for(int i = 0; i < rootSet.size(); i++ ) {
586          root = (PlanRecord)rootSet.elementAt(i);
587          root.setState(type);
588       }
589    }
590 
591    protected void book(int type, Goal goal, Vector rootSet) {
592       PlanRecord rec = lookUp(goal);
593       if ( rec != null ) {
594          rec.setState(type);
595          rootSet.removeElement(rec);
596       }
597    }
598 
599    public void reject(Vector goals, Vector records) {
600 
601       Core.DEBUG(3,"Planner Reject goals called " + goals);
602       removeFromIndexTable(goals);
603       Vector rootSet = new Vector();
604       PlanRecord root;
605 
606       for(int i = 0; i < records.size(); i++ ) {
607          root = ((PlanRecord)records.elementAt(i)).getRoot();
608          if ( !rootSet.contains(root) ) rootSet.addElement(root);
609       }
610       Goal g;
611       for(int i = 0; i < goals.size(); i++ ) {
612          g = (Goal)goals.elementAt(i);
613          reject(g,rootSet);
614          notifyMonitors(g,FAIL,PlanningEvent.PLANNING);
615       }
616       for(int i = 0; i < rootSet.size(); i++ ) {
617          root = (PlanRecord)rootSet.elementAt(i);
618          root.dispose();
619       }
620    }
621 
622    protected void reject(Goal goal, Vector rootSet) {
623     // Core.DEBUG(3,"Planner Reject goals called " + goals);
624       PlanRecord rec = lookUp(goal);
625       if ( rec != null ) {
626          rec.dispose();
627          rootSet.removeElement(rec);
628       }
629    }
630 
631    Vector schedule(String key, PlanRecord parent, Vector path,
632                    Vector goals, PlannerQueryStruct struct, boolean mode) {
633       Core.DEBUG(3,"schedule: 0");
634       Vector lpath;
635       Goal goal;
636       Vector sub_records, records = new Vector();
637       for(int i = 0; i < goals.size(); i++ ) {
638          goal = (Goal)goals.elementAt(i);
639          lpath = Misc.copyVector(path);
640          sub_records = schedule(key,parent,lpath,goal,struct,mode);
641          records = Misc.union(records,sub_records);
642       }
643       return records;
644    }
645 
646    Vector schedule(String key, PlanRecord parent, Vector path,
647                    Goal goal, PlannerQueryStruct struct, boolean mode) {
648 
649       Core.DEBUG(3,"schedule: 1");
650       // Attempt to exploit side-effects of children of sister goals
651       if ( parent != null ) {
652          goal = new Goal(goal);
653          Fact desc = goal.getFact();
654          int required = desc.getNumber();
655          required = anySideEffect(desc,parent,goal.getId(),required);
656          if ( required == 0 )
657             return new Vector();
658          else {
659             desc.setNumber(required);
660             goal.setFact(desc);
661          }
662       }
663 
664       if ( !validTime(goal.getEndTime()) ) {
665          addToExternal(struct.external,goal);
666          return new Vector();
667       }
668 
669       Vector tasks = context.TaskDb().findAll(goal.getFact(),path);
670 
671       Core.DEBUG(3,"Tasks for: " + goal.getFactType());
672       Core.DEBUG(3,tasks);
673 
674       return schedule(key,parent,path,goal,tasks,struct,mode);
675    }
676 
677    protected void addToExternal(Vector List, Goal goal) {
678       Core.DEBUG(3,"Adding to external ... attempt");
679       String id = goal.getId();
680       Goal g;
681       for(int i = 0; i < List.size(); i++ ) {
682          g = (Goal)List.elementAt(i);
683          if ( id.equals(g.getId()) ) return;
684       }
685       Core.DEBUG(3,"Adding to external ... done");
686       List.addElement(goal);
687    }
688 
689 
690    Vector schedule(String key, PlanRecord parent, Vector path, Goal goal,
691                    Vector tasks, PlannerQueryStruct struct, boolean mode) {
692 
693       Core.DEBUG(3,"schedule: 2");
694       Core.DEBUG(3,"schedule: 2 Path0 = " + path);
695       Vector records;
696       Task t;
697 
698       if ( tasks == null )
699          tasks = context.TaskDb().findAll(goal.getFact(),path);
700 
701       Core.DEBUG(3,"schedule: 2 Path1 = " + path);
702 
703       while( !tasks.isEmpty() ) {
704          t = (Task)tasks.firstElement();
705          records = (t.isPrimitive())
706                    ? schedule_primitive(key,parent,path,goal,tasks,struct,mode)
707                    : schedule_summary(key,parent,path,goal,tasks,struct,mode);
708 
709          if ( !records.isEmpty() )
710             return records;
711       }
712       addToExternal(struct.external,goal);
713       return new Vector();
714    }
715 
716 
717    Vector schedule_summary(String key, PlanRecord parent, Vector path,
718                            Goal goal, Vector tasks, PlannerQueryStruct struct,
719                            boolean mode) {
720 
721       Core.DEBUG(3,"schedule_summary");
722       Core.DEBUG(3,"schedule_summary: Path = " + path);
723       SummaryTask task = (SummaryTask)tasks.firstElement();
724       tasks.removeElementAt(0);
725 
726       Vector records;
727       String node;
728       Decomposition decomposition;
729 
730       decomposition = new Decomposition(this,key,parent,path,goal,task);
731       records = expand_summary(decomposition,struct,mode);
732       Core.DEBUG(3,"schedule_summary end:");
733       Core.DEBUG(3,struct);
734       return records;
735    }
736 
737    Vector expand_summary(Decomposition decomposition, PlannerQueryStruct struct,
738                          boolean mode) {
739 
740       Core.DEBUG(3,"expand_summary");
741 
742       Vector records, path, sub_tasks;
743       Fact[] consumed, produced;
744       String node, key;
745       Vector all_records = new Vector();
746       PlanRecord rec, parent;
747       SuppliedDb given;
748       TaskDb db = context.TaskDb();
749       Goal goal;
750 
751       decomposition.reset();
752       while( (node = decomposition.nextNode()) != null ) {
753          Core.DEBUG(3,"expand_summary node ... " + node);
754          if ( !decomposition.isScheduled(node) &&
755               !decomposition.isQueued(node) ) {
756             consumed = decomposition.getPreconditions(node);
757             produced = decomposition.getPostconditions(node);
758             path = decomposition.getPath(node);
759             goal = decomposition.getGoal(node);
760             Core.DEBUG(3,"expand_summary: goal = " + goal);
761             sub_tasks = db.findAll(consumed,produced,path);
762             key = decomposition.getKey(node);
763             parent = decomposition.getParentRecord(node);
764             records = schedule(key,parent,path,goal,sub_tasks,struct,!EXPAND);
765             Core.DEBUG(3,"expand_summary record:");
766             Core.DEBUG(3,records);
767             if ( !records.isEmpty() ) {
768                decomposition.setRecords(node,records);
769                all_records = Misc.union(all_records,records);
770             }
771             else {
772                struct.decompositions.put(goal.getId(),decomposition);
773                given = decomposition.getSuppliedDb(node);
774                goal.setSuppliedDb(given);
775                decomposition.setQueued(node,true);
776             }
777          }
778       }
779       if ( decomposition.allNodesScheduled() ) {
780          decomposition.enforceLinks();
781          decomposition.reset();
782          while( (node = decomposition.nextNode()) != null ) {
783             Core.DEBUG(3,"expand_summary node: " + node);
784             rec = decomposition.getRecord(node); // REM ALL RECS
785             Core.DEBUG(3,"expand_summary getRecord: " + rec);
786             if ( rec != null ) {
787                key = decomposition.getKey(node);
788                path = decomposition.getPath(node);
789                records = schedule_children(key,rec,path,struct,mode);
790                all_records = Misc.union(all_records,records);
791             }
792          }
793       }
794       Core.DEBUG(3,"expand_summary end");
795       Core.DEBUG(3,struct);
796       return all_records;
797    }
798 
799    Vector schedule_primitive(String key, PlanRecord parent, Vector path,
800                              Goal goal, Vector tasks, PlannerQueryStruct struct,
801                              boolean mode) {
802 
803       Core.DEBUG(3,"schedule_primitive");
804       PrimitiveTask task = (PrimitiveTask)tasks.firstElement();
805       tasks.removeElementAt(0);
806 
807       PlanRecord rec;
808       int stime, etime, duration, top, lstime, ttime;
809       boolean space_found;
810       Fact consumed;
811       Vector sub_records;
812       Vector records = new Vector();
813 
814       ResourceDb db = context.ResourceDb();
815       SuppliedDb given = goal.getSuppliedDb();
816 
817       etime = goal.getEndTime();
818       ttime = task.getTime();
819       if ( goal.isContinuous() ) {
820          stime = goal.getStartTime();
821          lstime = stime-ttime;
822          duration = etime - lstime;
823          if ( duration/ttime < goal.getInvocations() )
824             return records;
825       }
826       else {
827          lstime = etime-ttime;
828          duration = ttime;
829       }
830 
831       if ( !validTime(etime)  ) return records;
832       if ( !validTime(lstime) ) return records;
833 
834       // check local & negation
835       for(int i = 0; i < task.countPreconditions(); i++ ) {
836          consumed = task.getPrecondition(i);
837          if ( consumed.isLocal() ) {
838             // Locals can be obtained from either the ResourceDb
839 	    // or the SuppliedDb
840             Core.DEBUG(3,"Checking consumed:\n" + consumed.pprint());
841             Core.DEBUG(3,"IsLocal: true");
842 
843             Fact x1 = null, x2 = null, x = null;
844             if ( given != null ) {
845                x1 = given.evalLocal(consumed);
846                Core.DEBUG(3,"SuppliedDb contains fact:\n" + x1.pprint());
847             }
848             x2 = db.evalLocal(consumed);
849             if ( x1 == null && x2 == null )
850                return records;
851 
852             if ( x1 != null && x2 != null ) {
853                Core.ERROR(x1.disjoin(x2),1016,this);
854                x = x1;
855             }
856             else if ( x1 == null )
857                x = x2;
858             else
859                x = x1;
860 
861             Core.DEBUG(3,"Db contains fact:\n" + x.pprint());
862             Bindings b = new Bindings(context.whoami());
863             consumed.unifiesWith(x,b);
864             task.resolve(b);
865             Core.DEBUG(3,"IsLocal bindings: " + b);
866          }
867          else if ( consumed.isNegative() ) {
868             Core.DEBUG(3,"Checking negative:\n" + consumed.pprint());
869             Core.DEBUG(3,"IsNegative: true");
870 
871             if ( !db.evalNegative(consumed) )
872                return records;
873 
874             Core.DEBUG(3,"Db does not contain fact:\n" + consumed.pprint());
875          }
876       }
877 
878       for(top = etime-1; validTime(top-duration); top-- ) {
879          for(int proc = 0; proc < plannerWidth; proc++ ) {
880             space_found = true;
881             for(int i = top; space_found && i > top-duration; i-- )
882                space_found = isFreeCell(proc,i) & space_found;
883             if ( space_found ) {
884                rec = new PlanRecord(this,key,parent,goal,task,proc,
885                                     top-duration+1,top+1);
886                rec.setPath(Misc.copyVector(path));
887                rec.setAlternativeTasks(tasks);
888 
889                for(int i = top; i > top-duration; i-- )
890                   assignCell(proc,i,rec);
891 
892                struct.internal.addElement(rec);
893                records.addElement(rec);
894                path.addElement(goal.getFact());
895 
896                sub_records = schedule_children(key,rec,path,struct,mode);
897                records = Misc.union(records,sub_records);
898                return records;
899             }
900          }
901       }
902 
903       // !space_found
904 
905       Core.DEBUG(3,"Space not found for: " + goal.getFactType());
906       Core.DEBUG(3,"tduration = " + duration);
907       Core.DEBUG(3,"tetime = " + etime);
908       Core.DEBUG(3,"tlstime = " + lstime);
909 
910       return records;
911    }
912 
913    protected Vector schedule_children(String key, PlanRecord rec, Vector path,
914                                       PlannerQueryStruct struct, boolean mode) {
915 
916       Vector external, subgoals, sub_records, lpath;
917       Vector records = new Vector();
918       ResourceDb db = context.ResourceDb();
919       SuppliedDb given = rec.getSuppliedDb();
920 
921       if ( mode == EXPAND ) {
922          external = struct.external;
923          struct.external = new Vector();
924          do {
925             if ( given != null )
926                given.allocateResources(rec);
927             subgoals = db.allocateResources(rec);
928             if ( !subgoals.isEmpty() ) {
929                lpath = Misc.copyVector(path);
930                sub_records = schedule(key,rec,lpath,subgoals,struct,mode);
931                records = Misc.union(records,sub_records);
932             }
933          } while ( struct.external.isEmpty() && !subgoals.isEmpty() );
934          struct.external = Misc.union(struct.external,external);
935       }
936       return records;
937    }
938 
939 
940    protected void resume_planning(DelegationStruct[] ds,
941                                   PlannerQueryStruct struct) {
942       Goal g;
943       boolean found;
944       Decomposition decomposition = null;
945       String key;
946       Vector path, records;
947       PlanRecord rec = null;
948 
949       Core.DEBUG(3,"resume_planning ... ds/struct");
950       Core.DEBUG(3,ds);
951       Core.DEBUG(3,struct);
952 
953       String consumer, consumer_id;
954       String producer, producer_id;
955       String use_ref;
956       int amount, start;
957       boolean consumed;
958 
959       struct.external.removeAllElements();
960       for(int i = 0; i < ds.length; i++ ) {
961          // assume only one goal in request
962          g = (Goal)ds[i].goals.elementAt(0);
963 
964          decomposition = (Decomposition)struct.decompositions.remove(g.getId());
965          if ( decomposition == null ) {
966             found = false;
967             for(int j = 0; !found && j < struct.internal.size(); j++ ) {
968                rec = (PlanRecord)struct.internal.elementAt(j);
969                found = rec.hasSubgoal(g.getId());
970             }
971             Core.ERROR(found,1017,this);
972 
973             producer = ds[i].agent;
974             producer_id = g.getId();
975             consumer = context.whoami();
976             consumer_id = rec.getGoal().getId();
977             use_ref = context.newId("used");
978             start = rec.getStartTime();
979             int precond_position = rec.getConsumedPosition(producer_id);
980             consumed = rec.isPreconditionConsumed(precond_position);
981             amount = rec.getAmountUsed(precond_position);
982             Core.ERROR(amount > 0,1031,this);
983 
984             g.addConsumer(producer, producer_id, consumer, consumer_id,
985                           use_ref, ds[i].key, start, amount, consumed);
986 
987             rec.getConsumedDb().update(producer_id+"/"+use_ref, producer_id);
988 
989             path = rec.getChildPath();
990             key = rec.getKey();
991             schedule_children(key,rec,path,struct,EXPAND);
992          }
993          else {
994             String node = decomposition.getNodeWithGoalId(g.getId());
995             Core.DEBUG(3,"getNodeWithGoalId " + g.getId() + " " + node);
996             decomposition.setImage(node,g,ds[i].agent,ds[i].key);
997             expand_summary(decomposition,struct,EXPAND);
998          }
999       }
1000    }
1001 
1002    public void goalConfirmed(Vector original_goals, Vector confirmed_goals,
1003                              Vector selection) {
1004       Goal g, g1;
1005       Vector records;
1006       DelegationStruct ds;
1007       ProducerRecord pr;
1008       ConsumerRecord cr;
1009       PlanRecord rec;
1010       ConsumedDb cdb;
1011       ProducedDb pdb;
1012       boolean test;
1013       SuppliedDb given;
1014 /*
1015    REM: some check may be made with original_goals
1016 */
1017       for(int i = 0; i < confirmed_goals.size(); i++ ) {
1018          g = (Goal)confirmed_goals.elementAt(i);
1019 
1020          records = g.getProducerRecords();
1021          for(int j = 0; records != null && j < records.size(); j++ ) {
1022             pr = (ProducerRecord)records.elementAt(j);
1023             if ( pr.consumer.equals(context.whoami()) ) {
1024                rec = (PlanRecord)this.get(pr.consumer_id);
1025                Core.ERROR(rec,1001,this);
1026                cdb = rec.getConsumedDb();
1027                test = cdb.update(pr.producer_id+"/"+pr.use_ref,pr.use_ref);
1028                Core.ERROR(test,1002,this);
1029             }
1030             else {
1031                for(int k = 0; k < selection.size(); k++ ) {
1032                   ds = (DelegationStruct)selection.elementAt(k);
1033                   // Assumes one element only in ds.goals
1034                   g1 = (Goal)ds.goals.elementAt(0);
1035                   given = g1.getSuppliedDb();
1036                   if ( given != null && given.isReserved(pr.supply_ref) )
1037                      g1.addProducer(pr);
1038                }
1039             }
1040          }
1041 
1042          records = g.getConsumerRecords();
1043          for(int j = 0; records != null && j < records.size(); j++ ) {
1044             cr = (ConsumerRecord)records.elementAt(j);
1045             if ( cr.producer.equals(context.whoami()) ) {
1046                rec = (PlanRecord)this.get(cr.producer_id);
1047                Core.ERROR(rec,1003,this);
1048                pdb = rec.getProducedDb();
1049                Core.DEBUG(3,"CdB replacing " + cr.producer_id + " with " +
1050                                  cr.consumer_id+"/"+cr.use_ref);
1051                Core.DEBUG(3,records);
1052                Core.DEBUG(3,rec);
1053                
1054                test = pdb.replaceOrAdd(cr.producer_id,
1055                                        cr.consumer_id + "/" + cr.use_ref,
1056                                        cr.start, cr.amount, cr.consumed );
1057                Core.DEBUG(3,"Replacement is " + test);
1058                Core.ERROR(test,1004,this);
1059             }
1060             else {
1061                for(int k = 0; k < selection.size(); k++ ) {
1062                   ds = (DelegationStruct)selection.elementAt(k);
1063                   // Assumes one element only in ds.goals
1064                   g1 = (Goal)ds.goals.elementAt(0);
1065                   g1.addConsumer(cr);
1066                }
1067             }
1068          }
1069       }
1070    }
1071 
1072    protected boolean validTime(int t) {  
1073       return (now + plannerLength >= t && t >= now);
1074    }
1075 
1076    protected boolean validProc(int proc) {
1077       return ( proc >= 0 && proc < plannerWidth);
1078    }
1079 
1080    protected boolean isFreeCell(int proc, int t) {
1081       Core.ERROR(proc >= 0 && proc < plannerWidth, 1018,this);
1082       Core.ERROR(now + plannerLength > t && t >= now, 1019,this);
1083       return (table[proc][t-now] == null);
1084    }
1085 
1086     /*** 
1087         1.3 promoted to public - used to decide which job to execute
1088     */
1089     //synchronized
1090   public void shuffle() {
1091       checkRecords();
1092       for(int i = 0; i < plannerWidth; i++ ) {
1093          for(int j = 0; j < plannerLength-1; j++ )
1094             assignCell(i,j+now,table[i][j+1]);
1095          assignCell(i,plannerLength-1+now,null);
1096       }
1097       now++;
1098    }
1099 
1100 //synchronized
1101     synchronized void  executeEarliest() {
1102       PlanRecord rec;
1103       Enumeration enum  = this.elements();
1104       while( enum.hasMoreElements() ) {
1105          rec = (PlanRecord)enum.nextElement();
1106          if ( rec.getState() == PlanRecord.FIRM &&
1107               rec.isDiscrete() &&
1108               rec.getStartTime() > now &&
1109               rec.hasEnoughResources() )
1110             executeEarliest(rec);
1111       }
1112    }
1113    
1114    
1115    //synchronized           
1116     synchronized void executeEarliest(PlanRecord rec) {
1117       int stime, etime, duration, top;
1118       boolean space_found;
1119 
1120       etime = rec.getEndTime();
1121       stime = rec.getStartTime();
1122       duration = etime - stime;
1123 
1124       if ( !validTime(etime) ) return;
1125       if ( !validTime(stime) ) return;
1126 
1127       for( top = now+1; top < stime; top++ ) {
1128          for( int proc = 0; proc < plannerWidth; proc++ ) {
1129             space_found = true;
1130             for( int i = top; i < top+duration; i++ ) {
1131                space_found = space_found && (isFreeCell(proc,i) ||
1132                              rec.isOnCell(proc,i));
1133                if ( !space_found ) break;
1134             }
1135             if ( space_found ) {
1136                Core.DEBUG(3,"Reassigning rec " + rec.getId() +
1137                                  " from [" + stime + "," + etime + "] to [" +
1138                                  top + "," + (top+duration) +"]");
1139                rec.reassign(proc,top);
1140                for(int i = top; i < top+duration; i++ )
1141                   assignCell(proc,i,rec);
1142                return;
1143             }
1144          }
1145       }
1146    }
1147 
1148 
1149     // synchronized
1150    synchronized boolean incrementProcessorTime(PlanRecord rec,int time) {
1151       if ( !validTime(time) ) return false;
1152       if ( isFreeCell(rec.getProc(),time) ) {
1153          rec.incrementTime(time);
1154          assignCell(rec.getProc(),time,rec);
1155          return true;
1156       }
1157       else {
1158          // shift other records to different processors if possible
1159          // then rec.incrementTime()
1160       }
1161       return false;
1162    }
1163 
1164 
1165     /*** 
1166         start Firmly booked tasks & cancel Tentatively booked ones
1167         1.3 - promoted to public 
1168         // synched ?
1169         */
1170    synchronized public void checkRecords() { 
1171       PlanRecord rec, root;
1172       ExecutionMonitor monitor = context.ExecutionMonitor();
1173       for( int i = 0; i < plannerWidth; i++ ) {
1174          if ( (rec = table[i][0]) != null ) {
1175             switch( rec.getState() ) {
1176                case PlanRecord.JEOPARDY:
1177                case PlanRecord.AGREEMENT:
1178                   break;
1179                case PlanRecord.TEMP:
1180                case PlanRecord.TENTATIVE:
1181                   // NOTIFY MONITORS FREE CELL
1182                   root = rec.getRoot();
1183                   root.dispose();
1184                   break;
1185                case PlanRecord.FIRM:
1186                   if ( rec.exec() )
1187                      ; // NOTIFY MONITORS START CELL
1188                   break;
1189                case PlanRecord.RUNNING:
1190                   if ( rec.overRun() )
1191                      ; // NOTIFY MONITORS STOP CELL
1192                   else 
1193                      break;
1194                case PlanRecord.FAILED:
1195                   // NOTIFY MONITORS CELL FAILED
1196                   root = rec.getRoot();
1197                   root.dispose();
1198                   break;
1199                case PlanRecord.COMPLETED:
1200                   // NOTIFY MONITORS CELL COMPLETED
1201                   rec.dispose();
1202                   break;
1203             }
1204          }
1205       }
1206       if ( context.getExecuteEarliest() ) executeEarliest();
1207    }
1208  
1209  
1210    public void notifyReceived(Fact f1, String goalId, String subgoalId) {
1211     try {
1212       Core.DEBUG(3,"NotifyReceived...\n" + f1.pprint());
1213       PlanRecord rec = (PlanRecord)this.get(goalId);
1214       Core.ERROR(rec,1020,this);
1215       context.ResourceDb().add(f1);
1216       rec.preconditionExists(subgoalId);}
1217       catch (Exception e) { 
1218         e.printStackTrace(); 
1219       }
1220       
1221    }
1222 
1223    protected void assignCell(int proc, int t, PlanRecord rec) {
1224       Core.ERROR(proc >= 0 && proc < plannerWidth,1021,this);
1225       Core.ERROR(now + plannerLength > t && t >= now,1022,this);
1226       table[proc][t-now] = rec;
1227    }
1228 
1229    void freeCell( int proc, int t) {
1230       Core.ERROR(proc >= 0 && proc < plannerWidth,1023,this);
1231       if ( now + plannerLength > t && t >= now ) {
1232          table[proc][t-now] = null;
1233       }
1234    }
1235 
1236    void freeCells(int proc, int s, int e) {
1237       for(int i = s; i < e; i++ ) freeCell(proc,i);
1238    }
1239 
1240    public Goal recreateSubgoal(Goal goal) {
1241       PlanRecord rec;
1242       String goalId = goal.getId();
1243       Enumeration enum = this.elements();
1244       while( enum.hasMoreElements() ) {
1245          rec = (PlanRecord) enum.nextElement();
1246          if ( rec.hasSubgoal(goalId) )
1247             return rec.recreateSubgoal(goal);
1248       }
1249       Goal g = new Goal(goal);
1250       g.setId(context.newId("subgoal"));
1251       g.setImage(goal.getId());
1252       g.setConfirmTime(new Time(now() +
1253                        context.getReplanPeriod()));
1254       return g;
1255    }
1256 
1257 
1258    public void reconfirmParentOf(Vector goals) {
1259       for( int i = 0; i < goals.size(); i++ )
1260          reconfirmParentOf( (Goal)goals.elementAt(i) );
1261    }
1262 
1263    
1264    public void reconfirmParentOf(Goal g) {
1265       PlanRecord rec;
1266       String goalId = g.getId();
1267       Enumeration enum = this.elements();
1268       while( enum.hasMoreElements() ) {
1269          rec = (PlanRecord) enum.nextElement();
1270          if ( rec.hasSubgoal(goalId) ) {
1271             rec.reconfirm();
1272             return;
1273          }
1274          else if ( rec.getGoal().getId().equals(goalId) ) {
1275             if ( rec.getParent() != null ) {
1276                rec.getParent().reconfirm();
1277                return;
1278             }
1279          }
1280       }
1281       Core.DEBUG(3,"reconfirmParentOf error ...  " + g);
1282       Core.ERROR(null,1024,this);
1283    }
1284    
1285    
1286    public void failParentOf(Vector goals) {
1287       for( int i = 0; i < goals.size(); i++ )
1288          failParentOf( (Goal)goals.elementAt(i) );
1289    }
1290    
1291    
1292    public void failParentOf(Goal g) {
1293       PlanRecord rec;
1294       String goalId = g.getId();
1295       Enumeration enum = this.elements();
1296       while( enum.hasMoreElements() ) {
1297          rec = (PlanRecord) enum.nextElement();
1298          if ( rec.hasSubgoal(goalId) ) {
1299             rec.setState(PlanRecord.FAILED);
1300             return;
1301          }
1302       }
1303       Core.DEBUG(3,"failParentOf error ...  " + g);
1304 //      Core.ERROR(null,1025,this);
1305    }
1306    
1307    
1308    void softFailParentOf(Vector goals, PlannerQueryStruct struct, int mode) {
1309       Core.DEBUG(3,"softFailParentOf Goal = " + goals);
1310       Core.DEBUG(3,"softFailParentOf PlannerQueryStruct = " + struct);
1311 
1312       PlanRecord rec = null;
1313       Goal g, goal = null;
1314       String goalId;
1315       boolean found;
1316       Decomposition decomposition;
1317 
1318       for(int i = 0; i < goals.size(); i++ ) {
1319          g = (Goal)goals.elementAt(i);
1320          goalId = g.getId();
1321 
1322          // remove unachievable goal (g) from external list
1323          for(int j = 0; j < struct.external.size(); j++ ) {
1324             goal = (Goal)struct.external.elementAt(j);
1325             if ( goalId.equals(goal.getId()) ) {
1326                struct.external.removeElementAt(j--);
1327                break;
1328             }
1329          }
1330                
1331          decomposition = (Decomposition)struct.decompositions.remove(g.getId());
1332          if ( decomposition != null ) {
1333             // decomposition.softFail(struct,mode);
1334          }
1335          else {
1336             found = false;
1337             for(int j = 0; !found && j < struct.internal.size(); j++ ) {
1338                rec = (PlanRecord) struct.internal.elementAt(j);
1339                found = rec.hasSubgoal(goalId);
1340             }
1341             if ( found ) // swapped !found to found
1342                rec.softFail(struct,mode);
1343          }
1344       }
1345 
1346       // check records for validity
1347       for(int i = 0; i < struct.internal.size(); i++ ) {
1348          rec = (PlanRecord)struct.internal.elementAt(i);
1349          if ( !containsRecord(rec) )
1350             struct.internal.removeElementAt(i--);
1351       }
1352       // check decompositions for validity
1353       Enumeration enum = struct.decompositions.keys();
1354       while( enum.hasMoreElements() ) {
1355          goalId = (String)enum.nextElement();
1356          found = false;
1357          for(int i = 0; !found && i < struct.external.size(); i++ ) {
1358             goal = (Goal)struct.external.elementAt(i);
1359             found = goalId.equals(goal.getId());
1360          }
1361          if ( !found ) struct.decompositions.remove(goalId);
1362       }
1363    }
1364 
1365    /***
1366     * Use a PlanningMonitor if your code needs to react to changes in the Planner
1367     */
1368    public void addPlanningMonitor(PlanningMonitor monitor, long type) {
1369       if ( (type & PlanningEvent.START_MASK) != 0 )
1370          eventMonitor[START].add(monitor);
1371       if ( (type & PlanningEvent.FAIL_MASK) != 0 )
1372          eventMonitor[FAIL].add(monitor);
1373       if ( (type & PlanningEvent.SUCCEED_MASK) != 0 )
1374          eventMonitor[SUCCEED].add(monitor);
1375    }
1376 
1377    public void removePlanningMonitor(PlanningMonitor monitor, long type) {
1378       if ( (type & PlanningEvent.START_MASK) != 0 )
1379          eventMonitor[START].remove(monitor);
1380       if ( (type & PlanningEvent.FAIL_MASK) != 0 )
1381          eventMonitor[FAIL].remove(monitor);
1382       if ( (type & PlanningEvent.SUCCEED_MASK) != 0 )
1383          eventMonitor[SUCCEED].remove(monitor);
1384    }
1385 
1386    /***
1387     * Use a PlanStepMonitor if your code needs to react to state changes in a
1388     * particular plan
1389     */
1390    public void addPlanStepMonitor(PlanStepMonitor monitor, long type,
1391                                   boolean notify_previous) {
1392       addPlanStepMonitor(monitor,type);
1393       if ( !notify_previous ) return;
1394 
1395       Enumeration enum = elements();
1396       PlanRecord record;
1397       PlanStepEvent event;
1398 
1399       while( enum.hasMoreElements() ) {
1400          record = (PlanRecord)enum.nextElement();
1401          event = new PlanStepEvent(this,record,PlanStepEvent.CREATE_MASK);
1402          monitor.planStepCreatedEvent(event);
1403       }
1404    }
1405 
1406    public void addPlanStepMonitor(PlanStepMonitor monitor, long type) {
1407       if ( (type & PlanStepEvent.CREATE_MASK) != 0 )
1408          eventMonitor[CREATE].add(monitor);
1409       if ( (type & PlanStepEvent.DISPOSE_MASK) != 0 )
1410          eventMonitor[DISPOSE].add(monitor);
1411       if ( (type & PlanStepEvent.STATE_CHANGE_MASK) != 0 )
1412          eventMonitor[STATE_CHANGE].add(monitor);
1413    }
1414 
1415    public void removePlanStepMonitor(PlanStepMonitor monitor, long type) {
1416       if ( (type & PlanStepEvent.CREATE_MASK) != 0 )
1417          eventMonitor[CREATE].remove(monitor);
1418       if ( (type & PlanStepEvent.DISPOSE_MASK) != 0 )
1419          eventMonitor[DISPOSE].remove(monitor);
1420       if ( (type & PlanStepEvent.STATE_CHANGE_MASK) != 0 )
1421          eventMonitor[STATE_CHANGE].remove(monitor);
1422    }
1423 
1424    void notifyMonitors(Goal goal, int event_type, int sub_type) {
1425       if ( eventMonitor[event_type].isEmpty() ) return;
1426 
1427       Enumeration enum = eventMonitor[event_type].elements();
1428       PlanningMonitor monitor;
1429       PlanningEvent event;
1430       switch(event_type) {
1431          case START:
1432               event = new PlanningEvent(this,goal,PlanningEvent.START_MASK,sub_type);
1433               while( enum.hasMoreElements() ) {
1434                  monitor = (PlanningMonitor)enum.nextElement();
1435                  monitor.planningStartedEvent(event);
1436               }
1437               break;
1438          case FAIL:
1439               event = new PlanningEvent(this,goal,PlanningEvent.FAIL_MASK,sub_type);
1440               while( enum.hasMoreElements() ) {
1441                  monitor = (PlanningMonitor)enum.nextElement();
1442                  monitor.planningFailedEvent(event);
1443               }
1444               break;
1445          case SUCCEED:
1446               event = new PlanningEvent(this,goal,PlanningEvent.SUCCEED_MASK,sub_type);
1447               while( enum.hasMoreElements() ) {
1448                  monitor = (PlanningMonitor)enum.nextElement();
1449                  monitor.planningSucceededEvent(event);
1450               }
1451               break;
1452       }
1453    }
1454 
1455    void notifyMonitors(PlanRecord record, int type) {
1456       if ( eventMonitor[type].isEmpty() ) return;
1457 
1458       Enumeration enum = eventMonitor[type].elements();
1459       Core.ERROR(enum,2001,this);
1460       PlanStepMonitor monitor;
1461       PlanStepEvent event;
1462       switch(type) {
1463          case CREATE:
1464               event = new PlanStepEvent(this,record,PlanStepEvent.CREATE_MASK);
1465               while( enum.hasMoreElements() ) {
1466                  monitor = (PlanStepMonitor)enum.nextElement();
1467                  monitor.planStepCreatedEvent(event);
1468               }
1469               break;
1470          case DISPOSE:
1471               event = new PlanStepEvent(this,record,PlanStepEvent.DISPOSE_MASK);
1472               while( enum.hasMoreElements() ) {
1473                  monitor = (PlanStepMonitor)enum.nextElement();
1474                  monitor.planStepDisposedEvent(event);
1475               }
1476               break;
1477          case STATE_CHANGE:
1478               event = new PlanStepEvent(this,record,PlanStepEvent.STATE_CHANGE_MASK);
1479               while( enum.hasMoreElements() ) {
1480                  monitor = (PlanStepMonitor)enum.nextElement();
1481                  monitor.planStepStateChangedEvent(event);
1482               }
1483               break;
1484       }
1485    }
1486    
1487    
1488    /*** 
1489     now introduced to prevent the need for a call back to the context object 
1490     to get timeing - decouples the Planner a bit..
1491     */
1492    protected double now () { 
1493     return context.now(); 
1494    }
1495    
1496 }