Home : Course Map : Chapter 15 :
Server for the Experiment Simulation
JavaTech
Course Map
Chapter 15

Client/ServerDesign
Processing Steps
Data Server
Run Data Server
    Demo 1
Data Client
DataClient Applet
    Demo 2
Client/Server Sim
SimServer
    Demo 3
SimClient
Run SimClient
    Demo 4
Exercises

     About JavaTech
     Codes List
     Exercises
     Feedback
     References
     Resources
     Tips
     Topic Index
     Course Guide
     What's New

The SimServer program provides each client a dedicated simulator of the mass drop experiment discussed in Chapter 9: Physics. The server connects to the clients via a socket. The main program hands off the client socket to an instance of SimWorker whose job is to tend to the needs of the client.

The text area in the main program displays messages that indicate the status of the connections to the clients and of the simulations. A text field allows for choosing a different port for the socket connections.

The SimWorker creates an instance of SimApplet, which derives from the mass drop simulator code in the Chapter 9: Physics demonstration. (The SimClient holds the analysis part of that demo.) The SimWorker then acts as an intermediary between the SimApplet and the SimClient.

The SimWorker implements the SimController interface, which includes the method

message(int flag, int iData, double [][] fp).

The SimApplet instance uses this method to pass instructions and data to the SimWorker (and thus on to the SimClient.) An interface allows for the possibility that other simulators could be used with SimWorker or, conversely, that other types of workers that implement the interface could be used without modifying the simulator code.


The SimServer Application

SimServer.java - This application monitors a ServerSocket for requests for connections from clients. The port can be selected in the text field (default 2222). The text area shows messages indicating the status of the connection with the client(s). When a connection socket is created, it is handed over to a SimWorker object that is dedicated to serving that client. Maintains a list of clients and limits the total to a set number.

New classes:

SimWorker.java - created by the SimServer to tend to the socket connection to the client. It also creates an instance of SimApplet to carry out the simulation. When the client sends an instruction to start the run, the instruction goes through the SimWorker object. Similarly, the data from the simulation goes through the SimWorker to the client. Various setup parameters are also passed back and forth between the simulator and the client via the SimWorker.

SimApplet.java - simulate the dropping of a mass and the measurement of its position every DT time interval. Derived from Ch. 9: Physics: DropTestAnalysisApplet.java.

SimController.java - interface implemented by SimWorker. Provides a generic message() method for the SimApplet object to callback to the SimWorker object that created it. Could allow for using the SimWorker with other types of simulators.

NetStreamMethods.java - convenient utility class with methods for reading and writing over the socket streams.

SimUtil.java - another convenience class that holds some constants used in the communications protocols.

+ Previous classes:
Ch.9: Physics:
DropPanelDetect.java, DropModelDetect.java,
               DropDetector2.java, Detector.java

Ch.6: Tech: PlotPanel.java, PlotFormat.java

 

//package SimClientServer;

// Begun from StartJApp5

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

/**
  *  Server for a Prototype Remote Data Monitoring System
  *
  * SimServer provides simulated data to multiple instances of SimClient.
  * A SimClient connects via a socket and executes a simple login procedure.
  *
  * An instance of the thread class SimWorker is assigned to each
  * client socket. Via the socket, the client sends setup information for
  * the simulation, initiates a "run" of the sim, and data from each
  * drop is sent to the client.The SimWorker includes methods to read
  * and write strings and numerical data with streams to the client.
  *
  *  Multiple clients can connect simultaneously. The default max users value
  *  is set to 10.
  *
 **/
