Home : Course Map : Chapter 9 : Java : Tech :
Histogram Stream Classes Demo
JavaTech
Course Map
Chapter 9

Introduction
Overview
Streams
Wrappers,Buffers
Console I/O
  Text Output 
     Demo 1

  Formatter/printf()
     Demo 2

  Tex 2t Input
     Demo 3

  Scanner
     
Demo 4
File Class
  File I/O
  File Output-Text
     Demo 5

  Formatter to File
     Demo 6

  File Input - Text
    Demo 7

  Scanner - Files
     Demo 8

  File I/O - Binary
     Demo 9
   Demo 10
File Chooser Dialog
  Demo 11

Character Codes
  Demo 12
   Demo13
Object I/O
Types to Bytes
Stream Filters
Other I/O Topics
Exercises

    Supplements
Character I/O
  Demo 1   Demo 2
Random Access
  Demo 3
ZIP/GZIP Streams
  Demo 4
Piped Streams
  Demo 5
NIO Framework
More NIO
  Demo 6

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

We have seen that some stream classes represent endpoints: the sources and destinations of data. For example a file can be a source or destination via the FileInputStream and FileOutputStream classes. Other stream classes, such as buffer wrappers, modify, handle, or monitor the data in some way as it passes through.

In this demo we create classes that allow histograms to become the destination for a stream. In the next section we will look at stream classes that filter the histogram data as it heads for its destination.

The demonstration applet below illustrates how we can use the stream framework not only to grab data from a disk file or send text to a console but to move data within a program in a systematic, consistent manner.

In the program HistStreamApplet shown below, simulated data is sent to a histogram via an output stream provided by a StreamedHistPanel, which is a subclass of our HistPanel component class. (See source code listings below.) The StreamedHistPanel includes an inner class called HistogramOutputStream that extends OutputStream. The class HistogramOutputStream overrides the write(int b) method in OutputStream with a method that adds data to the histogram in the StreamedHistPanel object (it can access the histogram since it is an inner class of StreamedHistPanel) .

For HistogramOutputStream, a histogram becomes the destination for the stream just as an array is the destination for ByteArrayInputStream or a string is the destination for StringWriter.

The applet, derived from the TimerHistFillApplet.java example in Chapter 8: Tech : Timers, simulates readings from three sensors using MakeSensorData and streams the data to three corresponding StreamedHistPanel components on the interface. The sensor readings include differing offsets (called pedestals) and response sensitivities (slopes) as is typical with real hardware.

Note that in our HistogramOutputStream we use the full four bytes of the integer argument in write(int b). In the normal use of this method in OutputStream subclasses, only the lowest order byte would be written. If we wanted to stream data from MakeSensorData to other streams, such as to a FileOutputStream, we would need to pack the sensor reading in only the lower order byte (and thus lose precision), or into a byte array as we did in the previous example.

In a similar way, you could make a histogram or histogram panel into a source stream by creating an InputStream subclass that reads data from the histogram.

We note that StreamedHistPanel follows the example of streaming to a TextArea given in Harold, 1999.


HistStreamApplet.java - This applet/application uses StreamedHistPanel objects to display the contents of 3 Histograms that simulate distributions of sensor data. The basic form of the class goes as TimerHistFillApplet in Chapter 8: Tech. However, data is sent to the StreamedHistPanels as OutputStreams.

Data is generated from an instance of MakeSensorData in a thread to simulate the input of data events at random times. The three sensors each produce a Gaussian distribution of values but they are transformed by different slopes and offsets (or pedestals) to represent variations in the sensor hardware.

+ New classes:
StreamedHistPanel.java - This class extends HistPanel. It provides a HistPanel destination for an output stream. It uses an instance of HistPanel to display the data and to hold the data values in an array.

It uses an innter class called HistogramOutputStream that extends OutputStream. An instance of this stream object is provided with the getOutputStream() method. Data written to theo HistogramOutputStream is added to the histogram that is displayed on the panel.

Follows similar pattern as the StreamedTextArea class in "Java IO" by E.R.Harold.

MakeSensorData.java - Runnable class that generates a Guassian distributed random data and write it to the HistogramOutputStream for display on the histogram.

+ Previous classes:
Chapter 6:Tech: Histogram.java, HistPanel.java
Chapter 6:Tech: PlotPanel.java, PlotFormat.java

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

/**
  * This applet/application uses StreamedHistPanels to display the contents
  * of 3 Histograms to show distributions of sensor data. The basic form
  * of the class goes as TimerHistFill_JApplet11. However, data is sent to
  * the StreamedHistPanels as OutputStreams.
  *
  * Data is generated from an instance of MakeSensorData in a thread
  * to simulate the input of data events at random times. The three
  * sensors each produce a Gaussian distribution of values but they
  * are transformed by different slopes and offsets  (or pedestals)
  * to represent variations in the sensor hardware.
 **/
