372 lines
13 KiB
Java
372 lines
13 KiB
Java
|
|
||
|
|
||
|
package org.thinkname.ap.main;
|
||
|
|
||
|
|
||
|
|
||
|
import jade.core.AID;
|
||
|
import jade.core.Agent;
|
||
|
import jade.core.behaviours.OneShotBehaviour;
|
||
|
import jade.core.behaviours.TickerBehaviour;
|
||
|
import jade.domain.DFService;
|
||
|
import jade.domain.FIPAException;
|
||
|
import jade.domain.FIPAAgentManagement.DFAgentDescription;
|
||
|
import jade.domain.FIPAAgentManagement.Property;
|
||
|
import jade.domain.FIPAAgentManagement.ServiceDescription;
|
||
|
import jade.lang.acl.ACLMessage;
|
||
|
import jade.proto.SubscriptionInitiator;
|
||
|
import jade.util.Logger;
|
||
|
import jade.util.leap.Iterator;
|
||
|
|
||
|
import java.util.Map;
|
||
|
|
||
|
import org.thinkname.ap.contact.Contact;
|
||
|
import org.thinkname.ap.contact.ContactListChanges;
|
||
|
import org.thinkname.ap.contact.ContactLocation;
|
||
|
import org.thinkname.ap.contact.ContactManager;
|
||
|
import org.thinkname.ap.msn.MsnAgent;
|
||
|
import org.thinkname.ap.msn.MsnEvent;
|
||
|
import org.thinkname.ap.msn.MsnEventMgr;
|
||
|
|
||
|
import android.location.Location;
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Main behaviour executed by the MsnAgent during its setup.
|
||
|
* <p>
|
||
|
* It basically performs two main operations:
|
||
|
* <ul>
|
||
|
* <li> Periodically update location of phone owner contact (updated by mock GPS)on the DF (JADE Directory Facilitator)
|
||
|
* <li> Periodically update other contact's locations anytime we receive a notification from the DF
|
||
|
* </ul>
|
||
|
*
|
||
|
* Moreover it sends events to the gui to issue a refresh, anytime something in the contact list changes.
|
||
|
|
||
|
*/
|
||
|
|
||
|
public class ContactsUpdaterBehaviour extends OneShotBehaviour {
|
||
|
|
||
|
/**
|
||
|
* Time between each update of the my contact location on the DF.
|
||
|
* Read from configuration file
|
||
|
*/
|
||
|
private long msnUpdateTime;
|
||
|
|
||
|
private ContactLocation myContactLocation;
|
||
|
|
||
|
/**
|
||
|
* Instance of the Jade Logger for debugging
|
||
|
*/
|
||
|
private final Logger myLogger = Logger.getMyLogger(this.getClass().getName());
|
||
|
|
||
|
/**
|
||
|
* Instantiates a new contacts updater behaviour.
|
||
|
*
|
||
|
* @param updateTime the update time
|
||
|
* @param myContactLocation location of the my contact
|
||
|
*/
|
||
|
public ContactsUpdaterBehaviour(long updateTime, ContactLocation myContactLocation){
|
||
|
msnUpdateTime = updateTime;
|
||
|
this.myContactLocation = myContactLocation;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Overrides the Behaviour.action() method. This method is executed by the agent thread.
|
||
|
* It basically defines two sub behaviours, which are in charge of periodically updating the DF and receiving
|
||
|
* DF notifications.
|
||
|
*/
|
||
|
public void action() {
|
||
|
try {
|
||
|
//first thing to do is to register on the df and save current location if any
|
||
|
DFAgentDescription myDescription = new DFAgentDescription();
|
||
|
//fill a msn service description
|
||
|
ServiceDescription msnServiceDescription = new ServiceDescription();
|
||
|
msnServiceDescription.setName(MsnAgent.msnDescName);
|
||
|
msnServiceDescription.setType(MsnAgent.msnDescType);
|
||
|
myDescription.addServices(msnServiceDescription);
|
||
|
|
||
|
ContactManager.getInstance().resetModifications();
|
||
|
|
||
|
DFAgentDescription[] onlineContacts = DFService.search(myAgent, myDescription);
|
||
|
|
||
|
updateContactList(onlineContacts);
|
||
|
|
||
|
MsnEvent event = MsnEventMgr.getInstance().createEvent(MsnEvent.VIEW_REFRESH_EVENT);
|
||
|
|
||
|
Map<String,Contact> cMap = ContactManager.getInstance().getAllContacts();
|
||
|
Map<String,ContactLocation> cLocMap = ContactManager.getInstance().getAllContactLocations();
|
||
|
ContactListChanges changes = ContactManager.getInstance().getModifications();
|
||
|
|
||
|
myLogger.log(Logger.FINE,"Thread "+ Thread.currentThread().getId() + "After reading local contacts and first df query: " +
|
||
|
"Adding to VIEW_REFRESH_EVENT this list of changes: " + changes.toString());
|
||
|
event.addParam(MsnEvent.VIEW_REFRESH_PARAM_LISTOFCHANGES, changes);
|
||
|
event.addParam(MsnEvent.VIEW_REFRESH_CONTACTSMAP, cMap);
|
||
|
event.addParam(MsnEvent.VIEW_REFRESH_PARAM_LOCATIONMAP, cLocMap);
|
||
|
MsnEventMgr.getInstance().fireEvent(event);
|
||
|
|
||
|
DFUpdaterBehaviour updater = new DFUpdaterBehaviour(myAgent,msnUpdateTime, myContactLocation);
|
||
|
MsnAgent agent = (MsnAgent) myAgent;
|
||
|
DFSubscriptionBehaviour subBh = new DFSubscriptionBehaviour(myAgent,agent.getSubscriptionMessage());
|
||
|
|
||
|
myAgent.addBehaviour(updater);
|
||
|
myAgent.addBehaviour(subBh);
|
||
|
} catch (Exception e) {
|
||
|
// TODO Auto-generated catch block
|
||
|
myLogger.log(Logger.SEVERE, "Severe error: ", e);
|
||
|
e.printStackTrace();
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Utility method that updates the contact list extracting contact info and location from DF descriptions.
|
||
|
*
|
||
|
* @param onlineContactsDescs array of {@link DFAgentDescription} objects that define services as results of a DF query
|
||
|
*/
|
||
|
private void updateContactList(DFAgentDescription[] onlineContactsDescs) {
|
||
|
|
||
|
for (int i = 0; i < onlineContactsDescs.length; i++) {
|
||
|
Iterator serviceDescIt = onlineContactsDescs[i].getAllServices();
|
||
|
|
||
|
if (serviceDescIt.hasNext()){
|
||
|
ServiceDescription desc = (ServiceDescription) serviceDescIt.next();
|
||
|
|
||
|
Iterator propertyIt = desc.getAllProperties();
|
||
|
Location loc = Helper.extractLocation(propertyIt);
|
||
|
|
||
|
AID cId = onlineContactsDescs[i].getName();
|
||
|
if (!cId.equals(myAgent.getAID())){
|
||
|
//Create an online contact (or update it)
|
||
|
String phoneNumber = cId.getLocalName();
|
||
|
ContactManager.getInstance().addOrUpdateOnlineContact(phoneNumber, loc);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Static class that provides some Helper methods useful for extracting contact data
|
||
|
*/
|
||
|
private static class Helper {
|
||
|
|
||
|
/**
|
||
|
* Extract a Location from a list of properties from a Service description
|
||
|
*
|
||
|
* @param it iterator over the list of properties
|
||
|
* @return the location on the map described by this set of properties
|
||
|
*/
|
||
|
|
||
|
public static Location extractLocation(Iterator it){
|
||
|
Location loc= new Location("mygps");
|
||
|
|
||
|
while (it.hasNext()){
|
||
|
Property p = (Property) it.next();
|
||
|
|
||
|
String propertyName = p.getName();
|
||
|
|
||
|
if (propertyName.equals(DFUpdaterBehaviour.PROPERTY_NAME_LOCATION_ALT)){
|
||
|
double altitude = Double.parseDouble((String) p.getValue());
|
||
|
loc.setAltitude(altitude);
|
||
|
} else if (propertyName.equals(DFUpdaterBehaviour.PROPERTY_NAME_LOCATION_LAT)){
|
||
|
double latitude = Double.parseDouble((String) p.getValue());
|
||
|
loc.setLatitude(latitude);
|
||
|
} else if (propertyName.equals(DFUpdaterBehaviour.PROPERTY_NAME_LOCATION_LONG)){
|
||
|
double longitude = Double.parseDouble((String) p.getValue());
|
||
|
loc.setLongitude(longitude);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return loc;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sub behaviour that handles notification messages for modification of DF entries.
|
||
|
* After this behaviour is added, a DF subscription message is sent.
|
||
|
* Method handleInform() shall be called hereafter each time the DF is modified by other contacts either by updating it
|
||
|
* with a new location or by adding/removing a contact
|
||
|
*/
|
||
|
private class DFSubscriptionBehaviour extends SubscriptionInitiator
|
||
|
{
|
||
|
|
||
|
/**
|
||
|
* Instantiates a new dF subscription behaviour.
|
||
|
*
|
||
|
* @param agent the agent
|
||
|
* @param msg the subscription message to be sent
|
||
|
*/
|
||
|
public DFSubscriptionBehaviour(Agent agent, ACLMessage msg) {
|
||
|
super(agent, msg);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Overrides SubscriptionInitiator.handleInform(), defining what to do each time the DF is modified by a contact
|
||
|
* Basically it adds/removes/updates contacts from ContactList according to what has happened to DF.
|
||
|
* It also fires events on the GUI any time a view refresh is needed.
|
||
|
*
|
||
|
* @param inform the message from DF containing a list of changes
|
||
|
*/
|
||
|
protected void handleInform(ACLMessage inform) {
|
||
|
|
||
|
myLogger.log(Logger.FINE, "Thread "+ Thread.currentThread().getId() + ": Notification received from DF");
|
||
|
ContactManager.getInstance().resetModifications();
|
||
|
|
||
|
try {
|
||
|
|
||
|
DFAgentDescription[] results = DFService.decodeNotification(inform.getContent());
|
||
|
|
||
|
if (results.length > 0) {
|
||
|
|
||
|
for (int i = 0; i < results.length; ++i) {
|
||
|
DFAgentDescription dfd = results[i];
|
||
|
AID contactAID = dfd.getName();
|
||
|
// Do something only if the notification deals with an agent different from the current one
|
||
|
if (!contactAID.equals(myAgent.getAID())){
|
||
|
|
||
|
myLogger.log(Logger.INFO,"Thread "+ Thread.currentThread().getId() + ":df says that agent "+myAgent.getAID().getLocalName() +" updates or registers" );
|
||
|
Iterator serviceIter = dfd.getAllServices();
|
||
|
|
||
|
//Registered or updated
|
||
|
if (serviceIter.hasNext()){
|
||
|
ServiceDescription serviceDesc = (ServiceDescription) serviceIter.next();
|
||
|
Iterator propertyIt = serviceDesc.getAllProperties();
|
||
|
Location loc = Helper.extractLocation(propertyIt);
|
||
|
String phoneNum = contactAID.getLocalName();
|
||
|
if(phoneNum=="18754371111")
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
ContactManager.getInstance().addOrUpdateOnlineContact(phoneNum, loc);
|
||
|
} else {
|
||
|
myLogger.log(Logger.INFO,"Thread "+ Thread.currentThread().getId() + ":df says that agent "+myAgent.getAID().getLocalName() +" deregisters" );
|
||
|
String phoneNumber = contactAID.getLocalName();
|
||
|
Contact c = ContactManager.getInstance().getContact(phoneNumber);
|
||
|
|
||
|
if(phoneNumber=="18754371111")
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
ContactManager.getInstance().setContactOffline(phoneNumber);
|
||
|
MsnEvent event = MsnEventMgr.getInstance().createEvent(MsnEvent.CONTACT_DISCONNECT_EVENT);
|
||
|
event.addParam(MsnEvent.CONTACT_DISCONNECT_PARAM_CONTACTNAME, c.getName());
|
||
|
MsnEventMgr.getInstance().fireEvent(event);
|
||
|
}
|
||
|
|
||
|
MsnEvent event = MsnEventMgr.getInstance().createEvent(MsnEvent.VIEW_REFRESH_EVENT);
|
||
|
ContactListChanges changes = ContactManager.getInstance().getModifications();
|
||
|
Map<String,Contact> cMap = ContactManager.getInstance().getAllContacts();
|
||
|
Map<String,ContactLocation> cLocMap = ContactManager.getInstance().getAllContactLocations();
|
||
|
|
||
|
myLogger.log(Logger.FINE,"Thread "+ Thread.currentThread().getId() + ":Adding to VIEW_REFRESH_EVENT this list of changes: " + changes.toString());
|
||
|
event.addParam(MsnEvent.VIEW_REFRESH_PARAM_LISTOFCHANGES, changes);
|
||
|
event.addParam(MsnEvent.VIEW_REFRESH_CONTACTSMAP, cMap);
|
||
|
event.addParam(MsnEvent.VIEW_REFRESH_PARAM_LOCATIONMAP, cLocMap);
|
||
|
MsnEventMgr.getInstance().fireEvent(event);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
catch (Exception e) {
|
||
|
myLogger.log(Logger.WARNING, "See printstack for Exception.", e);
|
||
|
e.printStackTrace();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Extends {@link TickerBehaviour} and defines the operations needed to update the location of the my contact
|
||
|
* on the DF.
|
||
|
* My Contact location is continuously updated by the local LocationProvider (GPS) according to actual contact
|
||
|
* position, then this value is periodically written to the DF by this behaviour.
|
||
|
*/
|
||
|
private class DFUpdaterBehaviour extends TickerBehaviour {
|
||
|
|
||
|
/**
|
||
|
* Instance of Jade logger for debugging
|
||
|
*/
|
||
|
private final Logger myLogger = Logger.getMyLogger(this.getClass().getName());
|
||
|
|
||
|
/**
|
||
|
* The default name for Latitude property on the DF
|
||
|
*/
|
||
|
public static final String PROPERTY_NAME_LOCATION_LAT="Latitude";
|
||
|
|
||
|
/**
|
||
|
* The default name for Longitude property on the DF
|
||
|
*/
|
||
|
public static final String PROPERTY_NAME_LOCATION_LONG="Longitude";
|
||
|
|
||
|
/**
|
||
|
* The default name for Altitude property on the DF
|
||
|
*/
|
||
|
public static final String PROPERTY_NAME_LOCATION_ALT="Altitude";
|
||
|
|
||
|
private ContactLocation myContactLocation;
|
||
|
|
||
|
/**
|
||
|
* Instantiates a new dF updater behaviour.
|
||
|
*
|
||
|
* @param a instance of the agent
|
||
|
* @param period update period in milliseconds
|
||
|
* @param contactLocation location of the my contact
|
||
|
*/
|
||
|
public DFUpdaterBehaviour(Agent a, long period, ContactLocation contactLocation) {
|
||
|
super(a, period);
|
||
|
myContactLocation = contactLocation;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Overrides the TickerBehaviour, defining how to update the location on the df (registration to DF has already been performed
|
||
|
* during agent setup). Three properties are defined for storing latitude/longitude/altitude.
|
||
|
* Only latitude and longitude are used, though.
|
||
|
*/
|
||
|
protected void onTick() {
|
||
|
|
||
|
try {
|
||
|
MsnAgent agent = (MsnAgent) myAgent;
|
||
|
DFAgentDescription description = agent.getAgentDescription();
|
||
|
|
||
|
ServiceDescription serviceDescription = (ServiceDescription) description.getAllServices().next();
|
||
|
serviceDescription.clearAllProperties();
|
||
|
|
||
|
//retrieve
|
||
|
ContactLocation curMyLoc = ContactManager.getInstance().getMyContactLocation();
|
||
|
if(!curMyLoc.equals(myContactLocation)){
|
||
|
|
||
|
Property p = new Property(PROPERTY_NAME_LOCATION_LAT,new Double(curMyLoc.getLatitude()));
|
||
|
serviceDescription.addProperties(p);
|
||
|
p = new Property(PROPERTY_NAME_LOCATION_LONG,new Double(curMyLoc.getLongitude()));
|
||
|
serviceDescription.addProperties(p);
|
||
|
p= new Property(PROPERTY_NAME_LOCATION_ALT,new Double(curMyLoc.getAltitude()));
|
||
|
serviceDescription.addProperties(p);
|
||
|
DFService.modify(myAgent, description);
|
||
|
myContactLocation = curMyLoc;
|
||
|
}
|
||
|
|
||
|
} catch (FIPAException fe) {
|
||
|
myLogger.log(Logger.SEVERE, "Error in updating DF", fe);
|
||
|
}
|
||
|
catch(Exception e) {
|
||
|
myLogger.log(Logger.SEVERE,"*** Uncaught Exception for agent " + myAgent.getLocalName() + " ***",e);
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|