/** * Title: GPS Simulator * Copyright: Copyright (c) 2002 * Company: University of Oregon Computer Science Dept. * * @author Jason Prideaux * @version 1.0 * */ package controller; import gui.*; import gui.tray.*; import util.*; import agent.*; import transport.*; import transport.protocol.*; import javax.swing.JFrame; import java.awt.*; import java.util.*; /* ====================================================================== */ /** This class is the core of the simulator. It initializes the interface * components, holds the agents, and controls the sockets for sending and * recieving data. * */ public class Controller implements SocketReceiverListener{ /** The chart is the visual map. */ private ChartComp chart; /** The main frame which displays the map ChartComp. */ private SimulatorFrame frame; /** An interface component for viewing agent stats, etc. */ private TrayDialog tray; /** The agent that is being moved, or has center of screen. */ private Agent current_agent; /** The list of all access points in the simulation. */ private Vector points = new Vector(); /** The list of all users in the simulation. */ private Vector users = new Vector(); /** Components that want to listen for when information changes in controller. */ private Vector listeners = new Vector(); /* ===================================================================== */ /** This constructor initializes the controller by creating the interface * components, so that the user can begin interacting with the simulator. * It also starts the SocketReceiver for listening to incoming messages. * */ public Controller(){ chart = new ChartComp(this, false ); chart.loadChart(&quot;uo_data.xml&quot;); frame = new SimulatorFrame(this, chart); tray = new TrayDialog(this, frame); try{ Thread.sleep(500); }catch(Exception e){} frame.setVisible(true); SocketReceiver sr = new SocketReceiver(this, 2323); sr.start(); } //Constructor /* ===================================================================== */ /** This method is for adding a new user into the simulator. The user is * created based on the given information, and is added into the map, and * all controller listeners are notified of the new addition. * * @param r The range for the new user. * @param p The port for the new user. * */ public void addUser(int r, int p){ Point pt = frame.getChartPane().getScrollPosition(); Dimension d= frame.getChartPane().getViewportSize(); User u = new User( new Point((int)(pt.getX()+(d.width)/2),(int)(pt.getY()+(d.height)/2)), p ); current_agent = u; users.add( current_agent); current_agent.setRange(r); current_agent.setName( &quot;user&quot;+users.indexOf(current_agent) ); int index = users.indexOf(u); updateAgentPosition( current_agent ); fireControllerChange(); } //method: addUser /* ===================================================================== */ /** This method is for removing a user from the simulator. The actual * user is given to the method, and the remove from the simulation, and * all controller listeners are notified of the new removal. * * @param u The User object to remove from the users list. * */ public void removeUser(User u){ users.remove(u); if( current_agent.getName().equals( u.getName()) &amp;&amp; users.size() &gt; 0){ current_agent = (User)users.elementAt(0); } chart.repaint(); fireControllerChange(); } //method: removeUser /* ===================================================================== */ /** This method is for adding a new access point into the simulator. The * accesspoint is created based on the given information, and is added * into the map, and all controller listeners are notified of the new * addition. * * @param r The range for the new Access Point * */ public void addAccessPoint(int r ){ Point pt = frame.getChartPane().getScrollPosition(); Dimension d= frame.getChartPane().getViewportSize(); AccessPoint u = new AccessPoint( &quot; &quot;, new Point((int)(pt.getX()+(d.width)/2),(int)(pt.getY()+(d.height)/2)), r ); current_agent = u; points.add( u); u.setRange(r); u.setName( &quot;access&quot;+points.indexOf(u) ); updateAgentPosition( u ); fireControllerChange(); } //method: addAccessPoint /* ===================================================================== */ /** This method is for removing a access point from the simulator. The actual * access point is given to the method, and the remove from the simulation, and * all controller listeners are notified of the new removal. * * @param u The Access Point object to remove from the points list. * */ public void removeAccessPoint(AccessPoint u){ points.remove(u); chart.repaint(); fireControllerChange(); } //method: removeAccessPoint /* ===================================================================== */ /** This method updating an Agent's gps Position. It will get the pixel * location, and then turn this into a gps Position. * * @param u The agent whose Position needs to be set by this method. * */ public void updateAgentPosition(Agent u){ // Get the location Point chart.updatePos(u.getLocation(), users, points); Point poi = u.getLocation(); // Turn pixel into gps coordinates. Position chart_pos = chart.getChartData().transPos(poi); Position old = u.getPosition(); //Retain some old Position information. chart_pos.setHeading( old.getHeading() ); chart_pos.setTime( old.getTime() ); u.setPosition( chart_pos ); } //method: updateAgentPosition /* ===================================================================== */ /** This method returns the Vector of all the current users in the simulator. * * @return Vector All users in the simulation. * */ public Vector getUsers(){ return users; } //method: getUsers /* ===================================================================== */ /** This method returns the Vector of all current access points in the * simulator. * * @return Vector All Access Points in the simulation. * */ public Vector getPoints(){ return points; } //method: getPoints /* ===================================================================== */ /** This method returns the current agent in focus or being moved around * in the simulator map. * * @return Agent The current Agent being moved or center on screen. * */ public Agent getCurrentAgent(){ return current_agent; } //method: getCurrentAgent /* ===================================================================== */ /** This method sets who the current is. * * @param a The Agent to make the current agent. * */ public void setCurrentAgent(Agent a){ current_agent = a; } //method: setCurrentAgent /* ===================================================================== */ /** This method is for centering the given agent on the map. Also returns * the agent object in case it is needed once centered. * * @param name The name of Agent to find and center on screen. * @return Agent The Agent object that was centered. * */ public Agent centerAgent(String name){ Agent a = null; // Find the user or access point. for( int i=0; i <users.size(); i++ ){ a = (Agent)users.elementAt(i); if( name.indexOf(""+a.getName()) > -1 ){ current_agent = a; fireControllerChange(); frame.centerScreen(a.getLocation()); } } for( int i=0; i <points.size(); i++ ){ a = (Agent)points.elementAt(i); if( name.indexOf(""+a.getName()) > -1 ){ current_agent = a; fireControllerChange(); frame.centerScreen(a.getLocation()); } } return a; } //method: centerAgent /* ===================================================================== */ /** This method is called when a User's location has changed. A NMEA * Message is created and then sent to the User's corresponding iPAQ via * a port number. * * @param u The user who has changed location. * */ public void locationChange(User u){ Position p = u.getPosition(); Message msg = new NMEAMessage(u.getPort(), 2323, p.getLatitude(), p.getLongitude(), p.getHeading(), p.getTime()); SocketSender ss = new SocketSender(msg); ss.start(); } //method: locationChange /* ===================================================================== */ /** This method is called to send a message to all agents. * * @param msg Message to send * */ public void sendToAll(Sim2PMessage msg){ User a = null; // Find the user or access point. for( int i=0; i <users.size(); i++ ){ a = (User)users.elementAt(i); msg.setTo(a.getPort()); SocketSender ss = new SocketSender(msg); ss.start(); } } //method: sendToAll /* ===================================================================== */ /** This method simply tests to see if the tray is visible, if not visible * then it is assumed to be dead. * * @return boolean True if tray is alive and visible, false otherwise. * */ public boolean isTrayAlive(){ try{ if( tray.isVisible()){ return true; } }catch(Exception e){ return false; } return false; } //method: isTrayAlive /* ===================================================================== */ /** This method creates a new TrayDialog for performing various functions * in the simulator, and viewing information. * */ public void makeTray(){ tray = new TrayDialog(this, frame); fireControllerChange(); } //method: makeTray /* ===================================================================== */ /** This method returns the main JFrame for the iSIM application. * * @return JFrame The main JFrame for the simulation. * */ public JFrame getFrame(){ return frame; } //method: getFrame /* ===================================================================== */ /** This method returns the ChartComp which holds the simulation map, and * draws all the users. * * @return CharComp The JComponent that is the map. * */ public ChartComp getChartComp(){ return(chart); } //method: getChartComp /* ===================================================================== */ /** This method sets whether the wireless ranges should be displayed or * not. * * @param ranges True to show the wireless range on map, false otherwise. * */ public void setRanges(boolean ranges){ chart.setRanges(ranges); } //method: setRanges /* ===================================================================== */ /** This method changes the current map/chart used in the simulation. * The GUI Frame and ChartComp handle the details behind this. * * @param chartName The new chart file name. * @param scale The scale for the map image. * @param precision The drawing position for lines on map. * */ public void changeChart(String chartName){ frame.changeChart(chartName); } //method: changeChart /* ===================================================================== */ /** This method is for reseting the data insdie the controller. It removes * all agents (users and access points). * */ public void reset(){ users = new Vector(); points = new Vector(); current_agent = null; chart.repaint(); fireControllerChange(); } //method: reset /* ===================================================================== */ /** This method returns the pixel distance between two points. * * @param p1 The first point. * @param p2 The second point. * */ private double diff(Point p1, Point p2){ double x = p1.getX() - p2.getX(); if( x < 0 ) x = x * -1; double y = p1.getY() - p2.getY(); if( y < 0 ) y = y * -1; return Math.sqrt( x*x + y*y ); } //method: diff /* ===================================================================== */ /** This method is required by SocketReceiverListener and is called by the * SocketReceiver object. It delivers a message to us from some other * client application wanting to send a message. * * This method uses a multicasting approach and sends the message to all * agents in range and up to a certain number of hops designated in the * message itself. It calls a Thread now to do the message passing, so * that bandwidth delays can be simulated. * * @param msg The incoming Message that the simulator has received. * */ public void incomingMessage(Message msg){ if( msg instanceof MulticastMessage ){ System.out.println("Received Multicast message from an iPAQ"); MessageSender ms = new MessageSender( (MulticastMessage)msg ); ms.start(); } //if } //method: incomingMessage /* ===================================================================== */ /** This methods adds a listener to the list of listeners this controller * knows about. * * @param listener * */ public void addControllerListener(ControllerListener listener){ listeners.add(listener); } //method: addControllerListener /* ===================================================================== */ /** This method removes a listener from the list of listeners that * this controller knows about. * * @param listener * */ public void removeControllerListener(ControllerListener listener){ listeners.remove(listener); } //method: removeControllerListener /* ===================================================================== */ /** This method notifies all subscribed listeners that they must * refresh themselves. */ public void fireControllerChange(){ ControllerListener[] informMe = new ControllerListener[listeners.size()]; informMe = (ControllerListener[])listeners.toArray(informMe); for(int i = 0; i < informMe.length; i++){ informMe[i].refresh(); } } //method: fireControllerChange /* ===================================================================== */ /** This class is used for Multicasting a message to all users in wireless * range. It adds in timing delays based on ratio of max range/distance * to simulate bandwidth loss and delay due to wireless signal * degradation. * */ class MessageSender extends Thread{ /** The message to send in this thread. */ private MulticastMessage msg; /* ================================================================ */ /** This Costructor preps the Thread for the given message to send. * * @param msg The message to send to other users in range. * */ public MessageSender(MulticastMessage msg){ this.msg = msg; } //Constructor /* ================================================================ */ /** This method excutes the thread. It sends the message to all * Users in range, adds a delay, and sends to Users that be reached * in a number of hops specified by the message. * */ public void run(){ System.out.println("Processing a Multicast message..."); int hops = msg.getHops(); int from = msg.getFrom(); double diff = 0; Agent a = null; Agent temp = null; Vector tosendto = new Vector(); // Find the agent that sent the message. for( int i=0; i < users.size(); i++ ){ temp = (Agent)users.elementAt(i); if( from == ((User)temp).getPort() ){ a = temp; } } tosendto.add(a); System.out.println(" Sent by "+ ((User)a).getPort()); //Now send message to all agents except sender. if( a != null ){ // Send to all agents in one hop. for( int i=0; i < users.size(); i++ ){ temp = (Agent)users.elementAt(i); diff = diff(a.getLocation(),temp.getLocation()); System.out.println( "diff: "+diff); if( diff < (a.getRange()+temp.getRange()) && !(tosendto.contains(temp)) ){ // Impose a delay based on the distance and range. try{ Thread.sleep( (int)(2000*((a.getRange()+temp.getRange())/diff)) ); }catch(Exception e){} msg.setTo(((User)temp).getPort()); SocketSender ss = new SocketSender(msg); ss.start(); tosendto.add(temp); } } for( int i=0; i < points.size(); i++ ){ temp = (Agent)points.elementAt(i); diff = diff(a.getLocation(),temp.getLocation()); if( diff < (a.getRange()+temp.getRange()) && !(tosendto.contains(temp)) ){ tosendto.add(temp); } } // Now find those within hops. int h = 1; while( h < hops ){ h++; // Impose a delay inbetween hops try{ Thread.sleep(2000); }catch(Exception e){} for( int j=0; j < tosendto.size(); j++ ){ a = (Agent)tosendto.elementAt(j); // Find all agents in one hop. for( int i=0; i < users.size(); i++ ){ temp = (Agent)users.elementAt(i); diff = diff(a.getLocation(),temp.getLocation()); if( !(tosendto.contains(temp)) && diff < (a.getRange()+temp.getRange()) ){ // Impose a delay based on the distance and range. try{ Thread.sleep( (int)(2000*((a.getRange()+temp.getRange())/diff)) ); }catch(Exception e){} msg.setTo(((User)temp).getPort()); SocketSender ss = new SocketSender(msg); ss.start(); tosendto.add(temp); } } for( int i=0; i < points.size(); i++ ){ diff = diff(a.getLocation(),temp.getLocation()); temp = (Agent)points.elementAt(i); if( !(tosendto.contains(temp)) && diff < (a.getRange()+temp.getRange()) ){ tosendto.add(temp); } } } } //while } } //method: run } //class: MessageSender } //class: Controller