/*
* Created on Jul 11, 2006 Copyright (C) 2001-6, Anthony Harrison anh23@pitt.edu
* (jactr.org) This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version. This library is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU Lesser General Public License for more details. You should have
* received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.jactr.modules.pm.visual;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeSet;
import java.util.concurrent.Executor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.commonreality.identifier.IIdentifier;
import org.jactr.core.buffer.IActivationBuffer;
import org.jactr.core.chunk.IChunk;
import org.jactr.core.chunktype.IChunkType;
import org.jactr.core.concurrent.ExecutorServices;
import org.jactr.core.event.ACTREventDispatcher;
import org.jactr.core.event.IParameterEvent;
import org.jactr.core.model.event.IModelListener;
import org.jactr.core.model.event.ModelEvent;
import org.jactr.core.model.event.ModelListenerAdaptor;
import org.jactr.core.runtime.ACTRRuntime;
import org.jactr.core.utils.parameter.IParameterized;
import org.jactr.core.utils.parameter.ParameterHandler;
import org.jactr.modules.pm.AbstractPerceptualModule;
import org.jactr.modules.pm.common.event.IPerceptualMemoryModuleEvent;
import org.jactr.modules.pm.common.memory.IActivePerceptListener;
import org.jactr.modules.pm.common.memory.IPerceptualEncoder;
import org.jactr.modules.pm.common.memory.IPerceptualMemory;
import org.jactr.modules.pm.common.memory.map.IFINSTFeatureMap;
import org.jactr.modules.pm.visual.buffer.IVisualActivationBuffer;
import org.jactr.modules.pm.visual.buffer.IVisualLocationBuffer;
import org.jactr.modules.pm.visual.event.IVisualModuleListener;
import org.jactr.modules.pm.visual.event.VisualModuleEvent;
import org.jactr.modules.pm.visual.memory.IVisualMemory;
import org.jactr.modules.pm.visual.six.DefaultEncodingTimeEquation;
import org.jactr.modules.pm.visual.six.DefaultSearchTimeEquation;
/**
* abstract impl that address most of the trivial details. Clients must provide
* and configure the actual IVisualMemory used.
*
* @see http://jactr.org/node/137
* @author harrison
*/
public abstract class AbstractVisualModule extends AbstractPerceptualModule
implements IVisualModule, IParameterized
{
/**
* Logger definition
*/
static private final transient Log LOGGER = LogFactory
.getLog(AbstractVisualModule.class);
static final public String ENCODING_TIME_EQUATION_PARAM = "VisualEncodingTimeEquationClass";
static final public String SEARCHING_TIME_EQUATION_PARAM = "VisualSearchTimeEquationClass";
static final public String ENABLE_BUFFER_STUFF_PARAM = "EnableVisualBufferStuff";
static private Collection<String> SETABLE_PARAMS;
static
{
ArrayList<String> params = new ArrayList<String>();
params.add(ENCODING_TIME_EQUATION_PARAM);
params.add(SEARCHING_TIME_EQUATION_PARAM);
params.add(IVisualMemory.VISUAL_FIELD_WIDTH_PARAM);
params.add(IVisualMemory.VISUAL_FIELD_HORIZONTAL_RESOLUTION_PARAM);
params.add(IVisualMemory.VISUAL_FIELD_HEIGHT_PARAM);
params.add(IVisualMemory.VISUAL_FIELD_VERTICAL_RESOLUTION_PARAM);
params.add(IPerceptualMemory.NUMBER_OF_FINSTS_PARAM);
params.add(IPerceptualMemory.FINST_DURATION_TIME_PARAM);
params.add(IPerceptualMemory.NEW_FINST_ONSET_DURATION_TIME_PARAM);
params.add(IVisualMemory.MOVEMENT_TOLERANCE_PARAM);
params.add(ENABLE_BUFFER_STUFF_PARAM);
params.add(STRICT_SYNCHRONIZATION_PARAM);
SETABLE_PARAMS = Collections.unmodifiableCollection(params);
}
private IVisualLocationBuffer _visualLocationBuffer;
private IVisualActivationBuffer _visualActivationBuffer;
private IVisualSearchTimeEquation _searchTimeEquation;
private IVisualEncodingTimeEquation _encodingTimeEquation;
private IChunkType _visualLocationChunkType;
private IChunkType _visualChunkType;
private IChunk _highestChunk;
private IChunk _lowestChunk;
private IChunk _currentChunk;
private IChunk _lessThanCurrentChunk;
private IChunk _greaterThanCurrentChunk;
private boolean _bufferStuffEnabled = true;
private Map<String, String> _parameterMap;
private ACTREventDispatcher<IVisualModule, IVisualModuleListener> _listener;
private IModelListener _modelListener;
private IVisualMemory _visualMemory;
public AbstractVisualModule()
{
super("visual");
_searchTimeEquation = new DefaultSearchTimeEquation();
_encodingTimeEquation = new DefaultEncodingTimeEquation();
_parameterMap = new LinkedHashMap<String, String>();
_listener = new ACTREventDispatcher<IVisualModule, IVisualModuleListener>();
setDefaultParameters();
}
@Override
public void dispose()
{
_searchTimeEquation = null;
_encodingTimeEquation = null;
_parameterMap.clear();
_parameterMap = null;
_listener.clear();
_listener = null;
/*
* and the buffers
*/
_visualActivationBuffer.dispose();
_visualActivationBuffer = null;
_visualLocationBuffer.dispose();
_visualLocationBuffer = null;
// so that getModel() calls return valid values
super.dispose();
}
protected void setDefaultParameters()
{
setParameter(IVisualMemory.VISUAL_FIELD_WIDTH_PARAM, "160");
setParameter(IVisualMemory.VISUAL_FIELD_HEIGHT_PARAM, "120");
setParameter(IVisualMemory.VISUAL_FIELD_HORIZONTAL_RESOLUTION_PARAM, "160");
setParameter(IVisualMemory.VISUAL_FIELD_VERTICAL_RESOLUTION_PARAM, "120");
setParameter(IVisualMemory.VISUAL_FIELD_HEIGHT_PARAM, "90");
setParameter(IPerceptualMemory.NEW_FINST_ONSET_DURATION_TIME_PARAM, "0.5");
setParameter(IPerceptualMemory.FINST_DURATION_TIME_PARAM, "3");
setParameter(IPerceptualMemory.NUMBER_OF_FINSTS_PARAM, "4");
setParameter(IVisualMemory.MOVEMENT_TOLERANCE_PARAM, "0.5");
setParameter(ENCODING_TIME_EQUATION_PARAM,
DefaultEncodingTimeEquation.class.getName());
setParameter(SEARCHING_TIME_EQUATION_PARAM, DefaultSearchTimeEquation.class
.getName());
}
public void addListener(IVisualModuleListener listener, Executor executor)
{
_listener.addListener(listener, executor);
}
public void removeListener(IVisualModuleListener listener)
{
_listener.removeListener(listener);
}
public boolean hasListeners()
{
return _listener.hasListeners();
}
public void dispatch(VisualModuleEvent event)
{
if (_listener.hasListeners()) _listener.fire(event);
}
/**
* called during install process since models rely on only one status buffer,
* the visual location buffer should use the visual activation buffer.
*
* @return
*/
abstract protected IVisualLocationBuffer createVisualLocationBuffer(
IVisualActivationBuffer buffer);
/**
* called during install process
*
* @return
*/
abstract protected IVisualActivationBuffer createVisualActivationBuffer();
abstract protected IVisualMemory createVisualMemory();
public IVisualLocationBuffer getVisualLocationBuffer()
{
return _visualLocationBuffer;
}
public IVisualActivationBuffer getVisualActivationBuffer()
{
return _visualActivationBuffer;
}
@Override
protected Collection<IActivationBuffer> createBuffers()
{
_visualActivationBuffer = createVisualActivationBuffer();
_visualLocationBuffer = createVisualLocationBuffer(_visualActivationBuffer);
ArrayList<IActivationBuffer> rtn = new ArrayList<IActivationBuffer>();
rtn.add(_visualLocationBuffer);
rtn.add(_visualActivationBuffer);
return rtn;
}
public IChunk getLowestChunk()
{
if (_lowestChunk == null) _lowestChunk = getNamedChunk("lowest");
return _lowestChunk;
}
public IChunk getHighestChunk()
{
if (_highestChunk == null) _highestChunk = getNamedChunk("highest");
return _highestChunk;
}
public IChunk getLessThanCurrentChunk()
{
if (_lessThanCurrentChunk == null)
_lessThanCurrentChunk = getNamedChunk("less-than-current");
return _lessThanCurrentChunk;
}
public IChunk getGreaterThanCurrentChunk()
{
if (_greaterThanCurrentChunk == null)
_greaterThanCurrentChunk = getNamedChunk("greater-than-current");
return _greaterThanCurrentChunk;
}
public IChunk getCurrentChunk()
{
if (_currentChunk == null) _currentChunk = getNamedChunk("current");
return _currentChunk;
}
public IVisualSearchTimeEquation getSearchTimeEquation()
{
return _searchTimeEquation;
}
public void setSearchTimeEquation(IVisualSearchTimeEquation equation)
{
_searchTimeEquation = equation;
}
public IVisualEncodingTimeEquation getEncodingTimeEquation()
{
return _encodingTimeEquation;
}
public void setEncodingTimeEquation(IVisualEncodingTimeEquation equation)
{
_encodingTimeEquation = equation;
}
public IVisualMemory getVisualMemory()
{
return _visualMemory;
}
public IPerceptualMemory getPerceptualMemory()
{
return getVisualMemory();
}
public IChunkType getVisualLocationChunkType()
{
if (_visualLocationChunkType == null)
_visualLocationChunkType = getNamedChunkType(VISUAL_LOCATION_CHUNK_TYPE);
return _visualLocationChunkType;
}
public IChunkType getVisualChunkType()
{
if (_visualChunkType == null)
_visualChunkType = getNamedChunkType(VISUAL_CHUNK_TYPE);
return _visualChunkType;
}
public void assignFINST(IChunk visualChunk)
{
if (visualChunk == null) return;
// IIdentifier identifier = (IIdentifier) visualChunk
// .getMetaData(IAfferentObjectEncoder.COMMONREALITY_IDENTIFIER_META_KEY);
IIdentifier identifier = (IIdentifier) visualChunk
.getMetaData(IPerceptualEncoder.COMMONREALITY_IDENTIFIER_META_KEY);
if (identifier != null)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("assigning finst for " + visualChunk);
// _visicon.getVisualMap().getFINSTFeatureMap().flagAsAttended(identifier,
// visualChunk, _visicon.getFINSTTimeSpan());
getVisualMemory().getFINSTFeatureMap().flagAsAttended(identifier,
visualChunk, getVisualMemory().getFINSTSpan());
}
else if (LOGGER.isDebugEnabled())
LOGGER.debug("could not find identifier for " + visualChunk);
}
@Override
public void setParameter(String key, String value)
{
if (STRICT_SYNCHRONIZATION_PARAM.equalsIgnoreCase(key))
super.setParameter(key, value);
else
{
_parameterMap.put(key, value);
if (_visualMemory != null) _visualMemory.setParameter(key, value);
}
}
/**
* return parameter value - null if not defined.
*
* @param key
* Description of the Parameter
* @return The parameter value
*/
@Override
public String getParameter(String key)
{
if (STRICT_SYNCHRONIZATION_PARAM.equalsIgnoreCase(key))
return super.getParameter(key);
if (ENCODING_TIME_EQUATION_PARAM.equalsIgnoreCase(key))
return _encodingTimeEquation.getClass().getName();
if (SEARCHING_TIME_EQUATION_PARAM.equalsIgnoreCase(key))
return _searchTimeEquation.getClass().getName();
return _parameterMap.get(key);
}
/**
* Return list of all parameters that can be set.
*
* @return The setableParameters value
*/
@Override
public Collection<String> getSetableParameters()
{
TreeSet<String> params = new TreeSet<String>(_parameterMap.keySet());
params.addAll(SETABLE_PARAMS);
params.addAll(super.getSetableParameters());
return params;
}
/**
* we apply the parameters after the visual map has been created so that we
* can pass through some parameters
*/
protected void applyParameters()
{
for (String key : _parameterMap.keySet())
{
String value = _parameterMap.get(key);
if (ENABLE_BUFFER_STUFF_PARAM.equalsIgnoreCase(key))
_bufferStuffEnabled = ParameterHandler.booleanInstance().coerce(value);
else if (ENCODING_TIME_EQUATION_PARAM.equalsIgnoreCase(key))
try
{
setEncodingTimeEquation((IVisualEncodingTimeEquation) ParameterHandler
.classInstance().coerce(value).newInstance());
}
catch (Exception e)
{
LOGGER.error("Could not create visual encoding time equation "
+ value, e);
}
else if (SEARCHING_TIME_EQUATION_PARAM.equalsIgnoreCase(key))
try
{
setSearchTimeEquation((IVisualSearchTimeEquation) ParameterHandler
.classInstance().coerce(value).newInstance());
}
catch (Exception e)
{
/**
* Error : error
*/
LOGGER.error("Could not create visual search time equation " + value,
e);
}
else
_visualMemory.setParameter(key, value);
}
}
@Override
public void initialize()
{
super.initialize();
_modelListener = new ModelListenerAdaptor() {
double _lastCheckTime = -1;
/**
* called at the top of each cycle.. here is where we will perform any
* buffer stuffing all pending timed events will have fired before we get
* here..
*/
@Override
public void cycleStarted(ModelEvent event)
{
if (!_bufferStuffEnabled) return;
if (getVisualMemory().getLastChangeTime() >= _lastCheckTime)
{
_visualLocationBuffer.checkForBufferStuff();
_lastCheckTime = event.getSimulationTime();
}
}
};
getModel().addListener(_modelListener, ExecutorServices.INLINE_EXECUTOR);
_visualMemory = createVisualMemory();
applyParameters();
/*
* we also add a listener to ourselves to keep track of the finsts..
*/
IVisualModuleListener listener = new IVisualModuleListener() {
public void moduleReset(IPerceptualMemoryModuleEvent event)
{
// TODO Auto-generated method stub
}
public void perceptAttended(IPerceptualMemoryModuleEvent event)
{
/*
* flag it as attended
*/
IChunk visualChunk = event.getChunk();
/*
* this is possible if the underlying percept is removed or invalidated
* between encoding and the chunk actually making it into the buffer.
*/
if (visualChunk.hasBeenDisposed()) return;
IIdentifier identifier = (IIdentifier) visualChunk
.getMetaData(IPerceptualEncoder.COMMONREALITY_IDENTIFIER_META_KEY);
if (identifier != null)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("assigning finst for " + visualChunk);
// _visicon.getVisualMap().getFINSTFeatureMap().flagAsAttended(identifier,
// visualChunk, _visicon.getFINSTTimeSpan());
getVisualMemory().getFINSTFeatureMap().flagAsAttended(identifier,
visualChunk, getVisualMemory().getFINSTSpan());
}
else if (LOGGER.isDebugEnabled())
LOGGER.debug("could not find identifier for " + visualChunk);
}
public void perceptIndexFound(IPerceptualMemoryModuleEvent event)
{
// TODO Auto-generated method stub
}
public void parameterChanged(IParameterEvent pe)
{
}
public void trackedObjectMoved(VisualModuleEvent event)
{
// TODO Auto-generated method stub
}
public void trackingObjectStarted(VisualModuleEvent event)
{
// TODO Auto-generated method stub
}
public void trackingObjectStopped(VisualModuleEvent event)
{
// TODO Auto-generated method stub
}
};
addListener(listener, ExecutorServices.INLINE_EXECUTOR);
IActivePerceptListener finstListener = new IActivePerceptListener() {
public void newPercept(IIdentifier identifier, IChunk chunk)
{
// this is quick enough that we can process it in line
// and it may be needed in the search right now..
IFINSTFeatureMap finstMap = getVisualMemory().getFINSTFeatureMap();
if (finstMap != null && !finstMap.isAttended(identifier))
finstMap.flagAsNew(identifier, chunk, getVisualMemory()
.getNewFINSTOnsetDuration());
}
public void reencoded(IIdentifier identifier, IChunk oldChunk,
IChunk newChunk)
{
// noop
}
public void removed(IIdentifier identifier, IChunk chunk)
{
// noop
}
public void updated(IIdentifier identifier, IChunk chunk)
{
// noop
}
};
_visualMemory.addListener(finstListener, ExecutorServices.INLINE_EXECUTOR);
}
@Override
protected void connectToCommonReality()
{
super.connectToCommonReality();
/*
* attach the visual memory
*/
_visualMemory.attach(ACTRRuntime.getRuntime().getConnector()
.getAgent(getModel()));
}
@Override
protected void disconnectFromCommonReality()
{
super.disconnectFromCommonReality();
_visualMemory.detach();
}
}