BDIAgent.java

458 lines | 13.071 kB Blame History Raw Download
/*
 * Created on 13/12/2009 10:13:03 
 */
package br.pucrio.inf.les.bdijade.core;

import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.proto.states.MsgReceiver;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import br.pucrio.inf.les.bdijade.belief.Belief;
import br.pucrio.inf.les.bdijade.event.GoalListener;
import br.pucrio.inf.les.bdijade.goal.Goal;
import br.pucrio.inf.les.bdijade.goal.GoalStatus;
import br.pucrio.inf.les.bdijade.message.BDIAgentMsgReceiver;
import br.pucrio.inf.les.bdijade.message.BDIAgentMsgReceiver.BDIAgentMatchExpression;
import br.pucrio.inf.les.bdijade.reasoning.BeliefRevisionStrategy;
import br.pucrio.inf.les.bdijade.reasoning.DeliberationFunction;
import br.pucrio.inf.les.bdijade.reasoning.OptionGenerationFunction;
import br.pucrio.inf.les.bdijade.reasoning.PlanSelectionStrategy;
import br.pucrio.inf.les.bdijade.util.DefaultCapability;
import br.pucrio.inf.les.bdijade.util.reasoning.DefaultBeliefRevisionStrategy;
import br.pucrio.inf.les.bdijade.util.reasoning.DefaultDeliberationFunction;
import br.pucrio.inf.les.bdijade.util.reasoning.DefaultOptionGenerationFunction;
import br.pucrio.inf.les.bdijade.util.reasoning.DefaultPlanSelectionStrategy;

/**
 * This class is an extension of {@link Agent} that has a current set of goals,
 * which are selected to become intentions, i.e. to tried to be achieve by means
 * of the selection and execution of plans. It also have a set of
 * {@link Capability}. It has a behavior that runs the BDI-interpreter. This
 * agent also have a {@link MsgReceiver} behavior to receive all messages that
 * the agent current plans can process.
 * 
 * @author ingrid
 */
public class BDIAgent extends Agent {

	/**
	 * This class is a {@link CyclicBehaviour} that runs during all the
	 * {@link BDIAgent} life in order to provide the reasoning engine.
	 * 
	 * @author ingrid
	 */
	class BDIInterpreter extends CyclicBehaviour {

		private static final long serialVersionUID = -6991759791322598475L;

		private Log log;

		public BDIInterpreter(BDIAgent bdiAgent) {
			super(bdiAgent);
			this.log = LogFactory.getLog(this.getClass());
		}

		/**
		 * This method is a variation of the BDI-interpreter cycle of the BDI
		 * architecture. It first reviews the beliefs of this agent, by menas of
		 * the {@link BeliefRevisionStrategy}.
		 * 
		 * After it removes from the intention set the ones that achieved their
		 * end, i.e. achieved, no longer desired and unachievable, and notifies
		 * goal listeners about this event.
		 * 
		 * Then, it generate a new set of goals, dropping existing ones that
		 * were not selected and also creating new ones. This new set of goals
		 * is defined by the {@link OptionGenerationFunction}.
		 * 
		 * Finally, from the set of selected goals, they are now filtered to
		 * select the current agent intentions. The non-selected goals will be
		 * set to wait ({@link Intention#doWait()}) and the selected ones will
		 * be tried to be achieved ({@link Intention#tryToAchive()}).
		 * 
		 * @see jade.core.behaviours.Behaviour#action()
		 */
		@Override
		public void action() {
			log.trace("Beginning BDI-interpreter cycle.");

			log.trace("Reviewing beliefs.");
			beliefRevisionStrategy.reviewBeliefs(BDIAgent.this);

			synchronized (intentions) {
				Map<Goal, GoalStatus> goalStatus = new HashMap<Goal, GoalStatus>();
				Iterator<Intention> it = intentions.iterator();
				while (it.hasNext()) {
					Intention intention = it.next();
					GoalStatus status = intention.getStatus();
					switch (status) {
					case ACHIEVED:
					case NO_LONGER_DESIRED:
					case UNACHIEVABLE:
						intention.fireGoalFinishedEvent();
						it.remove();
						break;
					default:
						goalStatus.put(intention.getGoal(), status);
						break;
					}
				}

				Set<Goal> generatedGoals = optionGenerationFunction
						.generateGoals(goalStatus);
				Set<Goal> newGoals = new HashSet<Goal>(generatedGoals);
				newGoals.removeAll(goalStatus.keySet());
				for (Goal goal : newGoals) {
					addGoal(goal);
				}
				Set<Goal> removedGoals = new HashSet<Goal>(goalStatus.keySet());
				removedGoals.removeAll(generatedGoals);
				for (Goal goal : removedGoals) {
					it = intentions.iterator();
					while (it.hasNext()) {
						Intention intention = it.next();
						if (intention.getGoal().equals(goal)) {
							intention.noLongerDesire();
							intention.fireGoalFinishedEvent();
							it.remove();
						}
					}
				}

				goalStatus = new HashMap<Goal, GoalStatus>();
				for (Intention intention : intentions) {
					goalStatus.put(intention.getGoal(), intention.getStatus());
				}
				Set<Goal> selectedGoals = deliberationFunction
						.filter(goalStatus);
				log.trace("Selected goals to be intentions: "
						+ selectedGoals.size());
				for (Intention intention : intentions) {
					if (selectedGoals.contains(intention.getGoal())) {
						intention.tryToAchive();
					} else {
						intention.doWait();
					}
				}

				if (intentions.isEmpty()) {
					log.trace("No goals or intentions - blocking cycle.");
					this.block();
				}
			}

			log.trace("BDI-interpreter cycle finished.");
		}

