/**
*  File Name : BaseStation.java
*
*  The University of Texas at Arlington
*  Software Engineering Center for Telecommunications
*  Object Oriented Testing Project
*
*  (c) Copyright 2000 University of Texas at Arlington
*  ALL RIGHTS RESERVED
*
*  Input                        :  Different channels used in a wireless system are simulated using different ports
*                                    20101 - Reverse control channel for sending controls to the Base Station
*                                    20001 - Forward control channel for receiving controls from the Base Station
*                                    20111 - Reverse data channel for sending data to Base Station
*                                    20011 - Forward data channel for receiving data from Base Station
*                                  The Handset sends controls to Base Station in the following format: PhoneNum MesgType
*                                    The MesgType has the following values
*                                       4 for registration
*                                      10 for callinitiation
*                                       0 for acknowledgement
*                                      12 for ringback
*                                      16 for connect
*  Output                       : The BaseStation connects to the Mobile Switching Center on the following ports
*                                     7777 - for sending controls to MSC
*				                      6666 - for receiving controls from MSC
*				                      8888 - for sending data to MSC
*				                      9999 - for receiving data to MSC
*  Supported Requirements       : This code handles call requests from
*                                  (1) A handset to another handset in the same Base Station
*                                  (2) A handset to another handset in a different Base Station
*                                  (3) Many handsets in the same Base Station
*                                  (4) Many handsets in different Base Stations
*
*  Classes in this file         : BaseStation, BaseStationController, BaseStationControllerData,
*                                  BaseStationData, BaseStationForwardCrtl,BaseStationForwardData,
*                                  BaseStationTranceiverControl, ConnectionHandler, DataHandler,
*                                  HandsetSocket, Status, Storage
*
*  Update History:
*
*  Date         Author                          Changes
*  ------------------------------------------------------------------------------
*  04/27/00     Susheela Smitha Kommaraju    Initial code which handles
*                                           call requests in the same Base Station
*
*  05/01/00     Susheela Smitha Kommaraju    Initiating connections between Handsets
*												in different Base Stations
*
*  05/04/00     Susheela Smitha Kommaraju     Transfering data between different
*											  Handsets in different Base Stations
*			      and  Satish Jonnalgedda
*
*  Functional Description       : This code handles call requests from any number of handsets.
*                                  Connection between handsets is established and handsets can
*                                  exchange messages
*  Error Messages               : Null pointer exceptions result when the assumptions are not met.
*                                 Otherwise there are no error messages
*  Constraints                  : This code works under normal conditions i.e., when the handsets
*									are connected to both the Control channels.
*
*  Assumptions                  : It is assumed that the Handset is never busy and is always ready to receive messages.
*
*
**/
import java.io.*;
import java.net.*;
import java.util.*;

public class BaseStation
{
    public static void main(String args[])
    {
      try
      {
    	ServerSocket rc = new ServerSocket(20101);  // receives controls from Handset
	    ServerSocket fc = new ServerSocket(20001);  // writes controls to Handset
        ServerSocket rd = new ServerSocket(20111);  // receives data from Handset
        ServerSocket fd = new ServerSocket(20011);  // writes data to Handset

        Storage stm = new Storage();      // Storage is a class which stores phone numbers
						//	and IP address using hash tables
        Status sta = new Status();        // Status is a class which stores phone numbers
					//  and if they are connected to a Handset
					//    in the same basestation or different base station
        HandsetSocket hs = new HandsetSocket(); // stores the sockets at which the Handsets are listerns for controls
	    HandsetSocket datas = new HandsetSocket(); // stores the sockets at which the Base Station
												   //     can send data to Handset
        HandsetSocket datar = new HandsetSocket(); // stores the sockets at which the Handsets send data
        new BaseStationController(hs,stm, sta).start();  // class BSC does forwarding and
					 // receiving of messages and data to and from MSC
	    new BaseStationControllerData(datas, datar,stm, sta).start();
    	new BaseStationTransceiverControl(hs,rc,stm, sta).start();  // class BTSC does connecting of handsets
        new BaseStationForwardCrtl(hs,fc).start();
	    new BaseStationForwardData(datas,fd).start();
        new BaseStationData(datas,datar,rd,stm, sta).start();// class BTSD handles conversation between handsets
      }
      catch(Exception e)
	  { System.out.println(e); }
    }
}

