package org.jactr.modules.pm.motor.managers; /* * default logging */ import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import javax.naming.OperationNotSupportedException; import javolution.util.FastList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.commonreality.efferent.IEfferentCommand; import org.commonreality.identifier.IIdentifier; import org.commonreality.modalities.motor.MotorUtilities; import org.commonreality.modalities.motor.MovementCommand; import org.commonreality.object.IEfferentObject; import org.commonreality.object.delta.DeltaTracker; import org.jactr.core.buffer.six.IStatusBuffer; import org.jactr.core.chunk.IChunk; import org.jactr.core.logging.Logger; import org.jactr.core.model.IModel; import org.jactr.core.production.request.ChunkTypeRequest; import org.jactr.core.production.request.IRequest; import org.jactr.core.queue.timedevents.RunnableTimedEvent; import org.jactr.core.runtime.ACTRRuntime; import org.jactr.core.slot.BasicSlot; import org.jactr.core.slot.IMutableSlot; import org.jactr.core.slot.ISlot; import org.jactr.core.slot.IUniqueSlotContainer; import org.jactr.modules.pm.buffer.IPerceptualBuffer; import org.jactr.modules.pm.common.efferent.EfferentCommandManager; import org.jactr.modules.pm.common.efferent.IndividualCommandManager; import org.jactr.modules.pm.motor.AbstractMotorModule; import org.jactr.modules.pm.motor.buffer.IMotorActivationBuffer; import org.jactr.modules.pm.motor.buffer.processor.MotorRequestDelegate; import org.jactr.modules.pm.motor.command.IMovement; import org.jactr.modules.pm.motor.event.MotorModuleEvent; public class MotorCommandManager extends EfferentCommandManager<MovementCommand> { /** * Logger definition */ static private final transient Log LOGGER = LogFactory .getLog(MotorCommandManager.class); static private final String PREPARE_ONLY = ":prepare-only"; static private final String ADJUSTMENT = MotorRequestDelegate.ADJUSTMENT; final private AbstractMotorModule _module; /** * all the currently submitted movements, keyed on command id */ final private Map<IIdentifier, IMovement> _commandMovementMap; /** * all the current and past submitted movements, keyed on muslce id */ final private Map<IIdentifier, IMovement> _muscleMovementMap; final private Map<IIdentifier, IMovement> _preparedMovements; private IMovement _lastPreparedMovement; private IMovement _lastExecutedMovement; public MotorCommandManager(AbstractMotorModule module) { super(module.getCommonRealityExecutor()); _module = module; _commandMovementMap = new HashMap<IIdentifier, IMovement>(); _muscleMovementMap = new HashMap<IIdentifier, IMovement>(); _preparedMovements = new HashMap<IIdentifier, IMovement>(); // delete on complete, abort or reject, but only after the callback setAutoDeleteEnabled(true); } public IMovement getPreparedMovement(IIdentifier muscleId) { try { getLock().readLock().lock(); if (muscleId == null) return _lastPreparedMovement; return _preparedMovements.get(muscleId); } finally { getLock().readLock().unlock(); } } @Override public void clear() { try { getLock().writeLock().lock(); /* * after the clear, we should still recevie the callback notifications * which should allow us to handle all these folks.. but we still need to * clear out prep and last collections */ _preparedMovements.clear(); double now = ACTRRuntime.getRuntime().getClock(_module.getModel()) .getTime(); FastList<IMovement> movements = FastList.newInstance(); movements.addAll(_commandMovementMap.values()); for (IMovement movement : movements) try { if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("Requesting abort of %s", movement)); abort(movement, now); } catch (Exception e) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Failed to clear " + movement + ", forcing ", e); MovementCommand command = getCommand(movement.getCommandIdentifier()); if (command != null) { commandAborted(command, true); remove(command); } } FastList.recycle(movements); } finally { getLock().writeLock().unlock(); } super.clear(); updateBuffer(); } public IMovement getMovementFromCommand(IIdentifier commandId) { return _commandMovementMap.get(commandId); } public IMovement getMovementFromMuscle(IIdentifier muscleId) { if (muscleId == null) return _lastExecutedMovement; return _muscleMovementMap.get(muscleId); } @Override protected boolean isInterestedIn(IEfferentCommand command) { return command instanceof MovementCommand && super.isInterestedIn(command); } private boolean isPrepareOnly(IMovement movement) { FastList<ISlot> container = FastList.newInstance(); movement.getChunkTypeRequest().getSlots(container); for (ISlot slot : container) if (slot.getName().equals(PREPARE_ONLY)) return true; return false; } private boolean isAdjustment(IMovement movement) { FastList<ISlot> container = FastList.newInstance(); movement.getChunkTypeRequest().getSlots(container); for (ISlot slot : container) if (slot.getName().equals(ADJUSTMENT)) return true; return false; } @Override protected void commandRemoved(MovementCommand command) { super.commandRemoved(command); IMovement movement = _commandMovementMap.get(command.getIdentifier()); if (movement != null) try { IIdentifier cId = movement.getCommandIdentifier(); getLock().writeLock().lock(); // _muscleMovementMap.remove(movement.getMuscleIdentifier()); _commandMovementMap.remove(cId); if (LOGGER.isDebugEnabled()) LOGGER.debug("Removed " + cId + ", leaving " + _commandMovementMap.size()); } finally { getLock().writeLock().unlock(); } updateBuffer(); } /** * @param object * @param parameters * {@link IMovement} movement command muscle * @return * @see org.jactr.modules.pm.common.efferent.EfferentCommandManager#createCommand(org.commonreality.object.IEfferentObject, * java.lang.Object[]) */ @Override protected MovementCommand createCommand(IEfferentObject object, Object... parameters) { IModel model = _module.getModel(); Movement movement = (Movement) parameters[0]; IIdentifier muscleIdentifier = movement.getMuscleIdentifier(); ChunkTypeRequest request = movement.getChunkTypeRequest(); IEfferentObject muscle = getAgent().getEfferentObjectManager().get( muscleIdentifier); MovementCommand command = (MovementCommand) _module.getCommandTranslator() .translate(request, muscle, model); movement.configure(_module.getMuscleManager().getMuscleState( muscleIdentifier), command.getIdentifier()); try { getLock().writeLock().lock(); _commandMovementMap.put(command.getIdentifier(), movement); _muscleMovementMap.put(muscle.getIdentifier(), movement); } finally { getLock().writeLock().unlock(); } return command; } public boolean canAdjust(ChunkTypeRequest request) { try { testStatesForBusy(request, new String[] { IPerceptualBuffer.EXECUTION_SLOT }); return true; } catch (Exception e) { return false; } } public boolean canAdjust(IMovement movement) { try { if (movement.getState() != IMovement.State.EXECUTING && movement.getState() != IMovement.State.PROCESSING) throw new IllegalStateException(String.format( "Movement %s is %s, cannot adjust", movement, movement.getState())); /* * we use a special test, something must be busy.. */ IEfferentObject muscle = getMuscle(movement.getMuscleIdentifier()); testStatesForBusy(muscle, MotorUtilities.getName(muscle), new String[] { IPerceptualBuffer.EXECUTION_SLOT }); return true; } catch (Exception e) { return false; } } /** * nudge an executing movement. There are no responses to these commands * currently.. * * @param request * @param requestTime * @return */ public IMovement adjust(IMovement movement, ChunkTypeRequest request, double requestTime) { if (movement.getState() != IMovement.State.EXECUTING && movement.getState() != IMovement.State.PROCESSING) throw new IllegalStateException(String.format( "Movement %s is %s, cannot adjust", movement, movement.getState())); /* * we use a special test, something must be busy.. */ IEfferentObject muscle = getMuscle(movement.getMuscleIdentifier()); testStatesForBusy(muscle, MotorUtilities.getName(muscle), new String[] { IPerceptualBuffer.EXECUTION_SLOT }); IIdentifier commandIdentifier = movement.getCommandIdentifier(); MovementCommand actualCommand = getCommand(commandIdentifier); if (!actualCommand.isAdjustable()) throw new IllegalArgumentException(String.format("%s is not adjustable", actualCommand)); Movement actualMovement = (Movement) movement; DeltaTracker<MovementCommand> tracker = new DeltaTracker<MovementCommand>( actualCommand); IModel model = _module.getModel(); /** * adjust the command.. */ try { _module.getCommandTranslator().adjust(request, tracker, actualCommand, _module.getModel()); /* * send out the changes.. */ if (tracker.hasChanged()) { send(tracker); if (Logger.hasLoggers(model)) Logger.log(model, Logger.Stream.MOTOR, String.format( "Adjusting %s with %s", actualMovement, request)); } } catch (OperationNotSupportedException onse) { throw new IllegalStateException(String.format("Cannot adjust %s", actualCommand)); } return movement; } public boolean canPrepare(ChunkTypeRequest request) { try { testStatesForNotBusyOrAborting(request, IPerceptualBuffer.PREPARATION_SLOT); return true; } catch (Exception e) { return false; } } /** * @param request * @param prepareOnly * @return */ public IMovement prepare(ChunkTypeRequest request, double requestTime, boolean prepareOnly) throws IllegalArgumentException, IllegalStateException { /* * can we prepare? */ testStatesForNotBusyOrAborting(request, IPerceptualBuffer.PREPARATION_SLOT); IModel model = _module.getModel(); IEfferentObject muscle = _module.getCommandTranslator().getMuscle(request, model); IIdentifier mId = muscle.getIdentifier(); MovementCommand actualCommand = null; if (prepareOnly) request.addSlot(new BasicSlot(PREPARE_ONLY, true)); Movement actualMovement = new Movement(request, mId, _module); actualMovement .setTimingInfo(new double[] { requestTime, _module.getPreparationTimeEquation().compute(actualMovement, _module) }); if (Logger.hasLoggers(model)) Logger.log(model, Logger.Stream.MOTOR, String.format("Preparing %s.", actualMovement)); try { getLock().writeLock().lock(); _preparedMovements.remove(mId); /* * actually create and send the command, this is a temporary copy for * now.. */ actualCommand = newCommandInternal(muscle, actualMovement); actualCommand.getIdentifier(); actualMovement.setState(IMovement.State.PREPARING, true); } catch (Exception e) { actualMovement.setTimingInfo(new double[0]); actualMovement.setState(IMovement.State.FAILED, true); if (actualCommand != null) remove(actualCommand); } finally { getLock().writeLock().unlock(); updateBuffer(); } return actualMovement; } @Override protected void commandAccepted(final MovementCommand command) { IIdentifier cId = command.getIdentifier(); final Movement movement = (Movement) getMovementFromCommand(cId); if (movement != null) try { if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("%s was accepted as %s.", movement, command)); /* * pull up the timing info so we can reset the states correctly.. */ Runnable resetState = new Runnable() { public void run() { modelMovementPrepared(movement, command); } }; getLock().writeLock().lock(); double[] timingInfo = movement.getTimingInfo(); movement.setTimingInfo(null); _preparedMovements.put(movement.getMuscleIdentifier(), movement); _lastPreparedMovement = movement; _module.getModel().getTimedEventQueue().enqueue( new RunnableTimedEvent(timingInfo[0] + timingInfo[1], resetState)); } finally { getLock().writeLock().unlock(); } } /** * called on model thread, this handles the states. removes the pending. * * @param movement * @param command */ protected void modelMovementPrepared(Movement movement, final MovementCommand command) { /* * clean up the pending states.. */ IIdentifier cId = command.getIdentifier(); if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("%s prepared.", movement)); boolean shouldExecute = !isPrepareOnly(movement); IModel model = _module.getModel(); /* * was an abort requested? */ try { if (movement.getState() == IMovement.State.ABORTING) { if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format( "%s was aborted, ignoring prep completion", movement)); IndividualCommandManager<MovementCommand> im = getIndividualManager(cId); if (im != null && !im.hasAborted()) _module.getCommonRealityExecutor().execute(new Runnable() { public void run() { if (LOGGER.isDebugEnabled()) LOGGER.debug("Forcing abort of nonrunning command " + command); commandAborted(command, true); remove(command); } }); return; } movement.setState(IMovement.State.PREPARED, true); if (Logger.hasLoggers(model)) Logger.log(model, Logger.Stream.MOTOR, String.format("Prepared %s.", movement)); } finally { updateBuffer(); if (_module.hasListeners()) _module.dispatch(new MotorModuleEvent(_module, movement, MotorModuleEvent.Type.PREPARED)); if (shouldExecute) execute(movement, ACTRRuntime.getRuntime().getClock(model).getTime()); } } @Override protected void commandRejected(final MovementCommand command) { IIdentifier cId = command.getIdentifier(); final Movement movement = (Movement) getMovementFromCommand(cId); if (movement != null) { if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("%s (%s) has rejected.", movement, command)); Runnable reset = new Runnable() { public void run() { modelMovementRejected(movement, command); } }; // run asap _module.getModel().getTimedEventQueue().enqueue( new RunnableTimedEvent(ACTRRuntime.getRuntime().getClock( _module.getModel()).getTime(), reset, reset)); remove(command); } } protected void modelMovementRejected(Movement movement, MovementCommand command) { Movement actualMovement = movement; if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("%s rejected.", movement)); try { actualMovement.setState(IMovement.State.FAILED, true); actualMovement.getMuscleState().set(_module.getFreeChunk(), IPerceptualBuffer.PROCESSOR_SLOT, IPerceptualBuffer.EXECUTION_SLOT); IModel model = _module.getModel(); if (Logger.hasLoggers(model)) Logger.log(model, Logger.Stream.MOTOR, String.format( "Rejected %s because %s.", movement, command.getResult())); } finally { updateBuffer(); if (_module.hasListeners()) _module.dispatch(new MotorModuleEvent(_module, movement, MotorModuleEvent.Type.REJECTED)); } } public boolean canExecute(ChunkTypeRequest request) { try { testStatesForNotBusyOrAborting(request, IPerceptualBuffer.PROCESSOR_SLOT, IPerceptualBuffer.EXECUTION_SLOT); return true; } catch (Exception e) { return false; } } public IMovement execute(IMovement movement, double requestTime) throws IllegalArgumentException, IllegalStateException { /* * can we execute? */ testStatesForNotBusyOrAborting(getMuscle(movement.getMuscleIdentifier()), movement.getChunkTypeRequest().toString(), IPerceptualBuffer.PROCESSOR_SLOT, IPerceptualBuffer.EXECUTION_SLOT); IIdentifier commandIdentifier = movement.getCommandIdentifier(); MovementCommand actualCommand = getCommand(commandIdentifier); Movement actualMovement = (Movement) movement; DeltaTracker<MovementCommand> tracker = new DeltaTracker<MovementCommand>( actualCommand); tracker.setProperty(IEfferentCommand.REQUESTED_START_TIME, requestTime); try { getLock().writeLock().lock(); if (movement.getState() == IMovement.State.ABORTING) { if (LOGGER.isDebugEnabled()) LOGGER.debug(commandIdentifier + " has been aborted. skipping"); return movement; } /* * */ actualMovement .setTimingInfo(new double[] { requestTime, _module.getProcessingTimeEquation().compute(actualMovement, _module) }); actualMovement.setState(IMovement.State.PROCESSING, true); execute(tracker); } catch (Exception e) { if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("Failed to execute %s", movement), e); actualMovement.setState(IMovement.State.FAILED, true); if (actualCommand != null) remove(actualCommand); } finally { getLock().writeLock().unlock(); updateBuffer(); } return movement; } @Override protected void commandRunning(final MovementCommand command) { IIdentifier cId = command.getIdentifier(); final Movement movement = (Movement) getMovementFromCommand(cId); if (movement != null) try { if (LOGGER.isDebugEnabled()) LOGGER .debug(String.format("%s (%s) has started.", movement, command)); Runnable resetState = new Runnable() { public void run() { modelMovementProcessed(movement, command); } }; getLock().writeLock().lock(); double[] timing = movement.getTimingInfo(); _lastExecutedMovement = movement; _module.getModel().getTimedEventQueue().enqueue( new RunnableTimedEvent(timing[0] + timing[1], resetState)); } finally { getLock().writeLock().unlock(); } } protected void modelMovementProcessed(Movement movement, MovementCommand command) { command.getIdentifier(); if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("%s processed.", movement)); IMovement.State mState = movement.getState(); try { if (mState != IMovement.State.PROCESSING) { if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("%s state is not processing (%s)", movement, mState)); if (mState == IMovement.State.COMPLETED) { if (LOGGER.isDebugEnabled()) LOGGER .debug(String .format( "%s completed before processing was done. No need to do anything", movement)); } else if (mState == IMovement.State.FAILED) { if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("%s failed", movement)); } else if (mState == IMovement.State.ABORTING) { if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format( "%s was aborted, ignoring proc completion", movement)); if (command.getRequestedState() != IEfferentCommand.RequestedState.ABORT) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Actual abort was not requested. doing so now."); /* * the abort request was made before CR acknowledged the start of * running, so it was never fully requested.. */ DeltaTracker<MovementCommand> tracker = new DeltaTracker<MovementCommand>( command); abort(tracker); } else if (LOGGER.isDebugEnabled()) LOGGER.debug("Actual abort was requested."); } } else { movement.setState(IMovement.State.EXECUTING, true); IModel model = _module.getModel(); if (Logger.hasLoggers(model)) Logger.log(model, Logger.Stream.MOTOR, String.format("Running %s.", movement)); } } finally { updateBuffer(); if (_module.hasListeners()) _module.dispatch(new MotorModuleEvent(_module, movement, MotorModuleEvent.Type.STARTED)); } } @Override protected void commandCompleted(final MovementCommand command) { IIdentifier cId = command.getIdentifier(); final Movement movement = (Movement) getMovementFromCommand(cId); if (movement != null) try { if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("Completed %s (%s).", movement, command)); Runnable reset = new Runnable() { public void run() { modelMovementCompleted(movement, command); } }; // run asap _module.getModel().getTimedEventQueue().enqueue( new RunnableTimedEvent(ACTRRuntime.getRuntime().getClock( _module.getModel()).getTime(), reset)); getLock().writeLock().lock(); // _muscleMovementMap.remove(movement.getMuscleIdentifier()); _commandMovementMap.remove(cId); } finally { getLock().writeLock().unlock(); } } protected void modelMovementCompleted(Movement movement, MovementCommand command) { if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("%s completed.", movement)); if (movement.getState() == IMovement.State.ABORTING) if (LOGGER.isDebugEnabled()) LOGGER .debug("Movement had been aborted, but completed in time. Ignoring abort"); try { movement.setState(IMovement.State.COMPLETED, true); IModel model = _module.getModel(); if (Logger.hasLoggers(model)) Logger.log(model, Logger.Stream.MOTOR, String.format("Completed %s.", movement)); } finally { updateBuffer(); if (_module.hasListeners()) _module.dispatch(new MotorModuleEvent(_module, movement, MotorModuleEvent.Type.COMPLETED)); } } public boolean canAbort(ChunkTypeRequest request) { try { testStatesForBusy(request, new String[] { IPerceptualBuffer.PREPARATION_SLOT, IPerceptualBuffer.PROCESSOR_SLOT, IPerceptualBuffer.EXECUTION_SLOT }); return true; } catch (Exception e) { return false; } } public boolean canAbort(IMovement movement) { /* * we use a special test, something must be busy.. */ try { IEfferentObject muscle = getMuscle(movement.getMuscleIdentifier()); testStatesForBusy(muscle, MotorUtilities.getName(muscle), new String[] { IPerceptualBuffer.PREPARATION_SLOT, IPerceptualBuffer.PROCESSOR_SLOT, IPerceptualBuffer.EXECUTION_SLOT }); return true; } catch (Exception e) { return false; } } public IMovement abort(IMovement movement, double requestTime) throws IllegalArgumentException, IllegalStateException { Movement actualMovement = (Movement) movement; if (actualMovement.getState() == IMovement.State.ABORTING) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Already aborting " + movement); return movement; } /* * we use a special test, something must be busy.. */ IEfferentObject muscle = getMuscle(movement.getMuscleIdentifier()); testStatesForBusy(muscle, MotorUtilities.getName(muscle), new String[] { IPerceptualBuffer.PREPARATION_SLOT, IPerceptualBuffer.PROCESSOR_SLOT, IPerceptualBuffer.EXECUTION_SLOT }); /* * now we do our thing. set as aborting, and issue the request */ IIdentifier commandIdentifier = movement.getCommandIdentifier(); MovementCommand actualCommand = getCommand(commandIdentifier); try { getLock().writeLock().lock(); /* * CR only accepts aborts if we are actually * running..(processing/executing) otherwise, we have to manage it * ourselves.. */ if (actualMovement.getState() == IMovement.State.PROCESSING || actualMovement.getState() == IMovement.State.EXECUTING) { if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format( "%s is actually running, requesting abort of %s", actualMovement, actualCommand)); DeltaTracker<MovementCommand> tracker = new DeltaTracker<MovementCommand>( actualCommand); abort(tracker); } else if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format( "%s is not actually running, handling abort internally", actualMovement)); actualMovement.setState(IMovement.State.ABORTING, true); } catch (Exception e) { LOGGER.error( String.format("Failed to issue abort on %s", actualMovement), e); actualMovement.setState(IMovement.State.FAILED, true); } finally { getLock().writeLock().unlock(); updateBuffer(); } return actualMovement; } @Override protected void commandAborted(final MovementCommand command, final boolean wasRequested) { IIdentifier cId = command.getIdentifier(); final Movement movement = (Movement) getMovementFromCommand(cId); if (movement != null) try { if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("%s (%s) was aborted because %s.", movement, command, wasRequested ? "model requested" : command .getResult())); Runnable reset = new Runnable() { public void run() { modelMovementAborted(movement, command, wasRequested); } }; // run asap _module.getModel().getTimedEventQueue().enqueue( new RunnableTimedEvent(ACTRRuntime.getRuntime().getClock( _module.getModel()).getTime(), reset)); getLock().writeLock().lock(); // _muscleMovementMap.remove(movement.getMuscleIdentifier()); _commandMovementMap.remove(cId); } finally { getLock().writeLock().unlock(); } } protected void modelMovementAborted(Movement movement, MovementCommand command, boolean wasRequested) { if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("%s aborted.", movement)); IModel model = _module.getModel(); if (Logger.hasLoggers(model)) Logger.log(model, Logger.Stream.MOTOR, String.format("Aborted %s %s.", movement, wasRequested ? "at user request" : "because " + command.getResult())); try { IMovement.State mState = movement.getState(); if (wasRequested) { movement.setState(IMovement.State.COMPLETED, true); if (LOGGER.isDebugEnabled()) LOGGER.debug("was requested, so clearing states"); } else { movement.setState(IMovement.State.FAILED, true); if (mState == IMovement.State.PREPARING) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Was preparing, so freeing proc/exec"); movement.getMuscleState().set(_module.getFreeChunk(), IPerceptualBuffer.PROCESSOR_SLOT, IPerceptualBuffer.EXECUTION_SLOT); } else if (mState == IMovement.State.PROCESSING || mState == IMovement.State.EXECUTING) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Was processing, so freeing prep"); movement.getMuscleState().set(_module.getFreeChunk(), IPerceptualBuffer.PREPARATION_SLOT); } else if (LOGGER.isWarnEnabled()) LOGGER.warn("Unknown state during abort : " + movement.getState()); } } finally { updateBuffer(); if (_module.hasListeners()) _module.dispatch(new MotorModuleEvent(_module, movement, MotorModuleEvent.Type.ABORTED)); } } /** * update the state of the buffer based on the internal states.. */ private void updateBuffer() { IChunk free = _module.getFreeChunk(); IChunk error = _module.getErrorChunk(); IChunk aborting = _module.getAbortingChunk(); IChunk busy = _module.getBusyChunk(); Map<String, IChunk> status = new TreeMap<String, IChunk>(); IChunk globalState = free; try { getLock().readLock().lock(); /* * aborting supercedes error which supercedes busy, which supercedes free */ String[] slotNames = new String[] { IPerceptualBuffer.PREPARATION_SLOT, IPerceptualBuffer.PROCESSOR_SLOT, IPerceptualBuffer.EXECUTION_SLOT }; for (String slotName : slotNames) status.put(slotName, free); if (LOGGER.isDebugEnabled()) LOGGER.debug("determining global buffer state from " + _muscleMovementMap.size() + " muscles"); for (IMovement movement : _muscleMovementMap.values()) { Movement actualMovement = (Movement) movement; MuscleState mState = actualMovement.getMuscleState(); for (String slotName : slotNames) { IChunk value = (IChunk) mState.getSlot(slotName).getValue(); IChunk prior = status.get(slotName); if (aborting.equals(value)) { status.put(slotName, aborting); globalState = aborting; } else if (!aborting.equals(prior)) if (error.equals(value)) { status.put(slotName, error); if (!aborting.equals(globalState)) globalState = error; } else if (!error.equals(prior)) if (busy.equals(value)) { status.put(slotName, busy); if (!aborting.equals(globalState) && !error.equals(globalState)) globalState = busy; } else status.put(slotName, free); } } } finally { getLock().readLock().unlock(); } IMotorActivationBuffer buffer = _module.getBuffer(); for (Map.Entry<String, IChunk> slot : status.entrySet()) ((IMutableSlot) buffer.getSlot(slot.getKey())).setValue(slot.getValue()); buffer.setStateChunk(globalState); buffer.setModalityChunk(globalState); if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("%s %s=%s %s", buffer, IStatusBuffer.STATE_SLOT, globalState, status)); } private void testStatesForNotBusyOrAborting(IEfferentObject muscle, String details, String... slotNamesToTest) { IChunk busyChunk = _module.getBusyChunk(); IChunk abortingChunk = _module.getAbortingChunk(); IUniqueSlotContainer container = _module.getBuffer(); if (_module.isMuscleParallelismEnabled()) container = _module.getMuscleManager().getMuscleState( muscle.getIdentifier()); for (String slotNameToTest : slotNamesToTest) { Object slotValue = container.getSlot(slotNameToTest).getValue(); if (busyChunk.equals(slotValue)) throw new IllegalStateException(String.format( "%s.%s is currently busy, cannot execute %s", container, slotNameToTest, details)); if (abortingChunk.equals(slotValue)) throw new IllegalStateException(String.format( "%s.%s is currently aborting, cannot execute %s", container, slotNameToTest, details)); } } private void testStatesForNotBusyOrAborting(IRequest request, String... slotNamesToTest) { if (!(request instanceof ChunkTypeRequest)) throw new IllegalArgumentException("request must be chunktype request"); ChunkTypeRequest ctRequest = (ChunkTypeRequest) request; /* * what muscle are we talking about? */ IModel model = _module.getModel(); IEfferentObject muscle = _module.getCommandTranslator().getMuscle( ctRequest, model); if (muscle == null) throw new IllegalArgumentException("Could not get muscle for request " + ctRequest); testStatesForNotBusyOrAborting(muscle, request.toString(), slotNamesToTest); } private void testStatesForBusy(IRequest request, String... slotNamesToTest) { if (!(request instanceof ChunkTypeRequest)) throw new IllegalArgumentException("request must be chunktype request"); ChunkTypeRequest ctRequest = (ChunkTypeRequest) request; /* * what muscle are we talking about? */ IModel model = _module.getModel(); IEfferentObject muscle = _module.getCommandTranslator().getMuscle( ctRequest, model); if (muscle == null) throw new IllegalArgumentException("Could not get muscle for request " + ctRequest); testStatesForBusy(muscle, request.toString(), slotNamesToTest); } private void testStatesForBusy(IEfferentObject muscle, String details, String... slotNamesToTest) { IChunk busyChunk = _module.getBusyChunk(); IUniqueSlotContainer container = _module.getBuffer(); boolean isAnyBusy = false; if (_module.isMuscleParallelismEnabled()) container = _module.getMuscleManager().getMuscleState( muscle.getIdentifier()); for (String slotNameToTest : slotNamesToTest) if (busyChunk.equals(container.getSlot(slotNameToTest).getValue())) { isAnyBusy = true; break; } if (!isAnyBusy) throw new IllegalStateException("No movements busy"); } }