package socketclient;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import java.net.*;

/**
  *
  *  A Prototype Remote Data Monitoring System
  *
  *  DataClientdisplays data sent by DataServer. At a given time
  *  interval, DataClient requests a set of data from the server. 
  *  The server sends first an integer for the number of data points 
  *  to be sent and then sends that many integer values. 
  *
  *  DataClient runs both as a standalone app or in a browser web page.
  *
  *  The User Interface displays several items of interest:
  *   - The host IP and username are displayed in TextFields
  *   - Stop/Stop button can interrupt and restart the connection. 
  *   - A label displays the status of the connection 
  *   - a TextArea displays the current data. 
  *   - One histogram displays the values in each channel of a data set.
  *   - The second histogram displays the distribution of values for one
  *     channel of data. The number for the desired channel is entered 
  *     into a text field. 
  *
  *  The thread class fDataClientWorker is used to send  requests for 
  *  data and to read the data. The fDataClientWorker consists primarily 
  *  of a run () method. It calls back to the setData (int []) method 
  *  to send the data set obtained fromthe server. 
 **/
public class DataClient extends JApplet
             implements ActionListener
{
  // GUI setup

  // First histogram will show the data values in each set
  // obtained from the server.
  HistPanel fHistDataPanel = null;
  Histogram fHistData = null;

  // Second histogram plots the values of one channel in the
  // data set.
  HistPanel fHistChanPanel   = null; // Histogram of a data channel
  HistogramAdaptR1 fHistChan = null;

  // Initial array size in HistogramAdaptR1 to hold values.
  int fHistArraySize = 1000;

  // UI components
  JLabel     fStatusLabel      = null;
  JTextField fHostField        = null;
  JTextField fUserNameField    = null;
  JTextField fChanField        = null;
  JTextArea  fMessageArea      = null;

  // Flag for whether the applet is in a browser
  // or running via the main () below.
  boolean fInBrowser=true;

  //Buttons
  JButton fStartButton = null;
  JButton fClearButton = null;
  JButton fExitButton  = null;

  // Networking setup

  // Properties needed for the connection setup
  Socket fServer = null;
  DataClientWorker fDataClientWorker = null;

  int fDataServerPort     = 2222; // use 2222 as default
  String fClientPorfHostt = "";
  String fHost            = "127.0.0.1";
  String fUserName        = "Smith";
  int fChannelToMonitor   = 0;

  // Amount of data in each set read from the server
  int fNumDataChannels = 10;

  // flag for connection status
  boolean fConnected = false;

  // read data every 500 msecs
  int fTimeUpdate = 500;

  /**
    * Create a User Interface with a textarea with sroll bars
    *  and a Go button to initiate processing and a Clear button
    * to clear the textarea.
   **/
  public void init () {

    Container content_pane = getContentPane ();

    // Histograms:

    // First hist used to show the values in the data set
    // that is obtained in each transfer from the server.
    fHistData = new Histogram ("Data set values",
                             "Channel Number",
                              10, 0.0, 10.0);
    fHistDataPanel = new HistPanel (fHistData);


    // Second histogram displays the distribution of values
    // for one of the data channels. Use an adaptable histogram
    // since different channels could have different data ranges.
    fHistChan = new HistogramAdaptR1 ("Channel",
                           "Data Units",
                           50, 0.0, 50.0,
                           fHistArraySize);
    fHistChanPanel = new HistPanel (fHistChan);

    // The two hists go onto a sub-panel.
    JPanel hists_panel = new JPanel (new GridLayout (2,1));
    hists_panel.add (fHistDataPanel);
    hists_panel.add (fHistChanPanel);

    // Control fields:

    // First provide the inputs field for the host IP
    // and for the user name.
    fHostField = new JTextField (fHost,16);
    JLabel host_label = new JLabel ("Host: ");
    host_label.setHorizontalAlignment (SwingConstants.RIGHT);
    fUserNameField    = new JTextField (fUserName,16);
    JLabel name_label = new JLabel ("User Name: ");
    name_label.setHorizontalAlignment (SwingConstants.RIGHT);

    // Top line of controls = host and name inputs
    JPanel ctrls_panel1 = new JPanel ();
    ctrls_panel1.add (host_label);
    ctrls_panel1.add (fHostField);
    ctrls_panel1.add (name_label);
    ctrls_panel1.add (fUserNameField);

    // Next line holds the buttons, and the data
    // channel number to monitor.
    fStartButton = new JButton ("Start");
    fStartButton.addActionListener (this);
    fClearButton = new JButton ("Clear");
    fClearButton.addActionListener (this);
    fExitButton = new JButton ("Exit");
    if (fInBrowser)
        fExitButton.setEnabled (false);
    else
        fExitButton.addActionListener (this);

    JPanel buttons_panel = new JPanel ();
    buttons_panel.add (fStartButton);
    buttons_panel.add (fClearButton);
    buttons_panel.add (fExitButton);

    JLabel chan_label = new JLabel ("Channel: ");
    chan_label.setHorizontalAlignment (SwingConstants.RIGHT);
    fChanField = new JTextField ("0",5);

    JPanel chan_panel = new JPanel ();
    chan_panel.add (chan_label);
    chan_panel.add (fChanField);

    fStatusLabel = new JLabel ("Disconnected");
    fStatusLabel.setForeground (Color.RED);
    fStatusLabel.setHorizontalAlignment (SwingConstants.CENTER);

    // Now pack the components of the second ctrls
    // line of components
    JPanel ctrls_panel2 = new JPanel ();
    ctrls_panel2.add (buttons_panel);
    ctrls_panel2.add (chan_panel);
    ctrls_panel2.add (fStatusLabel);

    // Put the 2 lines of controls into a sub-panel
    JPanel ctrls_panel12 = new JPanel ();
    ctrls_panel12.add (ctrls_panel1);
    ctrls_panel12.add (ctrls_panel2);

    fMessageArea = new JTextArea ();
    fMessageArea.setEditable (false);
    // Add to a scroll pane so that a long list of
    // computations can be seen.
    JScrollPane area_scroll_pane = new JScrollPane (fMessageArea);

    // Use a GridBagLayout to apportion space for the
    // controls, text area and histograms.
    JPanel main_panel = new JPanel (new GridBagLayout ());

    GridBagConstraints c = new GridBagConstraints ();
    c.fill = GridBagConstraints.BOTH;


    // Put ctrls at top
    c.gridx = 0;
    c.gridy = 0;
    c. weightx = 1.0;
    c. weighty = 0.05;
    main_panel.add (ctrls_panel12,c);

    // Put text area below the controls
    c.gridx = 0;
    c.gridy = 1;
    c. weightx = 1.0;
    c. weighty = 0.25;
    main_panel.add (area_scroll_pane, c);

    // Put histograms in rest of the vertical space
    c.gridx = 0;
    c.gridy = 2;
    c. weightx = 1.0;
    c. weighty = 0.70;
    c.insets = new Insets (2,2,10,2);
    main_panel.add (hists_panel, c);

    // Add text area with scrolling to the content_pane.
    content_pane.add (main_panel);

  } // init

  /** Respond to the buttons. **/
  public void actionPerformed (ActionEvent e) {
    Object source = e.getSource ();
    if (source == fStartButton)  {
        if (fStartButton.getText ().equals ("Start") )
            start ();
        else
            stop ();
    } else if  (source == fClearButton) {
        fHistData.clear ();
        // For adaptable histogram, clear bins
        // and also clear internal data array.
        fHistChan.reset ();
        repaint ();

    } else if (!fInBrowser)// Exit button
        System.exit (0);
  } // actionPerformed

  /**
    * Make the connection to the server. Set up the DataReader
    * and begin recording the data from the server.
   **/
  public void start (){

    if (fConnected) stop ();

    // Clear the histograms
    fHistData.clear ();
    fHistData.clear ();

    // Get the current values of the host IP address and
    // and the username
    fHost = fHostField.getText ();
    fUserName = fUserNameField.getText ();
    try {
       fChannelToMonitor = Integer.parseInt (fChanField.getText ());
    } 
    catch (NumberFormatException ex) {
      println ("Bad channel value");
      return;
    }

    // Now try to connect to the DataServer
    try{
      if (connect () ) {
         // Successful so set flags and change button text
         fConnected = true;
         fStartButton.setText ("Stop");
         fStatusLabel.setText ("Connected");
         fStatusLabel.setForeground (Color.BLUE);

      } else {
        println ("* NOT CONNECTED *");
        fStatusLabel.setText ("Disconnected");
        fStatusLabel.setForeground (Color.RED);
      }
    } 
    catch (IOException e) {
        println ("* NOT CONNECTED *");
        fStatusLabel.setText ("Disconnected");
        fStatusLabel.setForeground (Color.RED);
    }
  } // start

  /**
    *  Connect to the server via a socket. Throws IOException
    *  if socket connection fails.
   **/
  boolean connect () throws IOException {

    println ("Attempting to connect to server ...");

    try {
       // Connect to the server using the host IP address
       // and the port at the server location
       fServer = new Socket (fHost, fDataServerPort);
    } catch (SecurityException se) {
       println ("Security Exception:\n"+se);
       return false;
    }

    println ("Server connected - create worker");

    // Create the worker to tend to this server
    fDataClientWorker = 
      new DataClientWorker (this, fServer, fUserName);
    fDataClientWorker.start ();

    return true;
  } // connect

  /** Stop the worker thread. **/
  public void stop () {

    // Disconnect and kill the fDataClientWorker thread
    fDataClientWorker.finish ();
    setDisconnected ();
  }

   /** Set buttons for restart. **/
  void setDisconnected () {
    fStartButton.setText ("Start");
    fStatusLabel.setText ("Disconnected");
    fStatusLabel.setForeground (Color.RED);
  }

  /**
    *  The DataClientWorder passes the data array from the server.
    *  Display the data set by packing a histogram.
    *
    *  Also, plot the distribution of one of the channels of the data.
    *  The channel number is given in the text field.
   **/
   void setData (int [] data) {

      // Display each data set
      fHistData.pack (data, 0, 0, 0.0, (double) (data.length) );
      fHistDataPanel.getScaling ();

      // Plot the distribution of one of the channels in the data.
      if (fChannelToMonitor >= 0 && fChannelToMonitor < data.length) {
          fHistChan.setTitle ("Channel "+ fChannelToMonitor);
          fHistChan.add ((double)data[fChannelToMonitor]);
          // Adapt since data varies from channel to channel.
          fHistChan.rebin ();
          // Now rescale and draw.
          fHistChanPanel.getScaling ();
      }
      repaint ();
   } // setData


  /**  Convenience method for sending messages to the  text area. **/
  public void println (String str) {
     fMessageArea.append (str +"\n");
     repaint ();
  }

  /** Create the applet and add to frame. **/
  public static void main (String[] args) {
    //
    int frame_width=500;
    int frame_height=650;

    // Create standalone version
    DataClient applet = new DataClient ();
    applet.fInBrowser = false;
    applet.init ();

    // Following anonymous class used to close window & exit program
    JFrame f = new JFrame ("Data Clent");
    f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);

    // Add applet to the frame
    f.getContentPane ().add ( applet);
    f.setSize (new Dimension (frame_width,frame_height));
    f.setVisible (true);
  } // main

} // class DataClient
 




