All Downloads are FREE. Search and download functionalities are using the official Maven repository.

astra.core.Agent Maven / Gradle / Ivy

There is a newer version: 1.4.2
Show newest version
package astra.core;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Queue;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import astra.event.Event;
import astra.event.GoalEvent;
import astra.event.ScopedBeliefEvent;
import astra.event.ScopedGoalEvent;
import astra.formula.Formula;
import astra.formula.Goal;
import astra.formula.Predicate;
import astra.formula.ScopedGoal;
import astra.messaging.AstraMessage;
import astra.messaging.MessageEvent;
import astra.reasoner.NewReasoner;
import astra.reasoner.Queryable;
import astra.reasoner.Reasoner;
import astra.term.ListTerm;
import astra.term.Performative;
import astra.term.Primitive;
import astra.term.Term;
import astra.tr.Function;
import astra.tr.TRContext;
import astra.trace.TraceEvent;
import astra.trace.TraceManager;

public class Agent extends Observable implements Queryable {
	// Agent Registry
	private static Map agents = new HashMap<>();
	private static List listeners = new ArrayList<>();

	public static void addRegistryListener(AgentRegistryListener listener) {
		listeners.add(listener);
	}

	/**
	 * Interface used by external objects that wish to receive updates about new agents
	 */
	public static interface AgentRegistryListener {
		void receive(Agent agent);
	}

	// static {
	// 	try {
	// 		out = new PrintWriter(new FileWriter("agents.log"));
	// 	} catch (IOException e) {
	// 		e.printStackTrace();
	// 	}
	// }

	// public static PrintWriter out;

	public static final Map timings = Collections.synchronizedMap(new HashMap());
	public static final Map iterations = Collections.synchronizedMap(new HashMap());
	
	/**
	 * This class models the notifications that are generated when an asynchronously executed action
	 * completes. Receipt of an instance of this class allows the agent to resume or fail the associated
	 * intention.
	 * 
	 * @author Rem Collier
	 *
	 */
	public static class Notification {
		Intention intention;
		String message;
		Throwable th;

		public Notification(Intention intention, String message) {
			this.intention = intention;
			this.message = message;
		}

		public Notification(Intention Intention, String message, Throwable th) {
			this.intention = Intention;
			this.message = message;
			this.th = th;
		}
		
		public void evaluate() {
			if (message != null) {
				intention.failed(message, th);
			}
			intention.resume();
		}
	}
	
	/**
	 * Promises are used to implement WAIT statements. When one of these statements is
	 * executed, the agent creates a promise and suspends the intention. Promises are
	 * evaluated on each iteration. When a promise is fulfilled (i.e. the associated 
	 * formula is matched), the agent executes the associated act(ion) which typically
	 * resumes the intention.
	 * 
	 * @author Rem
	 *
	 */
	public abstract static class Promise {
		public boolean isTrue;

		public Promise() {
			this(false);
		}

		public Promise(boolean isTrue) {
			this.isTrue = isTrue;
		}

		public boolean evaluatePromise(Agent agent) {
			boolean evaluated = evaluate(agent);
			return (isTrue && !evaluated) || (!isTrue && evaluated);
		}

		public abstract boolean evaluate(Agent agent);
		public abstract void act();
	}

	public static Agent getAgent(String name) {
		return agents.get(name);
	}

	public static boolean hasAgent(String name) {
		return agents.containsKey(name);
	}
	
	public static Set agentNames() {
		return agents.keySet();
	}

	// Agent States
	public static final int NEW 									= 0;
	public static final int ACTIVE									= 1;
	public static final int STEP									= 2;
	public static final int INACTIVE								= 3;
	public static final int TERMINATING								= 4;
	public static final int TERMINATED 								= 5;
	
	private String name;
	private int state = NEW;
	private Intention intention;
	
	// Trace variable is used to indicate whether or not ignored events should be logged.
	private boolean trace = false;
	
	// Synchronization Fields
	private Set tokens = new TreeSet<>();
    private Map> lockQueueMap = Collections.synchronizedMap(new HashMap<>());
    private Map lockMap =  Collections.synchronizedMap(new HashMap<>());
	