// This class accepts connections from handset sockets where control messages are sent from Base Station
class BaseStationForwardCrtl extends Thread
{   private ServerSocket fControl;
    private int num = 1;
    public HandsetSocket haso = new HandsetSocket();

    public BaseStationForwardCrtl (HandsetSocket hs,ServerSocket fc)
    {
           haso = hs;
           fControl = fc;
    }
    public void run()
    {
      try
      {
        for(;;)
              {   Socket forwardControl = fControl.accept();
                  InetAddress iAddr = forwardControl.getInetAddress();
                  haso.takeSocket(iAddr, forwardControl);
                  System.out.println("No. of Handsets accessing Forward Control Channels " + num);
                  num++;
         }
      }
      catch(Exception e)
      {  System.out.println(e);
      }
    }
}

// This class accepts connections from handset sockets where data are sent to Handset

class BaseStationForwardData extends Thread
{   private ServerSocket fData;
    private int num = 1;
    public HandsetSocket daso = new HandsetSocket();

    public BaseStationForwardData(HandsetSocket ds, ServerSocket fd)
    {
          fData = fd;
          daso = ds;
    }
    public void run()
    {
      try
      {
         for(;;)
         {
            Socket forwardData = fData.accept();
            InetAddress iAddr = forwardData.getInetAddress();
            daso.takeSocket(iAddr, forwardData);
            System.out.println("No. of Handsets accessing Forward Data Channels " + num);
            num++;
         }

      }
      catch(IOException e)
      { System.out.println(e);
      }
    }
}

// This class accepts connections from handset sockets where control messages are received from Handset

class BaseStationTransceiverControl extends Thread
{   private ServerSocket sControl;
    private int num = 1;
    public Storage x;
    public Status y;
    public HandsetSocket hs;

    public BaseStationTransceiverControl(HandsetSocket haso,ServerSocket rc,Storage stm, Status sta)
    {
           hs = haso;
           sControl = rc;
           x = stm;
           y = sta;
    }
    public void run()
    {
      try
      {
        for(;;)
	      {   Socket revControl = sControl.accept();
	          new ConnectionHandler(revControl, x, y, hs).start();
	          System.out.println("No. of Handsets accessing Reverse Control Channels " + num);
	          num++;
         }
      }
      catch(Exception e)
      {  System.out.println(e);
      }
    }
}

// This class accepts connections from handset sockets where data are received from Handset

class BaseStationData extends Thread
{   private ServerSocket rData;
    private int num = 1;
    public Storage x;
    public Status y;
    public HandsetSocket hs;
    public HandsetSocket hr;

    public BaseStationData(HandsetSocket datas,HandsetSocket datar,ServerSocket rd,Storage stm, Status sta)
    {
           hs = datas;
	   hr = datar;
           rData = rd;
           x = stm;
           y = sta;
    }
    public void run()
    {
      try
      {
        for(;;)
              {   Socket revData = rData.accept();
		  InetAddress iAddr = revData.getInetAddress();
		  hr.takeSocket(iAddr,revData);
                  new DataHandler(revData, x, y, hs, hr).start();
                  System.out.println("No. of Handsets accessing Reverse Data Channels " + num);
                  num++;
         }
      }
      catch(Exception e)
      {  System.out.println(e);
      }
    }
}

// This class handles connections between handsets

class ConnectionHandler extends Thread
{   private Socket incoming;
    private Storage storageobj;
    private Status statusobj;
    public HandsetSocket hs;

    public ConnectionHandler(Socket i, Storage x, Status y, HandsetSocket h)
        {  incoming = i;
           storageobj = x;
           statusobj = y;
	   hs = h;
         }

