View Javadoc

1   package cz.cuni.amis.pogamut.sposh.engine;
2   
3   import cz.cuni.amis.pogamut.sposh.elements.*;
4   import cz.cuni.amis.pogamut.sposh.engine.timer.ITimer;
5   import cz.cuni.amis.pogamut.sposh.exceptions.FubarException;
6   import cz.cuni.amis.pogamut.sposh.executor.ActionResult;
7   import cz.cuni.amis.pogamut.sposh.executor.IWorkExecutor;
8   import java.util.*;
9   
10  /**
11   *
12   * @author Honza
13   */
14  interface INodeExecutor {
15  
16      /**
17       * When this method is called, there is no executor on the stack lower than
18       * this one.
19       *
20       * @param workExecutor
21       * @return
22       */
23      NodeResult evaluate(IWorkExecutor workExecutor);
24  
25      NodeResult childEvaluated(NodeState result);
26  
27      LapType getType();
28  }
29  
30  enum NodeState {
31  
32      /**
33       * The node has been executed and has no need to be executed again
34       */
35      DONE,
36      /**
37       * Node has done something and is ready (if selected) to continue its
38       * processing. Basically I would like to continue, but if you can't, I can
39       * live with it.
40       *
41       * Example: primitive action will return this on RUNNING and its parent
42       * could be decorator with timeout. If timeout is not reached, FOLLOW child
43       * again, if timeout reached, return DONE.
44       */
45      CONTINUE,
46      /**
47       * Process returned child in next cycle.
48       */
49      FOLLOW,
50      /**
51       * Node has failed.
52       */
53      FAILED
54  }
55  
56  class NodeResult {
57  
58      final NodeState state;
59      final INodeExecutor executor;
60  
61      public NodeResult(NodeState state, INodeExecutor executor) {
62          this.state = state;
63          this.executor = executor;
64      }
65  }
66  
67  class DCNodeExecutor extends NodeExecutor<DriveCollection> {
68  
69      private final ITimer timer;
70      private final SenseListExecutor<DriveCollection> goalExecutor;
71      private List<DriveNodeExecutor> driveExecutors = new ArrayList<DriveNodeExecutor>();
72  
73      DCNodeExecutor(PoshPlan plan, LapPath path, VariableContext ctx, EngineLog engineLog, ITimer timer) {
74          super(plan, path, ctx, engineLog);
75  
76          DriveCollection dc = plan.getDriveCollection();
77          assert path.traversePath(plan) == dc;
78  
79          this.timer = timer;
80          this.goalExecutor = new SenseListExecutor<DriveCollection>(dc.getGoal(), this.path, ctx, engineLog);
81  
82          int driveId = 0;
83          for (DriveElement drive : dc.getDrives()) {
84              // TODO: Once confirmed that same, replace driveId
85              assert drive.getId() == driveId;
86              LapPath drivePath = path.concat(LapType.DRIVE_ELEMENT, driveId++);
87              driveExecutors.add(new DriveNodeExecutor(plan, drivePath, ctx, engineLog, timer));
88          }
89      }
90      // TODO: Remove and do some other way
91      private DriveNodeExecutor lastTriggeredDrive = null;
92  
93      public synchronized PoshEngine.EvaluationResultInfo fire(IWorkExecutor workExecuter) {
94          TriggerResult triggerResult = goalExecutor.fire(workExecuter, false);
95          if (triggerResult.wasSuccess()) {
96              return new PoshEngine.EvaluationResultInfo(PoshEngine.EvaluationResult.GOAL_SATISFIED, FireResult.Type.FULFILLED);
97          }
98  
99          for (DriveNodeExecutor driveExecutor : driveExecutors) {
100             if (driveExecutor.isElegible(workExecuter, timer.getTime())) {
101                 if (lastTriggeredDrive != null && lastTriggeredDrive != driveExecutor) {
102                     // DRIVE SWITCH
103                     // Some drive that has more priority than "lastTriggeredDrive" has to fire
104                     // => clean up the stack of deExecutor
105                     lastTriggeredDrive.interrupt();
106                 }
107                 lastTriggeredDrive = driveExecutor;
108 
109 
110                 NodeResult result;
111                 if (driveExecutor.stack.isEmpty()) {
112                     // Inserts node represented by drive action on the stack
113                     result = driveExecutor.evaluate(workExecuter);
114                 } else {
115                     INodeExecutor stackTop = driveExecutor.stack.peek();
116                     result = stackTop.evaluate(workExecuter);
117                 }
118 
119                 switch (result.state) {
120                     case CONTINUE:
121                         driveExecutor.stack.pop();
122                         break;
123                     case DONE:
124                         driveExecutor.stack.pop();
125                         break;
126                     case FAILED:
127                         driveExecutor.stack.pop();
128                         break;
129                     case FOLLOW:
130                         driveExecutor.stack.push(result.executor);
131                         break;
132                     default:
133                         throw new FubarException("Wrong type " + result.state);
134                 }
135                 
136                 
137                 // TODO: Remove later, this translation is to keep somehow compatible with tests
138                 FireResult.Type resultType;// = driveExecutor.fire(workExecuter, timer);
139                 switch (result.state) {
140                     case CONTINUE:
141                         throw new UnsupportedOperationException("TODO: implement");
142                     case DONE:
143                         throw new UnsupportedOperationException("TODO: implement");
144                     case FAILED:
145                         throw new UnsupportedOperationException("TODO: implement");
146                     case FOLLOW:
147                         throw new UnsupportedOperationException("TODO: implement");
148                     default:
149                         throw new FubarException("Wrong type " + result.state);
150                 }
151 
152  //               return new PoshEngine.EvaluationResultInfo(PoshEngine.EvaluationResult.ELEMENT_FIRED, resultType);
153             }
154         }
155         return new PoshEngine.EvaluationResultInfo(PoshEngine.EvaluationResult.NO_ELEMENT_FIRED, FireResult.Type.FAILED);
156     }
157 }
158 
159 abstract class NodeExecutor<NODE extends PoshElement> extends AbstractExecutor {
160 
161     protected final PoshPlan plan;
162     protected final NODE node;
163 
164     NodeExecutor(PoshPlan plan, LapPath path, VariableContext ctx, EngineLog engineLog) {
165         super(path, ctx, engineLog);
166         this.plan = plan;
167         this.node = path.<NODE>traversePath(plan);
168     }
169 //    @Override
170 //    public final LapType getType() {
171 //        return path.getLink(path.length() - 1).getType();
172 //    }
173 }
174 
175 class DriveNodeExecutor extends NodeExecutor<DriveElement> implements INodeExecutor {
176 
177     final Deque<INodeExecutor> stack = new LinkedList<INodeExecutor>();
178     private final ITimer timer;
179     private final SenseListExecutor<DriveElement> triggerExecutor;
180     private long lastFired = Integer.MAX_VALUE;
181 
182     public DriveNodeExecutor(PoshPlan plan, LapPath path, VariableContext ctx, EngineLog engineLog, ITimer timer) {
183         super(plan, path, ctx, engineLog);
184 
185         this.timer = timer;
186         this.triggerExecutor = new SenseListExecutor<DriveElement>(path, ctx, engineLog);
187     }
188 
189     INodeExecutor createExecutor(PrimitiveCall actionCall) {
190         // TODO: Implement
191         throw new UnsupportedOperationException("TODO: Implement");
192     }
193 
194     boolean isElegible(IWorkExecutor workExecutor, long timestamp) {
195         long passed = timestamp - lastFired;
196         Freq freq = node.getFreq();
197         if (Freq.compare(freq.tick(), passed) > 0) {
198             engineLog.fine("Max.firing frequency for drive " + node.getName() + " exceeded, has to be at least " + freq.tick() + "ms, but was only " + passed);
199             return false;
200         }
201         TriggerResult result = triggerExecutor.fire(workExecutor, true);
202         return result.wasSuccess();
203     }
204 
205     private PrimitiveCall getActionCall() {
206         return node.getAction().getActionCall();
207     }
208 
209     @Override
210     public NodeResult evaluate(IWorkExecutor workExecutor) {
211         engineLog.pathReached(path);
212         engineLog.finest("Stack of drive " + node.getName() + " is empty, adding initial element: " + getActionCall().toString());
213         lastFired = timer.getTime();
214 
215         return new NodeResult(NodeState.FOLLOW, createExecutor(getActionCall()));
216     }
217 
218     @Override
219     public NodeResult childEvaluated(NodeState result) {
220         engineLog.pathReached(path);
221         lastFired = timer.getTime();
222 
223         switch (result) {
224             case CONTINUE:
225                 return new NodeResult(NodeState.FOLLOW, createExecutor(getActionCall()));
226             case DONE:
227                 return new NodeResult(NodeState.DONE, this);
228             case FAILED:
229                 return new NodeResult(NodeState.FAILED, this);
230             default:
231                 throw new IllegalArgumentException("Unsupported result: " + result);
232         }
233     }
234 
235     @Override
236     public LapType getType() {
237         return LapType.DRIVE_ELEMENT;
238     }
239 
240     /**
241      * Continuous execution of the drive executor has been interrupted by
242      * another drive executor.
243      */
244     void interrupt() {
245         // TODO: In original #driveInterrupted, it cleared up to first occurance of ADExecutor
246         stack.clear();
247     }
248 }
249 
250 class ActionNodeExecutor extends NodeExecutor<TriggeredAction> implements INodeExecutor {
251 
252     public ActionNodeExecutor(PoshPlan plan, LapPath actionPath, VariableContext ctx, EngineLog engineLog) {
253         super(plan, actionPath, ctx, engineLog);
254     }
255 
256     /**
257      * Evaluate node
258      *
259      * @param workExecutor
260      * @return Result of node evaluation
261      */
262     @Override
263     public NodeResult evaluate(IWorkExecutor workExecutor) {
264         engineLog.pathReached(path);
265 
266         ActionResult result = workExecutor.executeAction(node.getActionCall().getName(), ctx);
267         switch (result) {
268             case FAILED:
269                 return new NodeResult(NodeState.FAILED, this);
270             case FINISHED:
271                 return new NodeResult(NodeState.DONE, this);
272             case RUNNING:
273                 return new NodeResult(NodeState.CONTINUE, this);
274             case RUNNING_ONCE:
275                 return new NodeResult(NodeState.DONE, this);
276             default:
277                 throw new IllegalStateException("Unexpected ActionResult: " + result);
278         }
279     }
280 
281     /**
282      * The executor has asked to follow node before and the node has finished.
283      */
284     @Override
285     public NodeResult childEvaluated(NodeState result) {
286         throw new FubarException("Action doesn't have children.");
287     }
288 
289     @Override
290     public final LapType getType() {
291         return LapType.ACTION;
292     }
293 }