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   * @(#)ProducedDb.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.Engine;
34  import zeus.actors.rtn.util.DelegationStruct;
35  
36  
37  /***
38   * The Produced Resources Database is an internal storage component used by
39   * the {@link Planner} in the course of its activities. It is unlikely
40   * that developers will need to call these methods directly.
41   */
42  
43  public class ProducedDb
44  {
45     protected Vector[] data = null;
46     protected int[] produced = null;
47     protected PlanRecord owner = null;
48  
49  
50     public ProducedDb () {
51     ;
52     }
53     
54  
55     public ProducedDb(PlanRecord owner, PrimitiveTask task) {
56        Assert.notNull(owner);
57        produced = task.numPostconditions();
58        this.owner = owner;
59        data = new Vector[produced.length];
60     }
61     
62     
63     public synchronized void add(int effect_position, PlanRecord parent,
64                                  int amount, String goal_id, boolean consumed) {
65        Assert.notFalse(effect_position >= 0 && effect_position < data.length);
66  
67        if ( data[effect_position] == null)
68             data[effect_position] = new Vector();
69  
70        EffectChain ch;
71        int start;
72        if ( parent != null ) {
73           int precond_position = parent.getConsumedPosition(goal_id);
74           start = parent.getStartTime();
75           Assert.notFalse(availableItems(effect_position,start,consumed) >=
76                           amount);
77           ch = new EffectChain(parent,precond_position,amount,start,consumed);
78           parent.replacePrecondition(goal_id,owner,effect_position,amount);
79        }
80        else {
81           start = owner.getEndTime();
82           Assert.notFalse(availableItems(effect_position,start,consumed) >= 
83                           amount);
84           ch = new EffectChain(goal_id,amount,start,consumed);
85        }
86        data[effect_position].addElement(ch);
87     }
88     
89     
90     public synchronized void add(int effect_position, EffectChain ch) {
91        Assert.notFalse(effect_position >= 0 && effect_position < data.length);
92        Assert.notFalse(availableItems(effect_position,ch.start,ch.consumed) >=
93                        ch.amount);
94  
95        if ( data[effect_position] == null)
96             data[effect_position] = new Vector();
97        data[effect_position].addElement(ch);
98     }
99     
100    
101    public int noItemsProduced(int position) {
102       return produced[position];
103    }
104    
105    
106    protected synchronized int availableItems(int position, int start,
107                                              boolean consumed) {
108       if ( data[position] == null ) return produced[position];
109       EffectChain ch;
110       int amount = 0;
111       for(int i = 0; i < data[position].size(); i++ ) {
112          ch = (EffectChain)data[position].elementAt(i);
113          if ( consumed ) {
114             if ( ch.consumed || ch.start > start ) 
115                amount += ch.amount;
116          }
117          else {
118             if ( ch.consumed && ch.start <= start ) 
119                amount += ch.amount;
120          }
121       }
122       return produced[position] - amount;
123    }
124    
125    
126    public synchronized int anySideEffect(int effect_position, PlanRecord parent,
127                                          Object precond, int required) {
128       int precond_position = -1;
129       String goal_id = null;
130       boolean is_string = false;
131       if ( precond instanceof String ) {
132          goal_id = (String)precond;
133          precond_position = parent.getConsumedPosition(goal_id);
134          is_string = true;
135       }
136       else { // must be an int
137          precond_position = ((Integer)precond).intValue();
138       }
139           
140       int start = parent.getStartTime();
141       boolean consumed = parent.isPreconditionConsumed(precond_position);
142       int available = availableItems(effect_position,start,consumed);
143       if ( available == 0 )
144          return required;
145 
146       Core.DEBUG(2,"ProducedDb checking for serendipitous effects");
147 
148       if ( data[effect_position] == null )
149          data[effect_position] = new Vector();
150 
151       EffectChain ch;
152       int amount = (available >= required) ? required : available;
153       ch = new EffectChain(parent,precond_position,amount,start,consumed);
154       data[effect_position].addElement(ch);
155       if ( is_string )
156         parent.replacePrecondition(goal_id,owner,effect_position,amount);
157       else
158         parent.chainPrecondition(owner,effect_position,amount,precond_position);
159 
160       return required - amount;
161    }
162 
163 
164    public synchronized void hardChain(int effect_position, int required,
165                                       PlanRecord parent, int precond_position) {
166 
167       Core.DEBUG(2,"ProducedDb hardChain effects ...1");
168       Core.DEBUG(2,owner.toString() + "::" + effect_position);
169       Core.DEBUG(2," ==");
170       Core.DEBUG(2,parent.toString() + "::" + precond_position);
171 
172       int start = parent.getStartTime();
173       boolean consumed = parent.isPreconditionConsumed(precond_position);
174       int available = availableItems(effect_position,start,consumed);
175 
176       if ( available == 0 ) {
177          System.err.println("ProducedDb hardChain effects: available == 0");
178          return;
179       }
180 
181       if ( data[effect_position] == null )
182          data[effect_position] = new Vector();
183 
184       EffectChain ch;
185       int amount = (available >= required) ? required : available;
186       ch = new EffectChain(parent,precond_position,amount,start,consumed);
187       data[effect_position].addElement(ch);
188       parent.chainPrecondition(owner,effect_position,amount,precond_position);
189    }
190    
191    
192    public synchronized void hardChain(int effect_position, String key,
193                                       int amount, int start, boolean consumed) {
194 
195       Core.DEBUG(2,"ProducedDb hardChain effects ...2");
196       Core.DEBUG(2,"effect_position: " + effect_position);
197       Core.DEBUG(2,"key: " + key);
198       Core.DEBUG(2,"amount: " + amount);
199       Core.DEBUG(2,"start: " + start);
200       Core.DEBUG(2,"consumed: " + consumed);
201 
202       Assert.notFalse(effect_position >= 0 && effect_position < data.length);
203 
204       if ( data[effect_position] == null )
205            data[effect_position] = new Vector();
206 
207       Core.DEBUG(2,"Current chains...");
208       Core.DEBUG(2,data[effect_position]);
209 
210       EffectChain ch;
211       Assert.notFalse(availableItems(effect_position,start,consumed)>=amount);
212       ch = new EffectChain(key,amount,start,consumed);
213       data[effect_position].addElement(ch);
214    }
215    
216    
217    public void allocatePostconditions(Fact[][] fact) {
218     debug (fact.toString()); 
219       for(int i = 0; i < fact.length; i++ ) {
220         debug (fact[i].toString()); 
221          allocatePostcondition(i,fact[i]);}
222    }
223    
224    
225    protected void allocatePostcondition(int position, Fact[] input) {
226       // REM: for now assume only one item per condition
227       Fact fact = input[0];
228 
229       int available;
230       debug ("in producedDB " + String.valueOf(position)); 
231       debug ("in producedDB "  + fact.toString()); 
232       if ( (available = fact.getNumber()) == 0 ) {
233          Core.USER_ERROR("Warning: integer expected in .fact.no field." +
234          "\nEnsure \'" + OntologyDb.NUMBER + "\' constraints are defined " +
235          "in all task specifications");
236          return;
237       }
238       if ( available < produced[position] ) {
239          Core.USER_ERROR("\nWarning: fewer items produced: " + available +
240                          " than expected " + produced[position]);
241          Core.USER_ERROR(fact.pprint());
242       }
243 
244       ResourceDb db = owner.getAgentContext().ResourceDb();
245       if ( data[position] == null ) {
246          db.replaceOrAdd(fact);
247          return;
248       }
249 
250       Fact f1;
251       Engine engine = owner.getAgentContext().Engine();
252       DelegationStruct ds;
253       EffectChain ch;
254 
255       for(int i = 0; available > 0 && i < data[position].size(); i++) {
256          ch = (EffectChain)data[position].elementAt(i);
257          if ( ch.isExternal() && available >= ch.amount ) {
258             if ( ch.consumed ) available = available - ch.amount;
259             f1 = new Fact(Fact.FACT, fact);
260             f1.setNumber(ch.amount);
261             ds = new DelegationStruct(owner.getAgentContext().whoami(),
262                                       "result", ch.key, f1);
263             debug (ds.toString());                           
264             engine.add(ds);
265          }
266       }
267       if ( available > 0 ) {
268          fact.setNumber(available);
269          db.replaceOrAdd(fact);
270       }
271       for(int i = 0; i < data[position].size(); i++) {
272          ch = (EffectChain)data[position].elementAt(i);
273          if ( !ch.isExternal() ) {
274             f1 = new Fact(Fact.VARIABLE, fact);
275             f1.setNumber(ch.amount);
276             ch.record.preconditionExists(owner,position,ch.amount,ch.position);
277          }
278       }
279    }
280    
281    /// i think that this may be responsible for ye oldy precondition unification. 
282    public boolean constrain(Bindings bindings) {
283       boolean status = true;
284       Vector List;
285       EffectChain ch;
286       for(int i = 0; status && i < data.length; i++ ) {
287          List = data[i];
288          for(int j = 0; status && List != null && j < List.size(); j++ ) {
289             ch = (EffectChain)List.elementAt(j);
290             if ( !ch.isExternal() )
291             // &= !!! i ask you..... bitwise and assignment - if you 
292             // find a true ever then make status true...
293                status &= ch.record.applyConstraints(bindings);
294          }
295       }
296       return status;
297    }
298 
299 
300    public PlanRecord getOwner() { return owner; }
301 
302 
303 
304    public synchronized void share(ProducedDb db,
305       PlanRecord parent_image, String key_image, 
306       PlanRecord parent, String key) {
307 
308       int required;
309       EffectChain ch;
310       for(int i = 0; i < produced.length; i++) {
311          required = db.noItemsProduced(i);
312          for(int j = 0; required > 0 && j < data[i].size(); j++ ) {
313             ch = (EffectChain)data[i].elementAt(j);
314             if ( ch.amount <= required ) {
315                // remove ch from data[i]
316                data[i].removeElementAt(j--);
317                required = required - ch.amount;
318                // now add ch to db at position i
319             }
320             else {
321                ch.amount = ch.amount - required;
322                // create new ch for db at position i
323                ch = new EffectChain(ch);
324                ch.amount = required;
325                required = 0;
326                // now add ch to db at position i
327             }
328 
329             if ( ch.isExternal() ) {
330                if ( ch.key.equals(key) )
331                   ch.key = key_image;
332             }
333             else {
334                if ( ch.record == parent ) 
335                   ch.record = parent_image;
336             }
337             // now adding ch to db at position i
338             db.add(i,ch);
339          } 
340       }
341    }
342    
343    
344    public synchronized boolean update(PlanRecord image, PlanRecord record) {
345       EffectChain ch;
346       for(int i = 0; i < data.length; i++ ) {
347          for(int j = 0; data[i] != null && j < data[i].size(); j++ ) {
348             ch = (EffectChain)data[i].elementAt(j);
349             if ( !ch.isExternal() && ch.record == record ) {
350                ch.record = image;
351                return true;
352             }
353          }
354       }
355       return false;
356    }
357    
358    
359    public synchronized boolean update(String image, String key) {
360       EffectChain ch;
361       for(int i = 0; i < data.length; i++ ) {
362          for(int j = 0; data[i] != null && j < data[i].size(); j++ ) {
363             ch = (EffectChain)data[i].elementAt(j);
364             if ( ch.isExternal() && ch.key.equals(key) ) {
365                ch.key = image;
366                return true;
367             }
368          }
369       }
370       return false;
371    }
372    
373    
374    public synchronized boolean replaceOrAdd(String key, String newKey,
375                                             int start, int amount,
376                                             boolean consumed) {
377       EffectChain ch;
378       for(int i = 0; i < data.length; i++ ) {
379          for(int j = 0; data[i] != null && j < data[i].size(); j++ ) {
380             ch = (EffectChain)data[i].elementAt(j);
381             if ( ch.isExternal() && ch.key.equals(key) ) {
382                Core.DEBUG(2,ch);
383                if ( ch.start == start && ch.amount == amount &&
384                     ch.consumed == consumed ) {
385                   // replacement valid
386                   ch.key = newKey;
387                   return true;
388                }
389 /*
390    Special case:
391 */
392                else if ( ch.start <= start && ch.amount == amount &&
393                     ch.consumed == true && consumed == true ) {
394                   // replacement valid
395                   ch.key = newKey;
396                   ch.start = start;
397                   return true;
398                }
399                else {
400                   ch = new EffectChain(newKey,amount,start,consumed);
401                   add(i,ch);
402                   return true;
403                }
404             }
405          }
406       }
407       ch = new EffectChain(newKey,amount,start,consumed);
408       add(owner.getTask().getActiveEffectPos(),ch);
409       return true;
410    }
411    
412    
413    public synchronized boolean hasAtMostOneParent(PlanRecord parent, String key) {
414       Vector records = new Vector();
415       Vector keys = new Vector();
416       EffectChain ch;
417       for(int i = 0; i < data.length; i++ ) {
418          for(int j = 0; data[i] != null && j < data[i].size(); j++ ) {
419             ch = (EffectChain)data[i].elementAt(j);
420             if ( ch.isExternal() ) {
421                if ( !keys.contains(ch.key) )
422                   keys.addElement(ch.key);
423             }
424             else {
425                if ( !records.contains(ch.record) )
426                   records.addElement(ch.record);
427             }
428          }
429       }
430       Core.DEBUG(3,"ProducedDb hasAtMostOneParent: parent = " + parent + " key = " + key);
431       Core.DEBUG(3,"ProducedDb hasAtMostOneParent: records = " + records);
432       Core.DEBUG(3,"ProducedDb hasAtMostOneParent: keys = " + keys);
433 
434       if ( parent != null ) {
435          return keys.isEmpty() && records.size() == 1 &&
436                 records.contains(parent);
437       }
438       else {
439          return records.isEmpty() && keys.size() == 1 && 
440 	        (keys.contains(key) || keys.contains(owner.getGoal().getId()));
441       }
442    }
443    
444    
445    public synchronized void remove(int effect_position,
446                                    PlanRecord parent,
447                                    int precond_position, int amount) {
448       EffectChain ch;
449       for(int j = 0; j < data[effect_position].size(); j++ ) {
450          ch = (EffectChain)data[effect_position].elementAt(j);
451          if ( ch.record == parent && ch.position == precond_position &&
452               ch.amount == amount ) {
453             data[effect_position].removeElementAt(j--);
454             return;
455          }
456       }
457       Assert.notNull(null);
458    }
459    
460    
461    public synchronized boolean references(PlanRecord parent) {
462       EffectChain ch;
463       for(int i = 0; i < data.length; i++ ) {
464          for(int j = 0; data[i] != null && j < data[i].size(); j++ ) {
465             ch = (EffectChain)data[i].elementAt(j);
466             if ( !ch.isExternal() && ch.record == parent )
467                return true;
468          }
469       }
470       return false;
471    }
472    
473    
474    public synchronized PlanRecord firstParent() {
475       EffectChain ch;
476       for(int i = 0; i < data.length; i++ ) {
477          for(int j = 0; data[i] != null && j < data[i].size(); j++ ) {
478             ch = (EffectChain)data[i].elementAt(j);
479             if ( !ch.isExternal() )
480                return ch.record;
481          }
482       }
483       return null;
484    }
485    
486    
487    public synchronized String firstKey() {
488       EffectChain ch;
489       for(int i = 0; i < data.length; i++ ) {
490          for(int j = 0; data[i] != null && j < data[i].size(); j++ ) {
491             ch = (EffectChain)data[i].elementAt(j);
492             if ( ch.isExternal() )
493                return ch.key;
494          }
495       }
496       return null;
497    }
498    public synchronized int firstPosition(PlanRecord parent) {
499       EffectChain ch;
500       for(int i = 0; i < data.length; i++ ) {
501          for(int j = 0; data[i] != null && j < data[i].size(); j++ ) {
502             ch = (EffectChain)data[i].elementAt(j);
503             if ( !ch.isExternal() && ch.record == parent )
504                return i;
505          }
506       }
507       Assert.notNull(null);
508       return -1;
509    }
510    
511    
512  
513    public synchronized Vector getAllParents() {
514       Vector output = new Vector();
515       EffectChain ch;
516       StringTokenizer st;
517       for(int i = 0; i < data.length; i++ ) {
518          for(int j = 0; data[i] != null && j < data[i].size(); j++ ) {
519             ch = (EffectChain)data[i].elementAt(j);
520             if ( !ch.isExternal() )
521                output.addElement(ch.record.getGoal().getId());
522             else {
523                st = new StringTokenizer(ch.key,"/");
524                output.addElement(st.nextToken());
525             }
526          }
527       }
528       return output;
529    }
530    
531    
532    public synchronized void notifyFailed(Vector Tasks, Vector path) {
533       EffectChain ch;
534       for(int i = 0; i < data.length; i++ ) {
535          for(int j = 0; data[i] != null && j < data[i].size(); j++ ) {
536             ch = (EffectChain)data[i].elementAt(j);
537             data[i].removeElementAt(j--);
538             if ( ch.isExternal() )
539                owner.raiseException(i,ch.key,ch.amount);
540             else {
541                if ( ch.record == owner.getParent() )
542                   ch.record.reallocateResource(ch.position,owner,i,
543                                                ch.amount,Tasks,path);
544                else 
545                   ch.record.reallocateResource(ch.position,owner,i,
546                                                ch.amount,null,null);
547             }
548          }
549       }
550    }
551    
552    
553    public synchronized void softNotifyFailed(Vector Tasks, Vector path,
554                                              PlannerQueryStruct struct,
555                                              int mode) {
556       EffectChain ch;
557       for(int i = 0; i < data.length; i++ ) {
558          for(int j = 0; data[i] != null && j < data[i].size(); j++ ) {
559             ch = (EffectChain)data[i].elementAt(j);
560             data[i].removeElementAt(j--);
561             if ( ch.isExternal() )
562                owner.softRaiseException(i,ch.key,ch.amount,struct,mode);
563             else {
564                if ( ch.record == owner.getParent() )
565                   ch.record.softReallocateResource(ch.position,owner,i,
566                      ch.amount,Tasks,path,struct,mode);
567                else 
568                   ch.record.softReallocateResource(ch.position,owner,i,
569                      ch.amount,null,null,struct,mode);
570             }
571          }
572       }
573    }
574    
575    
576 
577    public String toString() {
578       String out = "ProducedDb(" + "\n" + owner + "\n";
579       for(int i = 0; i < data.length; i++ ) {
580          if ( data[i] != null )
581             out += "data[" + i + "]: " + data[i] + "\n";
582       }
583       out += ")";
584       return out;
585    }
586    
587 
588    public synchronized boolean setConsumers(String key, Vector input) {
589       EffectChain ch;
590       ConsumerRecord e;
591       for(int i = 0; i < data.length; i++ ) {
592          for(int j = 0; data[i] != null && j < data[i].size(); j++ ) {
593             ch = (EffectChain)data[i].elementAt(j);
594             if ( ch.isExternal() && ch.key.equals(key) ) {
595                if ( !checkConsumers(ch.start,ch.amount,ch.consumed,input) )
596                   return false;
597                data[i].removeElementAt(j--);
598                for(int k = 0; k < input.size(); k++ ) {
599                   e = (ConsumerRecord)input.elementAt(k);
600                   ch = new EffectChain(e.consumer_id,e.amount,
601                                        e.start,e.consumed);
602                   data[i].addElement(ch);
603                }
604                return true;
605             }
606          }
607       }
608       return false;
609    }
610 
611 
612    protected synchronized boolean checkConsumers(int start, int amount,
613       boolean consumed, Vector temp) {
614 
615       ConsumerRecord e;
616       Vector current = new Vector();
617       for(int i = 0; i < temp.size(); i++ ) {
618          e = (ConsumerRecord)temp.elementAt(i);
619          if ( e.start < start ||
620               !checkCurrent(amount,current,e.start,e.consumed,e.amount) )
621             return false;
622          current.addElement(e);
623       }
624       current = null; // GC
625       return true;
626    }
627 
628 
629    protected synchronized boolean checkCurrent(int available, Vector current,
630       int start, boolean consumed, int amount) {
631 
632       int reserved = 0;
633       ConsumerRecord e;
634       for(int i = 0; i < current.size(); i++ ) {
635          e = (ConsumerRecord)current.elementAt(i);
636          if ( consumed ) {
637             if ( e.consumed || e.start > start )
638                reserved += e.amount;
639          }
640          else {
641             if ( e.consumed && e.start <= start )
642                reserved += e.amount;
643          }
644       }
645       return available - reserved >= amount;
646    }
647 
648 
649 public void debug(String str) { 
650  //  System.out.println("ProducedDb>> " + str); 
651 }
652 }