		public BDIAgent getMyAgent() {
			return (BDIAgent) this.myAgent;
		}

	}

	private static final long serialVersionUID = -841774495336214256L;
	private final BDIInterpreter bdiInterpreter;
	private BeliefRevisionStrategy beliefRevisionStrategy;
	private final Set<Capability> capabilities;
	private DeliberationFunction deliberationFunction;
	private final List<Intention> intentions;
	private OptionGenerationFunction optionGenerationFunction;
	private PlanSelectionStrategy planSelectionStrategy;

	/**
	 * Default constructor.
	 */
	public BDIAgent() {
		this.capabilities = new HashSet<Capability>();
		this.intentions = new LinkedList<Intention>();
		this.bdiInterpreter = new BDIInterpreter(this);
		this.beliefRevisionStrategy = new DefaultBeliefRevisionStrategy();
		this.optionGenerationFunction = new DefaultOptionGenerationFunction();
		this.deliberationFunction = new DefaultDeliberationFunction();
		this.planSelectionStrategy = new DefaultPlanSelectionStrategy();
	}

	/**
	 * Adds a capability to this agent.
	 * 
	 * @param capability
	 *            capability to be added.
	 */
	public void addCapability(Capability capability) {
		synchronized (capabilities) {
			capability.setMyAgent(this);
			this.capabilities.add(capability);
		}
	}

	/**
	 * Adds a new goal to this agent to be achieved.
	 * 
	 * @param goal
	 *            the goal to be achieved.
	 */
	public void addGoal(Goal goal) {
		this.addGoal(goal, null);
	}

	/**
	 * Adds a new goal to this agent to be achieved and adds a listener to
	 * observe its end.
	 * 
	 * @param goal
	 *            the goal to be achieved.
	 * @param goalListener
	 *            the listener to be notified.
	 */
	public void addGoal(Goal goal, GoalListener goalListener) {
		synchronized (intentions) {
			Intention intention = new Intention(this, goal);
			this.intentions.add(intention);
			this.bdiInterpreter.restart();
			if (goalListener != null) {
				intention.addGoalListener(goalListener);
			}
		}
	}

	/**
	 * Drops a given goal of this agent. If the goal is not part of the agent's
	 * current goal, no action is performed.
	 * 
	 * @param goal
	 *            the goal to be dropped.
	 */
	public void dropGoal(Goal goal) {
		synchronized (intentions) {
			for (Intention intention : intentions) {
				if (intention.getGoal().equals(goal)) {
					intention.noLongerDesire();
					return;
				}
			}
		}
	}

	/**
	 * Returns a collection of all beliefs from all capabilities of this agent.
	 * It may have two equivalent beliefs, i.e. beliefs with the same name.
	 * 
	 * @return the collection of all beliefs of this agent.
	 */
	public Collection<Belief<?>> getAllBeliefs() {
		synchronized (capabilities) {
			int size = 0;
			for (Capability capability : capabilities) {
				size += capability.getBeliefBase().size();
			}
			Collection<Belief<?>> beliefs = new ArrayList<Belief<?>>(size);
			for (Capability capability : capabilities) {
				beliefs.addAll(capability.getBeliefBase().getBeliefs());
			}
			return beliefs;
		}
	}

	/**
	 * Gets all goals of this agent. This goals are the ones in the goal set and
	 * the ones that are trying to be achieve in intentions.
	 * 
	 * @return the set of goals.
	 */
	public Set<Goal> getAllGoals() {
		synchronized (intentions) {
			Set<Goal> goals = new HashSet<Goal>();
			for (Intention intention : intentions) {
				goals.add(intention.getGoal());
			}
			return goals;
		}
	}