public class SimServer extends JFrame
       implements ActionListener, Runnable
{

  // GUI setup attributes

  JMenuItem fMenuClose;
  JTextArea  fTextArea;
  JTextField fTextField;
  JButton fStartButton;

  // Networking setup
  // Use a Vector to keep track of the DataWorker list.
  Vector fWorkerList;

  // Connect to clients
  ServerSocket fServerSocket;

  int fDataServerPort = 2222;// Default port number

  // Client counting and limit
  static int fClientCounter__ = 0;
  static int fMaxClients__ = 10;

  // Number of data values per set or "event"
  static int fNumDataVals__ = 6;

  // Flag for the server socket loop.
  boolean fKeepServing = true;

  /**
   *  Open frame for the user interface.
   */
  public static void main (String [] args) {
      // Can pass frame title in command line arguments
    String title = "Simulation Server";
    if (args.length != 0) title = args[0];
    SimServer f = new SimServer (title);
    f.setVisible (true);
  } // main

  /**
   *  Pass a title to the frame via the constructor
   *  argument. Build the GUI.
   */
  SimServer (String title) {
    super (title);

    // Create the vector to list the DataWorker objects
    fWorkerList = new Vector ();

    // Create a user interface.
    setLayout (new BorderLayout ());
    fTextArea = new JTextArea ("");
    JScrollPane area_scroll_pane = new JScrollPane (fTextArea);

    add (area_scroll_pane, "Center");

    // Create a panel with a textfield and two buttons
    fTextField = new JTextField ("2222");

    fStartButton = new JButton ("Start");
    fStartButton.addActionListener (this);


    JPanel panel = new JPanel (new GridLayout (1,2));
    JPanel button_panel = new JPanel ();
    panel.add (fTextField);
    button_panel.add (fStartButton);
    panel.add (button_panel);

    add (panel, "South");

    // Use the helper method makeMenuItem
    // for making the menu items and registering
    // their listener.
    JMenu m = new JMenu ("File");

    // Use File menu to hold Quit command.
    m.add (fMenuClose = makeMenuItem ("Quit"));

    JMenuBar mb = new JMenuBar ();
    mb.add (m);

    setJMenuBar (mb);
    setSize (400,400);

    setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
  } // ctor

  /**
    * Process events from the frame menu and the chooser.
   **/
  public void actionPerformed (ActionEvent e) {
    boolean status = false;

    String command = e.getActionCommand ();

    if ( command.equals ("Start") ) {

        try{
            fDataServerPort = Integer.parseInt (fTextField.getText ());
        }
        catch (NumberFormatException nfe){
          println ("Bad port number");
          return;
        }

        fStartButton.setEnabled (false);

        Thread thread = new Thread (this);
        thread.start ();

    } else if (command.equals ("Quit") ){
        fKeepServing = false;
        for (Enumeration en = fWorkerList.elements () ;
            en.hasMoreElements ();)
            ((SimWorker) (en.nextElement ())).signOff ();
        dispose ();
    }
  } // actionPerformed

  /**
    *  Create a ServerSocket and loop waiting for clients.
   **/
  public void run () {
    // The server_socket is used to make connections to
    // DataClients at this port number
    try {
      fServerSocket = new ServerSocket (fDataServerPort);
    }
    catch (IOException e) {
        println ("Error in server socket");
        return;
    }
    println ("Waiting for users...");
    // Loop here to grab clients
    while (fKeepServing) {
      try {
        // accept () blocks until a connection is made
        Socket socket = fServerSocket.accept ();

        // Do the setup this socket and then loop
        // back around to wait for the next DataClient.
        SimWorker worker = new SimWorker (this, socket);
        Thread thread = new Thread (worker);
          thread.start ();
      }
      catch (IOException ioe) {
        println ("IOException: <" + ioe + ">");
        break;
      }
      catch (Exception e) {
        println ("Exception: <" + e + ">");
        break;
      }
    }
  } // run

  /**
    * A DataWorker will set up the connection with the client. If it
    * decides that the conditions are OK, then it will invoke this
    * method so that the parent server will add the worker to its list.
    *
    * We synchronize the method to avoid any problems with multiple
    * clients interfering with each other.
   **/
  public synchronized void clientConnected (SimWorker worker) {
    fWorkerList.add (worker);
    fClientCounter__++;
  }

  /**
    * When a client disconnects, the DataWorker object will
    * call back to this method to remove itself from the list
    * of workers.
    *
    * We synchronize the method to avoid any problems with multiple
    * clients interfering with each other.
   **/
  public synchronized void clientDisconnected (String user,
                                               SimWorker worker) {
    println ("Client: "+user+" disconnected");
    fWorkerList.remove (worker);
    fClientCounter__--;
  } // clientDisconnected

  /**
    * When a DataWorker makes the connection, it checks to see if there
    * is room on the server for it.
    *
    * We synchronize the method to avoid any problems with multiple
    * clients interfering with each other.
   **/
  public synchronized boolean clientPermit () {
    if ( fWorkerList.size () < fMaxClients__)
        return true;
    else
        return false;
  } // clientPermit

  /**
    * This "helper method" makes a menu item and then
    * registers this object as a listener to it.
   **/
  private JMenuItem makeMenuItem (String name) {
    JMenuItem m = new JMenuItem ( name );
    m.addActionListener ( this );
    return m;
  } // makeMenuItem

  /**
    *  Utility method to send messages to the
    *  text area.
   **/
  public void println (String str) {
     fTextArea.append (str +"\n");
     repaint ();
  } // println

} // class SimServer
package SimClientServer;

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


