View Javadoc

1   package cz.cuni.amis.pogamut.sposh.engine;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   import java.util.logging.Logger;
6   
7   import cz.cuni.amis.pogamut.sposh.elements.ActionPattern;
8   import cz.cuni.amis.pogamut.sposh.elements.Competence;
9   import cz.cuni.amis.pogamut.sposh.elements.DriveElement;
10  import cz.cuni.amis.pogamut.sposh.elements.Freq;
11  import cz.cuni.amis.pogamut.sposh.elements.PoshPlan;
12  import cz.cuni.amis.pogamut.sposh.elements.PrimitiveCall;
13  import cz.cuni.amis.pogamut.sposh.elements.TriggeredAction;
14  import cz.cuni.amis.pogamut.sposh.engine.timer.ITimer;
15  import cz.cuni.amis.pogamut.sposh.executor.IWorkExecutor;
16  
17  /**
18   * Executor of DE.
19   * @author Honza
20   */
21  class DEExecutor extends AbstractExecutor {
22      // XXX: This should be removed, create a ElementExecutor
23      //      in constructor and use copy constructor instead
24  
25      private String name;
26      private PoshPlan plan;
27      private PrimitiveCall actionCall;
28      private SenseListExecutor<DriveElement> trigger;
29      private Freq freq;
30      private long lastFired = Integer.MAX_VALUE;
31      private ElementStackTrace stackTrace = new ElementStackTrace();
32      private TriggerResult triggerResult;
33      
34      DEExecutor(PoshPlan plan, DriveElement de, VariableContext ctx, Logger log) {
35          super(ctx, log);
36          trigger = new SenseListExecutor<DriveElement>(de.getTrigger(), ctx, log);
37          freq = de.getFreq();
38  
39          this.name = de.getName();
40          this.plan = plan;
41          this.actionCall = de.getAction().getActionCall();
42      }
43  
44      private StackElement createInitialStackElement(PoshPlan plan, PrimitiveCall actionCall) {
45      	return getElement(plan, actionCall, 
46  				  FireResult.Type.SURFACE_CONTINUE, 
47  				  FireResult.Type.SURFACE,
48  				  FireResult.Type.SURFACE,
49  				  FireResult.Type.SURFACE
50  			   ); 
51      }
52  
53      /**
54       * Is time since last fire less than specified frequency and
55       * are triggers fulfilled?
56       * 
57       * @param timestamp current time
58       * @return true if conditions for evaluation are fulfilled
59       */
60      public synchronized boolean isReady(long timestamp, IWorkExecutor workExecuter) {
61          long passed = timestamp - lastFired;
62          // Has to be at least "freq.tick()" ms, was "passed" ms
63          if (Freq.compare(freq.tick(), passed) > 0) {
64              info("Max.firing frequency exceeded, has to be at least " + freq.tick() + "ms, but was only " + passed);
65              return false;
66          }
67          triggerResult = trigger.fire(workExecuter, true);
68          return triggerResult.wasSuccess();
69      }
70      
71      /**
72       *
73       * @param workExecuter
74       * @param timer
75       * @return true if element was fired, false otherwise
76       */
77      public synchronized FireResult.Type fire(IWorkExecutor workExecuter, ITimer timer) {
78      	// TODO: optimalize by pre-caching indexes of adapts/competences on the stack 
79          // CHECK FOR COMPETENCES/ADAPTS GOALS/EXIT-CONDITIONS FIRST!!!
80      	List<CExecutor> competences = new ArrayList<CExecutor>();
81      	for (int i = stackTrace.size()-1; i >= 0; --i) {
82      		StackElement element = stackTrace.get(i);
83      		if (element.getExecutor() instanceof CExecutor) {
84      			competences.add((CExecutor) element.getExecutor());
85      			continue;
86      		}
87      		if (element.getExecutor() instanceof ADExecutor) {
88      			// IS EXIT-CONDITION FULLFILLED?
89      			if (((ADExecutor)element.getExecutor()).isExit(workExecuter)) {
90      				// EARLY SUCCEED!!!
91      				// => cut down the stack and continue
92      				stackTrace.cutDownToIncluding(element.getExecutor());
93      				competences.clear();
94      			} else {
95      				// EXIT-CONDITION NOT FULFILLED
96      				// => break and execute the drive
97      				break;
98      			}
99      			continue;
100     		}
101     	}
102     	// Check for competences goal from the parent to child 
103     	// Notice that "competences" are containing competences in different order then in the stack list
104     	for (int i = competences.size()-1; i >= 0; --i) {
105     		CExecutor competence = competences.get(i);
106     		if (competence.isGoalSatisfied(workExecuter)) {
107     			// competence goal has been satisfied
108     			// remove its sub-stack from the stack
109     			stackTrace.cutDownToIncluding(competence);
110     			break;
111     		}
112     	}
113     	
114     	// first, if stack is empty, add initial element
115         if (stackTrace.isEmpty()) {
116             stackTrace.add(createInitialStackElement(plan, actionCall));
117             return FireResult.Type.CONTINUE;
118         }
119 
120         // fire the element at the top of the stack
121         FireResult result = stackTrace.peek().getExecutor().fire(workExecuter);
122         lastFired = timer.getTime();
123 
124         info("The fired element returned: " + result.getType());
125         switch (result.getType()) {
126             case FULFILLED:
127                 // I wonder is this state even has meaning. If element was fulfilled,
128                 // shouldn't I just move up one level?
129                 // For now, handle it same way as original, according
130                 // to original sposh, handle it same way as failed case,
131                 // just element to DE action.
132                 stackTrace.removeAllElements();
133                 break;
134             case FAILED:
135                 // The element has failed, reset call stack, we will try again with a clean plate
136                 stackTrace.removeAllElements();
137                 break;
138             case FOLLOW:
139                 // Now, our executing element has decided, that some other element
140                 // should have preference. Add new element to the stack so it is
141                 // executed next time.
142                 stackTrace.push(result.getNextElement());
143                 break;
144             case CONTINUE:
145                 // We are supposed to continue to execute the currently executed element
146                 // In other words, do nothing
147                 break;                
148             case SURFACE:
149             case SURFACE_CONTINUE:
150                 // Return from this element. It is possible that the surfacing 
151                 // element was the default one, so if empty stack, add default
152                 stackTrace.pop();
153                 break;
154             default:
155                 throw new IllegalStateException("State \"" + result.getType() + "\" not expected. Serious error.");
156         }
157         return result.getType();
158     }
159 
160     ElementStackTrace getStackTrace() {
161         return stackTrace;
162     }
163 
164     /**
165      * Get name of this DEExecutor (same as the de element)
166      * @return
167      */
168     public String getName() {
169         return name;
170     }
171     
172     public TriggerResult getTriggerResult() {
173         return triggerResult;
174     }
175     
176     public void clearTriggerResult() {
177         triggerResult = null;
178     }
179     
180     /**
181      * Called by {@link DCExecutor} whenever other drive is switched-in interrupting this drive that was previously executed. 
182      */
183     public void driveInterrupted() {
184     	// Pop stack until ADExecutor is hit or stack emptied
185     	while (stackTrace.size() > 0 && !(stackTrace.peek().getExecutor() instanceof ADExecutor)) {
186     		stackTrace.pop();
187     	}
188     }
189 }