View Javadoc

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