package nars.lab.plugin.app.plan;
//this one is needed by temporal particle planner for example
//or by other plugins which demand adding of executions
//GCM is always in the lead and should be so it does not need to use this.
import java.util.HashSet;
import java.util.NavigableSet;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
import nars.util.Events;
import nars.util.Events.UnexecutableOperation;
import nars.storage.Memory;
import nars.entity.Concept;
import nars.entity.Task;
import nars.entity.TruthValue;
import nars.io.Symbols;
import nars.io.Texts;
import nars.language.Conjunction;
import nars.language.Implication;
import nars.language.Interval;
import nars.language.Term;
import nars.operator.Operation;
import nars.operator.Operator;
/**
* Operation execution and planning support. Strengthens and accelerates
* goal-reaching activity
*/
public class MultipleExecutionManager {
public final GraphExecutive graph;
public final Memory memory;
public final NavigableSet<Execution> tasks;
private final Set<Execution> tasksToRemove = new ConcurrentSkipListSet();
/**
* number of tasks that are active in the sorted priority buffer for
* execution
*/
int numActiveTasks = 1;
float maxExecutionsPerDuration = 1f;
/**
* time of last execution
*/
long lastExecution = -1;
/**
* motivation set on an executing task to prevent other tasks from
* interrupting it, unless they are relatively urgent. a larger value means
* it is more difficult for a new task to interrupt one which has already
* begun executing.
*/
float motivationToFinishCurrentExecution = 1.5f;
public MultipleExecutionManager(Memory mem) {
this.memory = mem;
this.graph = new GraphExecutive(mem, this);
this.tasks = new ConcurrentSkipListSet<Execution>() {
@Override
public boolean add(Execution e) {
boolean b = super.add(e);
if (!b) {
return false;
}
if (size() > numActiveTasks) {
Execution l = last();
remove(l);
if (l != e) {
removeExecution(l);
}
}
return true;
}
};
}
public void setNumActiveTasks(int numActiveTasks) {
this.numActiveTasks = numActiveTasks;
}
public int getNumActiveTasks() {
return numActiveTasks;
}
public static class Execution implements Comparable<Execution> {
/**
* may be null for input tasks
*/
public final Concept c;
public Task t; //TODO make private
public int sequence;
public long delayUntil = -1;
private float motivationFactor = 1;
private TruthValue desire;
public final MultipleExecutionManager executive;
final Memory memory;
public Execution(final MultipleExecutionManager executive, TruthValue desire) {
this.memory = executive.memory;
this.executive = executive;
this.desire = desire;
this.t = null;
this.c = null;
}
public Execution(Memory mem, final MultipleExecutionManager executive, final Concept concept, Task t) {
this.c = concept;
this.executive = executive;
this.desire = t.getDesire();
this.memory = mem;
memory.emit(Events.NewTaskExecution.class, this);
this.t = t;
}
public void setTask(Task t) {
this.t = t;
}
public TruthValue getDesireValue() {
return desire;
}
public void setDesire(TruthValue desire) {
this.desire = desire;
}
@Override
public int compareTo(final Execution a) {
final Execution b = this;
if (a == b) {
return 0;
}
float ap = a.getDesire();
float bp = b.getDesire();
if (bp != ap) {
return Float.compare(ap, bp);
} else {
if (a.c != null) {
float ad = a.c.getPriority();
float bd = b.c.getPriority();
if (ad != bd) {
return Float.compare(ad, bd);
} else {
float add = a.c.getDurability();
float bdd = b.c.getDurability();
return Float.compare(add, bdd);
}
}
}
return 0;
}
@Override
public boolean equals(final Object obj) {
if (obj instanceof Execution) {
return ((Execution) obj).t.equals(t);
}
return false;
}
public final float getDesire() {
return desire.getExpectation() * motivationFactor;
}
public final float getPriority() {
Concept cc = memory.concept(t.sentence.term);
if (cc != null) {
return cc.getPriority();
}
return 1;
}
public final float getDurability() {
return t.getDurability();
}
//public final float getMotivation() { return getDesire() * getPriority() * motivationFactor; }
public final void setMotivationFactor(final float f) {
this.motivationFactor = f;
}
@Override
public int hashCode() {
if (t == null) {
return desire.hashCode();
}
return t.hashCode();
}
@Override
public String toString() {
return "!" + Texts.n2Slow(getDesire()) + "|" + sequence + "! " + t.toString();
}
public void end() {
setMotivationFactor(0);
if (t != null) {
t.end();
}
}
public Task getTask() {
return t;
}
}
protected Execution getExecution(final Task parent) {
for (final Execution t : tasks) {
Task pt = t.t.getParentTask();
if (pt != null) {
if (pt.equals(parent)) {
return t;
}
}
}
return null;
}
public boolean addExecution(final Concept c, final Task t) {
Execution existingExecutable = getExecution(t.getParentTask());
boolean valid = true;
if (existingExecutable != null) {
//TODO compare motivation (desire * priority) instead?
//if the new task for the existin goal has a lower priority, ignore it
if (existingExecutable.getDesire() > t.getDesire().getExpectation()) {
//System.out.println("ignored lower priority task: " + t + " for parent " + t.parentTask);
valid = false;
}
//do not allow interrupting a lower priority, but already executing task
//TODO allow interruption if priority difference is above some threshold
if (existingExecutable.sequence > 0) {
//System.out.println("ignored late task: " + t + " for parent " + t.parentTask);
valid = false;
}
}
if (valid) {
final Execution te = new Execution(memory, this, c, t);
if (tasks.add(te)) {
//added successfully
memory.emit(Execution.class, te);
return true;
}
}
//t.end();
return false;
}
protected void removeExecution(final Execution t) {
if (tasksToRemove.add(t)) {
t.end();
}
}
protected void updateTasks() {
Set<Execution> t = new HashSet(tasks);
for (Execution e : tasksToRemove) {
t.remove(e);
}
tasks.clear();
for (Execution x : t) {
if (x.getDesire() > 0) { // && (x.getPriority() > 0)) {
tasks.add(x);
}
}
tasksToRemove.clear();
}
/** execute TaskExecution that contains a single operation, and when complete, reomve the task */
public void execute(Execution executing, final Operation op, final Task task) {
execute(op, task);
removeExecution(executing);
}
public void execute(final Operation op, final Task task) {
Operator oper = op.getOperator();
op.setTask(task);
oper.call(op, memory);
}
/**
* called during each memory cycle
*/
public void cycle() {
long now = memory.time();
//only execute something no less than every duration time
if (now - lastExecution < (memory.param.duration.get() / maxExecutionsPerDuration)) {
return;
}
lastExecution = now;
updateTasks();
updateSensors();
if (tasks.isEmpty()) {
return;
}
//System.out.println(now + " tasks=" + tasks);
if (memory.emitting(Execution.class)) {
if (tasks.size() > 1) {
for (Execution tcc : tasks) {
memory.emit(MultipleExecutionManager.class, memory.time(), tcc);
}
} else {
memory.emit(MultipleExecutionManager.class, memory.time(), tasks.first());
}
}
Execution executing = tasks.first();
Task top = executing.t;
Term term = top.getTerm();
if (term instanceof Operation) {
execute(executing, (Operation) term, top); //directly execute
return;
}
else {
memory.emit(UnexecutableOperation.class, executing, this);
}
//throw new RuntimeException("Unrecognized executable term: " + it.getSubject() + "[" + it.getSubject().getClass() + "] from " + top);
// //Example prediction
// if (memory.getCurrentBelief()!=null) {
// Term currentTerm = memory.getCurrentBelief().content;
// if (implication.containsVertex(currentTerm)) {
// particlePredict(currentTerm, 12, particles);
// }
// }
}
public static boolean isPlanTerm(final Term t) {
return ((t instanceof Interval) || (t instanceof Operation));
}
public boolean isExecutableTerm(final Term t) {
//don't allow ^want and ^believe to be active/have an effect,
//which means its only used as monitor
boolean isOp=t instanceof Operation;
if(isOp) {
Operator op=((Operation)t).getOperator();
if(op.equals(memory.getOperator("^want")) || op.equals(memory.getOperator("^believe"))) {
return false;
}
}
return (isOp || isSequenceConjunction(t));
//task.sentence.content instanceof Operation || (task.sentence.content instanceof Conjunction && task.sentence.content.getTemporalOrder()==TemporalRules.ORDER_FORWARD)))
}
public static boolean isSequenceConjunction(final Term c) {
if (c instanceof Conjunction) {
Conjunction cc = ((Conjunction) c);
return (cc.operator() == Symbols.NativeOperator.SEQUENCE);
//{
//return (cc.getTemporalOrder()==TemporalRules.ORDER_FORWARD) || (cc.getTemporalOrder()==TemporalRules.ORDER_CONCURRENT);
//}
}
return false;
}
public Task expected_task = null;
public Term expected_event = null;
public void executeConjunctionSequence(final Execution task, final Conjunction c) {
int s = task.sequence;
Term currentTerm = c.term[s];
long now = memory.time();
if (task.delayUntil > now) {
//not ready to execute next term
return;
}
if (currentTerm instanceof Operation) {
Concept conc = memory.concept(currentTerm);
execute((Operation) currentTerm, task.t);
task.delayUntil = now + memory.param.duration.get();
s++;
} else if (currentTerm instanceof Interval) {
Interval ui = (Interval) currentTerm;
task.delayUntil = memory.time() + Interval.magnitudeToTime(ui.magnitude, memory.param.duration);
s++;
} else {
/*System.err.println("Non-executable term in sequence: " + currentTerm + " in " + c + " from task " + task.t);*/
removeExecution(task); //was never executed, dont remove
}
if (s == c.term.length) {
//completed task
if (task.t.sentence.term instanceof Implication) {
expected_task = task.t;
expected_event = ((Implication) task.t.sentence.term).getPredicate();
}
removeExecution(task);
} else {
//still incomplete
task.sequence = s;
task.setMotivationFactor(motivationToFinishCurrentExecution);
}
}
public static boolean containsMentalOperator(final Task t) {
if(!(t.sentence.term instanceof Operation))
return false;
Operation o= (Operation)t.sentence.term;
return true;
}
//is input or by the system triggered operation
public static boolean isInputOrTriggeredOperation(final Task newEvent, Memory mem) {
if (newEvent.isInput()) return true;
if (containsMentalOperator(newEvent)) return true;
//if (newEvent.getCause()!=null) return true;
return false;
}
/*
public boolean isActionable(final Task newEvent, Memory mem) {
if(!((newEvent.isInput()))) {
return false;
}
Term newcontent=newEvent.sentence.content;
if(newcontent instanceof Operation) {
Term pred=((Operation)newcontent).getPredicate();
if(pred.equals(mem.getOperator("^want")) || pred.equals(mem.getOperator("^believe"))) {
return false;
}
}
return true;
}*/
// public static class TaskConceptContent {
//
// public final Task task;
// public final Concept concept;
// public final Term content;
//
// public static TaskConceptContent NULL = new TaskConceptContent();
//
// /** null placeholder */
// protected TaskConceptContent() {
// this.task = null;
// this.concept = null;
// this.content = null;
// }
//
// public TaskConceptContent(Task task, Concept concept, Term content) {
// this.task = task;
// this.concept = concept;
// this.content = content;
// }
//
// }
protected void updateSensors() {
//memory.logic.PLAN_GRAPH_EDGE.commit(graph.implication.edgeSet().size());
// memory.logic.PLAN_GRAPH_VERTEX.commit(graph.implication.vertexSet().size());
// memory.logic.PLAN_TASK_EXECUTABLE.commit(tasks.size());
}
}