    // Event Queue
	private Set filter = new TreeSet<>();
	private BlockingQueue eventQueue = new LinkedBlockingQueue<>();

	// Intention Management
	private BlockingQueue completed = new LinkedBlockingQueue<>();
	private List intentions = Collections.synchronizedList(new ArrayList<>());
    
	// Activated TR Function / null if no function active
	private Predicate trFunction;

	// Class Hierarchy
    private ASTRAClass clazz;
	private Map linearization = new TreeMap<>();

	// Reasoning Engine
	private Reasoner reasoner;
	private EventBeliefManager beliefManager;
	private List promises = new ArrayList<>();
	private List sensorArray = new LinkedList<>();

	// Message Listeners - listener pattern to notify other classes of
	// incoming/outgoing messages.
	private List messageListeners = new LinkedList<>();
	private long iteration = 0;

	// Agent Level Rule Set
	private Map> rules = new HashMap<>();

	public Agent(String name) {
		this.name = name;

		// initialize the timings table
		timings.put(name, 0l);
		iterations.put(name, 0l);
		
		beliefManager = new EventBeliefManager(this); 
		reasoner = new NewReasoner(this);
		reasoner.addSource(beliefManager);
		reasoner.addSource(this);
		agents.put(name, this);
		listeners.forEach(listener -> {
			listener.receive(this);
		});
		// out.println(name() + ",NEW");
		
		// TraceManager.getInstance().recordEvent(new TraceEvent(TraceEvent.NEW_AGENT, Calendar.getInstance().getTime(), this));
	}
	
	public boolean isRunnable() {
		return clazz.isRunnable();
	}
	
	public void addSource(Queryable source) {
		reasoner.addSource(source);
	}
	
	public String name() {
		return name;
	}
	
	public void setMainClass(ASTRAClass clazz) throws ASTRAClassNotFoundException {
		this.clazz = clazz;

		List list = clazz.getLinearization();
        for ( ASTRAClass claz : list ) {
            filter.addAll( claz.filter() );
            reasoner.addSource(claz);
        }
        
        Fragment prev = null;      
        for ( ASTRAClass claz : list ) {
        	claz.initialize(this);
        	Fragment fragment = claz.createFragment(this);
        	if (prev!=null) {
        		prev.next = fragment;
        	}
        	linearization.put(claz.getClass().getCanonicalName(), fragment);
        	prev = fragment;
        }
	}
	
	private List filteredClassList(List classList, String scope) throws ASTRAClassNotFoundException {
		if (trace) System.out.println("["+name()+"] Filtering classlist: " + scope);
		for (ASTRAClass c : classList) {
			if (c.getCanonicalName().equals(scope) || c.getClass().getSimpleName().equals(scope)) {
				if (trace) System.out.println("["+name()+"] Linearisation: " + c.getLinearization());
				return c.getLinearization();
			}
		}
		return null;
	}

	public void addRule(Rule rule) {
		String signature = rule.event.signature();
		System.out.println("Adding: " + rule);
		List ruleList = rules.get(signature);
		if (ruleList == null) {
			ruleList = new LinkedList<>();
			rules.put(signature, ruleList);
			filter.add(signature);
		}
		ruleList.add(rule);
	}

	public List rules() {
		List ruleList = new LinkedList<>();
		for (List list : rules.values()) {
			ruleList.addAll(list);
		}
		return ruleList;
	}