/**
  * SimServer creates instances of this class to service
  * SimClient connections. It creates a SimApplet mas drop
  * experiment simulator. Once the connection is set up using the Socket
  * instance passed to it from the main server program, the client can
  * initiate and run the simulation via the methods in this class.
  *
  * For convenience  (avoiding lots of try-catch coding, this class extends
  * the NetStreamMethods class and uses its methods to read and write with
  * the client.
  *
  * A SimWorker object runs via its own thread. To remain alive while waiting
  * for instructions from the client, a synchronized method executes the
  * wait () method. The callback  (see the message () method) from the
  * simulator will cause notifyAll () to release the thread from its wait
  * state.
  *
  * Each client deals with its own SimApplet simulation. The SimApplet
  * sends instructions and data back to its corresponding SimWorker via
  * the message () method of the SimController interface. The message ()
  * method also sends data on to the client.
 **/
public class SimWorker extends NetStreamMethods
                       implements Runnable, SimController {

  // The parent server.
  SimServer fServer;

  // Simulation variables
  SimApplet fSimApplet;
  JFrame fFrame; // Frame to hold the simulation applet

  // Name of the client
  String fUser;

  // Connection to the client passed from the main control progam.
  Socket fSocket;

  // Utility buffer arrayfServer.
  int [] fIBuffer;
  double [] fDBuffer;

  // Flag for the primary loop in the run () method.
  boolean fKeepRunning = true;

  /**
    *  Pass reference to the owner DataServer_JApp5 and the
    *  index number for this DataSender instance.


    *
    *  Create an instance of SimApplet and display in its own frame.
   **/
  public SimWorker (SimServer s, Socket socket) {
    fServer = s;
    fSocket = socket;

    // Create the simulation applet and open in a separate frame.
    fSimApplet = new SimApplet ();
    fSimApplet.fInBrowser = false;
    fSimApplet.init ();

    // Tell the SimApplet to communicate with this controller
    fSimApplet.setController (this);

    // Following anonymous class used to close window & exit program
    fFrame = new JFrame ("Simulated Drop Test");
    fFrame.addWindowListener
    ( new WindowAdapter ()
      {
        public void windowClosing (WindowEvent e) {
           fFrame.dispose ();
        }
      }
    );
    // Add applet to the frame
    fFrame.getContentPane ().add ( fSimApplet);
    fFrame.setSize (new Dimension (450,350));
    fFrame.setVisible (true);

    fServer.println (" Opened simulator");
  } // ctor

  /**
    *  The communications with the client take place in this
    *  run () method. It begins by setting up the socket communications
    *  and carrying out a login procedure. Then the parameters of the
    *  simulated experiment are sent to the client for its display  and
    *  analysis purposefServer.
   **/
  public void run  () {

    // Do all the stream setup with the client and carry out
    // the login procedure.
    if (!serviceSetup ()){
        signOff ();
        return;
    }

    fServer.println ("Client connection and login OK - Begin service...");

    // Send to the client the setup parameters for the simulated experiment
    if (!sendExptSetup ()){
        signOff ();
        return;
    }

    fServer.println ("Successfully sent simulated experiment setup to client");

    // Now begin the loop for communicating with the client.
    while (fKeepRunning){

      // Wait for a request from the DataClient
      int clientRequest = readNetInputInt ();
      if (fFailedIO) break;

      int nval = 0;

      // Use a switch statement to select the operation
      switch (clientRequest) {

        case SimUtil.START:// Start a run.
          // print message
          fServer.println (" Client "+fUser+ " starts simulation");

          // Begin by obtaining the run setup  (e.g. number of
          // events to simulate) from the client.

          // First the size of the integer array
          nval = readNetInputInt ();
          if (fFailedIO) break;

          // Read the integer array
          fIBuffer = readNetInputIntArray (fIBuffer, nval);
          if (fFailedIO ) break;

          // Then the size of the floating point array
          nval = readNetInputInt ();
          if (fFailedIO) break;

          // Read the double array.
          fDBuffer = readNetInputDoubleArray (fDBuffer, nval);
          if (fFailedIO ) break;

          // Now send the run setup arrays to the SimApple simulation.
          // The method returns after STARTING THE RUN of the simulator.
          fSimApplet.externalControl (SimUtil.START, fIBuffer, fDBuffer);

          // The run has started so this thread needs to wait until
          // all the data is sent from the simulation via the message method.
          // The run cannot be stopped once it startfServer.
          waitForData ();
          break;

        case SimUtil.STOP: // End simulation program.
          // print message
          fServer.println (" Client "+fUser+ " stops simulation");
          fFrame.dispose ();
          fSimApplet = null;
          fKeepRunning = false;
          break;

      }
      // A failed read or write ends program.
      if ( fFailedIO) break;
    }

    // Send message back to the text area in the frame.
    fServer.println (fUser + " has disconnected.");

    // Do any other tasks for ending the worker.
    signOff ();

  } // run

  /**
    *  Wait here for the SimApplet object to send the drop event data
    *  via the message () method. When finished, that method will
    *  invoke notifyAll (), whick will release this thread to continue.
   **/
  synchronized void waitForData (){

    // Remain here until notifyAll invoked in message () method.
    try{
        wait ();
    }
    catch (Exception e)
    {}

  } // waitForData

  /**
    *  SimApplet object "calls back" with this method. It can
    *  send its data for each event  (drop) and it can indicate
    *  the end of the run and then release the lock
    *  in waitForData ().

    *  SimData puts two 1-d arrays for the time and position
    *  measurements into a 2-D array.
   **/
  public synchronized void message (int cmd, int nval,
                                   double [][] data){

    // Tell the client what is coming
    writeNetOutputInt (cmd);
    if ( fFailedIO) return;

    // Then do one of the following
    switch  (cmd) {
      case SimUtil.EVENT_DATA: // Send client the drop measurements

        // Tell client how many values coming
        writeNetOutputInt (nval);
        if ( fFailedIO) break;

        // Send the time array.
        writeNetOutputDoubleArray (data[0], nval);
        if ( fFailedIO) break;

        // Send the position array
        writeNetOutputDoubleArray (data[1], nval);
        break;

      case SimUtil.RUN_DONE: // Run done, release
        notifyAll ();
        fServer.println ("Run done");
        break;
      }
  } // message

  /**
    * Set up the connection to the client. This requires obtaining the
    * IO streams, carrying out the login prototcol, and then starting
    * a DataWorker thread to tend to the client.
    *
    * The bookkeeping code is a bit messy because we check both reads
    * and writes for errors in case the connection breaks down.
    *
    * The reads catch their own IOExceptions and return a null, while
    * string writes use a PrintWriter that doesn't throw IOException. So
    * we use the checkError () method and throw it ourselvefServer.
   **/
  public boolean serviceSetup () {

    fServer.println ("Client setup...");

    // First get the in/out streams from the socket to the client
    try {
      fNetInputStream  = fSocket.getInputStream ();
    }
    catch (IOException ioe){
      fServer.println ("Unable to open input stream");
      return false;
    }

    try {
      fNetOutputStream = fSocket.getOutputStream ();
    } catch (IOException ioe){
      fServer.println ("Unable to open output streams");
      return false;
    }

    fServer.println ("Successfully opened streams to client");

    // Create a PrintWriter class for sending text to the client.
    // The writeNetOutputLine method will use this clasfServer.
    try{
      fPrintWriter = new PrintWriter (
         new OutputStreamWriter (fNetOutputStream, "8859_1"), true );
    }
    catch (IOException ioe){
      fServer.println ("Fails to open PrintWriter to client!");
      return false;
    }

    // Check if the server has room for this client.
    // If not, then send a message to this client to tell it the bad news.
    if (!fServer.clientPermit () ) {

        String msg= "Sorry, We've reached maximum of clients";
        writeNetOutputLine (msg);
        if (fFailedIO) {
            fServer.println ("Connection fails during login");
            return false;
        }
        fServer.println (msg);
        return false;
    }

    // Get a DataInputStream wrapper so we can use its readLine () methods.
    fNetInputReader  =
      new BufferedReader (new InputStreamReader (fNetInputStream));

    // Do a simple login protocol. Send a request for the users name.
    // Note a password check could be added here.
    writeNetOutputLine ( "Username: ");
    if (fFailedIO) {
      fServer.println ("Connection fails during login");
      return false;
    }

    // Read the user name.
    fUser = readNetInputLine ();
    if (fUser == null ) {
        fServer.println ("Connection fails during login");
        return false;
    }

    // Send a message that the login is OK.
    writeNetOutputLine ("Login successful");
    if (fFailedIO) {
        fServer.println ("Connection fails during login for " + fUser);
        return false;
    }
    fServer.println ("Login successful for " + fUser);
    fServer.println (fUser + " connected! ");
    fServer.println (fSocket.toString ());


    // The login is successful so now create a DataWorker to
    // service this client. Pass it an ID number
    fServer.clientConnected (this);

    // Get a data output stream for writing numerical data to the client
    fNetDataOutputStream = new DataOutputStream (fNetOutputStream);

    // Get a data input stream for reading numerical data from the client
    fNetDataInputStream = new DataInputStream (fNetInputStream);

    return true;
  } // serviceSetup

  /**
    *  Get the parameters for the experiment that SimApplet will simulate
    *  and send to the client.
   **/
  boolean sendExptSetup () {

    // Get the four setup parameters from the SimApplet
    fDBuffer = new double[4];
    fSimApplet.externalControl ( SimUtil.GET_SETUP, null,  fDBuffer);

    // Tell client the expt setup is coming
    writeNetOutputInt (SimUtil.INIT);
    if (fFailedIO) {
        fServer.println ("Failed sending setup  message to client");
        return false;
    }
    // Send array size.
    writeNetOutputInt (fDBuffer.length);
    if (fFailedIO) {
        fServer.println ("Failed sending setup array size to client");
        return false;
    }

    // Send the setup array
    writeNetOutputDoubleArray (fDBuffer, fDBuffer.length);
    if (fFailedIO) {
        fServer.println ("Failed sending setup array to client");
        return false;
    }

    // Setup sent successfully.
    return true;

  } // sendExptSetup

  /** Whenever this client disconnects tell the parent.**/
  public void signOff () {
    try {
      fSocket.close ();
    }
    catch  (Exception e){
      fServer.println ("Socket close exception for " + fUser);
    }
    // Tell server to remove this worker from its list
    fServer.clientDisconnected (fUser,this);

    // Shut down the applet
    fFrame.setVisible (false);
    fFrame.dispose ();
    fSimApplet = null;

    fServer.println ("Close sim & socket");
  } // signOff

} // class SimWorker
package SimClientServer;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