    public void run()
    {
       try
       {   BufferedReader in = new BufferedReader
                (new InputStreamReader(incoming.getInputStream()));
         for(;;)
         {
           String str = in.readLine();
	       System.out.println("BS receives " + str + " from Handset");
           StringTokenizer t = new StringTokenizer(str," ");
           String sourcePhoneNum = t.nextToken();
           String mesgType = t.nextToken();

           if (mesgType.equals("4"))
           {
	        InetAddress iAddr = incoming.getInetAddress();
                storageobj.takeAddress(sourcePhoneNum,iAddr);
	     //   BaseStationController.sendToMSC(str);
           }
           else if (mesgType.equals("10"))
           {
                BaseStationController.sendToMSC(str);
                storageobj.sendToHandset(hs, sourcePhoneNum, sourcePhoneNum + " 0");
           }
           else if (mesgType.equals("0"))
           {    if ((statusobj.getBSType(sourcePhoneNum)).equals("Same"))
		{
		    System.out.println("HS is in the same BS");
		    String otherHS = statusobj.getOtherHandset(sourcePhoneNum);
		    String mesg = otherHS + " 12";
		    storageobj.sendToHandset(hs, otherHS, mesg);
		}
		else
		{
		    statusobj.bsType.put(sourcePhoneNum, "Different");
                    BaseStationController.sendToMSC(str);
		}
           }
	   else if (mesgType.equals("16"))
	   {
		String bsType = statusobj.getBSType(sourcePhoneNum);
		if (bsType.equals("Same"))
		{
		    String destHandset = statusobj.getOtherHandset(sourcePhoneNum);
		    String connectMesg = destHandset + " 16";
		    storageobj.sendToHandset(hs,destHandset, connectMesg);
		}
		else
		{
		    BaseStationController.sendToMSC(str);
		}
	    }
         }
       }
       catch(Exception e)
       {   System.out.println(e);
       }
     }
}

// This class handles data transfer between handsets

class DataHandler extends Thread
{
    HandsetSocket hSend;
    HandsetSocket hRec;
    private Socket incoming;
    private Storage storageobj;
    private Status statusobj;

    public DataHandler(Socket i, Storage x, Status y, HandsetSocket hs, HandsetSocket hr)
    {
	incoming = i;
	storageobj = x;
	statusobj = y;
	hSend = hs;
	hRec = hr;
    }

    public void run()
    {
    try
	{
	    BufferedReader in = new BufferedReader
                (new InputStreamReader(incoming.getInputStream()));
         for(;;)
         {
           String str = in.readLine();
	       InetAddress iAddr = incoming.getInetAddress();
           System.out.println("BS receives " + str + " from Handset");
	       String scrNum = (String)(storageobj.inetToPhone.get(iAddr));
           String bstype = statusobj.getBSType(scrNum);
	       if (bstype.equals("Same"))
	       {
		      String destNum = statusobj.getOtherHandset(scrNum);
		      storageobj.sendToHandset(hSend, destNum, str);
              if (str.equals("."))
		      {
				  statusobj.updateTablesSame(scrNum,hSend,hRec,storageobj);
			  }
	       }
	       else if (bstype.equals("Different"))
	       {
		      String msend = scrNum + " " + str;
		      BaseStationController.sendDataToMSC(msend);
		      if (str.equals("."))
		      {
				  statusobj.updateTablesDifferent(scrNum,hSend,hRec,storageobj);   }
	          }
          }
	}
	catch(Exception e)
        {   System.out.println("Call terminated");
	    }
    }
}

// This class the sockets at which the handsets are connected so that the Base Station
//               knows which handset to send the message to

class HandsetSocket
{
    Hashtable hsock = new Hashtable();
    public synchronized void takeSocket(InetAddress i, Socket s)
    {
        //System.out.println("Before Adding" + hsock.size());
        hsock.put(i,s);
        //System.out.println("After Adding" + hsock.size());
    }
    public synchronized Socket giveSocket(InetAddress i)
    {
        return ((Socket)(hsock.get(i)));
    }
    public synchronized void updateTable(InetAddress i1)
    {
      try
      {
        (giveSocket(i1)).close();
	    hsock.remove(i1);
	    // System.out.println("Removed an entry ");
      }
      catch(Exception e)
      {  System.out.println(e);
      }
    }
}

// This class stores the information regarding the handset and sends the data as required