	public boolean handleEvent(Event event) {
		// out.println(name() + ",hevt");
		if (trace) {
			System.out.println("__________________________________________________________________________");
			System.out.println("["+name+"] Handling: "+event);
			System.out.println("["+name+"] EQ: "+eventQueue);
		}
		
		// Start by checking if the event should be handled through any active plan
		boolean handled = false;
		Object source = event.getSource();
		if (source != null) {
			if (Intention.class.isInstance(source)) {
				handled = ((Intention) source).handleEvent(event, this);
			} else if (RuleExecutor.class.isInstance(source)) {
				handled = ((RuleExecutor) source).intention().handleEvent(event, this);
			}
		} else {
			for (int i=0; i < intentions.size(); i++) {
				if (intentions.get(i).handleEvent(event, this)) handled = true;
			}
		}
		if (trace) System.out.println("[" + name() + "] NOT ACTIVE PLAN");
	  

		// If we did not handle it in the intention, check if it can be handled at the agent level
		if (!handled) {
			if (trace) System.out.println("["+name()+"] CHECKING LOCAL AGENT RULES");
			if (trace) System.out.println("["+name()+"] Event Signature: " + event.signature());
			if (trace) System.out.println("["+name()+"] Rules: " + rules.keySet());
			List list = rules.get(event.signature());
			if (list != null) {
				int i=0;
				while (i bindings = Helper.evaluateRule(this, rule, event);
					if (bindings != null) {
						if (source != null) {
							Intention intention = null;
							if (Intention.class.isInstance(source)) {
								intention = (Intention) source;
								intention.addSubGoal(event, rule, bindings, null);
								// System.out.println("["+agent.name()+"] \tADDING SUBGOAL: " + rule.event + "\n\tbindings: " + bindings);
							} else if (RuleExecutor.class.isInstance(source)) {
								RuleExecutor executor = (RuleExecutor) source;
								intention = executor.intention();
								intention.addSubGoal(event, rule, bindings, executor);
								// System.out.println("["+agent.name()+"] \tADDING RULE SUBGOAL: " + rule.event + "\n\tbindings: " + bindings);
							}	
							intention.resume();
						} else {
							// Intention i = null;
							// System.out.println("["+agent.name()+"] \tCREATING NEW INTENTION: " + rule.event + "\n\tbindings: " + bindings);
							addIntention(new Intention(this, event, rule, bindings));
							// System.out.println(i.toString());
						}

						handled = true;
					}
				}
			}
		}
		if (trace) System.out.println("[" + name() + "] NOT LOCAL RULE");

		// Next is to check the original agent program...
		if (!handled) {
			if (trace) System.out.println("[" + name + "] Checking program: " + event.signature());
			try {
				List classList = clazz.getLinearization();
				
				if (event instanceof ScopedGoalEvent) {
					// System.out.println("Scope: " + ((ScopedGoalEvent) event).scopedGoal().scope());
					classList = filteredClassList(classList, ((ScopedGoalEvent) event).scopedGoal().scope());
					// System.out.println("ClassList: " + classList);
					if (classList == null) {
						throw new RuntimeException("Agent Class could not be matched: " + ((ScopedGoalEvent) event).scopedGoal().scope());
					}
				}

				if (event instanceof ScopedBeliefEvent) {
					classList = filteredClassList(classList, ((ScopedBeliefEvent) event).scope());
					event = ((ScopedBeliefEvent) event).beliefEvent();
				}

				if (trace) System.out.println("[" + name + "] Filtered Classlist: " + classList);

				int i=0;
				while (!handled && i < classList.size()) {
					ASTRAClass cls = classList.get(i++);
					// System.out.println("Handling: " + cls.getCanonicalName());
					Fragment fragment = linearization.get(cls.getClass().getCanonicalName());
					if (fragment.getASTRAClass().handleEvent(event, this)) handled = true;
				}
			} catch (ASTRAClassNotFoundException e) {
				System.err.println("Problem generating linearisation of: " + clazz.getClass().getCanonicalName());
				e.printStackTrace();
			}
		}

		if (!handled) {
			// Still not handled, so we generate an error!
			if (source != null) {
				Intention lIntention = null;
				if (Intention.class.isInstance(source)) {
					lIntention = (Intention) source;
				} else if (RuleExecutor.class.isInstance(source)) {
					lIntention = ((RuleExecutor) source).intention();
				}
				
				lIntention.failed("Event was not matched to rule: " + event, null);
				lIntention.resume();
			} else {
				if (trace) System.out.println("[astra.core.Agent:"+name+"] Event: " + event +" was not handled");
			}
		}

		if (trace) System.out.println("__________________________________________________________________________");
		return handled;
	}