public class HistStreamApplet extends JApplet
             implements ActionListener, Updateable
{
  final static int NUM_HISTS = 3;

  // Histograms for the panels.
  Histogram [] fHistogram = new Histogram[NUM_HISTS];

  // The HistPanel subclass that accept data streams.
  StreamedHistPanel [] fHistPanel = new StreamedHistPanel[NUM_HISTS];

  // Create a panel to hold the histogram sub-panels
  JPanel fHistsPanel = null;

  // Array of treams to the panels
  OutputStream [] fDataOut = new OutputStream[NUM_HISTS];

  // Constants for three channels of sensor data.
  double [] fSlope    = {0.6, 1.2, 1.1};
  double [] fPedestal = {2.4, 1.3, 3.4};

  int fNumDataPoints = 100;

  boolean fMakingHist = false;
  boolean fUpdateDisplay = false;
  MakeSensorData fMakeData = null;

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

  // A text field for input strings
  JTextField fTextField = null;

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

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

  public void init () {
    Container content_pane = getContentPane ();
    JPanel panel = new JPanel (new BorderLayout ());

    // Use a textfield for an input parameter.
    fTextField =
      new JTextField (Integer.toString (fNumDataPoints), 10);

    // If return hit after entering text, the
    // actionPerformed will be invoked.
    fTextField.addActionListener (this);

    fGoButton = new JButton ("Go");
    fGoButton.addActionListener (this);

    fClearButton = new JButton ("Clear");
    fClearButton.addActionListener (this);

    fExitButton = new JButton ("Exit");
    fExitButton.addActionListener (this);

    JPanel control_panel = new JPanel ();

    control_panel.add (fTextField);
    control_panel.add (fGoButton);
    control_panel.add (fClearButton);
    control_panel.add (fExitButton);

    // Create a panel to hold the histogram sub-panels
    fHistsPanel = new JPanel (new GridLayout (1,3));

    // Create the HistPanels and their histograms.
    for (int i=0; i < NUM_HISTS; i++) {
        fHistogram[i] = new Histogram ("Sensor "+i,
                                "Data", 25,0.0,10.0);
        fHistPanel[i] = new StreamedHistPanel (fHistogram[i]);

        // Add the histogram panels to the container panel
        fHistsPanel.add (fHistPanel[i]);

        // Get the output streams for each panel.
        fDataOut[i] = fHistPanel[i].getOutputStream ();
    }
    makeHist ();
    fGoButton.setText ("Stop");
    fClearButton.setEnabled (false);
    if (fInBrowser) fExitButton.setEnabled (false);

    panel.add (fHistsPanel,"Center");
    panel.add (control_panel,"South");

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

  } // init

  public void actionPerformed (ActionEvent e) {
    Object source = e.getSource ();
    if (source == fGoButton || source == fTextField) {
      String str_num_data_points = fTextField.getText ();
      try {
        fNumDataPoints = Integer.parseInt (str_num_data_points);
      } catch (NumberFormatException ex) {
        // Could open an error dialog here but just
        // display a message on the browser status line.
        showStatus ("Bad input value");
        return;
      }
      if (!fMakingHist) {
        makeHist ();
        fGoButton.setText ("Stop");
        fClearButton.setEnabled (false);
      } else { // Stop button has been pushed
        fGoButton.setText ("Go");
        fClearButton.setEnabled (true);
        fMakingHist = false;
      }
    } else if (source == fClearButton ) {
        for (int i=0; i < NUM_HISTS; i++)
        {   fHistogram[i].clear ();}
        repaint ();
    }else if (!fInBrowser)
        System.exit (0);

  } // actionPerformed

  /** Create the histograms and start the timers. **/
  void makeHist () {
    // Only allow for one data maker at a time.
    if (fMakingHist) return;
    fMakingHist = true;

    // Create the runnable object to fill the histograms
    // The data will simualte sensors that return data with
    // slightly different slopes and pedestals.
    // The maximum delay between data readings will be 500msecs.
    fMakeData =
      new MakeSensorData (this, fDataOut, fNumDataPoints,
                          fSlope, fPedestal, 500);
    Thread data_thread = new Thread (fMakeData);

    // Before starting the filling, 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 ();

    // Start the timer after 100ms and then repeat calls
    // to run in PaintHistTask object every 250ms.
    fTimer.schedule (new PaintHistTask (), 100, 250);

    // Now start the data filling.
    data_thread.start ();
    return;

  } // makeHist

  /**
    * Use the inner class technique to define the
    * TimerTask subclass for signalling that the display
    * should be updated.
   **/
  class PaintHistTask extends java.util.TimerTask {
    public void run () {
       fUpdateDisplay = true;
    }
  } // class PaintHistTask

  /**
    *  Repaint the histogram display and turn off the
    *  update display flag. Return the makingHist flag
    *  to indicate if updating should continue.
   **/
  public boolean update (Object obj) {
    // Don't update the display until the timer
    // turns on the updateDisplay flag.
    if ( fUpdateDisplay) {
      // Possible this method called before outputPanel
      // created in the init (). So check if it exists
      // before attempting to repaint.
      if (fHistsPanel != null) fHistsPanel.repaint ();
      fUpdateDisplay = false;
    }
    return fMakingHist;

  } // update

  /** Called when the histogram filling is finished. **/
  public void done () {
    fMakingHist = false;

    // Stop the histogram display updates.
    fTimer.cancel ();

    // Update one last time
    fHistsPanel.repaint ();

    // Reset the buttons.
    fGoButton.setText ("Go");
    fClearButton.setEnabled (true);

  } // done

  public static void main (String[] args) {
    int frame_width=450;
    int frame_height=300;

    // Create applet and add to frame.
    HistStreamApplet applet = new HistStreamApplet ();
    applet.fInBrowser = false;
    applet.init ();

    // Following anonymous class used to close window & exit program
    JFrame f = new JFrame ("Demo");
    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 HistStreamApplet
import java.io.*;

/**
  *  This class provides provides a HistPanel destination for an
  *  output stream. It uses an instance of HistPanel to
  *  display the data and to hold the data values in an array.
  *
  *  Follows similar pattern as the StreamedTextArea class in
  *  "Java IO" by E.R.Harold.
 **/
public class StreamedHistPanel extends HistPanel
{
  OutputStream fHistStream = null;
  boolean fNewDataFlag = false;

  /** Create the panel with the histogram. **/
  public StreamedHistPanel (Histogram histogram) {
    super (histogram);
    fHistStream = new HistogramOutputStream ();
  } // ctor

  public OutputStream getOutputStream () {
    return fHistStream;
  }

  /**
    *  This internal class provides the stream to write data.
    *  to the histogram destination. The class extends the
    *  OutputStream so as to use the double and float type
    *  write methods. When data is written to the stream, a
    *  flag indicates that new data has arrived in case the
    *  GUI wants to update the display.
    *  


    *  For out needs here, we only override the write ()
    *  and writeFloat () methods.
   **/
  class HistogramOutputStream extends OutputStream {
    /**
     * In OutputStream write (int b), only the lower byte
     * of the int value is used but here we use the full
     * value
     */
    public synchronized void write (int data) {
      // Convert back to double and shift down two decimal
      // places.
      fHistogram.add (((double)data)/100.0);
      fNewDataFlag = true;
    }
  } // class HistogramOutputStream

} // class StreamedHistPanel
import java.io.*;

/**  This runnable class fills the histogram. **/
public class MakeSensorData implements Runnable
{
  OutputStream [] fDataOut;
  int fNumData;
  Updateable fOwner;

  // Maximum delay between data points with default
  int fMaxDelay = 1000;

  // Create an instance of the Random class for
  // producing our random values.
  static java.util.Random fRan = new java.util.Random ();

  // Constants for three channels of sensor data.
  double [] fSlope ;
  double [] fPedestal;

/**
   *  Constructor
   *  @param fOwner to call back with updates.
   *  @param fDataOut stream destinations for the data.
   *  @param fNumData Number of data readings.
   *  @param fMaxDelay maximum delay in msecs between data readings.
  **/
public MakeSensorData (Updateable owner,
           OutputStream [] dataOut, int numData,
           double [] slope, double [] pedestal,
           int maxDelay) {

    fDataOut  = dataOut;
    fSlope    = slope;
    fPedestal = pedestal;

    fNumData  = numData;
    fOwner = fOwner;

    // Maximum delay time between new data points
    fMaxDelay = maxDelay;

  } // ctor

  /**
    * Simulate data taking by generating Gaussian distributions
    * for three data channels, each with their own fPedestal and
    * fSlope.
   **/
  public void run () {

   for (int i=0; i < fNumData; i++) {
     for (int j=0; j < fDataOut.length; j++) {
       // Create some data. Make sure it is a positive value.
       double val = -1.0;
       // Signal events
       while (val < 0.0)
         val = fRan.nextGaussian () + 4.0;

       // Simulate variations in sensor fSlope and offsets
       val = fSlope[j] * val + fPedestal[j];

       // Shift up two decimal places before converting
       // to integer.
       int ival =  (int) (100 * val);
       try {
         // Send data to stream destination
         fDataOut[j].write (ival);
       }
       catch (IOException ioe)
       {}
     }

     // Update the fOwner. Stop updates if
     // false returned.
     if (!fOwner.update (this)) break;

     // Pause for random periods between events
     try {
       Thread.sleep (fRan.nextInt (fMaxDelay));
     }
     catch (InterruptedException e)
     {}
   }
   // Tell the fOwner that the data has been sent.
   fOwner.done ();
  } // run

} // class MakeSensorData

 

References & Web Resources

Latest update: Nov. 14, 2004

              Tech
Histogram I/O
Hist I/O - Get/Set
  Demo 1
Hist I/O - Objects
  Demo 2
HistogramStream
  Demo 3
Filtering Data
  Demo 4
Exercises

           Physics
Physics Model
Simulation Design
Physics Simulator
  Demo 1
Experiment Design
Experiment Sim.
  Demo 2
Analysis
Expt. + Analysis
  Demo 3
Exercises

  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.