package spimedb.plan;
import com.google.common.collect.Lists;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import spimedb.graph.MapGraph;
import java.io.PrintStream;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
public class Agent {
final static Logger logger = LoggerFactory.getLogger(Agent.class);
public transient final MapGraph<Goal,String> plan =
new MapGraph(new ConcurrentHashMap(), HashSet::new);
public transient final Map<String, GoalState> state = new ConcurrentHashMap();
final ExecutorService exe;
public Agent(ExecutorService exe) {
this.exe = exe;
}
public <A extends Agent> GoalState goal(Goal<A> t) {
return subgoal(new InvokedGoal<>(t));
}
<A extends Agent> GoalState subgoal(Goal<A> t) {
String tid = t.id();
return state.compute(tid, (tiid, prevState) -> {
if (prevState == null) {
plan.addVertex(t);
GoalState s = new GoalState(t);
exe.execute(()->{
s.setState(State.Running);
long start = System.currentTimeMillis();
try {
t.DO((A) Agent.this, (next) -> {
for (Goal n : next) {
if (n == null) continue;
plan.addEdge(t, n, "=>");
subgoal(n);
}
});
s.setState(State.OK);
} catch (Exception e) {
s.setState(State.Error, e);
}
long end = System.currentTimeMillis();
s.addTime( (end - start) );
});
return s;
} else {
switch (prevState.getState()) {
default:
break;
}
return prevState;
}
});
}
public void sync(long timeoutMS) {
try {
exe.awaitTermination(timeoutMS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
logger.error("awaitTermination: {}", e);
}
}
public void printState(PrintStream out) {
state.forEach( (t,s) -> {
out.println(s + "\t" + s.time() + "ms");
out.println("\t" + plan.vertex(s.goal, false));
});
}
enum State {
/** conditions not ready yet */
Wait,
/** can run at any time */
Ready,
/** executing */
Running,
/** ran successfully */
OK,
/** ran and errored, stalled */
Error
}
public static class GoalState {
public final Logger logger;
public final Goal goal;
long wallTime = 0;
public GoalState(Goal goal) {
this.goal = goal;
this.logger = LoggerFactory.getLogger(goal.id());
}
private State state = State.Ready;
@NotNull
public State getState() {
return state;
}
public void setState(@NotNull State state, Object... logged) {
this.state = state;
logger.info("{} {}", state.toString(), logged);
}
@Override
public String toString() {
return goal.toString() + ": " + state.toString();
}
void addTime(long t) {
wallTime += t;
}
/** the sum of wall time during which the goal executed, in milliseconds */
public long time() {
return wallTime;
}
}
private static class InvokedGoal<A extends Agent> extends SynchronousGoal<A> {
private final Goal<A> root;
public InvokedGoal(Goal<A> root) {
super(root.id(), System.currentTimeMillis());
this.root = root;
}
@Override
protected Iterable<Goal<? super A>> run(A context) throws RuntimeException {
return Lists.newArrayList(root);
}
}
}