//****************************************************************************** // AwtPduViewer.java: Applet // //****************************************************************************** package mil.navy.nps.awt; import java.applet.*; import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; import java.util.*; import mil.navy.nps.dis.BehaviorStreamBufferUDP; import mil.navy.nps.dis.*; // DIS Library /** AwtPduViewer is an applet that reads Pdus and displays their values on the screen. It is configured with a socket, which allows the applet to read data from the wire; it uses the package mil.navy.nps.dis, which implements the DIS protocol.

This file was generated by Microsquish J++, which is something of an experiment. We'll see how it works out. It's also my first applet that isn't completely trivial, so be a little gentle.

This is compliant with the JDK 1.02 spec; it makes use of deprecated aspects of that API rather than use JDK 1.1. This is primarily to get it working with Netscape 3.01. 1.1 browser implementations should be compatible, at least for the next release.

Netscape bitches & moans about doing a thread.suspend(), complaining about the possibility of deadlock. Not sure what this is all about, but it seems to work. (Note: JavaSoft has deprecated thread.suspend() and friends in 1.1 because of inherent problems w/ deadlock. This needs to be reworked.

This takes two parameters from the HTML file, "PduSocket" and"MulticastGroup". The first is the socket the applet listens on. The second is a multicast group, a feature that is not implemented right now since Netscape doesn't do multicast.

Testing: sent ESPdus to host (madison) from both madison and pinafore using mil.navy.nps.dis.Benchmark. HISTORY

10Apr97 DMcG New
@author Don McGregor ( http://www.npsnet.org/~mcgredo)

@version 1.0 @see UpdateThread @see TextAreaStream */ public class AwtPduViewer extends Applet implements Runnable, java.awt.event.ItemListener, java.awt.event.ActionListener { // Members for applet parameters // = private static String m_PduSocket = "3111"; private static String m_MulticastGroup = null; // no default value; unicast by default private static final String DEFAULT_MCAST_GROUP = "224.2.181.145"; private static final String DEFAULT_PORT = "62040"; // Parameter names. To change a name of a parameter, you need only make // a single change. Simply modify the value of the parameter string below. private static final String PARAM_PduSocket = "PduSocket"; private static final String PARAM_MulticastGroup = "MulticastGroup"; private static BehaviorStreamBufferUDP behaviorStreamBufferUDP = null; // Reads packets & stores them up private static Vector pduPool = null; // data structure that keeps list of arrived Pdus private static UpdateThread updateThread = null; // thread that updates screen display private static TextArea textArea = null; // scrolling text area where data is displayed private static java.awt.List list = null; // List of Pdus to choose from private static Button startButton = null; // start button private static Button stopButton = null; // stop button private static Button flushButton = null; // flush button private static PrintStream textAreaPrintStream = null; // print stream, sent to text area above private static SocketReadUI socketInfo = null; // UI widget with several fields relating to reading // from a socket in it public AwtPduViewer() { pduPool = new Vector(); updateThread = new UpdateThread(this); } public String getAppletInfo() { return "Name: AwtPduViewer\r\n" + "Author: Don McGregor\r\n" + "GNU Copyleft 1997"; } // The getParameterInfo() method returns an array of strings describing // the parameters understood by this applet. public String[][] getParameterInfo() { String[][] info = { { PARAM_PduSocket, "String", "Socket we listen to for Pdus" }, { PARAM_MulticastGroup, "String", "Multicast Group (Java 1.1 only)" }, }; return info; } /** Applet initialization code */ public void init() { System.out.println ("AwtPduViewer init() starting..."); String param; // a parameter passed into the applet TextAreaStream textAreaStream = null; Thread aThread = null; // Get parameters, if any, from the HTML file. If not parameters passed in, // use the defaults. // Dead code; this uses the UI to input parameters now, so there's no // need to pass them in from the html file. /* This is no longer used.....is set by UI in applet instead of via HTMl file param = getParameter(PARAM_MulticastGroup); if (param != null) m_MulticastGroup = param; else m_MulticastGroup = DEFAULT_MCAST_GROUP; // Parameter retrieval: socket to listen on. // Dead, done from UI now. param = getParameter(PARAM_PduSocket); if (param != null) m_PduSocket = DEFAULT_PORT; */ socketInfo = new SocketReadUI(); // buch UI code add(socketInfo); socketInfo.setAddress(DEFAULT_MCAST_GROUP); socketInfo.setPort(Integer.parseInt(DEFAULT_PORT)); // List box, contains scrolling display of arrived Pdus list = new java.awt.List(10, false); list.addItemListener(this); Dimension dim = new Dimension(20,40); list.setSize(dim); add(list); //System.out.println("AwtPduViewer: list size is " + list.size().width + " " + list.size().height); // scrolling text area to display output (lines, width) textArea = new TextArea(25,40); add(textArea); // Start and stop buttons to turn on or off socket. Initially, the // socket and thread are configured to be in the reading state, so // the start button is off, the stop button is on, and the flush // button is always on. startButton = new Button("Start"); startButton.setEnabled(true); add(startButton); startButton.addActionListener(this); stopButton = new Button("Stop"); add(stopButton); stopButton.addActionListener(this); stopButton.setEnabled(false); flushButton = new Button("Flush Pdus"); add(flushButton); flushButton.addActionListener(this); flushButton.setEnabled(true); // Wrap an output stream around the TextArea, so that we can // send stuff streaming to the display with a printf(). textAreaStream = new TextAreaStream(textArea); textAreaPrintStream = new PrintStream(textAreaStream); // Start up the thread that updates the display updateThread.start(); System.out.println ("AwtPduViewer init() starting..."); return; } /** Handles button presses */ public void actionPerformed(ActionEvent e) { Object source = e.getSource(); Thread aThread; if (source instanceof Button) { Button b = (Button) source; // Start button; start up reading threads, turn off button, toggle on "stop" button. if(b.getLabel() == "Start") { startButton.setEnabled(false); stopButton.setEnabled(true); socketInfo.setIsEnabled(false); if(behaviorStreamBufferUDP != null) behaviorStreamBufferUDP.cleanup(); // Close down sockets.... if(socketInfo.getMulticastSocket() == false) // Unicast socket { System.out.println("AwtPduViewer: instantiating unicast BehaviorStreamBufferUDP, " + "port " + socketInfo.getPort()); behaviorStreamBufferUDP = new BehaviorStreamBufferUDP(socketInfo.getPort()); } else // multicast socket { System.out.println("AwtPduViewer: instantiating multicast BehaviorStreamBufferUDP, " + socketInfo.getAddress() + "/" + socketInfo.getPort()); behaviorStreamBufferUDP = new BehaviorStreamBufferUDP(socketInfo.getAddress(), socketInfo.getPort()); } aThread = new Thread(behaviorStreamBufferUDP); aThread.start(); updateThread = new UpdateThread(this); updateThread.start(); this.repaint(); } // Stop button; suspend threads, turn off button, toggle on "start" button. if(b.getLabel() == "Stop") { startButton.setEnabled(true); stopButton.setEnabled(false); socketInfo.setIsEnabled(true); if(behaviorStreamBufferUDP != null) behaviorStreamBufferUDP.cleanup(); behaviorStreamBufferUDP = null; this.repaint(); } // Flush Pdu button hit--remove all the Pdus from the list, clear out // GUI display. if(b.getLabel() == "Flush Pdus") { int textLength = -1; // Lock access to pduPool, since this is also used in another thread, // and we don't want two people writing/modifiying it. synchronized(pduPool) { pduPool = new Vector(10); // everyting should be GC'd. } // Ditto for the GUI list; don't want to be adding to it in the updateDisplay // method at the same time we're flushing it. synchronized(list) { list.removeAll(); } // wipe out all the data in the TextArea textArea.selectAll(); textLength = textArea.getSelectionEnd(); // Deprecated; change for 1.1 textArea.replaceRange(new String(""), 0, textLength); } } return; } // Place additional applet clean up code here. destroy() is called when // when you applet is terminating and being unloaded. //------------------------------------------------------------------------- public void destroy() { // TODO: Place applet cleanup code here } // ANIMATION SUPPORT: // Draws the next image, if all images are currently loaded //-------------------------------------------------------------------------- private void displayImage(Graphics g) { } // AwtPduViewer Paint Handler //-------------------------------------------------------------------------- public void paint(Graphics g) { } // The start() method is called when the page containing the applet // first appears on the screen. The AppletWizard's initial implementation // of this method starts execution of the applet's thread. //-------------------------------------------------------------------------- public void start() { } // The stop() method is called when the page containing the applet is // no longer on the screen. The AppletWizard's initial implementation of // this method stops execution of the applet's thread. //-------------------------------------------------------------------------- public void stop() { } // THREAD SUPPORT // The run() method is called when the applet's thread is started. If // your applet performs any ongoing activities without waiting for user // input, the code for implementing that behavior typically goes here. For // example, for an applet that performs animation, the run() method controls // the display of images. //-------------------------------------------------------------------------- public void run() { // Threads are started from the init() method in this case, since // the behaviorStreamBufferUDP needs parameters passed in to work, updateThread // needs behaviorStreamBufferUDP, and init() is called after run(). repaint(); } /** Updates the display by querying the BehaviorStreamBufferUDP, returning any Pdus that have arrived since the last time we asked, and updating the display. This is kicked off by its own thread, so it runs asyncronously from the app. the display is updated every X seconds. We need to lock access to pduPool, since this is global data used elsewhere in the applet in another thread. Not sure about the requirement to lock list, the GUI element that displays the scrolling list. This is the only place we do a _write_ to it, so I think it's OK. */ public void updateDisplay() { Vector receivedPdus = null; // list of Pdus we get back from network monitor; // those that have arrived since the last time we asked Enumeration pduEnumeration; // Loop through list String PduNameAndTime = null; // string that has name of Pdu & timestamp String time = null; // string representation of 32-bit timestamp on Pdu while(stopButton.isEnabled()) // update until we stop stop getting pdus. { if(behaviorStreamBufferUDP != null) { receivedPdus = behaviorStreamBufferUDP.receivedPdus(); //System.out.println("AwtPduViewer: got " + receivedPdus.size() + " Pdus this cycle"); // Add the new Pdus to the existing pool of Pdus and to the end of the // GUI list, and place the name of the Pdu type in the display list. This // could probably be faster, but it's OK for now. pduEnumeration = receivedPdus.elements(); while(pduEnumeration.hasMoreElements()) { ProtocolDataUnit aPdu = (ProtocolDataUnit)pduEnumeration.nextElement(); // lock access to pduPool, since this may be used elsewhere in the program // at the same time. synchronized(pduPool) { pduPool.addElement(aPdu); } // add the name of the Pdu (eg, "Entity State", "Fire", etc) and the // 32-bit timestamp to the scrolling list PduNameAndTime = aPdu.pduName(); time = String.valueOf(aPdu.getTimestamp().longValue()/1000.0); PduNameAndTime = PduNameAndTime.concat(new String(" ")); PduNameAndTime = PduNameAndTime.concat(time); list.add(PduNameAndTime); } this.repaint(); try { updateThread.sleep(250); // sleep for 0.25 seconds } catch (InterruptedException intException) { throw new RuntimeException("Exception in AwtPduViewer; fitful sleep"); } } // if networkmonitor != null } // while getting pdus } /** itemStateChanged is the interface for ItemListener. When this object is added as a listener to an object, such as a awt.List, this gets the event when something is selected. We act on that by dumping out the contents of the selected item. */ public void itemStateChanged(ItemEvent ie) { int itemIndex = list.getSelectedIndex(); ProtocolDataUnit aPdu; Object source = ie.getSource(); // Scrolling list hit; print out selected PDU to text area if (ie.getStateChange() == ItemEvent.SELECTED) { int textLength = -1; String parameters; textArea.setText(new String("")); // Print the new data. Double protections on out-of-bounds indexes, // which can occur if the incoming PDUs are stopped, then flushed, // then some clicks on the list. itemIndex = list.getSelectedIndex(); if(itemIndex >=0) // Have something selected in list & list exists { aPdu = (ProtocolDataUnit)pduPool.elementAt(itemIndex); if(aPdu != null) // Have a PDU at that site aPdu.printValues(5,textAreaPrintStream); } } // if } // itemStateChanged } // end of class AwtPduViewer