	/**
	 * @return the beliefRevisionStrategy
	 */
	public BeliefRevisionStrategy getBeliefRevisionStrategy() {
		return beliefRevisionStrategy;
	}

	/**
	 * @return the capabilities
	 */
	public Set<Capability> getCapabilities() {
		synchronized (capabilities) {
			return capabilities;
		}
	}

	/**
	 * @return the deliberationFunction
	 */
	public DeliberationFunction getDeliberationFunction() {
		return deliberationFunction;
	}

	/**
	 * @return the intentions
	 */
	public Set<Intention> getIntentions() {
		synchronized (intentions) {
			Set<Intention> activeIntetions = new HashSet<Intention>();
			for (Intention intention : intentions) {
				if (!GoalStatus.WAITING.equals(intention.getStatus()))
					activeIntetions.add(intention);
			}
			return activeIntetions;
		}
	}

	/**
	 * @return the optionGenerationFunction
	 */
	public OptionGenerationFunction getOptionGenerationFunction() {
		return optionGenerationFunction;
	}

	/**
	 * @return the planSelectionStrategy
	 */
	public PlanSelectionStrategy getPlanSelectionStrategy() {
		return planSelectionStrategy;
	}

	/**
	 * This method initializes the BDI agent. It is invoked by the
	 * {@link #setup()} method.
	 */
	protected void init() {

	}

	/**
	 * Removes a capability from this agent.
	 * 
	 * @param capability
	 *            capability to be removed.
	 * 
	 * @return true if the capability exists and was removed.
	 */
	public boolean removeCapability(Capability capability) {
		synchronized (capabilities) {
			boolean removed = this.capabilities.remove(capability);
			if (removed) {
				capability.setMyAgent(null);
			}
			return removed;
		}
	}

	/**
	 * @param beliefRevisionStrategy
	 *            the beliefRevisionStrategy to set
	 */
	public void setBeliefRevisionStrategy(
			BeliefRevisionStrategy beliefRevisionStrategy) {
		if (beliefRevisionStrategy == null) {
			this.beliefRevisionStrategy = new DefaultBeliefRevisionStrategy();
		} else {
			this.beliefRevisionStrategy = beliefRevisionStrategy;
		}
	}

	/**
	 * @param deliberationFunction
	 *            the deliberationFunction to set
	 */
	public void setDeliberationFunction(
			DeliberationFunction deliberationFunction) {
		if (deliberationFunction == null) {
			this.deliberationFunction = new DefaultDeliberationFunction();
		} else {
			this.deliberationFunction = deliberationFunction;
		}
	}

	/**
	 * Sets a goal to be no longer desired.
	 * 
	 * @param goal
	 *            the goal to be no longer desired.
	 */
	public void setNoLongerDesired(Goal goal) {
		synchronized (intentions) {
			for (Intention intention : intentions) {
				if (intention.getGoal().equals(goal)) {
					intention.noLongerDesire();
					return;
				}
			}
		}
	}

	/**
	 * @param optionGenerationFunction
	 *            the optionGenerationFunction to set
	 */
	public void setOptionGenerationFunction(
			OptionGenerationFunction optionGenerationFunction) {
		if (optionGenerationFunction == null) {
			this.optionGenerationFunction = new DefaultOptionGenerationFunction();
		} else {
			this.optionGenerationFunction = optionGenerationFunction;
		}
	}

	/**
	 * @param planSelectionStrategy
	 *            the planSelectionStrategy to set
	 */
	public void setPlanSelectionStrategy(
			PlanSelectionStrategy planSelectionStrategy) {
		if (planSelectionStrategy == null) {
			this.planSelectionStrategy = new DefaultPlanSelectionStrategy();
		} else {
			this.planSelectionStrategy = planSelectionStrategy;
		}
	}

	/**
	 * Initializes the BDI agent. It adds the behavior to handle message
	 * received and can be processed by capabilities and the
	 * {@link BDIInterpreter} behavior as well..
	 * 
	 * @see jade.core.Agent#setup()
	 */
	@Override
	protected final void setup() {
		this.addBehaviour(new BDIAgentMsgReceiver(this,
				new BDIAgentMatchExpression()));
		this.addBehaviour(bdiInterpreter);
		this.addCapability(new DefaultCapability());
		init();
	}

	/**
	 * @see jade.core.Agent#takeDown()
	 */
	@Override
	protected void takeDown() {
		while (!capabilities.isEmpty()) {
			this.removeCapability(capabilities.iterator().next());
		}
	}

}