class Storage
{
    Hashtable inetToPhone = new Hashtable();
    Hashtable allAddr = new Hashtable();
    public Storage()
    { }
    public synchronized void takeAddress(String s, InetAddress i)
    {
       // System.out.println("Size of the hash table 1 before adding " + allAddr.size());
        allAddr.put(s, i);
	    inetToPhone.put(i,s);
       // System.out.println("For the key " + s+" the element in the hash table 1 is "+((InetAddress)(allAddr.get(s))));
       // System.out.println("Size of the hash table after adding " + allAddr.size());
    }
    public synchronized void broadcast(String s, HandsetSocket h)
    {
        Enumeration en = allAddr.elements();
        while (en.hasMoreElements())
        {
            try
            {
		        InetAddress i = (InetAddress)(en.nextElement());
		        Socket x = h.giveSocket(i);
                PrintWriter outPage = new PrintWriter(x.getOutputStream(),true);
                outPage.println(s);
            }
            catch(Exception e)
            {
                System.out.println(e);
            }
         }
	 System.out.println("Message " + s + " has been broadcast to all the handsets connected to BS");
         notifyAll();
     }
     public synchronized void sendToHandset(HandsetSocket h, String s, String m)
     {
         try
         {
	     InetAddress i = (InetAddress)(allAddr.get(s));
	     Socket x = h.giveSocket(i);
             PrintWriter outMesg = new PrintWriter(x.getOutputStream(),true);
             outMesg.println(m);
             System.out.println("BS sends message " + m + " to Handset");
         }
         catch(Exception e)
         {
             System.out.println(e);
         }
     }

}

// This class keeps track of which handsets are connected to which handsets

class Status
{
    Hashtable bsType = new Hashtable();
    Hashtable connectedTo = new Hashtable();
    public Status()
    { }
    public synchronized void addbsTypeSame(String h1, String h2, String b)
    {
       // System.out.println("size of hashtable bsType of status before adding " + bsType.size());
	   // System.out.println("size of hashtable connectedTo of status before adding " + connectedTo.size());
        bsType.put(h1, b);
        bsType.put(h2, b);
	    connectedTo.put(h1,h2);
	    connectedTo.put(h2,h1);
       // System.out.println("size of hashtable bsType of status after adding " + bsType.size());
	   // System.out.println("size of hashtable connectedTo of status after adding " + connectedTo.size());

    }
    public synchronized void addbsTypeDifferent(String h1, String b)
    {
        // System.out.println("size of hashtable bsType of status before adding " + bsType.size());
        bsType.put(h1, b);
        // System.out.println("size of hashtable bsType of status after adding " + bsType.size());
    }
    public synchronized String getOtherHandset(String s)
    {
        return ((String)(connectedTo.get(s)));
    }
    public synchronized String getBSType(String s)
    {
	   return ((String)(bsType.get(s)));
    }
    public synchronized void updateTablesSame(String s,HandsetSocket hs, HandsetSocket hr,Storage g)
    {
		String connectedHS = getOtherHandset(s);
		bsType.remove(s);
		bsType.remove(connectedHS);
		connectedTo.remove(s);
		connectedTo.remove(connectedHS);
		InetAddress i1 = (InetAddress)(g.allAddr.get(s));
		InetAddress i2 =(InetAddress)(g.allAddr.get(connectedHS));
		hs.updateTable(i1);
		hs.updateTable(i2);
		hr.updateTable(i1);
	    hr.updateTable(i2);
    }
    public synchronized void updateTablesDifferent(String s,HandsetSocket hs, HandsetSocket hr,Storage g)
    {
        bsType.remove(s);
        InetAddress i1 = (InetAddress)(g.allAddr.get(s));
        hs.updateTable(i1);
        hr.updateTable(i1);
    }

}

// This class handles sending of control messages and data messages to MSC

class BaseStationController extends Thread
{
    Storage sgobj;
    Status stobj;
    Socket outgoing;
    static PrintWriter out;
    static PrintWriter out2;
    BufferedReader in;
    Socket incoming;
    Socket dout;
    HandsetSocket hs;

