1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 package zeus.actors;
32
33 import java.util.*;
34 import zeus.util.*;
35 import zeus.concepts.*;
36 import zeus.concepts.fn.*;
37 import zeus.actors.event.*;
38
39 /***
40 * The Task Database is a simple storage component that holds the {@link Task}
41 * descriptions known by the owning agent. This information is used by
42 * the agent's {@link Planner} component. <p>
43 *
44 * The methods of this component are only likely to be of interest to
45 * developers who write code to monitor or change the abilities of agents.
46 * Change log
47 * ----------
48 * 12-06-01 Added agentName and orgDb to facilitate intialisation from extended types.
49 *
50 */
51
52 public class TaskDb extends Hashtable {
53 protected HSet[] eventMonitor = new HSet[4];
54
55 public static final int MODIFY = 0;
56 public static final int ADD = 1;
57 public static final int DELETE = 2;
58 public static final int ACCESS = 3;
59 public static final int NONE = 4;
60
61 protected static Random rand = new Random(System.currentTimeMillis());
62 protected static int CMIN = 10;
63 protected static int CMAX = 50;
64 protected boolean demo_version = false;
65
66
67 protected String agentName = null;
68 protected OrganisationDb orgDb = null;
69
70 protected AgentContext context = null;
71
72 protected GenSym gensym = null;
73
74
75
76 public TaskDb () {
77 for(int i = 0; i < eventMonitor.length; i++ )
78 eventMonitor[i] = new HSet();
79 String version = SystemProps.getProperty("demo.version");
80 demo_version = (Boolean.valueOf(version)).booleanValue();
81 }
82
83
84 public TaskDb(AgentContext context) {
85 Assert.notNull(context);
86 this.context = context;
87 this.agentName = context.whoami();
88 this.orgDb = context.getOrganisationDb();
89 this.gensym = context.getGenSym();
90 context.set(this);
91 for(int i = 0; i < eventMonitor.length; i++ )
92 eventMonitor[i] = new HSet();
93 String version = SystemProps.getProperty("demo.version");
94 demo_version = (Boolean.valueOf(version)).booleanValue();
95 }
96
97 /***
98 returns a reference to the agent context pointer
99 */
100 public AgentContext getAgentContext() {
101 return context;
102 }
103
104
105 /***
106 The primary method for adding new task abilities to the Task Database */
107 public void add(Task task) {
108 Assert.notNull(task);
109 debug ("LOADED TASK NAME " + task.getName());
110
111 Fact[] produced = task.getPostconditions();
112 Fact[] consumed = task.getPreconditions();
113 resolvePreconditions (task, consumed, produced);
114 resolveCostAndTime (task,consumed,produced);
115 int operation = addItem(produced,task);
116 if ( operation != NONE )
117 notifyMonitors(task,operation);
118 }
119
120
121 /***
122 resolveCostAndTime is used to check that the cost and timing of the task in terms of
123 the preconditions and postconditions of the task is properly resolved
124 . Works like this: get the cost out, check to see if it matches any of the consumed
125 (postconditions), then check to see if it matches any of the produced (preconditions)
126 If it does, copy the values for evaluation.
127 * ISSUES
128 *--------
129 *My main concern is that I have really foobared, and this used to be done somewhere else, but I have
130 *broken it : suggestions to me please!
131 *@author Simon Thompson
132 *@since 1.2.1
133 */
134 public void resolveCostAndTime (Task task, Fact[] consumed, Fact[] produced) {
135 ValueFunction time = task.getTimeFn();
136 ValueFunction cost = task.getCostFn();
137
138 debug("time = " + time.getClass().toString());
139 debug ("cost = " + cost.getClass().toString());
140
141 if (time instanceof ArithmeticFn){
142 resolveall((ArithmeticFn)time,task,consumed);
143 resolveall((ArithmeticFn)time,task,produced);
144 }
145 if (cost instanceof ArithmeticFn) {
146 resolveall((ArithmeticFn)cost,task,consumed);
147 resolveall((ArithmeticFn)cost,task,produced);
148 }
149
150 }
151
152
153 public void resolveall (ArithmeticFn target, Task task, Fact [] resolvers) {
154 debug("in resolveall");
155 for (int i = 0; i<resolvers.length; i++) {
156 Fact currentFact = resolvers[i];
157 debug ("doing " + currentFact.toString());
158 AttributeList attrs = currentFact.getAttributeList();
159 String [] attrNames = attrs.getNames();
160 for (int attrCount = 0; attrCount < attrNames.length; attrCount++) {
161 target.resolve(currentFact.ID()+"." + attrNames[attrCount],currentFact.getFn(attrNames[attrCount]));
162 debug (target.toString());
163 }
164
165
166 }
167 }
168
169
170 /***
171 resolvePreconditions is used to check through all the precondition attributes in the task as
172 they are loaded to see if there are any references to postcondition attributes, and then
173 resolve those references so that when task chaining and execution occur the
174 correct values will be used
175 *@since 1.2.1
176 *@author Simon Thompson
177 */
178 public void resolvePreconditions(Task task, Fact[] consumed, Fact[] produced) {
179 for (int count = 0; count<consumed.length; count++) {
180 Fact currentFact = consumed[count];
181 AttributeList attrs = currentFact.getAttributeList();
182 String [] attrName = attrs.getNames();
183 for (int attrCount = 0; attrCount < attrName.length; attrCount++) {
184 debug("before checknmap" + attrs.toString());
185 checkAndMapValue(attrName[attrCount],attrs,produced);
186 debug ("after checknmap " + attrs.toString());
187 }
188
189 }
190 if (task instanceof zeus.concepts.PrimitiveTask) {
191 ((PrimitiveTask) task).setPreconditions(consumed);
192 ((PrimitiveTask) task).setPostconditions(produced); }
193
194 }
195
196
197 /***
198 checkAndMapValue takes a value from an attribute and checks to see if it
199 is a reference to a post condition. If it is, then it will map the value of the
200 post condition reference to the precondition: if we have a precondition x.name = ?y.name
201 where the post condition is ?y and ?y.name is ?var123 then we should map the precondition
202 to :
203 x.name = ?var123
204 if this is made so, all will be well, and good, good, will have triumphed.
205
206 @since 1.2.1
207 @author Simon Thompson
208 */
209 public void checkAndMapValue (String attrName, AttributeList attrs, Fact [] produced) {
210 String value = attrs.getValue(attrName);
211 if (value.startsWith ("?var")) return;
212 for (int count = 0; count< produced.length; count ++) {
213 Fact currentPost = produced[count];
214 String currentPostName = currentPost.getId();
215 debug (currentPostName + " - " + value);
216 for (int i = 0; i < value.length() - currentPostName.length();i++) {
217 if (value.regionMatches (i, currentPostName, 0, currentPostName.length())) {
218 debug ("match");
219 String postAttr = value.substring(i+currentPostName.length()+1, value.length());
220 postAttr = postAttr.replace(')',' ');
221 postAttr = postAttr.trim();
222 String attrExp = value.substring (0, i);
223 String postVal = currentPost.getValue(postAttr);
224 ValueFunction postFn = currentPost.getFn(postAttr);
225 debug ("postVal = " + postVal);
226 if (postVal != null) {
227 ValueFunction currentVf = attrs.getFn(attrName);
228 if (currentVf instanceof VarFn) {
229 ((VarFn) currentVf).arg = postVal;
230 attrs.remove (attrName);
231 attrs.setValue(attrName,currentVf);
232 }
233 else if (currentVf instanceof ConstraintFn) {
234 ConstraintFn consVf= (ConstraintFn) currentVf;
235 consVf.arg = postFn;
236 int operator = consVf.getOperator();
237 String operand = ConstraintFn.operators[operator];
238 ConstraintFn newFn = new ConstraintFn(operand,consVf.arg);
239 debug (newFn.toString());
240 attrs.remove (attrName);
241 attrs.setValue(attrName,newFn);
242 }
243 else if (currentVf instanceof ArithmeticFn) {
244 ArithmeticFn arithVf = (ArithmeticFn) currentVf;
245 arithVf.resolve(postVal,postFn);
246 ValueFunction lhs = arithVf.getLHS();
247 debug ("LHS = " + lhs.toString() + " class " + lhs.getClass());
248 ValueFunction rhs = arithVf.getRHS();
249 debug ("RHS = " + rhs.toString() + " class " + rhs.getClass());
250 int operator = arithVf.getOperator();
251 String operand = ArithmeticFn.operators[operator];
252 ArithmeticFn newFn = new ArithmeticFn(lhs,rhs,operand);
253 attrs.remove(attrName);
254 attrs.setValue(attrName,newFn);
255
256
257 }
258 else {
259 debug ("NOT recognised >>>" + currentVf.getClass().toString());
260 }
261
262 debug ("attrs in checknmap " + attrs.toString());
263 }
264 }
265 }
266 }
267 }
268
269
270 public void add(Vector List) {
271 for(int i = 0; List != null && i < List.size(); i++ )
272 add((Task)List.elementAt(i));
273 }
274
275
276 public void add(Task[] List) {
277 for(int i = 0; List != null && i < List.length; i++ )
278 add(List[i]);
279 }
280
281 protected int addItem(Vector List, Task task) {
282 int op = NONE;
283 for(int i = 0; i < List.size(); i++ )
284 op = Math.min(op, addItem((Fact)List.elementAt(i),task));
285 return op;
286 }
287 protected int addItem(Fact[] List, Task task) {
288 int op = NONE;
289 for(int i = 0; i < List.length; i++ )
290 op = Math.min(op, addItem(List[i],task));
291 return op;
292 }
293
294
295 protected int addItem(Fact fact, Task task) {
296 int op = NONE;
297 if ( fact.isLocal() ) return op;
298
299 String type = fact.getType();
300 Hashtable obj;
301
302 if ( (obj = (Hashtable)this.get(type)) == null ) {
303 obj = new Hashtable();
304 this.put(type,obj);
305 }
306 op = obj.put(task.getName(),task) != null ? MODIFY : ADD;
307
308 if ( orgDb != null ) {
309 AbilitySpec a = new AbilitySpec(task.getName(),fact,0,0);
310 OrganisationDb org = orgDb;
311 org.add(agentName,a);
312 }
313 return op;
314 }
315
316 /*** The primary method for deleting task abilities from the Task Database */
317 public void del(Task task) {
318 Assert.notNull(task);
319 Fact[] produced = task.getPostconditions();
320 int operation = deleteItem(produced,task);
321 if ( operation != NONE )
322 notifyMonitors(task,operation);
323 }
324
325 public void del(Vector List) {
326 for(int i = 0; List != null && i < List.size(); i++ )
327 del((Task)List.elementAt(i));
328 }
329
330
331 public void del(Task[] List) {
332 for(int i = 0; List != null && i < List.length; i++ )
333 del(List[i]);
334 }
335
336 protected int deleteItem(Vector List, Task task) {
337 int op = NONE;
338 for(int i = 0; i < List.size(); i++ )
339 op = Math.min(op, deleteItem((Fact)List.elementAt(i),task));
340 return op;
341 }
342
343
344 protected int deleteItem(Fact[] List, Task task) {
345 int op = NONE;
346 for( int i = 0; i < List.length; i++ )
347 op = Math.min(op, deleteItem(List[i],task));
348 return op;
349 }
350
351
352 protected int deleteItem(Fact fact, Task task) {
353 int op = NONE;
354
355 String type = fact.getType();
356 Hashtable List;
357 Task t;
358
359 if ( (List = (Hashtable)this.get(type)) == null ) return op;
360
361 op = List.remove(task.getName()) != null ? DELETE : NONE;
362
363 if ( List.isEmpty() ) this.remove(type);
364
365 if ( orgDb != null ) {
366
367 AbilitySpec a = new AbilitySpec(task.getName(),fact,0,0);
368 OrganisationDb org = orgDb;
369 org.del(agentName,a);
370 }
371 return op;
372 }
373
374 /*** Returns a duplicate of the task object with the given name */
375 public Task getTask(String name) {
376 Task task;
377 Hashtable data;
378 Enumeration enum = this.elements();
379 while( enum.hasMoreElements() ) {
380 data = (Hashtable) enum.nextElement();
381 task = (Task)data.get(name);
382 notifyMonitors(task,ACCESS);
383 if ( task != null ) {
384 Task retTask = (Task) task.duplicate (Fact.VAR, gensym);
385 debug("Duplicate = " + retTask.toString());
386 debug("Original = " + task.toString());
387
388 return (task);}
389 }
390 return null;
391 }
392
393 /*** Deletes the task object with the given name */
394 public void deleteTask(String name) {
395 Task task;
396 Hashtable data;
397 Enumeration enum = this.elements();
398 while( enum.hasMoreElements() ) {
399 data = (Hashtable) enum.nextElement();
400 task = (Task)data.remove(name);
401 if ( task != null )
402 notifyMonitors(task,DELETE);
403 }
404 }
405
406 /*** Randomly chooses a task able to produces a fact matching the parameter */
407 public Task findOne(Fact fact) {
408 Task task = null;
409 Vector reduced = findAll(fact);
410 if ( !reduced.isEmpty() ) {
411 int pos = (int) (Math.random()*reduced.size());
412 task = (Task)reduced.elementAt(pos);
413 notifyMonitors(task,ACCESS);
414 }
415 reduced = null;
416 return task;
417 }
418
419 /*** Retrieves the tasks that produce a fact matching the parameter */
420 public Vector findAll(Fact fact) {
421 Hashtable obj;
422
423 if ( (obj = (Hashtable)this.get(fact.getType())) == null )
424 return new Vector();
425
426 return this.reduce(obj,fact);
427 }
428
429 public Vector findAll(Fact fact, Vector path) {
430 Core.DEBUG(3,"TaskDb findAll(2)");
431 Core.DEBUG(3,"tFact = " + fact);
432 Core.DEBUG(3,"tPath = " + path);
433
434 Vector prior = findAll(fact);
435 return checkPath(prior,path);
436 }
437
438 /*** Retrieves the tasks that produce and consume the same facts as the parameters */
439 public Vector findAll(Fact[] consumed, Fact[] produced, Vector path) {
440 Core.DEBUG(3,"TaskDb findAll(3)");
441
442 Assert.notFalse(produced.length > 0);
443
444 Core.DEBUG(3,consumed);
445 Core.DEBUG(3,produced);
446 Core.DEBUG(3,path);
447
448 int index = -1;
449 for(int i = 0; index == -1 && i < produced.length; i++ )
450 if ( !produced[i].isSideEffect() ) index = i;
451
452 Assert.notFalse(index != -1);
453 Vector prior = findAll(produced[index]);
454 Vector List = new Vector();
455 Fact[] preconds, effects;
456 Task task;
457 for(int i = 0; i < prior.size(); i++ ) {
458 task = (Task)prior.elementAt(i);
459 preconds = task.getPreconditions();
460 effects = task.getPostconditions();
461 if ( hasCondition(consumed,preconds) && hasCondition(produced,effects) )
462 List.addElement(task);
463 }
464 return checkPath(List,path);
465 }
466
467 protected boolean hasCondition(Fact[] test, Fact[] data) {
468 boolean state = true;
469 for(int i = 0; state && i < test.length; i++ )
470 state &= hasCondition(test[i],data);
471 return state;
472 }
473
474 protected boolean hasCondition(Fact test, Fact[] data) {
475 Bindings b = new Bindings(agentName);
476 for(int i = 0; i < data.length; i++, b.clear() )
477 if ( data[i].unifiesWith(test,b) ) return true;
478 return false;
479 }
480
481 protected Vector checkPath(Vector prior, Vector path) {
482 Core.DEBUG(3,"Checking path...");
483 Core.DEBUG(3,"tprior = " + prior);
484
485 if ( prior.isEmpty() ) return prior;
486
487 Vector posterior = new Vector();
488 Bindings b = new Bindings(agentName);
489 Task task;
490 boolean ok;
491 Fact f1;
492 Fact[] consumed;
493 for(int i = 0; i < prior.size(); i++ ) {
494 task = (Task)prior.elementAt(i);
495 notifyMonitors(task,ACCESS);
496 ok = true;
497 consumed = task.getPreconditions();
498 for(int j = 0; ok && j < consumed.length; j++ ) {
499 for(int k = 0; ok && k < path.size(); k++, b.clear()) {
500 f1 = (Fact)path.elementAt(k);
501 ok = ok && !consumed[j].unifiesWith(f1,b);
502 }
503 }
504 if ( ok ) posterior.addElement(task);
505 }
506 prior = null;
507
508 Core.DEBUG(3,"posterior = " + posterior);
509
510 return posterior;
511 }
512
513 protected Vector reduce(Hashtable List, Fact fact) {
514 Vector ReducedList = new Vector();
515 Task task, t;
516 Bindings b = new Bindings(agentName);
517
518 Enumeration enum = List.elements();
519 while( enum.hasMoreElements() ) {
520 task = (Task)enum.nextElement();
521 notifyMonitors(task,ACCESS);
522 t = (Task) task.duplicate(Fact.VAR,gensym);
523 Fact[] produced = t.getPostconditions();
524 Core.DEBUG(3,"TASK_IS\n" + t.pprint());
525 for(int j = 0; j < produced.length; j++, b.clear() ) {
526
527 Core.DEBUG(3,"TaskDb reduce(): unifying\n\trequired =\n" +
528 fact.pprint() + "\n\tproduced =\n" + produced[j].pprint());
529
530 if ( produced[j].unifiesWith(fact,b) && t.applyConstraints(b) ) {
531 Core.DEBUG(3,"After apply Constraints:\n" + t);
532 Core.DEBUG(3,"Bindings is\n" + b);
533
534 if ( t.isPrimitive() )
535 ((PrimitiveTask)t).setActiveEffect(j);
536 ReducedList.addElement(t);
537
538
539 if ( demo_version && t.isPrimitive() )
540 t.setCostFn(Integer.toString(CMIN + Math.abs(rand.nextInt()%(CMAX-CMIN))));
541
542
543 break;
544 }
545 }
546 }
547 sort(ReducedList);
548 return ReducedList;
549 }
550
551 /*** Sorts a vector of tasks according to their time and cost functions */
552 protected void sort(Vector List) {
553 Vector primitive = new Vector();
554 Task t;
555 for(int i = 0; i < List.size(); i++ ) {
556 t = (Task)List.elementAt(i);
557 notifyMonitors(t,ACCESS);
558 if ( t.isPrimitive() ) {
559 primitive.addElement(t);
560 List.removeElementAt(i--);
561 }
562 }
563 sort_basic(List);
564 sort_basic(primitive);
565 for(int i = 0; i < primitive.size(); i++ )
566 List.addElement(primitive.elementAt(i));
567 primitive = null;
568 }
569
570 protected void sort_basic(Vector List) {
571 boolean changed = true;
572 Task t1, t2;
573
574 while( changed ) {
575 changed = false;
576 for( int i = 0; i < List.size()-1; i++ ) {
577 t1 = (Task)List.elementAt(i);
578 notifyMonitors(t1,ACCESS);
579 t2 = (Task)List.elementAt(i+1);
580 notifyMonitors(t2,ACCESS);
581 if ( (t1.getCost() > t2.getCost()) ||
582 (t1.getCost() == t2.getCost() && t1.getTime() > t2.getTime()) ) {
583 List.setElementAt(t2,i);
584 List.setElementAt(t1,i+1);
585 changed = true;
586 }
587 }
588 }
589 }
590
591
592 /***
593 * Use this if your code needs to react to changes in the Task Database
594 */
595 public void addTaskMonitor(TaskMonitor monitor, long event_type,
596 boolean notify_previous) {
597 addTaskMonitor(monitor,event_type);
598 if ( !notify_previous ) return;
599
600 Enumeration enum1 = elements();
601 Task task;
602 Hashtable data;
603 TaskEvent event;
604
605 while( enum1.hasMoreElements() ) {
606 data = (Hashtable) enum1.nextElement();
607 Enumeration enum2 = data.elements();
608 while( enum2.hasMoreElements() ) {
609 task = (Task)enum2.nextElement();
610 event = new TaskEvent(this,task,TaskEvent.ACCESS_MASK);
611 monitor.taskAccessedEvent(event);
612 event = new TaskEvent(this,task,TaskEvent.ADD_MASK);
613 monitor.taskAddedEvent(event);
614 }
615 }
616 }
617
618 public void addTaskMonitor(TaskMonitor monitor, long event_type) {
619 if ( (event_type & TaskEvent.ADD_MASK) != 0 )
620 eventMonitor[ADD].add(monitor);
621 if ( (event_type & TaskEvent.MODIFY_MASK) != 0 )
622 eventMonitor[MODIFY].add(monitor);
623 if ( (event_type & TaskEvent.DELETE_MASK) != 0 )
624 eventMonitor[DELETE].add(monitor);
625 if ( (event_type & TaskEvent.ACCESS_MASK) != 0 )
626 eventMonitor[ACCESS].add(monitor);
627 }
628
629 public void removeTaskMonitor(TaskMonitor monitor, long event_type) {
630 if ( (event_type & TaskEvent.ADD_MASK) != 0 )
631 eventMonitor[ADD].remove(monitor);
632 if ( (event_type & TaskEvent.MODIFY_MASK) != 0 )
633 eventMonitor[MODIFY].remove(monitor);
634 if ( (event_type & TaskEvent.DELETE_MASK) != 0 )
635 eventMonitor[DELETE].remove(monitor);
636 if ( (event_type & TaskEvent.ACCESS_MASK) != 0 )
637 eventMonitor[ACCESS].remove(monitor);
638 }
639
640 protected void notifyMonitors(Task task, int type) {
641 if ( eventMonitor[type].isEmpty() ) return;
642
643 Enumeration enum = eventMonitor[type].elements();
644 TaskMonitor monitor;
645 TaskEvent event;
646 switch(type) {
647 case ADD:
648 event = new TaskEvent(this,task,TaskEvent.ADD_MASK);
649 while( enum.hasMoreElements() ) {
650 monitor = (TaskMonitor)enum.nextElement();
651 monitor.taskAddedEvent(event);
652 }
653 break;
654 case MODIFY:
655 event = new TaskEvent(this,task,TaskEvent.MODIFY_MASK);
656 while( enum.hasMoreElements() ) {
657 monitor = (TaskMonitor)enum.nextElement();
658 monitor.taskModifiedEvent(event);
659 }
660 break;
661 case DELETE:
662 event = new TaskEvent(this,task,TaskEvent.DELETE_MASK);
663 while( enum.hasMoreElements() ) {
664 monitor = (TaskMonitor)enum.nextElement();
665 monitor.taskDeletedEvent(event);
666 }
667 break;
668 case ACCESS:
669 event = new TaskEvent(this,task,TaskEvent.ACCESS_MASK);
670 while( enum.hasMoreElements() ) {
671 monitor = (TaskMonitor)enum.nextElement();
672 monitor.taskAccessedEvent(event);
673 }
674 break;
675 }
676 }
677
678 private void debug (String out) {
679
680 }
681
682 }