/**
  * This program simulates an experiment in which measurements
  * are made during the fall of a mass in a constant gravitational
  * field that will be used to determine the acceleration constant.
  * It illustrates the basic components involved with a simulation
  * of any physics experiment.
  *
  * The applet uses DropPanelDetect to simulate the dropping of a
  * mass in a constant gravitational field. The
  * DropDetector class draws the detector.
  *
  *
  * Data for each drop is passed to an instance of DropAnalyzer,
  * which saves the data. At the end of the run  (a set of drops),
  * the analyzer plots Yaverage vs. Time and fits the points to
  * a polynominal c + bT + cT^2. From the c coefficient the gravitational
  * value can be determined.
  *
  * The java.util.Timer and java.util.TimerTask are used
  * to update the animation of the drop.
  *
**/
public class SimApplet extends JApplet
{
  // Another class can control this applet simulation if it
  // implements the SimController interface.
  SimController fSimController;

  // 2-D array used to pass data to the controller.
  double fData[][] = new double [2][];

  // The DropPanel displays the animation of the
  // falling mass.
  DropPanelDetect fDropPanel;

  // The DropModel generates the physics data.
  DropModelDetect fDropModel;

  // Use a detector to measure the drop times.
  Detector fDetector;

  // Use the HistPanel JPanel subclass here
  HistPanel fHistPanel;

  Histogram fHistogram;
  int fNumDataPoints = 100;

  boolean fMakingHist = false;
  boolean fUpdateDisplay = false;

  // Use the java.util Timer and TimerTask combo
  // for timing events.
  java.util.Timer fTimer;

  // A text field for getting number of drops per
  // run and the position smearing std.dev. in cm.
  JTextField fMaxNumDropsField ;
  JTextField fPosSigmaFactorField;

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

  //Buttons
  JButton fGoButton;   // start drop
  JButton fClearButton;// resets histogram
  JButton fExitButton;

   // Starting coordinates of drop
  double fXBallStart =  25.0; // cm
  double fYBallStart = 200.0; // cm
  double fYBallEnd   =   0.0; // cm

  // Range of the drop time.
  double fTStart = 0.00; // sec
  double fTEnd   = 0.75; // sec

  // SD in the measured values for the marker positions and t (sec).
  //  (Allow for smearing of marker position for further experimentation.)
  double [] fMeasurementSigmas = {2.0, 0.0};

  // Integer and double arrays to pass info to the detector
  int []    fDetectorSetup;
  double [] fDetectorParameters;

  // Coordinate of ball.
  double fXBall;
  double fYBall;

  // Initial velocity.
  double fVfYBallStart = 0.0;
  double fVfXBallStart = 0.0;

  // Time in millisecs for the drop
  double fTDropTotal;
  double fTFrame = 0.020; // in secs

  // Speed up or slow down factor for animation:
  double fTFactor = 1.0;

  // Allow for multiple drops per "run"
  int fMaxNumDrops = 10;
  int fNumDrops = 0;

  // Number of times to measure the position during the drop.
  int fMaxNumMeasurements = 40;


  /**
    *  Initialize the display. Create Detector and Model
    *  objects to use for the physics and experiment simulation.
    *  DropPanelDetect displays the dropping ball and the
    *  detector. Add a HistPanel to display the data.
   **/
  public void init () {
    // Create a detector
    fDetector = new DropDetector2 ();

    // Need arrays to pass setup info to the detector.
    fDetectorSetup = new int[1];
    fDetectorSetup[0] = fMaxNumMeasurements;

    // Do a measurement for every frame
    fDetectorParameters = new double[1];
    fDetectorParameters[0] = fTFrame;

    // Pass the detector the necesary setup info
    fDetector.init (fDetectorSetup, fDetectorParameters, fMeasurementSigmas);

    // Create the drop physics model
    fDropModel = new DropModelDetect (fDetector);
    fDropModel.reset (fYBallStart, fVfYBallStart);

    // 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.
    JPanel panel = new JPanel (new BorderLayout ());

    // Create an instance of the DropPanel
    // Make the panel 10% taller than starting position.
    fDropPanel = new DropPanelDetect (fYBallStart * 1.1, 0.0, fDetector);

    panel.add (fDropPanel,"Center");

    // Add text area with scrolling to the contentPane.
    add (panel);
  }  // init

  /** Stop the run. **/
  public void stop () {
    runDone ();
  }

  /** In the constructor pass the reference to the SimController
    * to opperate this simulation.
   **/
  public void setController (SimController simController) {
    fSimController = simController;
  }

  /**
    * This method allows an external class, such as the SimServer,
    * to control the drop simulation. Pass the parameters needed for
    * the simulation via the array arguments.
   **/
  public void externalControl (int control, int [] valInt,
                               double [] valFP) {
    switch (control) {
      case SimUtil.START:
          // Get the number of drops & max measures per drop.
          fMaxNumDrops = valInt[0];
          fDetectorSetup[0] = valInt[1]; //= fMaxNumMeasurements

          // and the smearing on the position measurements
          fMeasurementSigmas[0] = valFP[0]; // = std. dev. on y

          // and the fTFrame
          fDetectorParameters[0] = valFP[1];// = fTFrame

          // Update the position smearing
          fDetector.init (fDetectorSetup, fDetectorParameters,
                        fMeasurementSigmas);

          // Reset everything and start the run again.
          dropReset ();
          break;

      case SimUtil.GET_SETUP:
          // Use the FP data buffer to send the init data to the
          // simulation controller.
          valFP[0] = fYBallEnd;
          valFP[1] = fYBallStart;
          valFP[2] = fTStart;
          valFP[3] = fTEnd;
          break;

      case SimUtil.STOP:
          runDone ();
          break;
     }
  } // externalControl

  /**
    * Use the inner class technique to define the
    * TimerTask subclass for stepping through the
    * drop calculation and the frame refresh.
    * Use the real time in the drop calculation instead
    * of the given frame rate times in case there were
    * delays from thread interruptions, the processing
    * in the parts of the program take extra time, etc.
   **/
  class PaintHistTask extends java.util.TimerTask {
    public void run () {
       // Drop the ball
       fYBall = fDropModel.step (fTFactor * fTFrame);

       // Update the position of the ball in the
       // animation and redraw the frame.
       fDropPanel.updatePosition (fXBall, fYBall);

       // Check if ball has crossed the finish line.
       if (fYBall <= fYBallEnd) dropDone ();
    } // run
  } // class PaintHistTask

