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
13
14 interface INodeExecutor {
15
16
17
18
19
20
21
22
23 NodeResult evaluate(IWorkExecutor workExecutor);
24
25 NodeResult childEvaluated(NodeState result);
26
27 LapType getType();
28 }
29
30 enum NodeState {
31
32
33
34
35 DONE,
36
37
38
39
40
41
42
43
44
45 CONTINUE,
46
47
48
49 FOLLOW,
50
51
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
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
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
103
104
105 lastTriggeredDrive.interrupt();
106 }
107 lastTriggeredDrive = driveExecutor;
108
109
110 NodeResult result;
111 if (driveExecutor.stack.isEmpty()) {
112
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
138 FireResult.Type resultType;
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
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
170
171
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
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
242
243
244 void interrupt() {
245
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
258
259
260
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
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 }