package org.jactr.embed;
/*
* default logging
*/
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiPredicate;
import java.util.function.Function;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jactr.core.concurrent.ExecutorServices;
import org.jactr.core.model.IModel;
import org.jactr.core.runtime.ACTRRuntime;
import org.jactr.core.runtime.controller.debug.BreakpointType;
import org.jactr.core.runtime.controller.debug.DebugController;
import org.jactr.core.runtime.controller.debug.event.BreakpointEvent;
import org.jactr.core.runtime.controller.debug.event.IBreakpointListener;
public class EmbedTools
{
/**
* Logger definition
*/
static private final transient Log LOGGER = LogFactory
.getLog(EmbedTools.class);
/**
* Utility function that returns a completable future for when the model
* executes until this point and time.{@link DebugController#resume()} must be
* called afterwards.
*
* @param controller
* @param when
* @return
*/
static public CompletableFuture<Double> runUntil(DebugController controller,
double when)
{
CompletableFuture<Double> future = runUntil(controller, (be, w) -> {
return be.getSource().getAge() >= w;
}, (be) -> be.getSource().getAge(), (m) -> when, BreakpointType.TIME);
return future;
}
/**
* Utility function that returns a completable future for when the model
* executes until this point in cycles.{@link DebugController#resume()} must
* be called afterwards.
*
* @param controller
* @param cycle
* @return
*/
static public CompletableFuture<Long> runUntil(DebugController controller,
long cycle)
{
CompletableFuture<Long> future = runUntil(controller, (be, c) -> be
.getSource().getProceduralModule().getNumberOfProductionsFired() >= c,
(be) -> be.getSource().getProceduralModule()
.getNumberOfProductionsFired(), (m) -> cycle, BreakpointType.CYCLE);
return future;
}
/**
* run a single cycle. In this instance, the controller must be suspended or
* not even started in order to work correctly.
*
* @param controller
* @return
*/
static public CompletableFuture<Long> runOneCycle(DebugController controller)
{
CompletableFuture<Long> future = runUntil(controller, (be, c) -> be
.getSource().getProceduralModule().getNumberOfProductionsFired() >= c,
(be) -> be.getSource().getProceduralModule()
.getNumberOfProductionsFired(), (m) -> m.getProceduralModule()
.getNumberOfProductionsFired() + 1, BreakpointType.CYCLE);
return future;
}
/**
* @param controller
* @param hasReached
* uses breakpoint and triggerEvent value to determine if breakpoint
* was reached
* @param completionValue
* this is the value that is sent on completion of the future
* @param triggerEvent
* the value that is used in breakpoint tests
* @param type
* @return
*/
static protected <T> CompletableFuture<T> runUntil(
DebugController controller, BiPredicate<BreakpointEvent, T> hasReached,
Function<BreakpointEvent, T> completionValue,
Function<IModel, T> triggerEvent, BreakpointType type)
{
CompletableFuture<T> future = new CompletableFuture<T>();
/*
* when the break point is reached (configured below), we verify that the
* right point has been reached, then complete the future. Since there can
* be multiple models, we need multiple listeners
*/
Collection<IBreakpointListener> bpls = new ArrayList<IBreakpointListener>();
// retained for cleanup
Collection<Object> triggerValues = new ArrayList<Object>();
for (IModel model : ACTRRuntime.getRuntime().getModels())
{
// the test could be model specific
T triggerValue = triggerEvent.apply(model);
triggerValues.add(triggerValue);
IBreakpointListener bl = (be) -> {
if (hasReached.test(be, triggerValue))
future.complete(completionValue.apply(be));
};
bpls.add(bl);
/*
* add the listener
*/
controller.addListener(bl, ExecutorServices.INLINE_EXECUTOR);
/*
* add the breakpoint
*/
controller.addBreakpoint(model, type, triggerValue);
/*
* if the runtime is already running, it is possible to reach the
* breakpoint before we've finished installing.. hmmm
*/
}
/*
* when the future is completed, this will be called, cleaning up after
* ourselves.
*/
future.thenRun(() -> {
/*
* make sure we remove all the breakpoints after firing
*/
// inefficient, but this shouldn't be run a bunch, at least not quickly
for (IModel model : ACTRRuntime.getRuntime().getModels())
for (Object triggerValue : triggerValues)
controller.removeBreakpoint(model, type, triggerValue);
/*
* and the listeners too
*/
for (IBreakpointListener listener : bpls)
controller.removeListener(listener);
});
/**
* make sure that we are running
*/
if (!controller.isRunning())
controller.start();
else if (controller.isSuspended()) controller.resume();
return future;
}
}