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

Revision 33, 17.8 KB checked in by bokumura, 10 years ago (diff)

Directory Restructure: All system properties have been deprecated and replaced by the system property: "CONFIG_DIR". If CONFIG_DIR is not specified at run time, it will default to the "config/" directory.

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