package jadex.bdi.runtime; import jadex.bdi.runtime.impl.AbstractPlan; import jadex.bdi.runtime.impl.SFlyweightFunctionality; import jadex.bdi.runtime.impl.flyweights.ElementFlyweight; import jadex.bdi.runtime.impl.flyweights.WaitAbstractionFlyweight; import jadex.bdi.runtime.interpreter.BDIInterpreter; import jadex.bdi.runtime.interpreter.MessageEventRules; import jadex.bdi.runtime.interpreter.OAVBDIRuntimeModel; import jadex.bdi.runtime.interpreter.PlanRules; import jadex.commons.ISuspendable; import jadex.commons.concurrent.IResultListener; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.Collection; /** * A plan (in our context more a plan body) contains * actions e.g. for accomplishing a target state. */ public abstract class Plan extends AbstractPlan implements ISuspendable//, IExternalCondition { //-------- methods -------- /** * The body method is called on the * instantiated plan instance from the scheduler. */ public abstract void body(); /** * The passed method is called on plan success. */ public void passed() { } /** * The failed method is called on plan failure/abort. */ public void failed() { } /** * The plan was aborted (because of conditional goal * success or termination from outside). */ public void aborted() { } /** * Create a new wait abstraction. * @return The wait abstraction. */ public IWaitAbstraction createWaitAbstraction() { return WaitAbstractionFlyweight.getWaitAbstractionFlyweight(getState(), getRCapability(), getState().createObject(OAVBDIRuntimeModel.waitabstraction_type)); } /** * Wait for a some time. * @param duration The duration. */ public void waitFor(long duration) { // Todo: check thread access. PlanRules.waitForWaitAbstraction(null, duration, getState(), getRCapability(), getRPlan()); } /** * Wait for a wait abstraction. * @param waitabstraction. */ public void waitForWaitAbstraction(IWaitAbstraction waitabs) { waitForWaitAbstraction(waitabs, -1); } /** * Halt the plan. The plan will remain in the agent until it is aborted. */ public void waitForEver() { // Todo: check thread access. Object wa = getState().createObject(OAVBDIRuntimeModel.waitabstraction_type); PlanRules.waitForWaitAbstraction(wa, -1, getState(), getRCapability(), getRPlan()); } /** * Wait for a clock tick. * todo: @param num The number of ticks? */ public void waitForTick() { PlanRules.waitForWaitAbstraction(null, PlanRules.TICK_TIMER, getState(), getRCapability(), getRPlan()); } /** * Wait for a condition to be satisfied. * @param condition The condition. * / public void waitForCondition(ICondition condition) { waitForCondition(condition, -1); }*/ /** * Wait for a condition or until the timeout occurs. * @param condition The condition. * @param timeout The timeout. * / public void waitForCondition(ICondition condition, long timeout) { getCapability().checkThreadAccess(); WaitAbstraction wa = new WaitAbstraction(getRCapability()); wa.addCondition(((ConditionWrapper)condition).getOriginalCondition()); wa.setTimeout(timeout, getRPlan()); eventWaitFor(wa); }*/ /** * Wait for a condition to be satisfied. * @param condition The condition. */ public void waitForCondition(String condition) { waitForCondition(condition, -1); } /** * Wait for a condition to be satisfied. * @param condition The condition. */ public void waitForCondition(String condition, long timeout) { // Todo: check thread access. Object wa = getState().createObject(OAVBDIRuntimeModel.waitabstraction_type); SFlyweightFunctionality.addCondition(wa, condition, getState(), getRCapability()); PlanRules.waitForWaitAbstraction(wa, timeout, getState(), getRCapability(), getRPlan()); } /** * A shortcut for dispatching a goal as subgoal of the active goal,, * and waiting for the subgoal to be finished (without timout). * @param subgoal The new subgoal. * @throws GoalFailureException when the goal fails. */ public void dispatchSubgoalAndWait(IGoal subgoal) { dispatchSubgoalAndWait(subgoal, -1); } /** * A shortcut for dispatching a goal as subgoal of the active goal * and waiting for the subgoal to be finished. * Additionally the subgoal will be dropped when finished. * This differs from the dispatchSubgoal implementation. * @param subgoal The new subgoal. * @param timeout The timeout. * @throws GoalFailureException when the goal fails. */ public void dispatchSubgoalAndWait(IGoal subgoal, long timeout) { dispatchSubgoal(subgoal); // Todo: check thread access. Object wa = getState().createObject(OAVBDIRuntimeModel.waitabstraction_type); SFlyweightFunctionality.addGoal(wa, (ElementFlyweight)subgoal, getState(), getRCapability()); PlanRules.waitForWaitAbstraction(wa, timeout, getState(), getRCapability(), getRPlan()); } /** * Wait for an internal event. * @param type The internal event type. */ public IInternalEvent waitForInternalEvent(String type) { return waitForInternalEvent(type, -1); } /** * Wait for an internal event. * @param type The internal event type. * @param timeout The timeout. */ public IInternalEvent waitForInternalEvent(String type, long timeout) { // Todo: check thread access. Object wa = getState().createObject(OAVBDIRuntimeModel.waitabstraction_type); SFlyweightFunctionality.addInternalEvent(wa, type, getState(), getRCapability()); return (IInternalEvent)PlanRules.waitForWaitAbstraction(wa, timeout, getState(), getRCapability(), getRPlan()); } /** * Send a message and wait for the answer. * @param me The message event. * @return The result event. */ public IMessageEvent sendMessageAndWait(IMessageEvent me) { return sendMessageAndWait(me, -1); } /** * Send a message and wait for the answer. * Adds a reply_with entry if not present, for tracking the conversation. * @param me The message event. * @param timeout The timeout. * @return The result event. */ public IMessageEvent sendMessageAndWait(IMessageEvent me, long timeout) { // Todo: check thread access. Object wa = getState().createObject(OAVBDIRuntimeModel.waitabstraction_type); SFlyweightFunctionality.addReply(wa, (ElementFlyweight)me, getState(), getRCapability()); Object[] ret = PlanRules.initializeWait(wa, timeout, getState(), getRCapability(), getRPlan()); sendMessage(me); if(ret[0]==null) { PlanRules.doWait(getState(), getRPlan()); ret[0] = PlanRules.afterWait(wa, (boolean[])ret[1], getState(), getRCapability(), getRPlan()); } return (IMessageEvent)ret[0]; } /** * Wait for a message event. * @param type The message event type. */ public IMessageEvent waitForMessageEvent(String type) { return waitForMessageEvent(type, -1); } /** * Wait for a message event. * @param type The message event type. * @param timeout The timeout. */ public IMessageEvent waitForMessageEvent(String type, long timeout) { // Todo: check thread access. Object wa = getState().createObject(OAVBDIRuntimeModel.waitabstraction_type); SFlyweightFunctionality.addMessageEvent(wa, type, getState(), getRCapability()); return (IMessageEvent)PlanRules.waitForWaitAbstraction(wa, timeout, getState(), getRCapability(), getRPlan()); } /** * Wait for a message. * @param msgevent The message event. */ public IMessageEvent waitForReply(IMessageEvent msgevent) { return waitForReply(msgevent, -1); } /** * Wait for a message. * @param msgevent The message event. */ public IMessageEvent waitForReply(IMessageEvent msgevent, long timeout) { // Ensure that the message event is registered and waitqueue is established. Otherwise // a message loss could happen. Object rmevent = ((ElementFlyweight)msgevent).getHandle(); if(!MessageEventRules.containsRegisteredMessageEvents(getState(), getRCapability(), rmevent)) throw new RuntimeException("Message event not registered: "+rmevent); if(!isEventRegisteredInWaitqueue(rmevent)) throw new RuntimeException("Messages to be used in waitForReply() have to be registered" + "in the event in the waitqueue before(!) being sent: "+rmevent); // Todo: check thread access. Object wa = getState().createObject(OAVBDIRuntimeModel.waitabstraction_type); SFlyweightFunctionality.addReply(wa, (ElementFlyweight)msgevent, getState(), getRCapability()); return (IMessageEvent)PlanRules.waitForWaitAbstraction(wa, timeout, getState(), getRCapability(), getRPlan()); } /** * Wait for a goal. * @param type The goal type. */ public IGoal waitForGoal(String type) { return waitForGoal(type, -1); } /** * Wait for a goal. * @param type The goal type. * @param timeout The timeout. */ public IGoal waitForGoal(String type, long timeout) { // Todo: check thread access. Object wa = getState().createObject(OAVBDIRuntimeModel.waitabstraction_type); SFlyweightFunctionality.addGoal(wa, type, getState(), getRCapability()); return (IGoal)PlanRules.waitForWaitAbstraction(wa, timeout, getState(), getRCapability(), getRPlan()); } /** * Wait for a goal. * @param goal The goal. */ public void waitForGoal(IGoal goal) { waitForGoal(goal, -1); } /** * Wait for a goal. * @param goal The goal. * @param timeout The timeout. */ public void waitForGoal(final IGoal goal, long timeout) { if(goal.isFinished()) { // Todo: check thread access. if(OAVBDIRuntimeModel.GOALPROCESSINGSTATE_FAILED.equals( getState().getAttributeValue(((ElementFlyweight)goal).getHandle(), OAVBDIRuntimeModel.goal_has_processingstate))) { throw new GoalFailureException("Goal failed: "+goal); } } else { // Todo: check thread access. Object wa = getState().createObject(OAVBDIRuntimeModel.waitabstraction_type); SFlyweightFunctionality.addGoal(wa, (ElementFlyweight)goal, getState(), getRCapability()); PlanRules.waitForWaitAbstraction(wa, timeout, getState(), getRCapability(), getRPlan()); } } /** * Wait for a belief (set) fact change. * @param belief The belief (set) type. * @return The changed fact value. */ public Object waitForFactChanged(String belief) { return waitForFactChanged(belief, -1); } /** * Wait for a belief (set) fact change. * @param belief The belief (set) type. * @param timeout The timeout. * @return The changed fact. */ public Object waitForFactChanged(String belief, long timeout) { // Todo: check thread access. Object wa = getState().createObject(OAVBDIRuntimeModel.waitabstraction_type); SFlyweightFunctionality.addFactChanged(wa, belief, getState(), getRCapability()); return PlanRules.waitForWaitAbstraction(wa, timeout, getState(), getRCapability(), getRPlan()); } /** * Wait for a belief set fact addition. * @param beliefset The belief set type. * @return The added fact value. */ public Object waitForFactAdded(String beliefset) { return waitForFactAdded(beliefset, -1); } /** * Wait for a belief (set) fact change. * @param beliefset The belief (set) type. * @param timeout The timeout. * @return The changed fact. */ public Object waitForFactAdded(String beliefset, long timeout) { // Todo: check thread access. Object wa = getState().createObject(OAVBDIRuntimeModel.waitabstraction_type); SFlyweightFunctionality.addFactAdded(wa, beliefset, getState(), getRCapability()); return PlanRules.waitForWaitAbstraction(wa, timeout, getState(), getRCapability(), getRPlan()); } /** * Wait for a belief set fact removal. * @param beliefset The belief set type. * @return The added fact value. */ public Object waitForFactRemoved(String beliefset) { return waitForFactRemoved(beliefset, -1); } /** * Wait for a belief (set) fact change. * @param beliefset The belief (set) type. * @param timeout The timeout. * @return The changed fact. */ public Object waitForFactRemoved(String beliefset, long timeout) { // Todo: check thread access. Object wa = getState().createObject(OAVBDIRuntimeModel.waitabstraction_type); SFlyweightFunctionality.addFactRemoved(wa, beliefset, getState(), getRCapability()); return PlanRules.waitForWaitAbstraction(wa, timeout, getState(), getRCapability(), getRPlan()); } /** * Wait for a belief set fact addition or removal. * @param beliefset The belief set type. * @return The added fact value. */ public Object waitForFactAddedOrRemoved(String beliefset) { return waitForFactAddedOrRemoved(beliefset, -1); } /** * Wait for a belief (set) fact addition or removal. * @param beliefset The belief (set) type. * @param timeout The timeout. * @return The changed fact. */ public Object waitForFactAddedOrRemoved(String beliefset, long timeout) { // Todo: check thread access. // todo: return change event to indicate the type of change Object wa = getState().createObject(OAVBDIRuntimeModel.waitabstraction_type); SFlyweightFunctionality.addFactAdded(wa, beliefset, getState(), getRCapability()); SFlyweightFunctionality.addFactRemoved(wa, beliefset, getState(), getRCapability()); return PlanRules.waitForWaitAbstraction(wa, timeout, getState(), getRCapability(), getRPlan()); } /** * Wait for an external condition * @param condition The external condition. */ public void waitForExternalCondition(IExternalCondition condition) { waitForExternalCondition(condition, -1); } /** * Wait for an external condition * @param condition The external condition. * @param timeout The timeout. */ public void waitForExternalCondition(IExternalCondition condition, long timeout) { // Todo: check thread access. Object wa = getState().createObject(OAVBDIRuntimeModel.waitabstraction_type); SFlyweightFunctionality.addExternalCondition(wa, condition, getState(), getRCapability()); PlanRules.waitForWaitAbstraction(wa, timeout, getState(), getRCapability(), getRPlan()); } //-------- helper methods -------- /** * Test if a message event has been registered at the waitqueue. * @param msgevent The message event. * @return True, if is registered. */ public boolean isEventRegisteredInWaitqueue(Object rmevent) { boolean ret = false; Object wqwa = getState().getAttributeValue(getRPlan(), OAVBDIRuntimeModel.plan_has_waitqueuewa); if(wqwa!=null) { Collection coll = getState().getAttributeValues(wqwa, OAVBDIRuntimeModel.waitabstraction_has_messageevents); if(coll!=null) ret = coll.contains(rmevent); } return ret; } //-------- deprecated methods -------- /** * Wait for an event. * @param filter The event filter. * //@deprecated Should be avoided but in certain cases maybe cannot * / public IEvent waitFor(IFilter filter) { return waitFor(filter, -1); }*/ /** * Wait for an event or until the timeout occurs. * @param filter The event filter. * @param timeout The timeout. * //@deprecated Should be avoided but in certain cases maybe cannot * / public IEvent waitFor(IFilter filter, long timeout) { WaitAbstraction wa = new WaitAbstraction(getRCapability()); wa.addFilter(filter); wa.setTimeout(timeout, getRPlan()); return eventWaitFor(wa); }*/ /** * Wait for a wait abstraction. * @param waitabstraction. * @return The dispatched element. */ public Object waitForWaitAbstraction(IWaitAbstraction waitabs, long timeout) { return PlanRules.waitForWaitAbstraction(((ElementFlyweight)waitabs).getHandle(), timeout, getState(), getRCapability(), getRPlan()); } //-------- ISuspendable -------- /** Property change listener handling support. */ // private PropertyChangeSupport pcs = new PropertyChangeSupport(this); protected SyncResultListener lis; /** * Suspend the execution of the plan. * @param timeout The timeout. */ public void suspend(long timeout) { if(lis==null) { lis = new SyncResultListener(); } if(!getInterpreter().isPlanThread()) throw new RuntimeException("SyncResultListener may only be used from plan thread."); waitForExternalCondition(lis, timeout); // Don't use waitForResult(), because of exception being thrown. lis.reset(); } /** * Resume the execution of the plan. */ public void resume() { // System.out.println(this+"resume"); lis.resultAvailable(this, null); // pcs.firePropertyChange("true", Boolean.FALSE, Boolean.TRUE); } /** * Get the monitor for waiting. * @return The monitor. */ public Object getMonitor() { BDIInterpreter pi = BDIInterpreter.getInterpreter(getState()); IPlanExecutor exe = pi==null? null: pi.getPlanExecutor(getRPlan()); return exe==null? null: exe.getMonitor(getRPlan()); } // /** // * Test if the condition holds. // */ // public boolean isTrue() // { // return true; // } // /** // * Add a property change listener. // */ // public void addPropertyChangeListener(PropertyChangeListener listener) // { // pcs.addPropertyChangeListener(listener); // } // // /** // * Remove a property change listener. // */ // public void removePropertyChangeListener(PropertyChangeListener listener) // { // pcs.removePropertyChangeListener(listener); // } //-------- sync result listener -------- /** * Listener that uses the suspend/resume capability of threaded plans. */ public class SyncResultListener implements IResultListener, IExternalCondition { //-------- attributes -------- /** The result. */ protected Object result; /** The exception. */ protected Exception exception; /** Flag if already resumed. */ protected boolean alreadyresumed; /** Flag if already suspended. */ protected boolean alreadysuspended; /** Property change listener handling support. */ private PropertyChangeSupport pcs = new PropertyChangeSupport(this); //-------- IResultListener -------- /** * Called when the result is available. * @param result The result. */ public void resultAvailable(Object source, Object result) { // System.out.println("resultAvailable: "+this+", "+result); SyncResultListener.this.result = result; SyncResultListener.this.alreadyresumed = true; pcs.firePropertyChange("true", Boolean.FALSE, Boolean.TRUE); } /** * Called when an exception occurred. * @param exception The exception. */ public void exceptionOccurred(Object source, Exception exception) { // System.out.println("exeception: "+this+", "+exception); // exception.printStackTrace(); SyncResultListener.this.exception = exception; SyncResultListener.this.alreadyresumed = true; pcs.firePropertyChange("true", Boolean.FALSE, Boolean.TRUE); } //-------- methods -------- /** * Wait for the result. * @return The result. */ public Object waitForResult() { return waitForResult(-1); } /** * Wait for the result. * @param timeout The timeout. * @return The result. * @throws TimeoutException when result is not available in specified time frame. */ public Object waitForResult(long timeout) { if(!getInterpreter().isPlanThread()) throw new RuntimeException("SyncResultListener may only be used from plan thread."); if(!alreadyresumed) { this.alreadysuspended = true; waitForExternalCondition(this, timeout); } Exception ex = exception; Object res = result; reset(); // if(ex instanceof RuntimeException) // throw (RuntimeException)ex; if(ex!=null) throw new RuntimeException(ex); return res; } /** * Reset to allow listener being reused */ protected void reset() { // System.out.println("resetting: "+this+", "+exception); alreadysuspended = false; alreadyresumed = false; exception = null; result = null; pcs.firePropertyChange("true", Boolean.TRUE, Boolean.FALSE); } //-------- IExternalCondition -------- /** * Test if the condition holds. */ public boolean isTrue() { return alreadyresumed; } /** * Add a property change listener. */ public void addPropertyChangeListener(PropertyChangeListener listener) { pcs.addPropertyChangeListener(listener); } /** * Remove a property change listener. */ public void removePropertyChangeListener(PropertyChangeListener listener) { pcs.removePropertyChangeListener(listener); } } }