package socketclient;

import java.io.*;
import java.net.*;

/**
  * DataClient creates an instance of this thread class to tend to the 
  * connectionto the data server.
  *
  * It periodically requests a set of data from the server and then
  * sends the data to the parent DataClient, which will update its 
  * display. 
 **/
public class DataClientWorker extends Thread
{
  // Flag for data running.
  boolean fKeepRunning = true;

  DataClient fDataClient;

  // Networking refs
  Socket fServer;
  String fUserName;

  // I/O with the server
  BufferedReader  fNetInputReader ;
  DataInputStream fNetInputDataStream;
  OutputStream    fNetOutputDataStream ;
  PrintWriter     fPrintWriter;

  // Number of data values
  int fNumChannels = -1;
  // Data array
  int [] fData = null;


  /** Receive the DataClient to provide with the data from 
    * the server.
   **/
  public DataClientWorker (DataClient c, Socket server, String userName) {
    fDataClient = c;
    fServer     = server;
    fUserName   = userName;
  }

  /** Remain in a loop to monitor the I/O from the server. 
    * Display the data.
   **/
  public void run () {

    // The socket connection was made by the caller, now
    // set up the streams and do a login
    try {
        if (!doConnection ()){
            fDataClient.println ("  Connection/login failed");
            return;
        }
    }
    catch (IOException ioe) {
      fDataClient.println ("  I/O exception with serve:"+ ioe);
    }

    int num_channels = -1;

    // This loops until either the connection is broken or the
    //stop button or stop key is hit
    while  (fKeepRunning) {

       // Ask the server to send data.
      try {
          writeNetOutputLine ("  send data");
      }
      catch  (IOException e){
        break;
      }

       // First number sent from server is an integer that gives
       // the number of data values to be sent.
      try {
          num_channels = readNetInputInt ();
      }
      catch  (IOException e) {
        break;
      }

      if (num_channels != fNumChannels) {
          fNumChannels = num_channels;
          fDataClient.println ("  Number data channels = " 
                               + fNumChannels);
      }

      if (fNumChannels < 1){
         fDataClient.println ("  no data");
         break;
      }

      // Create an array to hold the data if not available
      if (fData == null || fNumChannels != fData.length)
          fData = new int[fNumChannels];


      for (int i=0; i < fNumChannels; i++) {
        try {
          fData[i] = readNetInputInt ();
          // Pass the data to the parent program 
          fDataClient.setData (fData);
        }
        catch  (IOException e) {
          fDataClient.println ("IO Exception while reading data");
          break;
        }
      }

      // Ask for data every TimeUpdate
      try {
        Thread.sleep (fDataClient.fTimeUpdate);
      }
      catch (InterruptedException e)
      {}

    }

    if  (fServer != null) closeServer ();
    fDataClient.println ("disconnected");
    fDataClient.setDisconnected ();

  } // run

