source: tmcsimulator/trunk/src/tmcsim/client/CADClient.java @ 532

Revision 532, 22.3 KB checked in by jdalbey, 6 years ago (diff)

Login.java, CADClient.java: Implement #206, login screen has drop down box of student names read from a config file.

Line 
1package tmcsim.client;
2
3import java.awt.BorderLayout;
4import java.awt.Color;
5import java.awt.Dialog;
6import java.awt.Font;
7import java.awt.GraphicsEnvironment;
8import java.awt.Point;
9import java.awt.event.WindowEvent;
10import java.awt.event.WindowListener;
11import java.awt.image.BufferedImage;
12import java.io.File;
13import java.io.FileInputStream;
14import java.io.FileOutputStream;
15import java.io.IOException;
16import java.rmi.Naming;
17import java.rmi.RemoteException;
18import java.rmi.server.UnicastRemoteObject;
19import java.util.Properties;
20import java.util.Vector;
21import java.util.logging.Level;
22import java.util.logging.Logger;
23import javax.imageio.ImageIO;
24import javax.swing.ImageIcon;
25import javax.swing.JDialog;
26
27import javax.swing.JFrame;
28import javax.swing.JLabel;
29import javax.swing.JOptionPane;
30import javax.swing.JWindow;
31import javax.swing.SwingConstants;
32import javax.swing.UIManager;
33
34import tmcsim.client.cadclientgui.CADClientGUI;
35import tmcsim.client.cadclientgui.CardfileReader;
36import tmcsim.client.cadclientgui.GUIScriptReader;
37import tmcsim.client.cadclientgui.data.CADData;
38import tmcsim.client.cadclientgui.screens.Login;
39import tmcsim.client.cadclientgui.screens.ScreenManager;
40import tmcsim.common.CADEnums;
41import tmcsim.common.SimulationException;
42import tmcsim.common.RevisionNumber;
43import tmcsim.interfaces.CADClientInterface;
44import tmcsim.interfaces.CoordinatorInterface;
45
46/**
47 * CADClient is the main class for the CAD Client application. The main method
48 * instantiates an instance of the CADClient object with the default properties
49 * file "..\config\CADClient.properties" or the first argument fom the command
50 * line invocation. Properties data values are used to bind socket communication
51 * between the CAD Client and the CAD Simulator. The CADClientModel object is
52 * instantiated and the CAD Client registers itself with the CAD Simulator.
53 * Finally, the CADClientView is initialized, the model-view and observer
54 * relationships are established, and the view is shown.<br>
55 * <br>
56 * The properties file contains the following data: <br>
57 * <code>
58 * -----------------------------------------------------------------------------<br>
59 * Host Name     The host name where the CAD Simulator is located.<br>
60 * Port Number   The port number that the CAD Simulator is bound on.<br>
61 * CAD Position  The integer (>= 0) position for this CAD Client.<br>
62 * CAD User ID   The unique user id for this CAD Client.<br>
63 * Error File    Filename of error logging file.<br>
64 * -----------------------------------------------------------------------------<br>
65 * Example File: <br>
66 * CADSimulatorHost       = localhost<br>
67 * CADSimulatorSocketPort = 4444<br>
68 * CADPosition = 1 <br>
69 * CADUserID   = A12345<br>
70 * ErrorFile   = cad_client_err.txt<br>
71 * </code>
72 *
73 * @author Matthew Cechini (mcechini@calpoly.edu)
74 * @version $Date: 2009/04/17 16:27:47 $ $Revision: 1.8 $
75 */
76
77public class CADClient extends UnicastRemoteObject implements
78        CADClientInterface {
79
80    /** Error logger. */
81    private static Logger cadClientLogger = Logger.getLogger("tmcsim.client");
82
83    /**
84     * Enumeration containing properties name values. See CADClient class
85     * description for more information.
86     *
87     * @author Matthew Cechini
88     * @see CADClient
89     */
90    private static enum PROPERTIES {
91        CAD_SIM_HOST("CADSimulatorHost"), CAD_SIM_PORT("CADSimulatorSocketPort"), CAD_RMI_PORT(
92                "CADRmiPort"), CLIENT_CAD_POS("CADPosition"), CLIENT_USER_ID(
93                "CADUserID"), KEYBOARD_TYPE("KeyboardType"), DISPLAY_TYPE(
94                "DisplayType"), STUDENT_NAMES_FILE("StudentNamesFile");
95
96        public String name;
97
98        private PROPERTIES(String n) {
99            name = n;
100        }
101    }
102
103    /**
104     * CADClientSocket Object to handle socket communication between the Client
105     * and CAD Simulator.
106     */
107    private CADClientSocket theClientSocket;
108
109    /** Instance of the CADClientModel. */
110    private CADClientModel theClientScreenModel;
111
112    /** Instance of the CADClientView. */
113    private CADClientView theClientScreenView;
114
115    /**
116     * Instance of the CADCLientGUI Replaces CADClientView
117     */
118    private CADClientGUI theClientGUI;
119
120    /** Properties object for the CADClient class. */
121    private Properties cadClientProp;
122
123    /** RMI interface for communication with the remote Coordinator. */
124    private static CoordinatorInterface theCoorInt;
125
126    /** reference to itself to be used for disconnecting from CADSimulator */
127    private CADClientInterface client = this;
128
129    private static final String CONFIG_FILE_NAME = "cad_client_config.properties";
130   
131    /**
132     * Constructor. Initialize data from parsed properties file. Create a socket
133     * connection to the CADSimulator. The ClientScreenModel is initialized with
134     * the input and output I/O streams for socket communication. The
135     * ClientScreenModel registers with the CAD Simulator, using CAD position
136     * and userID read in from the properties file. The ClientScreenView is then
137     * created and initialized and set as an observer of the model.
138     *
139     * A thread is created with the runnable ClientScreenModel and is started.
140     * When this thread is no longer alive, or the ClientScrenView and
141     * CADClientSocket are closed. The program then exits.
142     *
143     * @param propertiesFile
144     *            File path (absolute or relative) to the properties file
145     *            containing configuration data.
146     */
147    public CADClient(String propertiesFile) throws SimulationException,
148            RemoteException {
149        if (!verifyProperties(propertiesFile))
150            System.exit(0);
151        // Display a splash screen. Ticket #199.
152        JDialog dlg = createSplashScreen();
153        dlg.setVisible(true);
154       
155        connect(cadClientProp.getProperty(PROPERTIES.CAD_SIM_HOST.name).trim(),
156                cadClientProp.getProperty(PROPERTIES.CAD_RMI_PORT.name).trim());
157
158        // Instantiate the Socket and Model Objects.
159        theClientSocket = new CADClientSocket(cadClientProp.getProperty(
160                PROPERTIES.CAD_SIM_HOST.name).trim(),
161                Integer.parseInt(cadClientProp.getProperty(
162                        PROPERTIES.CAD_SIM_PORT.name).trim()));
163        theClientScreenModel = new CADClientModel();
164        theClientScreenModel.initializeScreen(theClientSocket.getInputStream(),
165                theClientSocket.getOutputStream());
166
167        // Register this CAD Client with the Simulation Manager
168        theClientScreenModel.register(Integer.parseInt(cadClientProp
169                .getProperty(PROPERTIES.CLIENT_CAD_POS.name)), cadClientProp
170                .getProperty(PROPERTIES.CLIENT_USER_ID.name));
171
172        // Instantiate the CADScreenView and set up the model-view observer
173        // relationship.
174        theClientScreenView = new CADClientView(theClientScreenModel);
175        theClientScreenView.setVisible(false);
176
177        // TODO: set up model-view relationship similar to ClientView and
178        // ScreenView
179        // Can repurpose the old model, but may be better to copy over and
180        // modify in parallel
181        // This is required to perform powerline commands on the data
182        theClientGUI = new CADClientGUI();
183
184        // Each screen of the UI should have a reference to either it's parent
185        // object or the main client
186        // This ensures they all have access to each other and the data model
187        theClientGUI.screen = new ScreenManager(theCoorInt);
188        theClientGUI.login = new Login(cadClientProp.getProperty(PROPERTIES.STUDENT_NAMES_FILE.name).trim());
189        theClientGUI.client = this;
190
191        // setup keyboard settings for CAD Client
192        if (cadClientProp.getProperty(PROPERTIES.KEYBOARD_TYPE.name).trim()
193                .equals("CAD")) {
194            CADEnums.CAD_KEYS.setupCADKeyboard();
195        }
196        // STD
197        else {
198            CADEnums.CAD_KEYS.setupStandardKeyboard();
199        }
200
201        theClientScreenModel.addObserver(theClientScreenView);
202
203        // Initialize the display
204        if (cadClientProp.getProperty(PROPERTIES.DISPLAY_TYPE.name).equals(
205                "FULL_SCREEN")) {
206
207            theClientScreenView.addWindowListener(new WindowListener() {
208                public void windowClosed(WindowEvent e) {
209                }
210
211                public void windowOpened(WindowEvent e) {
212                }
213
214                public void windowIconified(WindowEvent e) {
215                }
216
217                public void windowDeiconified(WindowEvent e) {
218                }
219
220                public void windowActivated(WindowEvent e) {
221                }
222
223                public void windowDeactivated(WindowEvent e) {
224                }
225
226                public void windowClosing(WindowEvent e) {
227
228                    try {
229                        theClientSocket.closeSocket();
230                    } catch (SimulationException se) {
231                    }
232
233                    System.exit(0);
234                }
235            });
236            // Remove the splash screen.
237            dlg.dispose();
238           
239            theClientScreenView.initWindow();
240            theClientScreenView.setVisible(false);
241        } else {
242            JFrame cadFrame = new JFrame("CAD Client");
243            cadFrame.add(theClientScreenView.initBox());
244            cadFrame.setSize(800, 600);
245
246            cadFrame.addWindowListener(new WindowListener() {
247                public void windowClosed(WindowEvent e) {
248                }
249
250                public void windowOpened(WindowEvent e) {
251                }
252
253                public void windowIconified(WindowEvent e) {
254                }
255
256                public void windowDeiconified(WindowEvent e) {
257                }
258
259                public void windowActivated(WindowEvent e) {
260                }
261
262                public void windowDeactivated(WindowEvent e) {
263                }
264
265                public void windowClosing(WindowEvent e) {
266
267                    try {
268                        theClientSocket.closeSocket();
269                    } catch (SimulationException se) {
270                    }
271
272                    System.exit(0);
273                }
274            });
275            cadFrame.setVisible(true);
276        }
277
278        // Create the CAD Client thread to run the CADClientModel Object.
279        Thread clientThread = new Thread(theClientScreenModel);
280       
281        // Log that everything started okay
282        clientThread.start();
283            cadClientLogger.logp(Level.INFO, "CAD Client",
284                    "Constructor",
285                    "Initialization complete.");
286
287        ensureProperShutdown();
288    }
289
290    /**
291     * Connect to the Coordinator's RMI object, and register this object for
292     * callback with the Coordinator.
293     *
294     * @param hostname
295     *            Host name of the CAD Simulator.
296     * @param portNumber
297     *            Port number of the CAD Simulator RMI communication.
298     * @throws SimulationException
299     *             if there is an error creating the RMI connection.
300     */
301    protected void connect(String hostname, String portNumber)
302            throws SimulationException {
303
304        String coorIntURL = "";
305
306        try {
307            coorIntURL = "rmi://" + hostname + ":" + portNumber
308                    + "/coordinator";
309            theCoorInt = (CoordinatorInterface) Naming.lookup(coorIntURL);
310            theCoorInt.registerForCallback(this);
311        } catch (Exception e) {
312            throw new SimulationException(SimulationException.CAD_SIM_CONNECT,hostname,
313                    e);
314        }
315    }
316
317    /**
318     * This method verifies that the CAD Simulator Host and Port values are not
319     * null. Also, if a CAD Position or User ID do not exist in the properties
320     * file, the user is prompted to enter values. These values are written to
321     * the properties file. If the user cancels the process of entering these
322     * values, the verification fails.
323     *
324     * @param propertiesFile
325     *            File path (absolute or relative) to the properties file
326     *            containing configuration data.
327     * @return True if the properties file is valid, false if not.
328     * @throws SimulationException
329     *             if there is an exception in verifying the properties file, or
330     *             if the user cancels input.
331     */
332    private boolean verifyProperties(String propertiesFile)
333            throws SimulationException {
334
335        // Load the properties file.
336        try {
337            cadClientProp = new Properties();
338            cadClientProp.load(new FileInputStream(propertiesFile));
339        } catch (Exception e) {
340            cadClientLogger.logp(Level.SEVERE, "SimulationManager",
341                    "Constructor", "Exception in reading properties file.", e);
342
343            throw new SimulationException(SimulationException.INITIALIZE_ERROR,
344                    e);
345        }
346       
347
348        // Ensure that the properties file does not have null values for the
349        // CAD Simulator's connection information.
350        if (cadClientProp.getProperty(PROPERTIES.CAD_SIM_HOST.name) == null
351                || cadClientProp.getProperty(PROPERTIES.CAD_SIM_PORT.name) == null
352                || cadClientProp.getProperty(PROPERTIES.STUDENT_NAMES_FILE.name) == null) {
353            cadClientLogger.logp(Level.SEVERE, "SimulationManager",
354                    "Constructor", "Null value in properties file.");
355            throw new SimulationException(SimulationException.INITIALIZE_ERROR);
356        }
357
358        try {
359            // If the properties file does not specify a CAD position, prompt
360            // the
361            // user to select one. If the user selects a position, write the
362            // new properties values to the file. If the user cancels, else
363            // throw an exception.
364            if (cadClientProp.getProperty(PROPERTIES.CLIENT_CAD_POS.name) == null) {
365                if (getCADPosition())
366                    cadClientProp.store(new FileOutputStream(propertiesFile),
367                            "");
368                else
369                    throw new SimulationException(
370                            SimulationException.INITIALIZE_ERROR);
371            }
372
373            // If the properties file does not specifiy a CAD User ID, prompt
374            // the
375            // user to enter a value. If the user enters a valid ID, write the
376            // new properties values to the file. If the user cancels, else
377            // throw an exception.
378            if (cadClientProp.getProperty(PROPERTIES.CLIENT_USER_ID.name) == null) {
379                if (getUserID())
380                    cadClientProp.store(new FileOutputStream(propertiesFile),
381                            "");
382                else
383                    throw new SimulationException(
384                            SimulationException.INITIALIZE_ERROR);
385            }
386        } catch (IOException ioe) {
387            cadClientLogger.logp(Level.SEVERE, "SimulationManager",
388                    "Constructor",
389                    "Exception in writing to the properties file.");
390            throw new SimulationException(SimulationException.INITIALIZE_ERROR);
391        }
392
393        // Ensure that the properties file has a valid display type
394        if (cadClientProp.getProperty(PROPERTIES.DISPLAY_TYPE.name) == null
395                || (!cadClientProp.getProperty(PROPERTIES.DISPLAY_TYPE.name)
396                        .equals("FULL_SCREEN") && !cadClientProp.getProperty(
397                        PROPERTIES.DISPLAY_TYPE.name).equals("FRAME"))) {
398            cadClientLogger.logp(Level.SEVERE, "SimulationManager",
399                    "Constructor", "Invalid display type.");
400            throw new SimulationException(SimulationException.INITIALIZE_ERROR);
401        }
402
403        return true;
404    }
405
406    /**
407     * This method prompts the user to select a value for the CAD position. If
408     * the user cancels the method returns false, else the Properties object is
409     * updated and true is returned.
410     *
411     * @return True if the user successfully selected a CAD position, false if
412     *         not.
413     */
414    private boolean getCADPosition() {
415
416        Vector<Integer> positions = new Vector<Integer>();
417        for (int i = 0; i < 10; i++)
418            positions.add(i);
419
420        Object cadPos = null;
421
422        while (true) {
423            cadPos = JOptionPane.showInputDialog(null,
424                    "Please assign this workstation a CAD position number.",
425                    "CAD Position Asignment", JOptionPane.QUESTION_MESSAGE,
426                    null, positions.toArray(), positions.get(0));
427
428            // If the user pressed cancel, confirm the exit and return false.
429            if (cadPos == null) {
430                if (JOptionPane
431                        .showConfirmDialog(
432                                null,
433                                "CAD Client cannot load until a valid CAD "
434                                        + "position has been selected.  Do you wish to "
435                                        + "cancel loading the CAD Client?",
436                                "Confirm Exit", JOptionPane.YES_NO_OPTION,
437                                JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION)
438                    return false;
439            }
440            // Else the user selected a CAD position, exit the loop.
441            else {
442                break;
443            }
444        }
445
446        cadClientProp.setProperty(PROPERTIES.CLIENT_CAD_POS.name,
447                cadPos.toString());
448        return true;
449    }
450
451    /**
452     * This method prompts the user to enter a 5-character User ID. If the user
453     * cancels the method returns false, else the Properties object is updated
454     * and true is returned.
455     *
456     * @return True if the user successfully selected a CAD position, false if
457     *         not.
458     */
459    private boolean getUserID() {
460        String cadUID = null;
461
462        while (true) {
463            cadUID = JOptionPane.showInputDialog(null,
464                    "Please assign this workstation a 6-character CAD "
465                            + "User ID.", "CAD User ID Asignment",
466                    JOptionPane.QUESTION_MESSAGE);
467
468            // /If the user pressed cancel, confirm the exit and return false.
469            if (cadUID == null) {
470                if (JOptionPane.showConfirmDialog(null,
471                        "CAD Client cannot load until a valid User ID "
472                                + "has been entered.  Do you wish to "
473                                + "cancel loading the CAD Client?",
474                        "Confirm Exit", JOptionPane.YES_NO_OPTION,
475                        JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION)
476                    return false;
477            }
478            // If the user does not enter a valid User ID, notify and reprompt.
479            else if (cadUID.length() != 6) {
480                JOptionPane.showMessageDialog(null,
481                        "The User ID must be 6 characters.", "Invalid User ID",
482                        JOptionPane.WARNING_MESSAGE);
483            }
484            // Else the user entered a valid value, exit the loop.
485            else {
486                break;
487            }
488        }
489
490        cadClientProp.setProperty(PROPERTIES.CLIENT_USER_ID.name, cadUID);
491        return true;
492    }
493
494    /**
495     * Construct the CADClient with the properties file path, either from the
496     * command line arguments or default.
497     *
498     * @param args
499     *            Command line arguments.
500     */
501    public static void main(String[] args) {
502        System.out.println("CAD Client " + RevisionNumber.getAppVersion());
503        if(System.getProperty("CONFIG_DIR") == null){
504                System.setProperty("CONFIG_DIR", "config");
505        }
506
507        try {
508            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
509            new CADClient(System.getProperty("CONFIG_DIR") + System.getProperty("file.separator") + CONFIG_FILE_NAME);
510         
511        } catch (Exception e) {
512            cadClientLogger.logp(Level.SEVERE, "SimulationManager", "Main",
513                    "Error initializing application.");
514
515            JOptionPane.showMessageDialog(new JWindow(), e.getMessage(),
516                    "Error - Program Exiting", JOptionPane.ERROR_MESSAGE);
517
518            System.exit(-1);
519        }
520
521    }
522   
523    /** Create a Splash Screen that displays a "loading" message.
524     *  Implements ticket #199
525     * @return a JDialog with a "loading" message.
526     */
527    private JDialog createSplashScreen()
528    {
529        final int dialogSize = 268;  // desired width and height of dialog
530        // create the dialog
531        JDialog dlg = new JDialog(null,"VisiCAD loading",Dialog.ModalityType.MODELESS);
532        // make the dialog background a color similar to the login screen background
533        dlg.getContentPane().setBackground(new Color(78, 162, 210));
534        // create the text message to be displayed
535        JLabel dlgMsg = new JLabel("Just a moment ...", SwingConstants.CENTER); 
536        // configure the font and color of the text message
537        dlgMsg.setFont(new Font("Arial", Font.PLAIN, 22));
538        dlgMsg.setForeground(Color.white);
539        // Add the msg to the center of the dialog
540        dlg.add(dlgMsg,BorderLayout.CENTER); 
541        // Add TriTech banner to top of screen
542        try {
543            BufferedImage myPicture = ImageIO.read(new File("images/CADMenuImages/Tritech.png"));
544            JLabel picLabel = new JLabel(new ImageIcon(myPicture));
545            dlg.add(picLabel, BorderLayout.NORTH);
546        } catch (IOException ex) {
547            Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
548        }       
549        // Set how big we want the dialog to be
550        dlg.setSize(dialogSize, dialogSize);
551        // Calculate where to place the dialog on the screen
552        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
553        Point centerPoint = ge.getCenterPoint();
554        int dx = centerPoint.x - dialogSize/2;
555        int dy = centerPoint.y - dialogSize/2;
556        dlg.setLocation(dx, dy);
557        // return the completely built dialog
558        return dlg;
559    }
560   
561    public void refresh() {
562        theClientGUI.screen.refreshScreens();
563    }
564
565    public void ensureProperShutdown() {
566        Runtime.getRuntime().addShutdownHook(new Thread() {
567            public void run() {
568                try {
569                    theCoorInt.unregisterForCallback(client);
570                } catch (RemoteException e) {
571                    cadClientLogger.logp(Level.WARNING, "CADClient.java", "Main",
572                    "Error unregistering in ensureProperShutdown().");
573                }
574            }
575        });
576    }
577
578}
Note: See TracBrowser for help on using the repository browser.