Mega Code Archive

 
Categories / Java / Network Protocol
 

JModem - simple communications program

/*  * Copyright (c) Ian F. Darwin, http://www.darwinsys.com/, 1996-2002.  * All rights reserved. Software written by Ian F. Darwin and others.  * $Id: LICENSE,v 1.8 2004/02/09 03:33:38 ian Exp $  *  * Redistribution and use in source and binary forms, with or without  * modification, are permitted provided that the following conditions  * are met:  * 1. Redistributions of source code must retain the above copyright  *    notice, this list of conditions and the following disclaimer.  * 2. Redistributions in binary form must reproduce the above copyright  *    notice, this list of conditions and the following disclaimer in the  *    documentation and/or other materials provided with the distribution.  *  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  * POSSIBILITY OF SUCH DAMAGE.  *   * Java, the Duke mascot, and all variants of Sun's Java "steaming coffee  * cup" logo are trademarks of Sun Microsystems. Sun's, and James Gosling's,  * pioneering role in inventing and promulgating (and standardizing) the Java   * language and environment is gratefully acknowledged.  *   * The pioneering role of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for  * inventing predecessor languages C and C++ is also gratefully acknowledged.  */ import java.awt.BorderLayout; import java.awt.Font; import java.awt.event.KeyEvent; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.Writer; import java.util.Enumeration; import java.util.HashMap; import javax.comm.CommPortIdentifier; import javax.comm.PortInUseException; import javax.comm.SerialPort; import javax.comm.UnsupportedCommOperationException; import javax.swing.ButtonGroup; import javax.swing.JOptionPane; import javax.swing.JScrollPane; import javax.swing.JTextArea; /**  * JModem - simple communications program.  * WARNING this file was built with the NetBeans Developer IDE  * and parts of it should not be modified with a text editor.  * @author    Ian F. Darwin, http://www.darwinsys.com/  * @version   $Id: JModem.java,v 1.18 2004/04/11 23:50:40 ian Exp $  */ public class JModem extends javax.swing.JFrame {   /** The Model. */   JMModel theModel;   /** The TextArea */   JTextArea theTextArea;   /** The courier font for the text areas and fields. */   protected Font plainFont;   /** The valid baud rates (actually BPS rates). */   private int[] baudot = { 9600, 19200, 38400, 57600, 115200 };   /** The types of remote systems. */   private String sysTypes[] = { "Unix", "DOS", "Other" };   private int M_RECEIVE = -1, M_SEND = +1;   private int xferDirection = M_RECEIVE;   /** Constructor */   public JModem() {     theModel = new JMModel(this);     initComponents();     finishConstructor();     pack();   }   /** This method is called from within the constructor to    * initialize the form.    * WARNING: Do NOT modify this code. The content of this method is    * always regenerated by the FormEditor.    */   private void initComponents () {//GEN-BEGIN:initComponents     setTitle ("JModem");     addWindowListener (new java.awt.event.WindowAdapter () {         public void windowClosing (java.awt.event.WindowEvent evt) {           exitForm (evt);         }       }     );     getContentPane ().setLayout (new java.awt.BorderLayout ());     jMenuBar1 = new javax.swing.JMenuBar ();       fileMenu = new javax.swing.JMenu ();       fileMenu.setText ("File");         saveLogFileMenuItem = new javax.swing.JMenuItem ();         saveLogFileMenuItem.setText ("Save Log");         saveLogFileMenuItem.addActionListener (new java.awt.event.ActionListener () {             public void actionPerformed (java.awt.event.ActionEvent evt) {               saveLogFileMenuItemActionPerformed (evt);             }           }         );         fileMenu.add(saveLogFileMenuItem);         fileMenu.addSeparator();         exitMenuItem = new javax.swing.JMenuItem ();         exitMenuItem.setText ("Exit");         exitMenuItem.addActionListener (new java.awt.event.ActionListener () {             public void actionPerformed (java.awt.event.ActionEvent evt) {               exitMenuItemActionPerformed (evt);             }           }         );         fileMenu.add(exitMenuItem);       jMenuBar1.add(fileMenu);       helpMenu = new javax.swing.JMenu ();       helpMenu.setText ("Help");         helpAboutMenuItem = new javax.swing.JMenuItem ();         helpAboutMenuItem.setText ("Item");         helpAboutMenuItem.addActionListener (new java.awt.event.ActionListener () {             public void actionPerformed (java.awt.event.ActionEvent evt) {               helpAboutMenuItemActionPerformed (evt);             }           }         );         helpMenu.add(helpAboutMenuItem);       jMenuBar1.add(helpMenu);     setJMenuBar(jMenuBar1);     connectPanel = new javax.swing.JPanel ();     connectPanel.setLayout (new java.awt.FlowLayout ());       connectPanelLabel = new javax.swing.JLabel ();       connectPanelLabel.setText ("Connection");       connectPanelLabel.setForeground (java.awt.Color.red);       connectPanel.add (connectPanelLabel);       portsLabel = new javax.swing.JLabel ();       portsLabel.setText ("Port:");       connectPanel.add (portsLabel);       portsComboBox = new javax.swing.JComboBox ();       portsComboBox.addActionListener (new java.awt.event.ActionListener () {           public void actionPerformed (java.awt.event.ActionEvent evt) {             portsComboBoxActionPerformed (evt);           }         }       );       connectPanel.add (portsComboBox);       buadLabel = new javax.swing.JLabel ();       buadLabel.setText ("Speed");       connectPanel.add (buadLabel);       baudComboBox = new javax.swing.JComboBox ();       baudComboBox.addActionListener (new java.awt.event.ActionListener () {           public void actionPerformed (java.awt.event.ActionEvent evt) {             baudComboBoxActionPerformed (evt);           }         }       );       connectPanel.add (baudComboBox);       databitsPanel = new javax.swing.JPanel ();       databitsPanel.setPreferredSize (new java.awt.Dimension(50, 50));       databitsPanel.setMinimumSize (new java.awt.Dimension(0, 0));       databitsPanel.setLayout (new javax.swing.BoxLayout (databitsPanel, 1));         d7RadioButton = new javax.swing.JRadioButton ();         d7RadioButton.setText ("7");         databitsPanel.add (d7RadioButton);         d8RadioButton = new javax.swing.JRadioButton ();         d8RadioButton.setSelected (true);         d8RadioButton.setText ("8");         databitsPanel.add (d8RadioButton);       connectPanel.add (databitsPanel);       parityPanel = new javax.swing.JPanel ();       parityPanel.setPreferredSize (new java.awt.Dimension(50, 50));       parityPanel.setLayout (new javax.swing.BoxLayout (parityPanel, 1));         pNoneRadioButton = new javax.swing.JRadioButton ();         pNoneRadioButton.setSelected (true);         pNoneRadioButton.setText ("None");         pNoneRadioButton.addActionListener (new java.awt.event.ActionListener () {             public void actionPerformed (java.awt.event.ActionEvent evt) {               pNoneRadioButtonActionPerformed (evt);             }           }         );         parityPanel.add (pNoneRadioButton);         pEvenRadioButton = new javax.swing.JRadioButton ();         pEvenRadioButton.setText ("Even");         pEvenRadioButton.addActionListener (new java.awt.event.ActionListener () {             public void actionPerformed (java.awt.event.ActionEvent evt) {               evenRadioButtonActionPerformed (evt);             }           }         );         parityPanel.add (pEvenRadioButton);         pOddRadioButton = new javax.swing.JRadioButton ();         pOddRadioButton.setText ("Odd");         pOddRadioButton.addActionListener (new java.awt.event.ActionListener () {             public void actionPerformed (java.awt.event.ActionEvent evt) {               oddRadioButtonActionPerformed (evt);             }           }         );         parityPanel.add (pOddRadioButton);       connectPanel.add (parityPanel);       sysTypeLabel = new javax.swing.JLabel ();       sysTypeLabel.setText ("Remote:");       connectPanel.add (sysTypeLabel);       sysTypeComboBox = new javax.swing.JComboBox ();       sysTypeComboBox.addActionListener (new java.awt.event.ActionListener () {           public void actionPerformed (java.awt.event.ActionEvent evt) {             sysTypeComboBoxActionPerformed (evt);           }         }       );       connectPanel.add (sysTypeComboBox);       connectButton = new javax.swing.JButton ();       connectButton.setText ("Connect");       connectButton.addActionListener (new java.awt.event.ActionListener () {           public void actionPerformed (java.awt.event.ActionEvent evt) {             connectButtonActionPerformed (evt);           }         }       );       connectPanel.add (connectButton);     getContentPane().add(connectPanel, BorderLayout.NORTH);     xferPanel = new javax.swing.JPanel ();     xferPanel.setLayout (new java.awt.FlowLayout ());       xferPanelLabel = new javax.swing.JLabel ();       xferPanelLabel.setText ("File Transfer");       xferPanelLabel.setForeground (java.awt.Color.red);       xferPanel.add (xferPanelLabel);       jPanel6 = new javax.swing.JPanel ();       jPanel6.setLayout (new javax.swing.BoxLayout (jPanel6, 1));         sendRadioButton = new javax.swing.JRadioButton ();         sendRadioButton.setSelected (true);         sendRadioButton.setText ("Send");         sendRadioButton.addActionListener (new java.awt.event.ActionListener () {             public void actionPerformed (java.awt.event.ActionEvent evt) {               sendRadioButtonActionPerformed (evt);             }           }         );         jPanel6.add (sendRadioButton);         recvRadioButton = new javax.swing.JRadioButton ();         recvRadioButton.setText ("Receive");         recvRadioButton.addActionListener (new java.awt.event.ActionListener () {             public void actionPerformed (java.awt.event.ActionEvent evt) {               recvRadioButtonActionPerformed (evt);             }           }         );         jPanel6.add (recvRadioButton);       xferPanel.add (jPanel6);       xferFilenameLabel = new javax.swing.JLabel ();       xferFilenameLabel.setText ("Filename:");       xferPanel.add (xferFilenameLabel);       xferFileNameTF = new javax.swing.JTextField ();       xferFileNameTF.setPreferredSize (new java.awt.Dimension(100, 20));       xferPanel.add (xferFileNameTF);       jPanel7 = new javax.swing.JPanel ();       jPanel7.setLayout (new javax.swing.BoxLayout (jPanel7, 1));         xferModeTextRadioButton = new javax.swing.JRadioButton ();         xferModeTextRadioButton.setText ("Text");         jPanel7.add (xferModeTextRadioButton);         xferModeBinRadioButton = new javax.swing.JRadioButton ();         xferModeBinRadioButton.setSelected (true);         xferModeBinRadioButton.setText ("Binary");         jPanel7.add (xferModeBinRadioButton);       xferPanel.add (jPanel7);       xferButton = new javax.swing.JButton ();       xferButton.setText ("Transfer");       xferButton.addActionListener (new java.awt.event.ActionListener () {           public void actionPerformed (java.awt.event.ActionEvent evt) {             xferButtonActionPerformed (evt);           }         }       );       xferPanel.add (xferButton);     getContentPane ().add (xferPanel, BorderLayout.SOUTH);   }//GEN-END:initComponents   /** Save the session log to disk.    */   private void saveLogFileMenuItemActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveLogFileMenuItemActionPerformed     theModel.saveLogFile();   }//GEN-LAST:event_saveLogFileMenuItemActionPerformed   private void helpAboutMenuItemActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_helpAboutMenuItemActionPerformed     note("JModem 0.0 (c) 2000 Ian F. Darwin\nhttp://www.darwinsys.com/");   }//GEN-LAST:event_helpAboutMenuItemActionPerformed   private void baudComboBoxActionPerformed (java.awt.event.ActionEvent evt) {     // Add your handling code here:   }   private void portsComboBoxActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_portsComboBoxActionPerformed     // Add your handling code here:   }//GEN-LAST:event_portsComboBoxActionPerformed   /** A TextArea subclass with funky keypress forwarding: send to    * remote, not to local. This IS a terminal emulator, after all.    */   class MyTextArea extends JTextArea {     MyTextArea(int r, int c) {       super(r, c);     }     /** Handle local KeyEvents: send KeyTyped to the remote. */     protected void processComponentKeyEvent(java.awt.event.KeyEvent evt) {       if (evt.getID() != KeyEvent.KEY_TYPED)         return;       // send keystrokes to remote, for processing.       // do nothing locally, to avoid user keystrokes appearing twice!       if (theModel.state != JMModel.S_CONNECTED) {         getToolkit().beep();  // or just connect()?         return;       }       char ch = evt.getKeyChar();       if (ch == '\n') {    // XX if systemtype == dos         // sendChar('\r');         theModel.sendChar('\n');         return;       }       theModel.sendChar(ch);     }   }   /** Finish the initializations. */   private void finishConstructor() {     // Create the textarea with a JScrollpane wrapping it.     // Install it in Centre of the TextArea.     theTextArea = new MyTextArea(20, 80);     getContentPane().add(new JScrollPane(theTextArea), BorderLayout.CENTER);     plainFont = new Font("courier", Font.PLAIN, 13);     theTextArea.setFont(plainFont);     xferFileNameTF.setFont(plainFont);     theModel.populateComboBox();     portsComboBox.setSelectedIndex(0);     // Load up the baud rate combo box     for (int i=0; i<baudot.length; i++) {       baudComboBox.addItem(Integer.toString(baudot[i]));     }     baudComboBox.setSelectedIndex(0);     // Load up the System Type combo box     for (int i=0; i<sysTypes.length; i++) {       sysTypeComboBox.addItem(sysTypes[i]);     }     sysTypeComboBox.setSelectedIndex(0);     // put radio buttons into groups to enforce single-selection     ButtonGroup b1 = new ButtonGroup();     b1.add(d7RadioButton);     b1.add(d8RadioButton);     ButtonGroup b2 = new ButtonGroup();     b2.add(pNoneRadioButton);     b2.add(pEvenRadioButton);     b2.add(pOddRadioButton);     ButtonGroup b3 = new ButtonGroup();     b3.add(sendRadioButton);     b3.add(recvRadioButton);     ButtonGroup b4 = new ButtonGroup();     b4.add(xferModeTextRadioButton);     b4.add(xferModeBinRadioButton);     xferModeBinRadioButton.setEnabled(true);   }   private void recvRadioButtonActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_recvRadioButtonActionPerformed     xferDirection = M_RECEIVE;   }//GEN-LAST:event_recvRadioButtonActionPerformed   private void sendRadioButtonActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sendRadioButtonActionPerformed     xferDirection = M_SEND;   }//GEN-LAST:event_sendRadioButtonActionPerformed   private void exitMenuItemActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exitMenuItemActionPerformed     System.exit(0);   }//GEN-LAST:event_exitMenuItemActionPerformed   private void sysTypeComboBoxActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sysTypeComboBoxActionPerformed     // Add your handling code here:   }//GEN-LAST:event_sysTypeComboBoxActionPerformed   private void pNoneRadioButtonActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pNoneRadioButtonActionPerformed     // Add your handling code here:   }//GEN-LAST:event_pNoneRadioButtonActionPerformed   private void oddRadioButtonActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_oddRadioButtonActionPerformed     // Add your handling code here:   }//GEN-LAST:event_oddRadioButtonActionPerformed   private void evenRadioButtonActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_evenRadioButtonActionPerformed     // Add your handling code here:   }//GEN-LAST:event_evenRadioButtonActionPerformed   /** This method basically toggles between Connected mode and    * disconnected mode.    */   private void connectButtonActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_connectButtonActionPerformed     if (theModel.state == JMModel.S_CONNECTED) {       theModel.disconnect();  // calls our disconnect() if OK     } else {       theModel.connect();    // calls our connect() if OK     }   }//GEN-LAST:event_connectButtonActionPerformed   /** Show that we have connected to the serial port. */   void connect() {       connectButton.setText("Disconnect");       theTextArea.setEditable(true);       theTextArea.requestFocus();   }   /** Show that we have connected to the serial port. */   void disconnect() {       connectButton.setText("Connect");       theTextArea.setEditable(false);   }   private void xferButtonActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_xferButtonActionPerformed     // Do the transfer, using TModem class.     theModel.xfer();   }//GEN-LAST:event_xferButtonActionPerformed   /** Exit the Application */   private void exitForm(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_exitForm     System.exit (0);   }//GEN-LAST:event_exitForm // Some of these must be package-level visibility for JMModel, // until we re-define the interface to that class a little... // Variables declaration - do not modify//GEN-BEGIN:variables   private javax.swing.JPanel connectPanel;   private javax.swing.JPanel xferPanel;   private javax.swing.JLabel connectPanelLabel;   private javax.swing.JLabel portsLabel;   protected javax.swing.JComboBox portsComboBox;   private javax.swing.JLabel buadLabel;   protected javax.swing.JComboBox baudComboBox;   private javax.swing.JPanel databitsPanel;   private javax.swing.JPanel parityPanel;   private javax.swing.JLabel sysTypeLabel;   private javax.swing.JComboBox sysTypeComboBox;   private javax.swing.JButton connectButton;   private javax.swing.JRadioButton d7RadioButton;   private javax.swing.JRadioButton d8RadioButton;   private javax.swing.JRadioButton pNoneRadioButton;   private javax.swing.JRadioButton pEvenRadioButton;   private javax.swing.JRadioButton pOddRadioButton;   private javax.swing.JLabel xferPanelLabel;   private javax.swing.JPanel jPanel6;   private javax.swing.JLabel xferFilenameLabel;   private javax.swing.JTextField xferFileNameTF;   private javax.swing.JPanel jPanel7;   private javax.swing.JButton xferButton;   private javax.swing.JRadioButton sendRadioButton;   private javax.swing.JRadioButton recvRadioButton;   private javax.swing.JRadioButton xferModeTextRadioButton;   private javax.swing.JRadioButton xferModeBinRadioButton;   private javax.swing.JMenuBar jMenuBar1;   private javax.swing.JMenu fileMenu;   private javax.swing.JMenu helpMenu;   private javax.swing.JMenuItem saveLogFileMenuItem;   private javax.swing.JMenuItem exitMenuItem;   private javax.swing.JMenuItem helpAboutMenuItem; // End of variables declaration//GEN-END:variables   /** Tell if the user wants 7 or 8-bit words */   public int getDataBits() {     if (d7RadioButton.isSelected())       return 7;     if (d8RadioButton.isSelected())       return 8;     throw new IllegalStateException("No word size in radio button group");   }   /** Tell if the user wants even, odd, or no parity. */   public int getParity() {     if (pNoneRadioButton.isSelected()) return JMModel.PARITY_NONE;     if (pEvenRadioButton.isSelected()) return JMModel.PARITY_EVEN;     if (pOddRadioButton.isSelected())  return JMModel.PARITY_ODD;     throw new IllegalStateException("No parity in radio button group");   }   /** Get the filename */   public String getXferFileName() {     return xferFileNameTF.getText();   }   /** "One if by send, two if receive" */   public boolean isSend() {     if (sendRadioButton.isSelected())       return true;     if (recvRadioButton.isSelected())       return false;     throw new IllegalStateException("No send/recv set in radio button group");   }   /** Convenience routine: Show a standard-form information dialog */   void note(String message) {     JOptionPane.showMessageDialog(this, message,       "JModem Notice", JOptionPane.INFORMATION_MESSAGE);     return;   }   /** Convenience routine: Show a standard-form error dialog */   void err(String message) {     JOptionPane.showMessageDialog(this, message,       "JModem Error", JOptionPane.ERROR_MESSAGE);     return;   }   /** Main: just create and show the application class. */   public static void main(java.lang.String[] args) {     new JModem().setVisible(true);   } } /**  * JMModel -- Communications I/O for JModem. No GUI stuff here.  * @author   Ian F. Darwin, http://www.darwinsys.com/  * @version   $Id: JMModel.java,v 1.4 2004/04/11 23:50:40 ian Exp $  */ class JMModel extends java.lang.Object {   /** The View */   JModem theGUI;   /** The javax.com.CommPort object in use */   private SerialPort thePort;   /** The input and output streams */   private InputStream serialInput;   private OutputStream serialOutput;   /** The size of the static read buffer. */   protected static final int BUFSIZE = 1024;   /** A buffer for the read listener; preallocated once. */   static byte[] buf = new byte[BUFSIZE];   /** A Thread for reading from the remote. */   protected Thread serialReadThread;   /** A file transfer program */   protected TModem xferProg;   /** The state for disconnected and connected */   static int S_DISCONNECTED = 0, S_CONNECTED = 1;   /** The state, either disconnected or connected */   int state = S_DISCONNECTED;   /** The substate settings */   static int S_INTERACT = 0, S_XFER = 1;   /** The online state, either interactive or in xfer. Used by the    * main reader thread to avoid reading data meant for the xfer program.    */   int submode = S_INTERACT;   // Constants to hide the Comm API from our GUI.   public final static int PARITY_NONE = SerialPort.PARITY_NONE;   public final static int PARITY_EVEN = SerialPort.PARITY_EVEN;   public final static int PARITY_ODD  = SerialPort.PARITY_ODD;   private int[] baudot = { 9600, 19200, 38400, 57600, 115200 };   private String sysTypes[] = { "Unix", "DOS", "Other" };   protected HashMap portsIDmap = new HashMap();   /** Constructor */   public JMModel(JModem gui) {     theGUI = gui;   }   protected String DEFAULT_LOG_FILE = "jmodemlog.txt";;   /** Use normal java.io to save the JTextArea's session log    * into a file.    */     public void saveLogFile() {     String fileName = DEFAULT_LOG_FILE;     try {       Writer w = new FileWriter(fileName);       theGUI.theTextArea.write(w);       w.write('\r'); w.write('\n');  // in case last line is a prompt.       w.close();     } catch (IOException e) {       theGUI.err("Error saving log file:\n" + e.toString());       return;     }     theGUI.note("Session log saved to " + fileName);   }   /** Load the list of Serial Ports into the chooser.    * This code is far too chummy with the innards of class JModem.    */   void populateComboBox() {     // get list of ports available on this particular computer,     // by calling static method in CommPortIdentifier.     Enumeration pList = CommPortIdentifier.getPortIdentifiers();     // Process the list of ports, putting serial ports into ComboBox     while (pList.hasMoreElements()) {       CommPortIdentifier cpi = (CommPortIdentifier)pList.nextElement();       if (cpi.getPortType() == CommPortIdentifier.PORT_SERIAL) {         theGUI.portsComboBox.addItem(cpi.getName());         portsIDmap.put(cpi.getName(), cpi);       }     }   }   /** Connect to the chosen serial port, and set parameters. */   void connect() {     try {       // Open the specified serial port       CommPortIdentifier cpi = (CommPortIdentifier)portsIDmap.get(       theGUI.portsComboBox.getSelectedItem());       thePort = (SerialPort)cpi.open("JModem", 15*1000);       // Set the serial port parameters.       thePort.setSerialPortParams(         baudot[theGUI.baudComboBox.getSelectedIndex()],    // baud         theGUI.getDataBits() == 7 ?         SerialPort.DATABITS_7 : SerialPort.DATABITS_8,         SerialPort.STOPBITS_1,              // stop bits         theGUI.getParity());              // parity       thePort.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_IN &         SerialPort.FLOWCONTROL_RTSCTS_OUT);     } catch (PortInUseException pue) {       theGUI.err("Port in use: close other app, or use different port.");       return;     } catch (UnsupportedCommOperationException uoe) {       theGUI.err("Unsupported options error: try different settings");       return;     }     // Similar to "raw" mode: return when 1 or more chars available.     try {       thePort.enableReceiveThreshold(1);       if (!thePort.isReceiveThresholdEnabled()) {         theGUI.err("Could not set receive threshold");         disconnect();         return;       }       thePort.setInputBufferSize(buf.length);     } catch (UnsupportedCommOperationException ev) {       theGUI.err("Unable to set receive threshold in Comm API; port unusable.");       disconnect();       return;     }     // Get the streams     try {       serialInput = thePort.getInputStream();     } catch (IOException e) {       theGUI.err("Error getting input stream:\n" + e.toString());       return;     }     try {       serialOutput = thePort.getOutputStream();     } catch (IOException e) {       theGUI.err("Error getting output stream:\n" + e.toString());       return;     }     // Now that we're all set, create a Thread to read data from the remote     serialReadThread = new Thread(new Runnable() {     int nbytes = buf.length;       public void run() {         do {           try {             // If the xfer program is running, stay out of its way.               if (submode == S_XFER) {                 delay(1000);               continue;             }             nbytes = serialInput.read(buf, 0, buf.length);           } catch (IOException ev) {           theGUI.err("Error reading from remote:\n" + ev.toString());           return;           }           // XXX need an appendChar() method in MyTextArea           String tmp = new String(buf, 0, nbytes);           theGUI.theTextArea.append(tmp);           theGUI.theTextArea.setCaretPosition(           theGUI.theTextArea.getText().length());         } while (serialInput != null);       }     });     serialReadThread.start();     // Finally, tell rest of program, and user, that we're online.     state = S_CONNECTED;     theGUI.connect();   }   /** Break our connection to the serial port. */   void disconnect() {     // Tell java.io we are done with the input and output     try {       serialReadThread.stop();  // IGNORE DEPRECATION WARNINGS; the Java       // API still lacks a reliable termination method for Threads       // that are blocked on e.g., local disk reads.       serialInput.close();       serialOutput.close();       serialOutput = null;     } catch (IOException e) {       theGUI.err("IO Exception closing port:\n" + e.toString());     }     // Tell javax.comm we are done with the port.     thePort.removeEventListener();     thePort.close();     // Discard TModem object, if present.     xferProg = null;     // Tell rest of program we are no longer online.     state = S_DISCONNECTED;     theGUI.disconnect();   }   /** Convenience routine, due to useless InterruptedException */   public void delay(long milliseconds) {     try {       Thread.sleep(milliseconds);     } catch (InterruptedException e) {       // can't happen     }   }   /** Send one character to the remote */   void sendChar(char ch) {     if (state != S_CONNECTED)       return;     // System.err.println("--> " + ch);     try {       serialOutput.write(ch);     } catch (IOException e) {       theGUI.err("Output error on remote:\n" + e.toString() +         "\nClosing connection.");       disconnect();     }   }   /** Send a String of characters to the remote. */   private void sendString(String s) {     if (state != S_CONNECTED)       return;     try {       serialOutput.write(s.getBytes());     } catch (IOException e) {       theGUI.err("Output error on remote:\n" + e.toString() +         "\nClosing connection.");       disconnect();     }   }   /** Do one complete file transfer, using TModem */   public void xfer() {     if (state != S_CONNECTED) {       theGUI.err("Must be connected to do file transfers");       return;     }     if (xferProg == null) {       xferProg = new TModem(serialInput, serialOutput,          new PrintWriter(System.out)); // xerProg discarded in disconnect()     }     String fileName = theGUI.getXferFileName();     if (fileName.length() == 0) {       theGUI.err("Filename must be given");       return;     }     // Do the transfer!  If we are sending, send a "tmodem -r" to     // the other side; if receiving, send "tmodem -s" to ask it     // to send the file.     try {       if (theGUI.isSend()) {         if (!new File(fileName).canRead()) {             theGUI.err("Can't read file " + fileName + ".");           return;         }         // Other end must "r"eceive what we send.         sendString("tmodem -r " + fileName + "\r\n");         delay(500);    // let command echo back to us         submode = S_XFER;         xferProg.send(fileName);       } else {         // Other end must send for us to receive.         sendString("tmodem -s " + fileName + "\r\n");         delay(500);    // let command echo back to us         submode = S_XFER;         xferProg.receive(fileName);       }     } catch (InterruptedException e) {       theGUI.err("Timeout");       return;     } catch (IOException e) {       theGUI.err("IO Exception in transfer:\n" + e);       return;     } catch (Exception ev) {       theGUI.err("Protocol failure:\n" + ev);       return;     } finally {       submode = S_INTERACT;     }     theGUI.note("File Transfer completed");   } } /**  * a tiny version of Ward Christensen's MODEM program for UNIX.   * Written ~ 1980 by Andrew Scott Beals. Last revised 1982.  * A.D. 2000 - dragged from the archives for use in Java Cookbook.  *  * @author C version by Andrew Scott Beals, sjobrg.andy%mit-oz@mit-mc.arpa.  * @author Java version by Ian F. Darwin, ian@darwinsys.com  * $Id: TModem.java,v 1.8 2000/03/02 03:40:50 ian Exp $  */ class TModem {     protected final byte CPMEOF = 26;       /* control/z */     protected final int MAXERRORS = 10;     /* max times to retry one block */     protected final int SECSIZE = 128;      /* cpm sector, transmission block */     protected final int SENTIMOUT = 30;     /* timeout time in send */     protected final int SLEEP   = 30;       /* timeout time in recv */     /* Protocol characters used */     protected final byte    SOH = 1;    /* Start Of Header */     protected final byte    EOT = 4;    /* End Of Transmission */     protected final byte    ACK = 6;    /* ACKnowlege */     protected final byte    NAK = 0x15; /* Negative AcKnowlege */     protected InputStream inStream;     protected OutputStream outStream;     protected PrintWriter errStream;     /** Construct a TModem */     public TModem(InputStream is, OutputStream os, PrintWriter errs) {         inStream = is;         outStream = os;         errStream = errs;     }     /** Construct a TModem with default files (stdin and stdout). */     public TModem() {         inStream = System.in;         outStream = System.out;         errStream = new PrintWriter(System.err);     }     /** A main program, for direct invocation. */     public static void main(String[] argv) throws          IOException, InterruptedException {         /* argc must == 2, i.e., `java TModem -s filename' */         if (argv.length != 2)              usage();         if (argv[0].charAt(0) != '-')             usage();         TModem tm = new TModem();         tm.setStandalone(true);         boolean OK = false;         switch (argv[0].charAt(1)){         case 'r':              OK = tm.receive(argv[1]);              break;         case 's':              OK = tm.send(argv[1]);              break;         default:              usage();         }         System.out.print(OK?"Done OK":"Failed");         System.exit(0);     }     /* give user minimal usage message */     protected static void usage()     {         System.err.println("usage: TModem -r/-s file");         // not errStream, not die(), since this is static.         System.exit(1);     }     /** If we're in a standalone app it is OK to System.exit() */     protected boolean standalone = false;     public void setStandalone(boolean is) {         standalone = is;     }     public boolean isStandalone() {         return standalone;     }     /** A flag used to communicate with inner class IOTimer */     protected boolean gotChar;     /** An inner class to provide a read timeout for alarms. */     class IOTimer extends Thread {         String message;         long milliseconds;         /** Construct an IO Timer */         IOTimer(long sec, String mesg) {             milliseconds = 1000 * sec;             message = mesg;         }                  public void run() {           try {             Thread.sleep(milliseconds);           } catch (InterruptedException e) {             // can't happen           }           /** Implement the timer */           if (!gotChar)             errStream.println("Timed out waiting for " + message);             die(1);         }     }     /*      * send a file to the remote      */     public boolean send(String tfile) throws IOException, InterruptedException     {         char checksum, index, blocknumber, errorcount;         byte character;         byte[] sector = new byte[SECSIZE];         int nbytes;         DataInputStream foo;         foo = new DataInputStream(new FileInputStream(tfile));         errStream.println( "file open, ready to send");         errorcount = 0;         blocknumber = 1;         // The C version uses "alarm()", a UNIX-only system call,         // to detect if the read times out. Here we do detect it         // by using a Thread, the IOTimer class defined above.         gotChar = false;         new IOTimer(SENTIMOUT, "NAK to start send").start();         do {             character = getchar();             gotChar = true;             if (character != NAK && errorcount < MAXERRORS)                 ++errorcount;         } while (character != NAK && errorcount < MAXERRORS);         errStream.println( "transmission beginning");         if (errorcount == MAXERRORS) {             xerror();         }         while ((nbytes=inStream.read(sector))!=0) {             if (nbytes<SECSIZE)                 sector[nbytes]=CPMEOF;             errorcount = 0;             while (errorcount < MAXERRORS) {                 errStream.println( "{" + blocknumber + "} ");                 putchar(SOH);   /* here is our header */                 putchar(blocknumber);   /* the block number */                 putchar(~blocknumber);  /* & its complement */                 checksum = 0;                 for (index = 0; index < SECSIZE; index++) {                     putchar(sector[index]);                     checksum += sector[index];                 }                 putchar(checksum);  /* tell our checksum */                 if (getchar() != ACK)                     ++errorcount;                 else                     break;             }             if (errorcount == MAXERRORS)                 xerror();             ++blocknumber;         }         boolean isAck = false;         while (!isAck) {             putchar(EOT);             isAck = getchar() == ACK;         }         errStream.println( "Transmission complete.");         return true;     }     /*      * receive a file from the remote      */     public boolean receive(String tfile) throws IOException, InterruptedException     {         char checksum, index, blocknumber, errorcount;         byte character;         byte[] sector = new byte[SECSIZE];         DataOutputStream foo;         foo = new DataOutputStream(new FileOutputStream(tfile));         System.out.println("you have " + SLEEP + " seconds...");         /* wait for the user or remote to get his act together */         gotChar = false;         new IOTimer(SLEEP, "receive from remote").start();          errStream.println("Starting receive...");         putchar(NAK);         errorcount = 0;         blocknumber = 1;         rxLoop:         do {              character = getchar();             gotChar = true;             if (character != EOT) {                 try {                     byte not_ch;                     if (character != SOH) {                         errStream.println( "Not SOH");                         if (++errorcount < MAXERRORS)                             continue rxLoop;                         else                             xerror();                     }                     character = getchar();                     not_ch = (byte)(~getchar());                     errStream.println( "[" +  character + "] ");                     if (character != not_ch) {                         errStream.println( "Blockcounts not ~");                         ++errorcount;                         continue rxLoop;                     }                     if (character != blocknumber) {                         errStream.println( "Wrong blocknumber");                         ++errorcount;                         continue rxLoop;                     }                     checksum = 0;                     for (index = 0; index < SECSIZE; index++) {                         sector[index] = getchar();                         checksum += sector[index];                     }                     if (checksum != getchar()) {                         errStream.println( "Bad checksum");                         errorcount++;                         continue rxLoop;                     }                     putchar(ACK);                     blocknumber++;                     try {                         foo.write(sector);                     } catch (IOException e) {                         errStream.println("write failed, blocknumber " + blocknumber);                     }                 } finally {                 if (errorcount != 0)                     putchar(NAK);             }         }         } while (character != EOT);         foo.close();         putchar(ACK);   /* tell the other end we accepted his EOT   */         putchar(ACK);         putchar(ACK);         errStream.println("Receive Completed.");         return true;     }     protected byte getchar() throws IOException {         return (byte)inStream.read();     }     protected void putchar(int c) throws IOException {         outStream.write(c);     }     protected void xerror()     {         errStream.println("too many errors...aborting");         die(1);     }     protected void die(int how)     {         if (standalone)             System.exit(how);         else            System.out.println(("Error code " + how));     } }