source: tmcsimulator/trunk/src/tmcsim/paramicscommunicator/ParamicsCommunicator.java @ 52

Revision 52, 21.0 KB checked in by jdalbey, 10 years ago (diff)

ParamicsCommunicator?.java revised to simplify system testing

Line 
1package tmcsim.paramicscommunicator;
2
3import java.io.EOFException;
4import java.io.File;
5import java.io.FileInputStream;
6import java.io.FileOutputStream;
7import java.io.IOException;
8import java.io.ObjectInputStream;
9import java.io.ObjectOutputStream;
10import java.net.ServerSocket;
11import java.net.Socket;
12import java.net.SocketTimeoutException;
13import java.util.Observable;
14import java.util.Observer;
15import java.util.Properties;
16import java.util.TreeMap;
17import java.util.logging.Level;
18import java.util.logging.Logger;
19import javax.swing.JFrame;
20import javax.swing.JOptionPane;
21import javax.swing.UIManager;
22import org.w3c.dom.Document;
23import org.w3c.dom.Element;
24import tmcsim.common.CADProtocol.PARAMICS_ACTIONS;
25import tmcsim.common.CADProtocol.PARAMICS_COMM_TAGS;
26import tmcsim.paramicscommunicator.FileIOUpdate.IO_TYPE;
27import tmcsim.paramicscommunicator.FileRegUpdate.REG_TYPE;
28import tmcsim.paramicscommunicator.gui.ParamicsCommunicatorGUI;
29
30/**
31 * ParamicsCommunicator is the main class for this module. The Paramics
32 * Communicator is used to provide communication between the CAD Simulator and
33 * the Paramics traffic modeler. While the application is running, data is
34 * received on a socket from the CAD Simulator. Transmitted data are XML
35 * documents containing information and action commands. The CAD Simulator
36 * registers readers and writers with the ParamicsCommunicator. Any data read by
37 * a ParamicsReader is sent back to the CAD Simulator. All data to be written by
38 * a ParamicsWriter is received through the socket.<br><br>
39 * The properties file for the ParamicsCommunicator class contains the following
40 * data.<br>
41 * <code>
42 * -----------------------------------------------------------------------------<br>
43 * Socket Port The port number to use for socket communication.<br>
44 * Working Directory The working directory use for Paramics file
45 * communication.<br>
46 * Error File The target file to use for error logging.<br>
47 * -----------------------------------------------------------------------------<br>
48 * Example File: <br>
49 * SocketPort = 4450 <br>
50 * WorkingDirectory = c:\\tmc_simulator\\ <br>
51 * ErrorFile = sim_mgr_error.xml <br>
52 * -----------------------------------------------------------------------------<br>
53 * </code>
54 *
55 * @author Matthew Cechini (mcechini@calpoly.edu)
56 * @version $Date: 2009/04/17 16:27:46 $ $Revision: 1.7
57 */
58public class ParamicsCommunicator extends Observable implements Observer
59{
60
61    /**
62     * Error logger.
63     */
64    private static Logger paramLogger = Logger.getLogger("tmcsim.paramicscommunicator");
65
66    /**
67     * Enumeration containing property names.
68     *
69     * @author Matthew Cechini
70     */
71    private static enum PROPERTIES
72    {
73
74        SOCKET_PORT("SocketPort"),
75        WORKING_DIR("WorkingDirectory");
76        public String name;
77
78        private PROPERTIES(String n)
79        {
80            name = n;
81        }
82    }
83    /**
84     * Properties object.
85     */
86    private Properties paramicsCommProp = null;
87    /**
88     * Current working directory where files will be read and written
89     */
90    private String workingDirectory = null;
91    /**
92     * Socket used to create socket communication with the CAD Simulator.
93     */
94    private ServerSocket serverSocket = null;
95    /**
96     * Soccket used to communicate with CAD Simulator.
97     */
98    private Socket paramicsSocket = null;
99    /**
100     * Input Stream for reading data from the CAD Simulator.
101     */
102    private ObjectInputStream in = null;
103    /**
104     * Output Stream for writing data to the CAD Simulator.
105     */
106    private ObjectOutputStream out = null;
107    /**
108     * Map of all current ParamicsFileWriters referenced by I/O ID.
109     */
110    private TreeMap<String, ParamicsFileWriter> writers = null;
111    /**
112     * Map of all current ParamicsFileReaders referenced by I/O ID.
113     */
114    private TreeMap<String, ParamicsFileReader> readers = null;
115    /**
116     * The view class for the ParamicsCommunicator.
117     */
118    private static ParamicsCommunicatorGUI theGUI;
119    /**
120     * The thread used to initialize the socket connection with the CADSimulator
121     */
122    Thread initThread;
123
124    /**
125     * Constructor. Read in the property values. If the properties file does not
126     * contain a value for the working directory, open a dialog to prompt the
127     * user for the path of the Paramics working directory. An empty string is
128     * not accepted. A null signifies that the user pressed cancel. Prompt the
129     * user to accept the cancel and exit the application if confirmed. Continue
130     * until a valid directory has been entered, that exists, and append a '\'
131     * to the end of the directory if necessary.
132     *
133     * Initialize the Sockets and begin communication.
134     *
135     * @param propertiesFilePath File Path of ParamicsCommunicator properties
136     * file.
137     */
138    public ParamicsCommunicator(String propertiesFile)
139    {
140        writers = new TreeMap<String, ParamicsFileWriter>();
141        readers = new TreeMap<String, ParamicsFileReader>();
142
143        try
144        {
145            paramicsCommProp = new Properties();
146            paramicsCommProp.load(new FileInputStream(propertiesFile));
147
148            if (paramicsCommProp.getProperty(PROPERTIES.SOCKET_PORT.name) == null)
149            {
150                JOptionPane.showMessageDialog(theGUI,
151                        "Properties file missing CAD Simulator Port information.",
152                        "Invalid Configuration", JOptionPane.ERROR_MESSAGE);
153                System.exit(0);
154            }
155            else if (paramicsCommProp.getProperty(PROPERTIES.WORKING_DIR.name) == null
156                    || paramicsCommProp.getProperty(PROPERTIES.WORKING_DIR.name).length() == 0)
157            {
158
159                try
160                {
161                    String workingDir = null;
162
163                    while (workingDir == null || workingDir.length() == 0)
164                    {
165                        workingDir = JOptionPane.showInputDialog(null,
166                                "Please set the output directory for Paramics communication.",
167                                "Paramics Working Directory", JOptionPane.QUESTION_MESSAGE);
168
169                        if (workingDir == null)
170                        {
171                        }
172                        else if (!new File(workingDir).exists())
173                        {
174                            JOptionPane.showMessageDialog(null,
175                                    "Directory does not exist.",
176                                    "Invalid Working Directory", JOptionPane.WARNING_MESSAGE);
177
178                            workingDir = null;
179                        }
180                        else if (!new File(workingDir).isDirectory())
181                        {
182                            JOptionPane.showMessageDialog(null,
183                                    workingDir + " is not a directory.",
184                                    "Invalid Working Directory", JOptionPane.WARNING_MESSAGE);
185
186                            workingDir = null;
187                        }
188                    }
189
190                    if (workingDir.lastIndexOf("\\") != workingDir.length() - 1)
191                    {
192                        workingDir = workingDir + "\\";
193                    }
194
195                    paramicsCommProp.setProperty(PROPERTIES.WORKING_DIR.name, workingDir);
196                    paramicsCommProp.store(new FileOutputStream(propertiesFile), "");
197                } catch (IOException ioe)
198                {
199                    paramLogger.logp(Level.SEVERE, "ParamicsCommunicator", "Constructor",
200                            "Exception in writing properties file.", ioe);
201                }
202
203            }
204
205            workingDirectory = paramicsCommProp.getProperty(
206                    PROPERTIES.WORKING_DIR.name).trim();
207
208        } catch (Exception e)
209        {
210            paramLogger.logp(Level.SEVERE, "ParamicsCommunicator", "Constructor",
211                    "Exception in reading properties file.", e);
212        }
213
214        Integer desiredPort = Integer.parseInt(paramicsCommProp.getProperty(
215                PROPERTIES.SOCKET_PORT.name).trim());
216        /* Start a thread to initialize the sockets that talk to CADsimulator */
217        initThread = new Thread(new SocketStarter(desiredPort));
218        initThread.start();
219    }
220
221    /**
222     * For testing, we want to be able to provide a non-visible instance of the
223     * GUI to be used. (As main won't be called).
224     *
225     * @param viewer
226     */
227    public void setGUI(ParamicsCommunicatorGUI viewer)
228    {
229        theGUI = viewer;
230        addObserver(theGUI);
231    }
232
233    /**
234     * Start the thread that does the main work of the communicator. This method
235     * waits until the initial thread has opened the needed sockets to connect
236     * to the CADSimulator, then it starts the socket reader thread, which runs
237     * until the application is terminated. Usage: Normally this method is
238     * called immediately after constructing this object, but for testing
239     * purposes it is available to be invoked by the test harness after the
240     * Simulation Manager initiates a connection.
241     */
242    public void startReading()
243    {
244        while (initThread.isAlive())
245        {
246            try
247            {
248                Thread.sleep(1000);
249            } catch (InterruptedException ex)
250            {
251                paramLogger.logp(Level.SEVERE, "ParamicsCommunicator", "runstarter",
252                        "Sleep interrupted.");
253            }
254        }
255        Runnable sr = new SocketReader();
256        new Thread(sr).start();
257    }
258
259    /**
260     * Transmits a message XML document object to the CAD Simulator.
261     *
262     * @param mess The ParamicsCommMessage to be transmitted.
263     */
264    private void write(Document mess)
265    {
266
267        synchronized (paramicsSocket)
268        {
269            try
270            {
271                out.writeObject(mess);
272                out.flush();
273            } catch (Exception e)
274            {
275                paramLogger.logp(Level.SEVERE, "ParamicsCommunicator", "write",
276                        "Exception in writing to the socket.", e);
277            }
278        }
279    }
280
281    /**
282     * Observer/Observable update method. The Paramics Communicator observers
283     * registered ParamicsReaders. When messages are to be sent, they are sent
284     * through this method. All messages are ParamicsCommMessage objects. Send
285     * these messages to the write() method for transmission on the socket.
286     */
287    public void update(Observable o, Object arg)
288    {
289
290        if (arg instanceof Document)
291        {
292            write((Document) arg);
293        }
294    }
295
296    private class SocketReader implements Runnable
297    {
298
299        /**
300         * Runnable method. While this thread is not interrupted, read in an
301         * object from the socket input stream. If an object exists, call
302         * doMessage() to parse and perform the received action in the message.
303         */
304        public void run()
305        {
306
307            while (true)
308            {
309                try
310                {
311                    doMessage((Document) in.readObject());
312                } catch (SocketTimeoutException ste)
313                {
314                    //just try again
315                } catch (EOFException eofe)
316                {
317                    paramLogger.logp(Level.SEVERE, "ParamicsCommunicator",
318                            "run", "EOF Exception in reading data from the socket.", eofe);
319                } catch (Exception e)
320                {
321                    paramLogger.logp(Level.SEVERE, "ParamicsCommunicator",
322                            "run", "Exception in reading data from the socket.", e);
323
324                    JOptionPane.showMessageDialog(theGUI,
325                            "Connection has been lost to the CAD Simulator.  "
326                            + "Paramics Communicator will now shutdown.",
327                            "Dropped Connection", JOptionPane.ERROR_MESSAGE);
328                    break;
329                }
330                try
331                {
332                    // Sleep for a second while waiting in this infinite loop
333                    // to yield to other threads run by test harness.
334                    Thread.sleep(1000);
335                } catch (InterruptedException ex)
336                {
337                    paramLogger.logp(Level.INFO, "ParamicsCommunicator",
338                            "run", "Exception in reading data from the socket.", ex);
339                }
340            }
341
342
343            try
344            {
345                in.close();
346            } catch (Exception e)
347            {
348            }
349            try
350            {
351                out.close();
352            } catch (Exception e)
353            {
354            }
355            try
356            {
357                serverSocket.close();
358            } catch (Exception e)
359            {
360            }
361            try
362            {
363                paramicsSocket.close();
364            } catch (Exception e)
365            {
366            }
367
368        }
369    }
370
371    /**
372     * Perform the action represented in the received XML document message.
373     * First determine if the action is from a READER, WRITER, and RESET. If the
374     * paramics action is REGISTER, add a new ParamicsFileReader/Writer to the
375     * local list of readers/writers and update the GUI with a FileRegUpdate
376     * object. If the paramics action is UNREGISTER, remove the
377     * ParamicsFileReader/Writer from the local list of readers/writers and
378     * update the GUI with a FileRegUpdate object. If RESET is received, clear
379     * all readers and writers.
380     *
381     * @param mess Received XML document message.
382     */
383    private void doMessage(Document mess)
384    {
385
386        Element rootElement = mess.getDocumentElement();
387
388        String id = null;
389        String action = null;
390
391        switch (PARAMICS_COMM_TAGS.fromString(rootElement.getNodeName()))
392        {
393            case READER:
394                id = rootElement.getAttribute(PARAMICS_COMM_TAGS.ID.tag);
395                action = rootElement.getAttribute(PARAMICS_COMM_TAGS.ACTION.tag);
396
397                switch (PARAMICS_ACTIONS.fromString(action))
398                {
399                    case REGISTER:
400                        Integer interval = Integer.parseInt(rootElement.getChildNodes().item(0).getTextContent());
401                        String targetFile = rootElement.getChildNodes().item(1).getTextContent();
402
403                        readers.put(id, new ParamicsFileReader(workingDirectory, id,
404                                interval, targetFile));
405                        readers.get(id).addObserver(this);
406                        readers.get(id).addObserver(theGUI);
407
408                        setChanged();
409                        notifyObservers(new FileRegUpdate(IO_TYPE.READ,
410                                REG_TYPE.REGISTER, id, targetFile, interval));
411                        break;
412                    case UNREGISTER:
413                        readers.get(id).deleteObserver(this);
414                        readers.get(id).deleteObserver(theGUI);
415                        readers.remove(id);
416
417                        setChanged();
418                        notifyObservers(new FileRegUpdate(IO_TYPE.READ,
419                                REG_TYPE.UNREGISTER, id, null, null));
420                        break;
421                }
422                break;
423            case WRITER:
424                id = rootElement.getAttribute(PARAMICS_COMM_TAGS.ID.tag);
425                action = rootElement.getAttribute(PARAMICS_COMM_TAGS.ACTION.tag);
426
427                switch (PARAMICS_ACTIONS.fromString(action))
428                {
429                    case REGISTER:
430                        String targetFile = rootElement.getChildNodes().item(0).getTextContent();
431
432                        writers.put(id, new ParamicsFileWriter(id,
433                                workingDirectory, targetFile));
434                        writers.get(id).addObserver(theGUI);
435
436                        setChanged();
437                        notifyObservers(new FileRegUpdate(IO_TYPE.WRITE,
438                                REG_TYPE.REGISTER, id, targetFile, null));
439                        break;
440                    case UNREGISTER:
441                        writers.remove(id);
442
443                        writers.get(id).deleteObserver(theGUI);
444
445                        setChanged();
446                        notifyObservers(new FileRegUpdate(IO_TYPE.WRITE,
447                                REG_TYPE.UNREGISTER, id, null, null));
448                        break;
449                    case WRITE_FILE:
450                        writers.get(id).writeMessage((Element) rootElement.getChildNodes().item(0));
451                        break;
452                }
453                break;
454            case RESET:
455                readers.clear();
456                writers.clear();
457                break;
458        }
459    }
460
461    private class SocketStarter implements Runnable
462    {
463
464        Integer socketPort;
465
466        /**
467         * Method waits to accept a socket connection from the CAD Simulator.
468         * When a connection has been established the method exits. The input
469         * and output streams are created on the new socket.
470         *
471         * @param socketPort Socket port to use for establishing Socket
472         * communication.
473         * @throws IOException if there is an exception in establishing Socket
474         * communication.
475         */
476        //private void initializeSockets(Integer socketPort) throws IOException
477        public SocketStarter(Integer socketPort)
478        {
479            this.socketPort = socketPort;
480        }
481
482        public void run()
483        {
484
485            boolean waiting = true;
486
487            try
488            {
489                serverSocket = new ServerSocket(socketPort);
490                //delay for accept timeout(milliseconds)
491                serverSocket.setSoTimeout(10 * 1000);
492            } catch (IOException ioe)
493            {
494                paramLogger.logp(Level.SEVERE, "ParamicsCommunicator",
495                        "initializeSockets", "Exception in creating "
496                        + "the server socket on port " + socketPort + ", terminating.");
497                System.exit(1);
498            }
499
500            while (waiting)
501            {
502                try
503                {
504                    paramicsSocket = serverSocket.accept();
505                    waiting = false;
506                } catch (SocketTimeoutException ste)
507                {
508                    System.out.println("...waiting...");
509                    try
510                    {
511                        Thread.sleep(2000);
512                        paramLogger.logp(Level.INFO, "ParamicsCommunicator",
513                                "initializeSockets", "sleeping.");
514                    } catch (InterruptedException ex)
515                    {
516                        paramLogger.logp(Level.INFO, "ParamicsCommunicator",
517                                "initializeSockets", "Exception exiting for socket.", ex);
518                    }
519                } catch (IOException ioe)
520                {
521                    paramLogger.logp(Level.SEVERE, "ParamicsCommunicator",
522                            "initializeSockets", "Exception in creating "
523                            + "the receiving socket on port " + socketPort + ", terminating.");
524                    System.exit(1);
525                }
526
527            }
528
529
530            //** out must be performed before in to unlock for connecting socket **//
531            try
532            {
533                out = new ObjectOutputStream(paramicsSocket.getOutputStream());
534                in = new ObjectInputStream(paramicsSocket.getInputStream());
535            } catch (IOException ioe)
536            {
537                paramLogger.logp(Level.SEVERE, "ParamicsCommunicator",
538                        "initializeSockets", "Exception in creating "
539                        + "input and output streams on socket, terminating.");
540                System.exit(1);
541            }
542
543        }
544    }
545
546    /**
547     * Construct the ParamicsCommunicator with the properties file path, either
548     * from the command line arguments or default.
549     *
550     * @param args Command line arguments.
551     */
552    public static void main(String[] args)
553    {
554        System.setProperty("PARAMICS_COMM_PROPERTIES", "config/paramics_communicator_config.properties");
555
556        try
557        {
558            if (System.getProperty("PARAMICS_COMM_PROPERTIES") != null)
559            {
560                ParamicsCommunicator pc = new ParamicsCommunicator(System.getProperty("PARAMICS_COMM_PROPERTIES"));
561                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
562                theGUI = new ParamicsCommunicatorGUI();
563                pc.addObserver(theGUI);
564                theGUI.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
565                theGUI.setVisible(true);
566                pc.startReading();
567
568            }
569            else
570            {
571                throw new Exception("PARAMICS_COMM_PROPERTIES system property not defined.");
572            }
573        } catch (Exception e)
574        {
575            paramLogger.logp(Level.SEVERE, "ParamicsCommunicator", "Main",
576                    "Error occured initializing application", e);
577
578            JOptionPane.showMessageDialog(null, e.getMessage(),
579                    "Error - Program Exiting", JOptionPane.ERROR_MESSAGE);
580
581            System.exit(-1);
582        }
583
584
585    }
586}
Note: See TracBrowser for help on using the repository browser.