package org.jactr.tools.experiment.impl;
/*
* default logging
*/
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.commonreality.time.IClock;
import org.jactr.core.model.IModel;
import org.jactr.core.runtime.ACTRRuntime;
import org.jactr.tools.experiment.IDataLogger;
import org.jactr.tools.experiment.IExperiment;
import org.jactr.tools.experiment.impl.VariableResolver.IResolver;
import org.jactr.tools.experiment.lock.LockManager;
import org.jactr.tools.experiment.misc.ExperimentUtilities;
import org.jactr.tools.experiment.parser.ExperimentParser;
import org.jactr.tools.experiment.trial.ICompoundTrial;
import org.jactr.tools.experiment.trial.ITrial;
import org.jactr.tools.experiment.triggers.EndTrigger;
import org.jactr.tools.experiment.triggers.ITrigger;
import org.jactr.tools.experiment.triggers.NamedTriggerManager;
import org.jactr.tools.experiment.triggers.StartTrigger;
import org.jactr.tools.experiment.triggers.TimeTrigger;
import org.w3c.dom.Document;
public class BasicExperiment implements IExperiment
{
/**
* Logger definition
*/
static private final transient Log LOGGER = LogFactory
.getLog(BasicExperiment.class);
private List<ITrial> _allTrials;
private List<ITrial> _pendingTrials;
private final NamedTriggerManager _manager;
private String _name;
private final VariableResolver _resolver;
private final LockManager _lockManager;
private IDataLogger _collector;
private double _startTime;
private double _stopTime;
private StartTrigger _startTrigger;
private EndTrigger _endTrigger;
private Collection<ITrigger> _triggers;
private final IVariableContext _variableContext = new VariableContext();
private volatile ITrial _currentTrial;
private volatile boolean _shouldStop = false;
private IClock _clock = null;
public BasicExperiment()
{
_lockManager = new LockManager();
_manager = new NamedTriggerManager();
_resolver = new VariableResolver();
_allTrials = new ArrayList<ITrial>();
_pendingTrials = new ArrayList<ITrial>();
_triggers = new ArrayList<ITrigger>();
/**
* ${DataPath}
*/
_resolver.add(new VariableResolver.IResolver() {
public boolean isRelevant(String key)
{
return key.equalsIgnoreCase("DataPath");
}
public Object resolve(String key, IVariableContext context)
{
return getDataCollector().getPath();
}
});
/**
* ${trial}
*/
_resolver.add(new VariableResolver.IResolver() {
public boolean isRelevant(String key)
{
return key.equalsIgnoreCase("trial");
}
public Object resolve(String key, IVariableContext context)
{
ITrial trial = getTrial();
if (trial != null) return trial.getId();
return null;
}
});
/**
* ${time}
*/
_resolver.add(new VariableResolver.IResolver() {
public boolean isRelevant(String key)
{
return key.equalsIgnoreCase("time");
}
public Object resolve(String key, IVariableContext context)
{
return String.format("%1$f", getTime());
}
});
/**
* ${delta}
*/
_resolver.add(new VariableResolver.IResolver() {
public boolean isRelevant(String key)
{
return key.equalsIgnoreCase("delta");
}
public Object resolve(String key, IVariableContext context)
{
double start = getStartTime();
ITrial trial = getTrial();
if (trial != null) start = trial.getStartTime();
return String.format("%1$f", (getTime() - start));
}
});
_resolver.add(new IResolver() {
public boolean isRelevant(String key)
{
return key.equalsIgnoreCase("self");
}
public Object resolve(String key, IVariableContext context)
{
try
{
return context.get("=model").toString();
}
catch (Exception e)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("Could not get current model ", e);
return null;
}
}
});
_resolver.add(new IResolver() {
public boolean isRelevant(String key)
{
return key.equalsIgnoreCase("others");
}
public Object resolve(String key, IVariableContext context)
{
try
{
IModel model = (IModel) context.get("=model");
ArrayList<IModel> models = new ArrayList<IModel>(ACTRRuntime
.getRuntime().getModels());
models.remove(model);
StringBuilder sb = new StringBuilder();
for (IModel m : models)
sb.append(m.getName()).append(",");
if (sb.length() > 0) sb.delete(sb.length() - 1, sb.length());
return sb.toString();
}
catch (Exception e)
{
return null;
}
}
});
_resolver.add(new IResolver() {
public boolean isRelevant(String key)
{
return key.equalsIgnoreCase("all");
}
public Object resolve(String key, IVariableContext context)
{
try
{
ArrayList<IModel> models = new ArrayList<IModel>(ACTRRuntime
.getRuntime().getModels());
StringBuilder sb = new StringBuilder();
for (IModel m : models)
sb.append(m.getName()).append(",");
if (sb.length() > 0) sb.delete(sb.length() - 1, sb.length());
return sb.toString();
}
catch (Exception e)
{
return null;
}
}
});
}
public IVariableContext getVariableContext()
{
return _variableContext;
}
public LockManager getLockManager()
{
return _lockManager;
}
public void setStartTrigger(StartTrigger trigger)
{
_startTrigger = trigger;
}
public void setEndTrigger(EndTrigger trigger)
{
_endTrigger = trigger;
}
public double getTime()
{
return getClock().getTime();
}
public VariableResolver getVariableResolver()
{
return _resolver;
}
public String getName()
{
return _name;
}
public void addTrial(ITrial trial)
{
_allTrials.add(trial);
}
public void configure(Document document)
{
/*
* snag the parser to use..
*/
_name = document.getDocumentElement().getAttribute("name");
String parserClass = document.getDocumentElement().getAttribute("parser");
if (parserClass.length() == 0)
parserClass = ExperimentParser.class.getName();
try
{
ExperimentParser parser = (ExperimentParser) getClass().getClassLoader()
.loadClass(parserClass).newInstance();
configureParser(parser);
parser.configure(document, this);
}
catch (Exception e)
{
throw new RuntimeException("Failed to parse configuration ", e);
}
if (Boolean.parseBoolean(document.getDocumentElement().getAttribute(
"shuffle"))) Collections.shuffle(_allTrials);
}
/**
* if you need to add experiment specific parse handlers, do so here
*
* @param parser
*/
protected void configureParser(ExperimentParser parser)
{
}
public ITrial getTrial(boolean recursive)
{
ITrial rtn = _currentTrial;
while (recursive && rtn instanceof ICompoundTrial)
{
ITrial last = rtn;
rtn = ((ICompoundTrial) rtn).getCurrentTrial();
if (rtn == null)
{
rtn = last;
break;
}
}
return rtn;
}
public ITrial getTrial()
{
return getTrial(true);
}
public NamedTriggerManager getTriggerManager()
{
return _manager;
}
public void stop()
{
_shouldStop = true;
ITrial trial = getTrial(false);
if (trial != null && trial.isRunning()) trial.stop();
}
public void start()
{
new Thread(new Runnable() {
public void run()
{
started();
_pendingTrials.addAll(_allTrials);
ITrial next = null;
while ((next = getNextTrial()) != null && !_shouldStop)
{
_currentTrial = next;
try
{
_currentTrial.start();
_currentTrial.waitForStop();
}
catch (Exception e)
{
LOGGER.error("Failed to execute trial " + _currentTrial.getId(), e);
}
_currentTrial = null;
}
stopped();
}
}, "experiment-thread").start();
}
protected ITrial getNextTrial()
{
if (_pendingTrials.size() != 0) return _pendingTrials.remove(0);
return null;
}
public void setNextTrial(ITrial trial)
{
_pendingTrials.remove(trial);
_pendingTrials.add(0, trial);
}
public List<ITrial> getTrials()
{
return Collections.unmodifiableList(_allTrials);
}
/**
* calls trigger if available
*/
protected void started()
{
_startTime = getTime();
if (_startTrigger != null) _startTrigger.setArmed(true);
for (ITrigger trigger : _triggers)
trigger.setArmed(true);
}
/**
* called at the end of processing
*/
protected void stopped()
{
_stopTime = getTime();
for (ITrigger trigger : _triggers)
trigger.setArmed(false);
if (_endTrigger != null) _endTrigger.setArmed(true);
}
public void addTrigger(ITrigger trigger)
{
_triggers.add(trigger);
if (trigger instanceof TimeTrigger)
{
if (LOGGER.isWarnEnabled())
LOGGER
.warn(String
.format("Use of timed triggers in experiment block is not recommended as the model's clock may not be set yet"));
}
}
public double getStartTime()
{
return _startTime;
}
public double getStopTime()
{
return _stopTime;
}
public IDataLogger getDataCollector()
{
return _collector;
}
public void setDataCollector(IDataLogger collector)
{
_collector = collector;
}
@Override
public IClock getClock()
{
return _clock;
}
@Override
public void setClock(IClock clock)
{
_clock = clock;
}
}