	public synchronized void execute() {
		// out.println(name() + ",start_loop");
		for (SensorAdaptor adaptor : sensorArray) {
			adaptor.sense(this);
		}
		
		this.beliefManager.update();

		for (int i=0; i> query(Formula formula, Map bindings) {
		return reasoner.query(formula, bindings);
	}		

	public List> queryAll(Formula formula) {
		return reasoner.queryAll(formula);
	}

	public void initialize(Goal goal) {
		addEvent(new GoalEvent('+', goal));
	}

	public void initialize(ScopedGoal goal) {
		addEvent(new ScopedGoalEvent('+', goal));
	}

	public void initialize(Predicate predicate) {
		beliefManager.addBelief(predicate);
	}

	public void addIntention(Intention intention) {
		intentions.add(0, intention);
	}

	public Module getModule(String classname, String key) {
		Fragment fragment = linearization.get(classname == null ? this.clazz.getCanonicalName():classname);
		for (ASTRAClass claz : fragment.getLinearization()) {
			fragment = linearization.get(claz.getClass().getCanonicalName());
			Module module = fragment.getModule(key);
			if (module != null) {
            	return module;
            }
		}
		return null;
	}

	public EventBeliefManager beliefs() {
		return beliefManager;
	}

	public void receive(AstraMessage message) {
		for(AgentMessageListener listener : messageListeners) {
			listener.receive(message);
		}
		
        ListTerm list = new ListTerm();
        // rebuild params...
        // if (message.protocol != null) {
        // 	list.add(new Funct("protocol", new Term[] {Primitive.newPrimitive(message.protocol) }));
        // }
        // if (message.conversationId != null) {
        //  	list.add(new Funct("conversation_id", new Term[] {Primitive.newPrimitive(message.conversationId) }));
        //  }

		eventQueue.add(new MessageEvent( new Performative(message.performative), Primitive.newPrimitive( message.sender ), (Formula) message.content, list ) );
		// out.println(name() + ",new_message");
		lazyActivation();

		// System.out.println("[" + name+ "] received: " + message);
    }
	
	public synchronized boolean addEvent(Event event) {
		if (checkEvent(event)) {
			lazyActivation();
			if (trace()) System.out.println("["+name+"] added: "+ event + " filter: " + filter);
			return eventQueue.add(event);
		} else {
			if (trace) System.out.println("["+name+"] event filtered out by agent: "+ event);
		}
		return false;
	}

	private boolean checkEvent(Event event) {
		// Class Level check
		if (filter.contains(event.signature())) {
			return true;
		}
		
		// System.out.println(">>>>>>>>>>>>>>>>>> Event: " + event);
		// System.out.println("\tSource: " + event.getSource());
		// Intention level check
		Object source = event.getSource();
		if (source != null) {
			if (Intention.class.isInstance(source)) {
				// System.out.println("\tIntention Event");
				return ((Intention) source).checkEvent(event);
			} else if (RuleExecutor.class.isInstance(source)) {
				return ((RuleExecutor) source).intention().checkEvent(event);
			}
		} else {
			// NOT SURE WHY THIS WAS COMMENTED OUT...
			// Caused issue #44
			synchronized (intentions) {
				for (int i=0; i < intentions.size(); i++) {
					if (intentions.get(i).checkEvent(event)) return true;
				}
			}
		}
		return false;
	}

	public List intentions() {
		return intentions;
	}

	public void addSensorAdaptor(SensorAdaptor adaptor) {
		sensorArray.add(adaptor);
	}
	
	public void notifyDone(Notification notification) {
		completed.add(notification);
	}

	public void schedule(Task task) {
		Scheduler.schedule(task);
	}

    public synchronized boolean hasLock( String token, Intention Intention ) {
        return Intention.equals( lockMap.get( token ) );
    }

    public synchronized boolean requestLock( String token, Intention Intention ) {
        if ( tokens.contains( token ) ) {
            // No lock, so queue it..
            lockQueueMap.get( token ).addLast( Intention );
            Intention.suspend();
            return false;
        }

        tokens.add( token );
        lockQueueMap.put( token, new LinkedList<>() );
        lockMap.put( token, Intention );
        return true;
    }

    public synchronized void releaseLock( String token, Intention Intention) {
        if ( !tokens.contains( token ) ) {
            System.err.println( "[" + name() + "] Could not release lock on token: " + token );
        } else {
            if ( !lockMap.remove( token ).equals( Intention ) ) {
                System.out.println( "[ASTRAAgent.releaseLock()] Something strange: look at lock releasing" );
            }

            LinkedList queue = lockQueueMap.get( token );
            if ( queue.isEmpty() ) {
                tokens.remove( token );
            }
            else {
                Intention ctxt = queue.removeFirst();
                lockMap.put( token, ctxt );
                ctxt.resume();
            }
        }
    }

	public void unrequestLock(String token, Intention Intention) {
        if ( !tokens.contains( token ) ) {
            System.err.println( "[" + name() + "] Could not unrequest lock on token: " + token );
        } else {
	        LinkedList queue = lockQueueMap.get( token );
	        queue.remove(Intention);
	        if ( queue.isEmpty() ) {
	            tokens.remove( token );
	        }
        }		
	}
	
	public synchronized void terminate() {
		state = TERMINATING;
		agents.remove(name);
	}
	
	public synchronized boolean isTerminating() {
		return state == TERMINATING;
	}
	
	public Queue events() {
		return eventQueue;
	}

	public boolean startFunction(Predicate function) {
		if (trFunction != null) return false;
		trFunction = function;
		return true;
	}

	public boolean stopFunction() {
		if (trFunction == null) return false;
		trFunction = null;
		return true;
	}

	public Function getFunction(Predicate predicate) {
		Function function;
		Fragment fragment = linearization.get(clazz.getClass().getCanonicalName());
		while (fragment != null) {
			function = fragment.getASTRAClass().getFunction(predicate);
            if (function != null) {
            	return function;
            }
			fragment = fragment.next;
		}
		return null;
	}

	public ASTRAClass getASTRAClass() {
		return this.clazz;
	}

	public Intention intention() {
		return intention;
	}

	public void addAgentMessageListener(AgentMessageListener listener) {
		messageListeners.add(listener);
	}

	public void addPromise(Promise promise) {
		promises.add(promise);
	}

	public void dropPromise(Promise promise) {
		promises.remove(promise);
	}

	public boolean hasActiveFunction() {
		return this.trFunction != null;
	}

	public boolean hasSensors() {
		return !sensorArray.isEmpty();
	}
	
	public void setTrace(boolean trace) {
		this.trace = trace;
	}

	@Override
	public void addMatchingFormulae(Queue queue, Formula formula) {
		if (formula instanceof Goal) {
			for (Intention lIntention : intentions) {
				lIntention.addGoals(queue, (Goal) formula);
			}
		}
	}

	@Override
	public Iterator iterator(Formula formula) {
		if (formula instanceof Goal) {
			Queue queue = new LinkedList<>();
			for (Intention lIntention : intentions) {
				lIntention.addGoals(queue, (Goal) formula);
			}
			return queue.iterator();
		}
		return Queryable.EMPTY_LIST.iterator();
	}

	public boolean trace() {
		return trace;
	}

	boolean evts = true;
	public boolean hasEvents() {
		evts = (evts && !eventQueue.isEmpty()) ? false:true;
		return !eventQueue.isEmpty();
	}

	public String toString() {
		return name;
	}

	public boolean hasActiveIntentions() {
		return !intentions.isEmpty();
	}

	Object lock = new Object();

	public boolean isInactive() {
		return state == Agent.INACTIVE;
	}

	public boolean isActive() {
		return state == Agent.ACTIVE;
	}

	public boolean isStep() {
		return state == Agent.STEP;
	}

	public void setState(int state) {
		synchronized (lock) {
			this.state = state;
		}
	}

	public void lazyActivation() {
		synchronized (lock) {
			if (state == Agent.INACTIVE) {
				state = Agent.ACTIVE;
				Scheduler.schedule(this);
				// out.println(name() + ",RESUMING");
				// System.out.println("[" + name + "] RESUMING");
			}
			// System.out.println("HERE");
		}
	}

	public long getIteration() {
		return iteration;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy