|  | 
         
          |  
              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 |  |  |