    public BaseStationController(HandsetSocket h, Storage x, Status y)
    {  try
       {
           outgoing = new Socket("129.107.56.23",7777);        // for writing controls to MSC
           out = new PrintWriter(outgoing.getOutputStream(), true);
           incoming = new Socket("129.107.56.23",6666);    // for reading controls from MSC
           in = new BufferedReader(new InputStreamReader(incoming.getInputStream()));
	       dout = new Socket("129.107.56.23",8888);        // for writing data to MSC
           out2 = new PrintWriter(dout.getOutputStream(), true);

           sgobj = x;
           stobj = y;
	       hs = h;
       }
       catch(Exception e)
       {   System.out.println(e);
       }
    }

    public static void sendToMSC(String str)
    {
         String id = "1 ";
         try
         {
             String bsStr = id + str;
	         out.println(bsStr);
	         System.out.println("BSC writes " + bsStr + " to MSC");
         }
	     catch(Exception e)
	     {
		     System.out.println(e);
	     }
    }

    public static void sendDataToMSC(String s)
    {
         String id = "1 ";
         try
         {
            String bsStr = id + s;
            out2.println(bsStr);
            System.out.println("BSC writes " + bsStr + " to MSC");
         }
         catch(Exception e) {   System.out.println(e);    }
    }



    public void run()
    {  try
       {
          for(;;)
          {
	        String strMSC = in.readLine();
	        System.out.println("BSC reads " + strMSC + " from MSC");
            StringTokenizer st = new StringTokenizer(strMSC, " ");
            if (st.countTokens() == 2)
            {
               String phoneNum = st.nextToken();
               String mesgType = st.nextToken();
               if (mesgType.equals("5"))
               {
                   sgobj.broadcast(strMSC,hs);
		           stobj.bsType.put(phoneNum,"Different");
               }
	           else if (mesgType.equals("0"))
	           {
		           String cMesg = phoneNum + " 12";
		           sgobj.sendToHandset(hs,phoneNum,cMesg);
	           }
	           else if (mesgType.equals("16"))
	           {
		           sgobj.sendToHandset(hs, phoneNum, strMSC);
	           }
            }
            else if (st.countTokens() == 4)
            {
	           System.out.println("Entered the approved message ");
	           String approval = st.nextToken();
               String bstype = st.nextToken();
               String sourceNum = st.nextToken();
               String destNum = st.nextToken();
	           if (bstype.equals("Same"))
	           {
		          String bMesg = destNum + " 5";
		          sgobj.broadcast(bMesg,hs);
		          stobj.addbsTypeSame(sourceNum, destNum, bstype);

	           }
	           else if (bstype.equals("Different"))
	           {
		          stobj.addbsTypeDifferent(sourceNum, bstype);
	           }
            }
          }
        }
        catch(Exception e)
        {
           System.out.println(e);
        }
     }
}

// This class receives data from MSC

class BaseStationControllerData extends Thread
{
	Socket din;
	BufferedReader in2;
	HandsetSocket hSend;
    HandsetSocket hRec;
	Storage stmobj;
	Status staobj;

	public BaseStationControllerData(HandsetSocket hs, HandsetSocket hr, Storage stm, Status sta)
	{  try
       {
	       din = new Socket("129.107.56.23",9999);         // for reading data from MSC
           in2 = new BufferedReader(new InputStreamReader(din.getInputStream()));
	       hSend = hs;
	       hRec = hr;
	       stmobj = stm;
	       staobj = sta;
	   }
       catch(Exception e)
       {   System.out.println(e);
       }
    }
	public void run()
	{
	  for(;;)
	  {
	   try
	   {
		String data = in2.readLine();
		System.out.println("BSC reads " + data + " from MSC");
		int x = data.indexOf(" ");
		String destNum = data.substring(0,x);
		String s2 = data.substring(x+1);
		int y = s2.indexOf(";");
		String s3 = s2.substring(0,y);
		String mesg = s3.trim();
		stmobj.sendToHandset(hSend, destNum, mesg);
		if (mesg.equals("."))
			staobj.updateTablesDifferent(destNum,hSend,hRec,stmobj);
	   }
	   catch(IOException e)
	   {
	    System.out.println(e);
	   }
	  }
	}
}



    Source: geocities.com/vienna/7079/src/wireless

               ( geocities.com/vienna/7079/src)                   ( geocities.com/vienna/7079)                   ( geocities.com/vienna)