  /**
    *  Before each set of drops, need to create a new timer,
    *  and set up its schedule. The PaintHistTask innner class
    *  object will do the setup for each frame of a drop animation.
   **/
  void dropReset () {

    // Before starting the drop, create the timer task
    // that will cause the histogram display to update
    // during the filling.
    // Create a timer. TimerTask created in MakeHist ()
    fTimer = new java.util.Timer ();

    fDropModel.reset (fYBallStart, fVfYBallStart);
    fDropPanel.reset (fXBallStart, fYBallStart);

    // Reset time sum
    fTDropTotal = 0.0;
    fNumDrops = 0;
    fYBall = fYBallStart;
    fXBall = fXBallStart;

    // Reset the detector.
    fDetector.reset ();

    repaint ();

    // Start the timer after 20ms and then repeat calls
    // to run in PaintHistTask object by the rate set by
    // the fTFrame value.
    fTimer.schedule (new PaintHistTask (), 20, (int) (fTFrame*1000));

  } // dropReset

  /**
    *  Invoked after a drop ends bottom. Reset
    *  all the parameters to set up for another drop.
   **/
  public void dropDone () {
    // Get the data  (times and positions during the drop)
    // from the detector and analyze it
    fData = fDetector.getResults ();
    int num_measures = fDetector.getNumMeasurements ();

    // Send the event data to the controller
    // Two arrays  (times and positions) must be sent.
    fSimController.message (SimUtil.EVENT_DATA, num_measures, fData);

    ++fNumDrops;

    // Check if all drops completed.
    if (fNumDrops == fMaxNumDrops){
        // If so then finish up the data recording
        // for this run and return.
        runDone ();
        return;
    }

    // Reset time sum
    fTDropTotal = 0.0;
    fYBall = fYBallStart;
    fXBall = fXBallStart;

    fDropPanel.reset (fXBallStart, fYBallStart);
    fDropModel.reset (fYBallStart, fVfYBallStart);
    fDetector.reset ();
  } // dropDone

  /**
    *  Invoked when all the drops in a run  (set of drops) are done.
    *  Kills the timer to stop the animation.  (A new timer will be
    *  created in the dropReset () for the next run.)
    *  Tell the DropAnalyser instance to do its job on the run data.
   **/
  public void runDone () {
    // Stop the animation.
    fTimer.cancel ();

    // Tell controller that run is done.
    fSimController.message (SimUtil.RUN_DONE, -1, null);
  } // runDone

} // class SimApplet
package SimClientServer;

public interface SimController {
  public void message (int flag, int iData,
                       double [][] fp);
} // SimController
package SimClientServer;

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

/**
  * This utility class provides methods for streamed I/O
  * that catch the IO exceptions and set a flag to indicate
  * an IO error. This can be convenient when a program needs
  * many read/write operations and the try/catch bracketing
  * makes the code messy and difficult to read.
  *
  * The class also provides methods for reading a set of data
  * into an array. Similarily, there are methods for writing
  * arrays into the stream.
**/
public class NetStreamMethods {

  // I/O streams
  InputStream  fNetInputStream;
  OutputStream fNetOutputStream;

  // Wrapped I/O streams
  BufferedReader   fNetInputReader;
  DataInputStream  fNetDataInputStream;
  DataOutputStream fNetDataOutputStream;

  PrintWriter fPrintWriter;

  boolean fFailedIO = false; // Flag

  /**
    * Utility method to read a string line from a
    * BufferedReader stream
   **/
  String readNetInputLine () {
    try {
      return fNetInputReader.readLine ();
    }
    catch (IOException e) {
      fFailedIO = true;
      return null;
    }
  } // readNetInputLine

  /**
    * Utility method  to read an integer value
    * from a DataInputStream
   **/
  int readNetInputInt () {
    // Read one value at a time and fill array.
    try {
      return fNetDataInputStream.readInt ();
    }
    catch (IOException ioe) {
      fFailedIO = true;
      return -1;
    }
  } // readNetInputInt

  /**
    * Utility method to read a double value from
    * a DataInputStream.
   **/
  double readNetInputDouble () {

    // Read one value at a time and fill array.
    try {
      return fNetDataInputStream.readDouble ();
    }
    catch (IOException ioe) {
      fFailedIO = true;
      return -1.0;
    }
  } // readNetInputDouble