  /** Set up the streams with the server and then login. **/
  boolean doConnection () throws IOException {

    // Get the input and output streams from the socket
    InputStream in = fServer.getInputStream ();

    // Use the reader for obtaining text
    fNetInputReader = new BufferedReader (
        new InputStreamReader (in)) ;

    // User the DataInputStream for getting numerical values.
    fNetInputDataStream = new DataInputStream ( in );

    // Output stream for sending messages to the server.
    fNetOutputDataStream = fServer.getOutputStream ();

    // Write with a PrintWriter for sending text to the server.
    fPrintWriter= new PrintWriter (
      new OutputStreamWriter (fNetOutputDataStream, "8859_1"), true );

    // Now try the login procedure.
    if (!login ()) return false;

    return true;
  } // doConnection

  /** Here is a homemade login protocol. A password could 
    * easily be added.
   **/
  boolean login () {

    fDataClient.println ("Waiting for login prompt...");

    String msg_line=readNetInputLine ();
    if (msg_line == null) return false;
    fDataClient.println (msg_line);
    if (!msg_line.startsWith ("Username:")) return false;

    fDataClient.println ("Send username " + fUserName);
    try {
      writeNetOutputLine (fUserName);
    }
    catch (IOException e) {
      return false;
    }
    catch (Exception e) {
      fDataClient.println ("Error occurred in sending username!");
      return false;
    }

    fDataClient.println ("Waiting for response...");

    msg_line=readNetInputLine ();
    if (msg_line == null) return false;
    fDataClient.println (msg_line);

    return true;
  } // login