  /**
    * Utility method to read an integer array from
    * a DataInputStream.
   **/
  int [] readNetInputIntArray (int [] buffer, int numElements) {
    if ( buffer == null || buffer.length < numElements)
        buffer = new int[numElements];

    // Read one value at a time and fill array.
    for (int i=0; i < numElements; i++) {
        try {
          buffer[i] = fNetDataInputStream.readInt ();
        }
        catch (IOException ioe){
           fFailedIO = true;
           return null;
        }
    }
    return buffer;
  } // readNetInputIntArray

  /**
    *  Utility method  to read a double array from a DataInputStream
   **/
  double [] readNetInputDoubleArray (double [] buffer,
                                     int numElements) {
    if (buffer == null || buffer.length < numElements)
        buffer = new double[numElements];

    // Read one value at a time and fill array.
    for (int i=0; i< numElements; i++){
        try{
            buffer[i] = fNetDataInputStream.readDouble ();
        }
        catch (IOException ioe){
           fFailedIO = true;
           return null;
        }
    }
    return buffer;
  } // readNetInputDoubleArray

  /**
    * Output is wrapped with a PrintWriter, which doesn't throw
    * IOException. So we use the checkError () method and set the
    * flag for failed output.
   **/
  void writeNetOutputLine (String string) {
    fPrintWriter.println (string);
    if (fPrintWriter.checkError ()) {
        fFailedIO = true;
        return;
    }

    fPrintWriter.flush ();
    if (fPrintWriter.checkError ()) {
        fFailedIO = true;
        return;
    }
  } // writeNetOutputLine

  /**
    * Utility method to write integer values to the output stream.
    * If an IOException occurs, set flag and return;
   **/
  void writeNetOutputInt (int i) {
    try {
      fNetDataOutputStream.writeInt (i);
      fNetDataOutputStream.flush ();
    }
    catch (IOException ioe){
      fFailedIO = true;
      return;
    }
  } // writeNetOutputInt

  /**
    * Utility method to write float values to the output stream.
    * If an IOException occurs, set flag and return;
   **/
  void writeNetOutputFloat (float f) {
    try {
      fNetDataOutputStream.writeFloat (f);
      fNetDataOutputStream.flush ();
    }
    catch (IOException ioe){
      fFailedIO = true;
      return;
    }
  } // writeNetOutputFloat

  /**
    * Utility method to write float values to the output stream.
    * If an IOException occurs, set flag and return;
   **/
  void writeNetOutputDouble (double d) {
    try {
      fNetDataOutputStream.writeDouble (d);
      fNetDataOutputStream.flush ();
    }
    catch (IOException ioe) {
      fFailedIO = true;
      return;
    }
  } // writeNetOutputDouble

  /**
    * Utility method to write an array of int values to
    * the output stream.
    * If an IOException occurs, set flag and return;
   **/
  void writeNetOutputIntArray (int [] iArray,
                              int numElements){

    // Loop through array and write element
    // each to the stream.
    for (int i=0; i < numElements; i++) {
        // Pass only integer values;
        writeNetOutputInt (iArray[i]);
        if (fFailedIO) return;
    }
  } // writeNetOutputIntArray

  /**
    * Utility method to write an array of int values to
    * the output stream.
    * If an IOException occurs, set flag and return;
   **/
  void writeNetOutputDoubleArray (double [] dArray,
                                  int numElements) {

    // Loop through array and write element
    // each to the stream.
    for (int i=0; i < numElements; i++){
        // Pass only integer values;
        writeNetOutputDouble (dArray[i]);
        if (fFailedIO) return;
    }
  } // writeNetOutputDoubleArray

} // class NetStreamMethods
package SimClientServer;

/**
  *  This class is just for holding constants needed for
  *  communications between the server and client.
 **/
public class SimUtil {
  public static final int INIT        = 1;
  public static final int START       = 2;
  public static final int STOP        = 3;
  public static final int RUN_DONE    = 4;
  public static final int EVENT_DATA  = 5;
  public static final int GET_SETUP   = 6;
} // class SimUtil

 

 

Most recent update: Oct. 25, 2005
  
  Part I Part II Part III
Java Core 1  2  3  4  5  6  7  8  9  10  11  12 13 14 15 16 17
18 19 20
21
22 23 24
Supplements

1  2  3  4  5  6  7  8  9  10  11  12

Tech 1  2  3  4  5  6  7  8  9  10  11  12
Physics 1  2  3  4  5  6  7  8  9  10  11  12

Java is a trademark of Sun Microsystems, Inc.