  /** Do all of the steps needed to stop the connection. **/
  public void finish (){

     // Kill the thread and stop the server
     fKeepRunning = false;
     closeServer ();
  }

  /** Close the socket to the server. **/
  void closeServer () {
    if (fServer == null) return;
    try {
      fServer.close ();
      fServer = null;
    }
    catch (IOException e)
    {}
  }

  /**
    * The net input stream is wrappped in a DataInputStream
    * so we can use readLine, readInt and readFloat
   **/
  String readNetInputLine () {
    try {
      return fNetInputReader.readLine ();
    }
    catch (IOException e) {
      return null;
    }
  }

  /** Read an integer value from the socket stream **/
  int readNetInputInt () throws IOException {
        return fNetInputDataStream.readInt ();
  }

  /**  Read float value from the socket stream. **/
  float readNetInputFloat () throws IOException {
        return fNetInputDataStream.readFloat ();
  }

  /**
    * The net output is a PrintWriter class which doesn't throw
    * IOException itself. Instead we have to use the PrintWriter
    * checkError () method and throw an exception ourselves if there
    * was an output error.
   **/
  void writeNetOutputLine (String string) throws IOException {

    fPrintWriter.println (string);
    if (fPrintWriter.checkError ()) throw (new IOException ());

    fPrintWriter.flush ();
    if (fPrintWriter.checkError ()) throw (new IOException ());
  } // writeNetOutputLine

} // class DataClientWorker